Added 3 color correction profiles, added color correction setting to Cocoa GUI, improved cross-platform and cross-frontend save-state compatibility
This commit is contained in:
parent
c1f27d7b27
commit
65dd02cc52
@ -30,7 +30,8 @@
|
||||
@"GBTurbo": @(kVK_Space),
|
||||
|
||||
@"GBFilter": @"NearestNeighbor",
|
||||
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
||||
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
||||
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET)
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -145,6 +145,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
||||
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
|
||||
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
|
||||
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
|
||||
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
||||
GB_set_rgb_encode_callback(&gb, rgbEncode);
|
||||
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
||||
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
||||
@ -302,6 +303,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
||||
name:@"GBHighpassFilterChanged"
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(updateColorCorrectionMode)
|
||||
name:@"GBColorCorrectionChanged"
|
||||
object:nil];
|
||||
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateDMG"]) {
|
||||
[self initDMG];
|
||||
}
|
||||
@ -1287,4 +1293,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateColorCorrectionMode
|
||||
{
|
||||
if (GB_is_inited(&gb)) {
|
||||
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -5,4 +5,5 @@
|
||||
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
||||
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
||||
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
||||
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
||||
@end
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
NSPopUpButton *_graphicsFilterPopupButton;
|
||||
NSPopUpButton *_highpassFilterPopupButton;
|
||||
NSPopUpButton *_colorCorrectionPopupButton;
|
||||
NSButton *_aspectRatioCheckbox;
|
||||
}
|
||||
|
||||
@ -52,6 +53,18 @@
|
||||
return _highpassFilterPopupButton;
|
||||
}
|
||||
|
||||
- (void)setColorCorrectionPopupButton:(NSPopUpButton *)colorCorrectionPopupButton
|
||||
{
|
||||
_colorCorrectionPopupButton = colorCorrectionPopupButton;
|
||||
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"];
|
||||
[_colorCorrectionPopupButton selectItemAtIndex:mode];
|
||||
}
|
||||
|
||||
- (NSPopUpButton *)colorCorrectionPopupButton
|
||||
{
|
||||
return _colorCorrectionPopupButton;
|
||||
}
|
||||
|
||||
- (void)setHighpassFilterPopupButton:(NSPopUpButton *)highpassFilterPopupButton
|
||||
{
|
||||
_highpassFilterPopupButton = highpassFilterPopupButton;
|
||||
@ -128,6 +141,14 @@
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBAspectChanged" object:nil];
|
||||
}
|
||||
|
||||
- (IBAction)colorCorrectionChanged:(id)sender
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||
forKey:@"GBColorCorrection"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
||||
|
||||
}
|
||||
|
||||
- (NSButton *)aspectRatioCheckbox
|
||||
{
|
||||
return _aspectRatioCheckbox;
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -17,14 +17,14 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="292" height="378"/>
|
||||
<rect key="contentRect" x="196" y="240" width="292" height="426"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="378"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="426"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||
<rect key="frame" x="18" y="341" width="256" height="17"/>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||
<rect key="frame" x="18" y="389" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics Filter:" id="pXg-WY-8Q5">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -33,7 +33,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
||||
<rect key="frame" x="30" y="309" width="245" height="26"/>
|
||||
<rect key="frame" x="30" y="357" width="245" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="I1w-05-lGl">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -66,6 +66,36 @@
|
||||
<action selector="graphicFilterChanged:" target="QvC-M9-y7g" id="n87-t4-fbV"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
||||
<rect key="frame" x="18" y="335" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color Correction:" id="5Si-hz-EK3">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
||||
<rect key="frame" x="30" y="303" width="245" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="6go-Lb-a4m">
|
||||
<items>
|
||||
<menuItem title="Disabled" state="on" id="D2J-wV-1vu">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Correct Color Curves" id="B3Q-x1-VRl"/>
|
||||
<menuItem title="Emulate Hardware" id="XBL-hS-7ke"/>
|
||||
<menuItem title="Preserve Brightness" id="tlx-WB-Bkw"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T69-6N-dhT">
|
||||
<rect key="frame" x="30" y="223" width="245" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
@ -87,7 +117,7 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
|
||||
<rect key="frame" x="18" y="285" width="256" height="18"/>
|
||||
<rect key="frame" x="18" y="278" width="256" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Keep Aspect Ratio" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lsj-rC-Eo6">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
@ -97,7 +127,7 @@
|
||||
<action selector="changeAspectRatio:" target="QvC-M9-y7g" id="mQG-Ib-1jN"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
||||
<rect key="frame" x="18" y="201" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Button configuration:" id="YqW-Ds-VIC">
|
||||
@ -106,7 +136,7 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WU3-oV-KHO">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WU3-oV-KHO">
|
||||
<rect key="frame" x="18" y="255" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="High-pass Filter:" id="YLF-RL-b2D">
|
||||
@ -177,12 +207,13 @@
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="aspectRatioCheckbox" destination="Vfj-tg-7OP" id="Yw0-xS-DBr"/>
|
||||
<outlet property="colorCorrectionPopupButton" destination="VEz-N4-uP6" id="EO2-Vt-JFJ"/>
|
||||
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
||||
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
||||
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
||||
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="179" y="474"/>
|
||||
<point key="canvasLocation" x="179" y="498"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
|
@ -222,21 +222,77 @@ static void display_vblank(GB_gameboy_t *gb)
|
||||
|
||||
static inline uint8_t scale_channel(uint8_t x)
|
||||
{
|
||||
x &= 0x1f;
|
||||
return (x << 3) | (x >> 2);
|
||||
}
|
||||
|
||||
static inline uint8_t scale_channel_with_curve(uint8_t x)
|
||||
{
|
||||
return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255,}[x];
|
||||
}
|
||||
|
||||
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
||||
{
|
||||
uint8_t r = (color) & 0x1F;
|
||||
uint8_t g = (color >> 5) & 0x1F;
|
||||
uint8_t b = (color >> 10) & 0x1F;
|
||||
|
||||
if (gb->color_correction_mode == GB_COLOR_CORRECTION_DISABLED) {
|
||||
r = scale_channel(r);
|
||||
g = scale_channel(g);
|
||||
b = scale_channel(b);
|
||||
}
|
||||
else {
|
||||
r = scale_channel_with_curve(r);
|
||||
g = scale_channel_with_curve(g);
|
||||
b = scale_channel_with_curve(b);
|
||||
|
||||
if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) {
|
||||
uint8_t new_g = (g * 3 + b) / 4;
|
||||
uint8_t new_r = r, new_b = b;
|
||||
if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) {
|
||||
uint8_t old_max = MAX(r, MAX(g, b));
|
||||
uint8_t new_max = MAX(new_r, MAX(new_g, new_b));
|
||||
|
||||
if (new_max != 0) {
|
||||
new_r = new_r * old_max / new_max;
|
||||
new_g = new_g * old_max / new_max;
|
||||
new_b = new_b * old_max / new_max;
|
||||
}
|
||||
|
||||
uint8_t old_min = MIN(r, MIN(g, b));
|
||||
uint8_t new_min = MIN(new_r, MIN(new_g, new_b));
|
||||
|
||||
if (new_min != 0xff) {
|
||||
new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min);
|
||||
new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min);
|
||||
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);;
|
||||
}
|
||||
}
|
||||
r = new_r;
|
||||
g = new_g;
|
||||
b = new_b;
|
||||
}
|
||||
}
|
||||
|
||||
return gb->rgb_encode_callback(gb, r, g, b);
|
||||
}
|
||||
|
||||
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index)
|
||||
{
|
||||
if (!gb->rgb_encode_callback) return;
|
||||
uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->sprite_palettes_data;
|
||||
uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8);
|
||||
|
||||
// No need to &, scale channel does that.
|
||||
uint8_t r = scale_channel(color);
|
||||
uint8_t g = scale_channel(color >> 5);
|
||||
uint8_t b = scale_channel(color >> 10);
|
||||
assert (gb->rgb_encode_callback);
|
||||
(background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = gb->rgb_encode_callback(gb, r, g, b);
|
||||
(background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = GB_convert_rgb15(gb, color);
|
||||
}
|
||||
|
||||
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode)
|
||||
{
|
||||
gb->color_correction_mode = mode;
|
||||
for (unsigned i = 0; i < 32; i++) {
|
||||
GB_palette_changed(gb, false, i * 2);
|
||||
GB_palette_changed(gb, true, i * 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -34,8 +34,16 @@ typedef struct {
|
||||
bool obscured_by_line_limit;
|
||||
} GB_oam_info_t;
|
||||
|
||||
typedef enum {
|
||||
GB_COLOR_CORRECTION_DISABLED,
|
||||
GB_COLOR_CORRECTION_CORRECT_CURVES,
|
||||
GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
||||
GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS,
|
||||
} GB_color_correction_mode_t;
|
||||
|
||||
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
|
||||
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type);
|
||||
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);
|
||||
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
|
||||
#endif /* display_h */
|
||||
|
13
Core/gb.h
13
Core/gb.h
@ -162,6 +162,14 @@ typedef enum {
|
||||
#define DIV_CYCLES (0x100)
|
||||
#define INTERNAL_DIV_CYCLES (0x40000)
|
||||
#define FRAME_LENGTH 16742706 // in nanoseconds
|
||||
|
||||
#if !defined(MIN)
|
||||
#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
|
||||
#endif
|
||||
|
||||
#if !defined(MAX)
|
||||
#define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
|
||||
@ -361,8 +369,6 @@ struct GB_gameboy_internal_s {
|
||||
uint8_t oam[0xA0];
|
||||
uint8_t background_palettes_data[0x40];
|
||||
uint8_t sprite_palettes_data[0x40];
|
||||
uint32_t background_palettes_rgb[0x20];
|
||||
uint32_t sprite_palettes_rgb[0x20];
|
||||
int16_t previous_lcdc_x;
|
||||
bool stat_interrupt_line;
|
||||
uint8_t effective_scx;
|
||||
@ -405,6 +411,9 @@ struct GB_gameboy_internal_s {
|
||||
|
||||
/* I/O */
|
||||
uint32_t *screen;
|
||||
uint32_t background_palettes_rgb[0x20];
|
||||
uint32_t sprite_palettes_rgb[0x20];
|
||||
GB_color_correction_mode_t color_correction_mode;
|
||||
bool keys[GB_KEY_MAX];
|
||||
|
||||
/* Timing */
|
||||
|
@ -213,6 +213,11 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
||||
gb->rumble_callback(gb, gb->rumble_state);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 32; i++) {
|
||||
GB_palette_changed(gb, false, i * 2);
|
||||
GB_palette_changed(gb, true, i * 2);
|
||||
}
|
||||
|
||||
error:
|
||||
fclose(f);
|
||||
return errno;
|
||||
|
Loading…
Reference in New Issue
Block a user