2010-12-16 13 views
1

NTSC信号処理で画像をフィルタリングするC++で書かれたライブラリが見つかりました。あなたはここにそれを見ることができます:http://slack.net/~ant/libs/ntsc.html#nes_ntsc目的は、テレビにNESによって出力されたように画像を見せることです。C#画像データを複雑にする - 複雑なシナリオ

ライブラリのSNESバージョンをC#でラップしたい(実際にはNESバージョンを使用しますが、NTSCパレットデータのみで動作し、ビットマップは使用しません)。数時間コードを再生した後、敗北を認めて、皆さんに助けを求めてください。

ライブラリのC++コードを次に示します。私はdllexportsを追加しました。

/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown 
in parenthesis and should remain fairly stable in future versions. */ 
typedef struct snes_ntsc_setup_t 
{ 
    /* Basic parameters */ 
    double hue;  /* -1 = -180 degrees  +1 = +180 degrees */ 
    double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ 
    double contrast; /* -1 = dark (0.5)  +1 = light (1.5) */ 
    double brightness; /* -1 = dark (0.5)  +1 = light (1.5) */ 
    double sharpness; /* edge contrast enhancement/blurring */ 

    /* Advanced parameters */ 
    double gamma;  /* -1 = dark (1.5)  +1 = light (0.5) */ 
    double resolution; /* image resolution */ 
    double artifacts; /* artifacts caused by color changes */ 
    double fringing; /* color artifacts caused by brightness changes */ 
    double bleed;  /* color bleed (color resolution reduction) */ 
    int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ 
    float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ 

    unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */ 
} snes_ntsc_setup_t; 

enum { snes_ntsc_entry_size = 128 }; 
enum { snes_ntsc_palette_size = 0x2000 }; 
typedef unsigned long snes_ntsc_rgb_t; 
struct snes_ntsc_t { 
    snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; 
}; 

/* Initializes and adjusts parameters. Can be called multiple times on the same 
snes_ntsc_t object. Can pass NULL for either parameter. */ 
typedef struct snes_ntsc_t snes_ntsc_t; 
__declspec(dllexport) void snes_ntsc_init(snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup); 

/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT 
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. 
In_row_width is the number of pixels to get to the next input row. Out_pitch 
is the number of *bytes* to get to the next output row. */ 
__declspec(dllexport) void snes_ntsc_blit(snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, 
    long in_row_width, int burst_phase, int in_width, int in_height, 
    void* rgb_out, long out_pitch); 

ここでは、C++デモコードのスニペットを示します。それはSDLを使用します。

typedef struct image_t 
{ 
    unsigned char const* byte_pixels;/* 8-bit pixels */ 
    unsigned short const* rgb_16; /* 16-bit pixels */ 
    int width; 
    int height; 
    int row_width; /* number of pixels to get to next row (may be greater than width) */ 
} image_t; 

image_t image; 
int burst_phase = 0; 
snes_ntsc_setup_t setup = snes_ntsc_composite; 

snes_ntsc_t* ntsc = (snes_ntsc_t*) malloc(sizeof (snes_ntsc_t)); 
if (!ntsc) 
    fatal_error("Out of memory"); 
snes_ntsc_init(ntsc, &setup); 

load_bmp(&image, (argc > 1 ? argv [1] : "test.bmp"), 0); 
init_window(SNES_NTSC_OUT_WIDTH(image.width), image.height * 2); 

// lock the SDL image surface elsewhere... 
output_pitch = surface->pitch; 
output_pixels = (unsigned char*) surface->pixels; 

burst_phase = 0; 

snes_ntsc_blit(ntsc, image.rgb_16, image.row_width, burst_phase, 
    image.width, image.height, output_pixels, output_pitch); 

SNES_NTSC_OUT_WIDTHを使用すると、入力256

さらに、ライブラリは6 5 6において、内外両方のピクセル当たり16ビットで動作するように(デフォルトで)内蔵されている場合441を返すマクロですパターン。

、ドキュメントから、このデータのすべてを定義し使用して、とのtypedef、ここでのC#のいくつかで私の試みです:

