C# 编组图像数据 - 复杂场景

发布于 2024-10-08 05:05:26 字数 9630 浏览 0 评论 0原文

我找到了一个用 C++ 编写的库,可以使用 NTSC 信号处理来过滤图像。您可以在这里看到它: http://slack.net/~ant/libs/ntsc .html#nes_ntsc 目的是使图像看起来像是由 NES 输出到电视上的。

我想用 C# 封装 SNES 版本的库(实际上,我会做 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,则返回 441。

此外,该库(默认情况下)构建为以 6 5 6 模式在输入和输出中处理每像素 16 位。

使用来自文档、定义和 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 调用参数现在没问题,但对于 blit,我的输入参数全乱了。将鼠标悬停在 VS 中(在 C 端)上,显示看起来像地址的内容,然后显示一些 unicode 垃圾。我不知道这是否可以。该错误发生在内部 for 循环的 SNES_NTSC_RGB_OUT 行期间,但不是在第一次传递时发生。所述宏进行一系列数学运算,然后分配给第二个参数。所以这里是 blit 函数的代码:

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 参数仍然没有正确编组,即使我的 [Out] 就在上面?或者也许我计算的一些尺寸是错误的?再次对大量代码表示歉意。

I found a library written in C++ that filters images with NTSC signal processing. You can see it here: http://slack.net/~ant/libs/ntsc.html#nes_ntsc The purpose is to make an image look like it was output by an NES onto a television.

I want to wrap the SNES version of the library in C# (actually, I would do the NES version but it only works with NTSC palette data, and not bitmaps.) After playing with the code for a few hours, I've admitted defeat, and come to you all for help.

Here is some of the C++ code in the library. I have added the 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 );

Here's a snippet from the C++ demo code. It uses 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 is a macro that returns 441 if you input 256.

Additionally, the library is built (by default) to work with 16 bits per pixel both in and out, in the 6 5 6 pattern.

Using all of this data, from the docs, defines, and typedefs, here is my attempt at some of the 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);

So, the problem is that on the snes_ntsc_blit line, I get an AccessViolationException. This wonderfully unhelpful error is basically a segfault, but I have no idea which of dozens of possible mistakes I have made:

  1. Is my snes_ntsc_t table allocated incorrectly? Maybe I should use the Marshal for it?
  2. Could my image size (602x448) be wrong? Would I still get this error if I made it too big, or would that be a possible safeguard to eliminate it as an error?
  3. Are my struct declarations correct? I don't know if maybe some more things need marshalling, if I have the wrong types, or some other things need to be out parameters.
  4. Are my blit parameters correct? I have no idea really but it seems to match what is being asked.
  5. Do I need to marshal the bitmap data somehow? If so please explain?

I'm sorry to post such a huge question, but marshalling is the bane of my C# career. I hate it. Please help.

Edit

I put breakpoints in and was able to step into the C code. My parameters for the init call are ok now, but for blit, my input param is all messed up. Mousing over it in VS (on the C side), shows what looks like an address and then some unicode garbage. I can't tell if this is ok or not. The error happens during an SNES_NTSC_RGB_OUT line of the inner for loop, but not on the first pass. Said macro does a bunch of math and then assigns to the second parameter. So here's the code of the blit function:

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;
    }
}

Maybe my rgb_out parameter still isn't marshalled properly, even with my [Out] on it? Or maybe I'm calculating some sizes wrong? Again, apologies for the enormous amount of code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独留℉清风醉 2024-10-15 05:05:26

我认为您的问题可以追溯到您对 snes_ntsc_init 的调用。我通过使用您的代码在 C++(32 位)中创建一个非托管 DLL 和一个 C# 客户端来模拟它。在 DLL 中,函数 snes_ntsc_init 和 snes_ntsc_blit 不执行任何操作 - 我只是在它们上放置一个断点以查看编组参数的值。

这是我在 snes_ntsc_init 中发现的:

第二个参数“setup”是一个 struct*。因此,您必须将其作为 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 中,我可以看到“setup”参数包含这些值。这意味着它已正确编组。不一定是独立于体系结构的,因为其中可能仍然存在一些隐式的大小假设,但至少这是一个开始。

我认为现在尝试回答你的其他问题还为时过早。我建议您像我一样构建一个存根 DLL,并且至少正确地编组所有参数。然后你就可以担心语义了。

祝你好运!感谢您提出有趣的问题:-)

I think your problems go right back to your call to snes_ntsc_init. I mocked it up by making an unmanaged DLL in C++ (32-bit) and a C# client, using your code. In the DLL, the functions snes_ntsc_init and snes_ntsc_blit do nothing - I just put a breakpoint on them to see what values the marshalled arguments have.

This is what I found in snes_ntsc_init:

The second parameter, "setup", is a struct*. So you have to pass it as "ref setup" on the C# side.

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

I put a MarshalAs attribute on each struct member in the C# structure definition, to be sure that the sizes were correct. My DLL is 32-bit, but yours might be 64-bit. This is how I defined the struct in 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 is a float*, not a float. (Either a typo or just code blindness on your part... easy enough to make that mistake.) Also, since it's a pointer you'll have to use unsafe code to initialise it, or something like that. In my test, I just set it to IntPtr.Zero.

This is how I call the function from 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);

In the debugger, in the DLL, I can see that the "setup" argument contains these values. So that means it's marshalled correctly. Not necessarily architecture-independently, because there might still be some implicit size assumptions in there, but at least it's a start.

I think it's premature to attempt to answer your other questions. I recommend that you build a stub DLL like I did and at least get all your arguments marshalling correctly. Then you can worry about the semantics.

Good luck! And thanks for the interesting question :-)

眼眸 2024-10-15 05:05:26

在 C 中,unsigned long 是一个 32 位无符号整数 - 这对应于 C# 中的 uint,而不是 ulong,后者是 64 位位无符号整数。由于此结构是按值传递的,因此您可能向函数传递了太多数据,并用图像数据破坏了其余参数。更改 C# 绑定中 snes_ntsc_t 的定义以包含 uint[] 并看看会发生什么。

(您在其他一些地方也犯了同样的错误。您可以检查 long 类型的所有定义,并仔细检查 C 类型。AC long 将是 32-位并对应于 C# 的 int,而 C long long 将为 64 位并对应于 C# 的 long。)

In C, unsigned long is a 32-bit unsigned integer -- this corresponds to uint in C#, not ulong, which is a 64-bit unsigned integer. Since this struct is passed by value, you're probably passing way too much data to the function and clobbering the rest of the parameters with image data. Change your definition of snes_ntsc_t in the C# bindings to contain a uint[] and see what happens.

(You have made the same mistake a few other places. You might review all of your definitions for long types and double-check the C type. A C long will be 32-bit and correspond to C#'s int, while a C long long will be 64-bit and correspond to C#'s long.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文