From 159d9d0348bbc14a71a59180f3c5199eb278dd07 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 25 Dec 2020 14:14:17 +0200 Subject: [PATCH] Color temperature control --- Cocoa/Document.m | 13 ++++++++++ Cocoa/GBPreferencesWindow.h | 2 +- Cocoa/GBPreferencesWindow.m | 20 +++++++++++++++ Cocoa/Preferences.xib | 5 ++-- Core/display.c | 51 ++++++++++++++++++++++++++++++++----- Core/display.h | 1 + Core/gb.h | 1 + SDL/gui.c | 43 +++++++++++++++++++++++++++++++ SDL/gui.h | 4 +++ SDL/main.c | 3 +++ 10 files changed, 134 insertions(+), 9 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index ea7ef49d..a354e03c 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -689,6 +689,11 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) name:@"GBColorCorrectionChanged" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateLightTemperature) + name:@"GBLightTemperatureChanged" + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateFrameBlendingMode) name:@"GBFrameBlendingModeChanged" @@ -1835,6 +1840,14 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) } } +- (void) updateLightTemperature +{ + if (GB_is_inited(&gb)) { + GB_set_light_temperature(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"]); + } +} + + - (void) updateFrameBlendingMode { self.view.frameBlendingMode = (GB_frame_blending_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"]; diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h index ee697a8d..f0b75069 100644 --- a/Cocoa/GBPreferencesWindow.h +++ b/Cocoa/GBPreferencesWindow.h @@ -17,7 +17,7 @@ @property (strong) IBOutlet NSMenuItem *bootROMsFolderItem; @property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton; @property (strong) IBOutlet NSPopUpButton *rumbleModePopupButton; - +@property (weak) IBOutlet NSSlider *temperatureSlider; @property (weak) IBOutlet NSPopUpButton *dmgPopupButton; @property (weak) IBOutlet NSPopUpButton *sgbPopupButton; @property (weak) IBOutlet NSPopUpButton *cgbPopupButton; diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index 491f0c0f..dd13ca18 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -26,6 +26,7 @@ NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton; NSPopUpButton *_preferredJoypadButton; NSPopUpButton *_rumbleModePopupButton; + NSSlider *_temperatureSlider; } + (NSArray *)filterList @@ -91,11 +92,23 @@ [_colorCorrectionPopupButton selectItemAtIndex:mode]; } + - (NSPopUpButton *)colorCorrectionPopupButton { return _colorCorrectionPopupButton; } +- (void)setTemperatureSlider:(NSSlider *)temperatureSlider +{ + _temperatureSlider = temperatureSlider; + [temperatureSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"] * 256]; + temperatureSlider.continuous = YES; +} + +- (NSSlider *)temperatureSlider +{ + return _temperatureSlider; +} - (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton { _frameBlendingModePopupButton = frameBlendingModePopupButton; @@ -284,6 +297,13 @@ [[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil]; } +- (IBAction)lightTemperatureChanged:(id)sender +{ + [[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0) + forKey:@"GBLightTemperature"]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"GBLightTemperatureChanged" object:nil]; +} + - (IBAction)franeBlendingModeChanged:(id)sender { [[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem]) diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index 99c65435..73eb0abf 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -79,15 +79,16 @@ + - + - + diff --git a/Core/display.c b/Core/display.c index f5e81a84..6ac6be87 100644 --- a/Core/display.c +++ b/Core/display.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "gb.h" /* FIFO functions */ @@ -208,6 +209,26 @@ static void display_vblank(GB_gameboy_t *gb) GB_timing_sync(gb); } +static inline void temperature_tint(double temperature, double *r, double *g, double *b) +{ + if (temperature >= 0) { + *r = 1; + *g = pow(1 - temperature, 0.375); + if (temperature >= 0.75) { + *b = 0; + } + else { + *b = sqrt(0.75 - temperature); + } + } + else { + *b = 1; + double squared = pow(temperature, 2); + *g = 0.125 * squared + 0.3 * temperature + 1.0; + *r = 0.21875 * squared + 0.5 * temperature + 1.0; + } +} + static inline uint8_t scale_channel(uint8_t x) { return (x << 3) | (x >> 2); @@ -240,13 +261,12 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border) g = scale_channel(g); b = scale_channel(b); } + else if (GB_is_sgb(gb) || for_border) { + r = scale_channel_with_curve_sgb(r); + g = scale_channel_with_curve_sgb(g); + b = scale_channel_with_curve_sgb(b); + } else { - if (GB_is_sgb(gb) || for_border) { - return gb->rgb_encode_callback(gb, - scale_channel_with_curve_sgb(r), - scale_channel_with_curve_sgb(g), - scale_channel_with_curve_sgb(b)); - } bool agb = gb->model == GB_MODEL_AGB; r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r); g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g); @@ -301,6 +321,14 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border) } } + if (gb->light_temperature) { + double light_r, light_g, light_b; + temperature_tint(gb->light_temperature, &light_r, &light_g, &light_b); + r = round(light_r * r); + g = round(light_g * g); + b = round(light_b * b); + } + return gb->rgb_encode_callback(gb, r, g, b); } @@ -324,6 +352,17 @@ void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t m } } +void GB_set_light_temperature(GB_gameboy_t *gb, double temperature) +{ + gb->light_temperature = temperature; + if (GB_is_cgb(gb)) { + for (unsigned i = 0; i < 32; i++) { + GB_palette_changed(gb, false, i * 2); + GB_palette_changed(gb, true, i * 2); + } + } +} + /* STAT interrupt is implemented based on this finding: http://board.byuu.org/phpbb3/viewtopic.php?p=25527#p25531 diff --git a/Core/display.h b/Core/display.h index 5bdeba8d..fdaf172a 100644 --- a/Core/display.h +++ b/Core/display.h @@ -58,5 +58,6 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height); uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border); void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode); +void GB_set_light_temperature(GB_gameboy_t *gb, double temperature); bool GB_is_odd_frame(GB_gameboy_t *gb); #endif /* display_h */ diff --git a/Core/gb.h b/Core/gb.h index ed736e0f..c2e96db6 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -573,6 +573,7 @@ struct GB_gameboy_internal_s { uint32_t sprite_palettes_rgb[0x20]; const GB_palette_t *dmg_palette; GB_color_correction_mode_t color_correction_mode; + double light_temperature; bool keys[4][GB_KEY_MAX]; GB_border_mode_t border_mode; GB_sgb_border_t borrowed_border; diff --git a/SDL/gui.c b/SDL/gui.c index 3848d15f..63e42d84 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -110,6 +110,7 @@ configuration_t configuration = .volume = 100, .rumble_mode = GB_RUMBLE_ALL_GAMES, .default_scale = 2, + .color_temperature = 10, }; @@ -453,6 +454,33 @@ const char *current_color_correction_mode(unsigned index) [configuration.color_correction_mode]; } +const char *current_color_temperature(unsigned index) +{ + return (const char *[]){"12000K", + "11450K", + "10900K", + "10350K", + "9800K", + "9250K", + "8700K", + "8150K", + "7600K", + "7050K", + "6500K (White)", + "5950K", + "5400K", + "4850K", + "4300K", + "3750K", + "3200K", + "2650K", + "2100K", + "1550K", + "1000K"} + [configuration.color_temperature]; +} + + const char *current_palette(unsigned index) { return (const char *[]){"Greyscale", "Lime (Game Boy)", "Olive (Pocket)", "Teal (Light)"} @@ -533,6 +561,20 @@ static void cycle_color_correction_backwards(unsigned index) } } +static void decrease_color_temperature(unsigned index) +{ + if (configuration.color_temperature < 20) { + configuration.color_temperature++; + } +} + +static void increase_color_temperature(unsigned index) +{ + if (configuration.color_temperature > 0) { + configuration.color_temperature--; + } +} + static void cycle_palette(unsigned index) { if (configuration.dmg_palette == 3) { @@ -684,6 +726,7 @@ static const struct menu_item graphics_menu[] = { {"Default Window Scale:", cycle_default_scale, current_default_scale, cycle_default_scale_backwards}, {"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards}, {"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards}, + {"Ambient Light:", decrease_color_temperature, current_color_temperature, increase_color_temperature}, {"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards}, {"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards}, {"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards}, diff --git a/SDL/gui.h b/SDL/gui.h index f55464dc..84930e04 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -110,6 +110,10 @@ typedef struct { GB_rumble_mode_t rumble_mode; uint8_t default_scale; + + /* v0.14 */ + unsigned padding; + uint8_t color_temperature; } configuration_t; extern configuration_t configuration; diff --git a/SDL/main.c b/SDL/main.c index 45d016d1..a20d6445 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -120,6 +120,7 @@ static void open_menu(void) GB_audio_set_paused(false); } GB_set_color_correction_mode(&gb, configuration.color_correction_mode); + GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0); GB_set_border_mode(&gb, configuration.border_mode); update_palette(); GB_set_highpass_filter_mode(&gb, configuration.highpass_mode); @@ -496,6 +497,7 @@ restart: GB_set_rumble_mode(&gb, configuration.rumble_mode); GB_set_sample_rate(&gb, GB_audio_get_frequency()); GB_set_color_correction_mode(&gb, configuration.color_correction_mode); + GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0); update_palette(); if ((unsigned)configuration.border_mode <= GB_BORDER_ALWAYS) { GB_set_border_mode(&gb, configuration.border_mode); @@ -646,6 +648,7 @@ int main(int argc, char **argv) configuration.dmg_palette %= 3; configuration.border_mode %= GB_BORDER_ALWAYS + 1; configuration.rumble_mode %= GB_RUMBLE_ALL_GAMES + 1; + configuration.color_temperature %= 21; } if (configuration.model >= MODEL_MAX) {