[DllImport("snes.dll")] 
    internal static extern void snes_ntsc_init(snes_ntsc_t t, snes_ntsc_setup_t setup); 

    [DllImport("snes.dll")] 
    internal static extern void snes_ntsc_blit(snes_ntsc_t ntsc, IntPtr input, 
      long in_row_width, int burst_phase, int in_width, int in_height, 
      [Out]IntPtr rgb_out, [Out]long out_pitch); 

[StructLayout(LayoutKind.Sequential)] 
internal class snes_ntsc_t 
{ 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = (0x2000 * 128))] 
    public ulong[] table; 
} 

[StructLayout(LayoutKind.Sequential)] 
internal struct snes_ntsc_setup_t 
{ 
    /* Basic parameters */ 
    public double hue;  /* -1 = -180 degrees  +1 = +180 degrees */ 
    public double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ 
    public double contrast; /* -1 = dark (0.5)  +1 = light (1.5) */ 
    public double brightness; /* -1 = dark (0.5)  +1 = light (1.5) */ 
    public double sharpness; /* edge contrast enhancement/blurring */ 

    /* Advanced parameters */ 
    public double gamma;  /* -1 = dark (1.5)  +1 = light (0.5) */ 
    public double resolution; /* image resolution */ 
    public double artifacts; /* artifacts caused by color changes */ 
    public double fringing; /* color artifacts caused by brightness changes */ 
    public double bleed;  /* color bleed (color resolution reduction) */ 
    public int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ 
    public float decoder_matrix; /* optional RGB decoder matrix, 6 elements */ 

    public ulong bsnes_colortbl; /* undocumented; set to 0 */ 
} 

// Inside my main function... 

     snes_ntsc_t t = new snes_ntsc_t(); 
     t.table = new ulong[0x2000 * 128]; 

     snes_ntsc_setup_t setup = new snes_ntsc_setup_t(); 
     setup.merge_fields = 1; 

     snes_ntsc_init(t, setup); 

     Bitmap orig = (Bitmap)Bitmap.FromFile("test.png"); 
     Bitmap image = new Bitmap(orig.Width, orig.Height, PixelFormat.Format16bppRgb565); 
     using (Graphics g = Graphics.FromImage(image)) g.DrawImage(orig, new Rectangle(0, 0, orig.Width, orig.Height)); 
     orig.Dispose(); 

     BitmapData bits = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format16bppRgb565); 

     // this image size is given in the demo 
     Bitmap output = new Bitmap(441, 448); 

     BitmapData outbits = output.LockBits(new Rectangle(0, 0, output.Width, output.Height), ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb565); 

     IntPtr outscan = Marshal.AllocHGlobal(outbits.Height * outbits.Width * 2); 
     snes_ntsc_blit(t, bits.Scan0, bits.Stride, 0, bits.Width, bits.Height, outscan, outbits.Stride); 
     // copy back to Scan0, if that's correct. Help with that too? 

     image.UnlockBits(bits); 

ので、問題はsnes_ntsc_blitラインで、私が得るということですAccessViolationExceptionこの素晴らしく役に立たないエラーは基本的にセグメンテーション違反ですが、私は私が作った可能性ミスの数十のは考えていません:

  1. が誤って割り当てられた私のsnes_ntsc_tテーブルですが?私はそれのためにマーシャルを使うべきでしょうか?
  2. 私の画像サイズ(602x448)は間違っていますか?あまりにも大きすぎるとこのエラーが出るのですか、それともそれをエラーとして排除する可能性がありますか?
  3. 私の構造体宣言は正しいですか?タイプが間違っていたり、他のものがパラメータから外れる必要がある場合、マーシャリングが必要なものがあるかどうかは分かりません。
  4. 私のblitパラメータは正しいですか?私は本当に考えていないが、それは尋ねられているものと一致するようだ。
  5. ビットマップデータを何とか整列化する必要がありますか?もしそうなら、説明してください。

私はこのような大きな質問を投稿して申し訳ありませんが、マーシャリングは私のC#キャリアの悩みです。私はそれが嫌いです。助けてください。

編集

私は内にブレークポイントを入れて、Cのコードにステップすることができました。 initコールの私のパラメータは今はokですが、blitの場合、私の入力パラメータはすべて乱れています。 VS(C側)にそれを載せて、アドレスのように見えるものとユニコードのゴミを表示します。これが大丈夫かどうかわかりません。エラーは、内部forループのSNES_NTSC_RGB_OUT行中に発生しますが、最初のパスでは発生しません。前記マクロは数学を行い、次に第2パラメータに代入する。だからここブリット関数のコードは次のとおりです。

