Я нашел библиотеку, написанную на C ++, которая фильтрует изображения с обработкой сигналов NTSC.Вы можете увидеть это здесь: http://slack.net/~ant/libs/ntsc.html#nes_ntsc Цель состоит в том, чтобы изображение выглядело так, как будто оно было выведено NES на телевизор.
Я хочу обернуть версию библиотеки 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 - это макрос, который возвращает 441, если вы введете 256.
Кроме того, библиотека встроена (по умолчанию) для работы с 16 битами на пиксель как ви вне, в шаблоне 6 5 6.
Используя все эти данные из документов, определений и typedefs, вот моя попытка некоторых из 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.Эта поразительно бесполезная ошибка в основном является ошибкой, но я понятия не имею, какую из десятков возможных ошибок я допустил:
- Неправильно ли выделена моя таблица
snes_ntsc_t
?Может быть, мне стоит использовать для этого маршала? - Может быть размер моего изображения (602x448) может быть неправильным?Я все еще получу эту ошибку, если я сделаю ее слишком большой, или это будет возможной защитой, чтобы устранить ее как ошибку?
- Верны ли мои объявления структуры?Я не знаю, может быть, нужно выполнить еще какие-то действия, если у меня неправильные типы, или нужно что-то другое из параметров.
- Верны ли мои параметры blit?Я понятия не имею, но похоже, что это соответствует тому, что спрашивают.
- Нужно ли как-то маршалировать растровые данные?Если да, пожалуйста, объясните?
Извините, что опубликовал такой огромный вопрос, но сортировка - проклятие моей карьеры в C #.Я ненавижу это.Пожалуйста, помогите.
Редактировать
Я установил контрольные точки и смог войти в C-код.Мои параметры для вызова init в настоящее время в порядке, но для blit
мой входной параметр все испорчен.Наведение над ним в VS (на стороне C) показывает, что выглядит как адрес, а затем какой-то юникодный мусор.Я не могу сказать, нормально это или нет.Ошибка происходит во время строки SNES_NTSC_RGB_OUT внутреннего цикла for, но не при первом проходе.Упомянутый макрос выполняет кучу математических операций, а затем присваивает второй параметр.Итак, вот код функции 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] на нем?Или, может быть, я неправильно рассчитываю некоторые размеры?Опять же, извиняюсь за огромное количество кода.