diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index cfaf83e..274dce6 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -114,6 +114,7 @@ public: void setColorCorrection(bool enable); void setColorCorrectionMode(unsigned colorCorrectionMode); + void setColorCorrectionBrightness(float colorCorrectionBrightness); void setDarkFilterLevel(unsigned darkFilterLevel); video_pixel_t gbcToRgb32(const unsigned bgr15); diff --git a/libgambatte/libretro/libretro.cpp b/libgambatte/libretro/libretro.cpp index 97a0f19..52a5d4e 100644 --- a/libgambatte/libretro/libretro.cpp +++ b/libgambatte/libretro/libretro.cpp @@ -366,6 +366,7 @@ Special 3" }, // So many... place on seperate lines for readability... { "gambatte_gbc_color_correction", "Color correction; GBC only|always|disabled" }, { "gambatte_gbc_color_correction_mode", "Color correction mode; accurate|fast" }, + { "gambatte_gbc_frontlight_position", "Color correction - frontlight position; central|above screen|below screen" }, { "gambatte_dark_filter_level", "Dark Filter Level (percent); 0|5|10|15|20|25|30|35|40|45|50" }, { "gambatte_gb_hwmode", "Emulated hardware (restart); Auto|GB|GBC|GBA" }, { "gambatte_gb_bootloader", "Use official bootloader (restart); enabled|disabled" }, @@ -626,6 +627,18 @@ static void check_variables(void) } gb.setColorCorrectionMode(colorCorrectionMode); + float colorCorrectionBrightness = 0.5f; /* central */ + var.key = "gambatte_gbc_frontlight_position"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "above screen")) + colorCorrectionBrightness = 1.2f; + else if (!strcmp(var.value, "below screen")) + colorCorrectionBrightness = 0.0f; + } + gb.setColorCorrectionBrightness(colorCorrectionBrightness); + unsigned darkFilterLevel = 0; var.key = "gambatte_dark_filter_level"; var.value = NULL; diff --git a/libgambatte/src/gambatte-memory.h b/libgambatte/src/gambatte-memory.h index 2a0824f..f0cb932 100644 --- a/libgambatte/src/gambatte-memory.h +++ b/libgambatte/src/gambatte-memory.h @@ -49,6 +49,7 @@ public: unsigned rtcdata_size() { return cart_.rtcdata_size(); } void display_setColorCorrection(bool enable) { lcd_.setColorCorrection(enable); } void display_setColorCorrectionMode(unsigned colorCorrectionMode) { lcd_.setColorCorrectionMode(colorCorrectionMode); } + void display_setColorCorrectionBrightness(float colorCorrectionBrightness) { lcd_.setColorCorrectionBrightness(colorCorrectionBrightness); } void display_setDarkFilterLevel(unsigned darkFilterLevel) { lcd_.setDarkFilterLevel(darkFilterLevel); } video_pixel_t display_gbcToRgb32(const unsigned bgr15) { return lcd_.gbcToRgb32(bgr15); } void clearCheats() { cart_.clearCheats(); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 2e3040a..b211dec 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -158,6 +158,10 @@ void GB::setColorCorrectionMode(unsigned colorCorrectionMode) { p_->cpu.mem_.display_setColorCorrectionMode(colorCorrectionMode); } +void GB::setColorCorrectionBrightness(float colorCorrectionBrightness) { + p_->cpu.mem_.display_setColorCorrectionBrightness(colorCorrectionBrightness); +} + void GB::setDarkFilterLevel(unsigned darkFilterLevel) { p_->cpu.mem_.display_setDarkFilterLevel(darkFilterLevel); } diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index bdd2324..7516ba1 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -155,6 +155,7 @@ class LCD void setColorCorrection(bool colorCorrection); void setColorCorrectionMode(unsigned colorCorrectionMode); + void setColorCorrectionBrightness(float colorCorrectionBrightness); void setDarkFilterLevel(unsigned darkFilterLevel); video_pixel_t gbcToRgb32(const unsigned bgr15); private: @@ -228,6 +229,7 @@ class LCD bool colorCorrection; unsigned colorCorrectionMode; + float colorCorrectionBrightness; unsigned darkFilterLevel; void doCgbColorChange(unsigned char *const pdata, video_pixel_t *const palette, unsigned index, const unsigned data); diff --git a/libgambatte/src/video_libretro.cpp b/libgambatte/src/video_libretro.cpp index 08b9e53..391d48d 100644 --- a/libgambatte/src/video_libretro.cpp +++ b/libgambatte/src/video_libretro.cpp @@ -22,6 +22,17 @@ #include #include +/* GBC colour correction factors */ +#define GBC_CC_R 0.87 +#define GBC_CC_G 0.66 +#define GBC_CC_B 0.79 +#define GBC_CC_RG 0.115 +#define GBC_CC_RB 0.14 +#define GBC_CC_GR 0.18 +#define GBC_CC_GB 0.07 +#define GBC_CC_BR -0.05 +#define GBC_CC_BG 0.225 + namespace gambatte { void LCD::setDmgPaletteColor(const unsigned index, const video_pixel_t rgb32) @@ -53,6 +64,12 @@ namespace gambatte refreshPalettes(); } + void LCD::setColorCorrectionBrightness(float colorCorrectionBrightness_) + { + colorCorrectionBrightness = colorCorrectionBrightness_; + refreshPalettes(); + } + void LCD::setDarkFilterLevel(unsigned darkFilterLevel_) { darkFilterLevel = darkFilterLevel_; @@ -146,16 +163,16 @@ namespace gambatte // // Constants // > Luminosity factors: photometric/digital ITU BT.709 - static const float lumaR = 0.2126; - static const float lumaG = 0.7152; - static const float lumaB = 0.0722; + static const float lumaR = 0.2126f; + static const float lumaG = 0.7152f; + static const float lumaB = 0.0722f; // Calculate luminosity float luma = (lumaR * r) + (lumaG * g) + (lumaB * b); // Get 'darkness' scaling factor // > User set 'dark filter' level scaled by current luminosity // (i.e. lighter colours affected more than darker colours) - float darkFactor = 1.0 - ((static_cast(darkFilterLevel) * 0.01) * luma); - darkFactor = darkFactor < 0.0 ? 0.0 : darkFactor; + float darkFactor = 1.0f - ((static_cast(darkFilterLevel) * 0.01f) * luma); + darkFactor = darkFactor < 0.0f ? 0.0f : darkFactor; // Perform scaling... r = r * darkFactor; g = g * darkFactor; @@ -193,7 +210,7 @@ namespace gambatte else { // Use Pokefan531's "gold standard" GBC colour correction - // (https://forums.libretro.com/t/real-gba-and-ds-phat-colors/1540/159) + // (https://forums.libretro.com/t/real-gba-and-ds-phat-colors/1540/174) // NB: The results produced by this implementation are ever so slightly // different from the output of the gbc-colour shader. This is due to the // fact that we have to tolerate rounding errors here that are simply not @@ -206,17 +223,26 @@ namespace gambatte static const float targetGamma = 2.2; static const float displayGammaInv = 1.0 / targetGamma; // Perform gamma expansion - float rCorrect = std::pow(static_cast(r) * rgbMaxInv, targetGamma); - float gCorrect = std::pow(static_cast(g) * rgbMaxInv, targetGamma); - float bCorrect = std::pow(static_cast(b) * rgbMaxInv, targetGamma); - // Perform colour mangling (lots of magic numbers...) - rCorrect = (0.86629 * rCorrect) + (0.13361 * gCorrect); - gCorrect = (0.02429 * rCorrect) + (0.70857 * gCorrect) + (0.26714 * bCorrect); - bCorrect = (0.11337 * rCorrect) + (0.11448 * gCorrect) + (0.77215 * bCorrect); + float adjustedGamma = targetGamma - colorCorrectionBrightness; + float rFloat = std::pow(static_cast(r) * rgbMaxInv, adjustedGamma); + float gFloat = std::pow(static_cast(g) * rgbMaxInv, adjustedGamma); + float bFloat = std::pow(static_cast(b) * rgbMaxInv, adjustedGamma); + // Perform colour mangling + float rCorrect = (GBC_CC_R * rFloat) + (GBC_CC_GR * gFloat) + (GBC_CC_BR * bFloat); + float gCorrect = (GBC_CC_RG * rFloat) + (GBC_CC_G * gFloat) + (GBC_CC_BG * bFloat); + float bCorrect = (GBC_CC_RB * rFloat) + (GBC_CC_GB * gFloat) + (GBC_CC_B * bFloat); + // Range check... + rCorrect = rCorrect > 0.0f ? rCorrect : 0.0f; + gCorrect = gCorrect > 0.0f ? gCorrect : 0.0f; + bCorrect = bCorrect > 0.0f ? bCorrect : 0.0f; // Perform gamma compression rCorrect = std::pow(rCorrect, displayGammaInv); gCorrect = std::pow(gCorrect, displayGammaInv); bCorrect = std::pow(bCorrect, displayGammaInv); + // Range check... + rCorrect = rCorrect > 1.0f ? 1.0f : rCorrect; + gCorrect = gCorrect > 1.0f ? 1.0f : gCorrect; + bCorrect = bCorrect > 1.0f ? 1.0f : bCorrect; // Perform image darkening, if required if (darkFilterLevel > 0) {