void snes_ntsc_blit(snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, 
    int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch) 
{ 
    int chunk_count = (in_width - 1)/snes_ntsc_in_chunk; 
    for (; in_height; --in_height) 
    { 
     SNES_NTSC_IN_T const* line_in = input; 
     SNES_NTSC_BEGIN_ROW(ntsc, burst_phase, 
      snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN(*line_in)); 
     snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; 
     int n; 
     ++line_in; 

     for (n = chunk_count; n; --n) 
     { 
      /* order of input and output pixels must not be altered */ 
      SNES_NTSC_COLOR_IN(0, SNES_NTSC_ADJ_IN(line_in [0])); 
      SNES_NTSC_RGB_OUT(0, line_out [0], SNES_NTSC_OUT_DEPTH); 
      SNES_NTSC_RGB_OUT(1, line_out [1], SNES_NTSC_OUT_DEPTH); 

      SNES_NTSC_COLOR_IN(1, SNES_NTSC_ADJ_IN(line_in [1])); 
      SNES_NTSC_RGB_OUT(2, line_out [2], SNES_NTSC_OUT_DEPTH); 
      SNES_NTSC_RGB_OUT(3, line_out [3], SNES_NTSC_OUT_DEPTH); 

      SNES_NTSC_COLOR_IN(2, SNES_NTSC_ADJ_IN(line_in [2])); 
      SNES_NTSC_RGB_OUT(4, line_out [4], SNES_NTSC_OUT_DEPTH); 
      SNES_NTSC_RGB_OUT(5, line_out [5], SNES_NTSC_OUT_DEPTH); 
      SNES_NTSC_RGB_OUT(6, line_out [6], SNES_NTSC_OUT_DEPTH); 

      line_in += 3; 
      line_out += 7; 
     } 

     /* finish final pixels */ 
     SNES_NTSC_COLOR_IN(0, snes_ntsc_black); 
     SNES_NTSC_RGB_OUT(0, line_out [0], SNES_NTSC_OUT_DEPTH); 
     SNES_NTSC_RGB_OUT(1, line_out [1], SNES_NTSC_OUT_DEPTH); 

     SNES_NTSC_COLOR_IN(1, snes_ntsc_black); 
     SNES_NTSC_RGB_OUT(2, line_out [2], SNES_NTSC_OUT_DEPTH); 
     SNES_NTSC_RGB_OUT(3, line_out [3], SNES_NTSC_OUT_DEPTH); 

     SNES_NTSC_COLOR_IN(2, snes_ntsc_black); 
     SNES_NTSC_RGB_OUT(4, line_out [4], SNES_NTSC_OUT_DEPTH); 
     SNES_NTSC_RGB_OUT(5, line_out [5], SNES_NTSC_OUT_DEPTH); 
     SNES_NTSC_RGB_OUT(6, line_out [6], SNES_NTSC_OUT_DEPTH); 

     burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; 
     input += in_row_width; 
     rgb_out = (char*) rgb_out + out_pitch; 
    } 
} 

多分私RGB_OUTパラメータはまだもそれに私の[出力]で、適切に整列化されていませんか?または、私はいくつかのサイズを間違って計算しているのでしょ繰り返しになりますが、膨大な量のコードには謝罪します。

答えて

1

私はあなたの問題がsnes_ntsc_initの呼び出しに戻ってくると思います。私はあなたのコードを使用して、C++(32ビット)とC#クライアントでアンマネージDLLを作成することによってそれを嘲笑しました。 DLLでは、関数snes_ntsc_initとsnes_ntsc_blitは何もしません。マーシャリングされた引数にどのような値が含まれているかを見るためにブレークポイントを設定します。これは私がsnes_ntsc_initで見つけたものです

二番目のパラメータ、 "設定" には、*構造体です。 C#側では "ref setup"として渡す必要があります。

[DllImport("snes_server.dll")] 
internal static extern 
void snes_ntsc_init(snes_ntsc_t t, ref snes_ntsc_setup_t setup); 

