diff --git a/Core/camera.c b/Core/camera.c index 0c6539f..f7921e9 100644 --- a/Core/camera.c +++ b/Core/camera.c @@ -1,5 +1,37 @@ #include "camera.h" +/* This is not a completely emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported. */ + + +static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y) +{ + if (x >= 128) { + x = 0; + } + if (y >= 112) { + y = 0; + } + + long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (rand() & 0xFF); + + static const double gain_values[] = {0.8809390, 0.9149149, 0.9457498, 0.9739758, + 1.0000000, 1.0241412, 1.0466537, 1.0677433, + 1.0875793, 1.1240310, 1.1568911, 1.1868043, + 1.2142561, 1.2396208, 1.2743837, 1.3157323, + 1.3525190, 1.3856512, 1.4157897, 1.4434309, + 1.4689574, 1.4926697, 1.5148087, 1.5355703, + 1.5551159, 1.5735801, 1.5910762, 1.6077008, + 1.6235366, 1.6386550, 1.6531183, 1.6669808}; + /* Multiply color by gain value */ + color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F]; + + + /* Color is multiplied by the exposure register to simulate exposure. */ + color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000; + + return color; +} + uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr) { if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) { @@ -14,25 +46,24 @@ uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr) uint8_t ret = 0; + /* This is not a complete emulation of the chip's image proccessing algorithm, it only emulates the features used by + the actual GameBoy Camera ROM. */ + for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) { - long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x,y) : (rand() & 0xFF); + long color = get_processed_color(gb, x, y); - static const double gain_values[] = {0.8809390, 0.9149149, 0.9457498, 0.9739758, - 1.0000000, 1.0241412, 1.0466537, 1.0677433, - 1.0875793, 1.1240310, 1.1568911, 1.1868043, - 1.2142561, 1.2396208, 1.2743837, 1.3157323, - 1.3525190, 1.3856512, 1.4157897, 1.4434309, - 1.4689574, 1.4926697, 1.5148087, 1.5355703, - 1.5551159, 1.5735801, 1.5910762, 1.6077008, - 1.6235366, 1.6386550, 1.6531183, 1.6669808}; - /* Multiply color by gain value */ - color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENCHANCEMENT_FLAGS] & 0x1F]; + static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5}; + double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7]; + if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) { + color += (color * 4) * edge_enhancement_ratio; + color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio; + color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio; + color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio; + color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio; + } - /* Color is multiplied by the exposure register to simulate exposure. */ - color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000; - /* The camera's registers are used as a threshold pattern, which defines the dithering */ uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START; diff --git a/Core/camera.h b/Core/camera.h index 9c009f6..5adda5d 100644 --- a/Core/camera.h +++ b/Core/camera.h @@ -4,9 +4,10 @@ enum { GB_CAMERA_SHOOT_AND_1D_FLAGS = 0, - GB_CAMERA_GAIN_AND_EDGE_ENCHANCEMENT_FLAGS = 1, + GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS = 1, GB_CAMERA_EXPOSURE_HIGH = 2, GB_CAMERA_EXPOSURE_LOW = 3, + GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE = 4, GB_CAMERA_DITHERING_PATTERN_START = 6, GB_CAMERA_DITHERING_PATTERN_END = 0x35, };