C#構造体定義の各構造体メンバにMarshalAs属性を入れて、サイズが正しいことを確認します。私のDLLは32ビットですが、あなたのものは64ビットかもしれません。これは、C#で構造体を定義した方法です。

[StructLayout(LayoutKind.Sequential)] 
internal struct snes_ntsc_setup_t 
{ 
    [MarshalAs(UnmanagedType.R8)] public double hue; 
    [MarshalAs(UnmanagedType.R8)] public double saturation; 
    [MarshalAs(UnmanagedType.R8)] public double contrast; 
    [MarshalAs(UnmanagedType.R8)] public double brightness; 
    [MarshalAs(UnmanagedType.R8)] public double sharpness; 

    [MarshalAs(UnmanagedType.R8)] public double gamma; 
    [MarshalAs(UnmanagedType.R8)] public double resolution; 
    [MarshalAs(UnmanagedType.R8)] public double artifacts; 
    [MarshalAs(UnmanagedType.R8)] public double fringing; 
    [MarshalAs(UnmanagedType.R8)] public double bleed; 
    [MarshalAs(UnmanagedType.I4)] public int merge_fields; 
    [MarshalAs(UnmanagedType.SysInt)] public IntPtr decoder_matrix; 

    [MarshalAs(UnmanagedType.SysInt)] public IntPtr bsnes_colortbl; 
} 

decoder_matrixはfloat *であり、floatではありません。 (あなたの誤植や誤ったコードジネスネス...間違いを起こしやすい)。また、ポインタなので、安全でないコードを使用して初期化する必要があります。私のテストでは、IntPtr.Zeroに設定しました。

これは私がC#のから関数を呼び出す方法です:

snes_ntsc_setup_t setup = new snes_ntsc_setup_t(); 
setup.merge_fields = 1; 
setup.hue = 0.1; 
setup.saturation = 0.2; 
setup.contrast = 0.3; 
setup.brightness = 0.4; 
setup.sharpness = 0.5; 
setup.gamma = 0.6; 
setup.artifacts = 0.7; 
setup.fringing = 0.8; 
setup.bleed = 0.9; 
setup.merge_fields = 10; 
setup.decoder_matrix = IntPtr.Zero; 
setup.bsnes_colortbl = IntPtr.Zero; 
snes_ntsc_init(t, ref setup); 

デバッガでは、DLLに、私は、「セットアップ」の引数は、これらの値が含まれていることを見ることができます。つまり、それは正しくマーシャルされたことを意味します。暗黙的なサイズの仮定がまだ残っている可能性があるため、必ずしもアーキテクチャに依存するとは限りませんが、少なくとも開始点です。

あなたの他の質問に答えるのは時期尚早だと思います。私はあなたがスタブのDLLをビルドすることをお勧めします。少なくとも、すべての引数を正しくマーシャリングしてください。次に、セマンティクスについて心配することができます。

幸運を祈る!興味深い質問に感謝します:-)

+0

ありがとう!あなたがそうするまで、私は実際にはアンマネージコードでブレークポイントを実行していませんでした。私のinitはうまく見えますが、私のblitのparamsはもう一方の側でゴミに見えます。編集を参照してください。 – Tesserex

+0

私はそれを修正しました - 私のマーシャリングは良かった、それは悪い私の主張でした。しかし、私のバージョンとCのデモを並べて、それを理解するのを助けました。ありがとう! – Tesserex

+0

クール。あなたがうまく働いてうれしいです:-) –

1

Cではunsigned longは32ビットの符号なし整数です。これはC#ではuintに対応し、ulongではなく64ビットの符号なし整数です。この構造体は値渡しであるため、おそらく関数にデータを渡しすぎて残りのパラメータが画像データで壊れている可能性があります。 uint[]を含むようにC#バインディングのsnes_ntsc_tの定義を変更し、何が起こるかを見てください。

(あなたは、いくつかの他の場所と同じ間違いをした。あなたはlongタイプとダブルチェックCタイプのためのあなたの定義をすべて確認することがあります。CながらAC longは、32ビットであるとC#のintに対応させていただきますlong longは64ビットになり、C#のlongに対応します)

+0

訂正していただきありがとうございますが、依然として壊れています。たぶんそれは複数のことが間違っている。 – Tesserex