Custom palette and editor

This commit is contained in:
Lior Halphon 2021-11-05 19:07:27 +02:00
parent f237b1e9b9
commit 178860e715
9 changed files with 833 additions and 30 deletions

View File

@ -10,6 +10,7 @@
#include "GBCheatWindowController.h"
#include "GBTerminalTextFieldCell.h"
#include "BigSurToolbar.h"
#import "GBPaletteEditorController.h"
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
/* Todo: Split into category files! This is so messy!!! */
@ -256,23 +257,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on)
- (void) updatePalette
{
switch ([[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"]) {
case 1:
GB_set_palette(&gb, &GB_PALETTE_DMG);
break;
case 2:
GB_set_palette(&gb, &GB_PALETTE_MGB);
break;
case 3:
GB_set_palette(&gb, &GB_PALETTE_GBL);
break;
default:
GB_set_palette(&gb, &GB_PALETTE_GREY);
break;
}
GB_set_palette(&gb, [GBPaletteEditorController userPalette]);
}
- (void) updateBorderMode
@ -1952,7 +1937,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
{
bool shouldResume = running;
[self stop];
NSSavePanel * savePanel = [NSSavePanel savePanel];
NSSavePanel *savePanel = [NSSavePanel savePanel];
[savePanel setAllowedFileTypes:@[@"png"]];
[savePanel beginSheetModalForWindow:self.printerFeedWindow completionHandler:^(NSInteger result) {
if (result == NSModalResponseOK) {

9
Cocoa/GBHueSliderCell.h Normal file
View File

@ -0,0 +1,9 @@
#import <Cocoa/Cocoa.h>
@interface NSSlider (GBHueSlider)
-(NSColor *)colorValue;
@end
@interface GBHueSliderCell : NSSliderCell
-(NSColor *)colorValue;
@end

113
Cocoa/GBHueSliderCell.m Normal file
View File

@ -0,0 +1,113 @@
#import "GBHueSliderCell.h"
@interface NSSliderCell(privateAPI)
- (double)_normalizedDoubleValue;
@end
@implementation GBHueSliderCell
{
bool _drawingTrack;
}
-(NSColor *)colorValue
{
double hue = self.doubleValue / 360.0;
double r = 0, g = 0, b =0 ;
double t = fmod(hue * 6, 1);
switch ((int)(hue * 6) % 6) {
case 0:
r = 1;
g = t;
break;
case 1:
r = 1 - t;
g = 1;
b = 0;
case 2:
g = 1;
b = t;
break;
case 3:
g = 1 - t;
b = 1;
break;
case 4:
b = 1;
r = t;
break;
case 5:
b = 1 - t;
r = 1;
break;
}
return [NSColor colorWithRed:r green:g blue:b alpha:1.0];
}
-(void)drawKnob:(NSRect)knobRect
{
[super drawKnob:knobRect];
NSRect peekRect = knobRect;
peekRect.size.width /= 2;
peekRect.size.height /= 2;
peekRect.origin.x += peekRect.size.width / 2;
peekRect.origin.y += peekRect.size.height / 2;
NSColor *color = self.colorValue;
if (!self.enabled) {
color = [color colorWithAlphaComponent:0.5];
}
[color setFill];
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:peekRect];
[path fill];
[[NSColor colorWithWhite:0 alpha:0.25] setStroke];
[path setLineWidth:0.5];
[path stroke];
}
-(double)_normalizedDoubleValue
{
if (_drawingTrack) return 0;
return [super _normalizedDoubleValue];
}
-(void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped
{
if (!self.enabled) {
[super drawBarInside:rect flipped:flipped];
return;
}
_drawingTrack = true;
[super drawBarInside:rect flipped:flipped];
_drawingTrack = false;
NSGradient *gradient = [[NSGradient alloc] initWithColors:@[
[NSColor redColor],
[NSColor yellowColor],
[NSColor greenColor],
[NSColor cyanColor],
[NSColor blueColor],
[NSColor magentaColor],
[NSColor redColor],
]];
rect.origin.y += rect.size.height / 2 - 0.5;
rect.size.height = 1;
rect.size.width -= 2;
rect.origin.x += 1;
[[NSColor redColor] set];
NSRectFill(rect);
rect.size.width -= self.knobThickness + 2;
rect.origin.x += self.knobThickness / 2 - 1;
[gradient drawInRect:rect angle:0];
}
@end
@implementation NSSlider (GBHueSlider)
- (NSColor *)colorValue
{
return ((GBHueSliderCell *)self.cell).colorValue;
}
@end

View File

@ -0,0 +1,18 @@
#import <AppKit/AppKit.h>
#import <Core/gb.h>
@interface GBPaletteEditorController : NSObject<NSTableViewDataSource, NSTableViewDelegate>
@property (weak) IBOutlet NSColorWell *colorWell0;
@property (weak) IBOutlet NSColorWell *colorWell1;
@property (weak) IBOutlet NSColorWell *colorWell2;
@property (weak) IBOutlet NSColorWell *colorWell3;
@property (weak) IBOutlet NSColorWell *colorWell4;
@property (weak) IBOutlet NSButton *disableLCDColorCheckbox;
@property (weak) IBOutlet NSButton *manualModeCheckbox;
@property (weak) IBOutlet NSSlider *brightnessSlider;
@property (weak) IBOutlet NSSlider *hueSlider;
@property (weak) IBOutlet NSSlider *hueStrengthSlider;
@property (weak) IBOutlet NSTableView *themesList;
@property (weak) IBOutlet NSMenu *menu;
+ (const GB_palette_t *)userPalette;
@end

View File

@ -0,0 +1,378 @@
#import "GBPaletteEditorController.h"
#import "GBHueSliderCell.h"
#import <Core/gb.h>
#define MAGIC 'SBPL'
typedef struct __attribute__ ((packed)) {
uint32_t magic;
bool manual:1;
bool disabled_lcd_color:1;
unsigned padding:6;
struct GB_color_s colors[5];
int32_t brightness_bias;
uint32_t hue_bias;
uint32_t hue_bias_strength;
} theme_t;
static double blend(double from, double to, double position)
{
return from * (1 - position) + to * position;
}
@implementation NSColor (GBColor)
- (struct GB_color_s)gbColor
{
NSColor *sRGB = [self colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
return (struct GB_color_s){round(sRGB.redComponent * 255), round(sRGB.greenComponent * 255), round(sRGB.blueComponent * 255)};
}
- (uint32_t)intValue
{
struct GB_color_s color = self.gbColor;
return (color.r << 0) | (color.g << 8) | (color.b << 16) | 0xFF000000;
}
@end
@implementation GBPaletteEditorController
- (NSArray<NSColorWell *> *)colorWells
{
return @[_colorWell0, _colorWell1, _colorWell2, _colorWell3, _colorWell4];
}
- (void)updateEnabledControls
{
if (self.manualModeCheckbox.state) {
_brightnessSlider.enabled = false;
_hueSlider.enabled = false;
_hueStrengthSlider.enabled = false;
_colorWell1.enabled = true;
_colorWell2.enabled = true;
_colorWell3.enabled = true;
if (!(_colorWell4.enabled = self.disableLCDColorCheckbox.state)) {
_colorWell4.color = _colorWell3.color;
}
}
else {
_colorWell1.enabled = false;
_colorWell2.enabled = false;
_colorWell3.enabled = false;
_colorWell4.enabled = true;
_brightnessSlider.enabled = true;
_hueSlider.enabled = true;
_hueStrengthSlider.enabled = true;
[self updateAutoColors];
}
}
- (NSColor *)autoColorAtPositon:(double)position
{
NSColor *first = [_colorWell0.color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
NSColor *second = [_colorWell4.color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
double brightness = 1 / pow(4, (_brightnessSlider.doubleValue - 128) / 128.0);
position = pow(position, brightness);
NSColor *hue = _hueSlider.colorValue;
double bias = _hueStrengthSlider.doubleValue / 256.0;
double red = 1 / pow(4, (hue.redComponent * 2 - 1) * bias);
double green = 1 / pow(4, (hue.greenComponent * 2 - 1) * bias);
double blue = 1 / pow(4, (hue.blueComponent * 2 - 1) * bias);
NSColor *ret = [NSColor colorWithRed:blend(first.redComponent, second.redComponent, pow(position, red))
green:blend(first.greenComponent, second.greenComponent, pow(position, green))
blue:blend(first.blueComponent, second.blueComponent, pow(position, blue))
alpha:1.0];
return ret;
}
- (IBAction)updateAutoColors:(id)sender
{
if (!self.manualModeCheckbox.state) {
[self updateAutoColors];
}
else {
[self savePalette:sender];
}
}
- (void)updateAutoColors
{
if (_disableLCDColorCheckbox.state) {
_colorWell1.color = [self autoColorAtPositon:8 / 25.0];
_colorWell2.color = [self autoColorAtPositon:16 / 25.0];
_colorWell3.color = [self autoColorAtPositon:24 / 25.0];
}
else {
_colorWell1.color = [self autoColorAtPositon:1 / 3.0];
_colorWell2.color = [self autoColorAtPositon:2 / 3.0];
_colorWell3.color = _colorWell4.color;
}
[self savePalette:nil];
}
- (IBAction)disabledLCDColorCheckboxChanged:(id)sender
{
[self updateEnabledControls];
}
- (IBAction)manualModeChanged:(id)sender
{
[self updateEnabledControls];
}
- (IBAction)updateColor4:(id)sender
{
if (!self.disableLCDColorCheckbox.state) {
self.colorWell4.color = self.colorWell3.color;
}
[self savePalette:self];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
if (themes.count == 0) {
[defaults setObject:@"Untitled Palette" forKey:@"GBCurrentTheme"];
[self savePalette:nil];
return 1;
}
return themes.count;
}
-(void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSString *oldName = [self tableView:tableView objectValueForTableColumn:tableColumn row:row];
if ([oldName isEqualToString:object]) {
return;
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @[] mutableCopy];
NSString *newName = object;
unsigned i = 2;
if (!newName.length) {
newName = @"Untitled Palette";
}
while (themes[newName]) {
newName = [NSString stringWithFormat:@"%@ %d", object, i];
}
themes[newName] = themes[oldName];
[themes removeObjectForKey:oldName];
if ([oldName isEqualToString:[defaults stringForKey:@"GBCurrentTheme"]]) {
[defaults setObject:newName forKey:@"GBCurrentTheme"];
}
[defaults setObject:themes forKey:@"GBThemes"];
[tableView reloadData];
[self awakeFromNib];
}
- (IBAction)deleteTheme:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults stringForKey:@"GBCurrentTheme"];
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @[] mutableCopy];
[themes removeObjectForKey:name];
[defaults setObject:themes forKey:@"GBThemes"];
[_themesList reloadData];
[self awakeFromNib];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSString *name = [self tableView:nil objectValueForTableColumn:nil row:_themesList.selectedRow];
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"GBCurrentTheme"];
[self loadPalette];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
}
- (void)tableViewSelectionIsChanging:(NSNotification *)notification
{
[self tableViewSelectionDidChange:notification];
}
- (void)awakeFromNib
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
NSString *theme = [defaults stringForKey:@"GBCurrentTheme"];
if (theme && themes[theme]) {
unsigned index = [[themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] indexOfObject:theme];
[_themesList selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:false];
}
else {
[_themesList selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:false];
}
[self tableViewSelectionDidChange:nil];
}
- (IBAction)addTheme:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
NSString *newName = @"Untitled Palette";
unsigned i = 2;
while (themes[newName]) {
newName = [NSString stringWithFormat:@"Untitled Palette %d", i++];
}
[defaults setObject:newName forKey:@"GBCurrentTheme"];
[self savePalette:sender];
[_themesList reloadData];
[self awakeFromNib];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
return [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)][row];
}
- (void)loadPalette
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *theme = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]];
NSArray *colors = theme[@"Colors"];
if (colors.count == 5) {
unsigned i = 0;
for (NSNumber *color in colors) {
uint32_t c = [color unsignedIntValue];
self.colorWells[i++].color = [NSColor colorWithRed:(c & 0xFF) / 255.0
green:((c >> 8) & 0xFF) / 255.0
blue:((c >> 16) & 0xFF) / 255.0
alpha:1.0];
}
}
_disableLCDColorCheckbox.state = [theme[@"DisabledLCDColor"] boolValue];
_manualModeCheckbox.state = [theme[@"Manual"] boolValue];
_brightnessSlider.doubleValue = [theme[@"BrightnessBias"] doubleValue] * 128 + 128;
_hueSlider.doubleValue = [theme[@"HueBias"] doubleValue] * 360;
_hueStrengthSlider.doubleValue = [theme[@"HueBiasStrength"] doubleValue] * 256;
[self updateEnabledControls];
}
- (IBAction)savePalette:(id)sender
{
NSDictionary *theme = @{
@"Colors":
@[@(_colorWell0.color.intValue),
@(_colorWell1.color.intValue),
@(_colorWell2.color.intValue),
@(_colorWell3.color.intValue),
@(_colorWell4.color.intValue)],
@"DisabledLCDColor": _disableLCDColorCheckbox.state? @YES : @NO,
@"Manual": _manualModeCheckbox.state? @YES : @NO,
@"BrightnessBias": @((_brightnessSlider.doubleValue - 128) / 128.0),
@"HueBias": @(_hueSlider.doubleValue / 360.0),
@"HueBiasStrength": @(_hueStrengthSlider.doubleValue / 256.0)
};
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @[] mutableCopy];
themes[[defaults stringForKey:@"GBCurrentTheme"]] = theme;
[defaults setObject:themes forKey:@"GBThemes"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
}
+ (const GB_palette_t *)userPalette
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
switch ([defaults integerForKey:@"GBColorPalette"]) {
case 1: return &GB_PALETTE_DMG;
case 2: return &GB_PALETTE_MGB;
case 3: return &GB_PALETTE_GBL;
default: return &GB_PALETTE_GREY;
case -1: {
static GB_palette_t customPalette;
NSArray *colors = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]][@"Colors"];
if (colors.count == 5) {
unsigned i = 0;
for (NSNumber *color in colors) {
uint32_t c = [color unsignedIntValue];
customPalette.colors[i++] = (struct GB_color_s) {c, c >> 8, c >> 16};
}
}
return &customPalette;
}
}
}
- (IBAction)export:(id)sender
{
NSSavePanel *savePanel = [NSSavePanel savePanel];
[savePanel setAllowedFileTypes:@[@"sbp"]];
savePanel.nameFieldStringValue = [NSString stringWithFormat:@"%@.sbp", [[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"]];
if ([savePanel runModal] == NSModalResponseOK) {
theme_t theme = {0,};
theme.magic = MAGIC;
theme.manual = _manualModeCheckbox.state;
theme.disabled_lcd_color = _disableLCDColorCheckbox.state;
unsigned i = 0;
for (NSColorWell *well in self.colorWells) {
theme.colors[i++] = well.color.gbColor;
}
theme.brightness_bias = (_brightnessSlider.doubleValue - 128) * (0x40000000 / 128);
theme.hue_bias = round(_hueSlider.doubleValue * (0x80000000 / 360.0));
theme.hue_bias_strength = (_hueStrengthSlider.doubleValue) * (0x80000000 / 256);
size_t size = sizeof(theme);
if (theme.manual) {
size = theme.disabled_lcd_color? 5 + 5 * sizeof(theme.colors[0]) : 5 + 4 * sizeof(theme.colors[0]);
}
[[NSData dataWithBytes:&theme length:size] writeToURL:savePanel.URL atomically:false];
}
}
- (IBAction)import:(id)sender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setAllowedFileTypes:@[@"sbp"]];
if ([openPanel runModal] == NSModalResponseOK) {
NSData *data = [NSData dataWithContentsOfURL:openPanel.URL];
theme_t theme = {0,};
memcpy(&theme, data.bytes, MIN(sizeof(theme), data.length));
if (theme.magic != MAGIC) {
NSBeep();
return;
}
_manualModeCheckbox.state = theme.manual;
_disableLCDColorCheckbox.state = theme.disabled_lcd_color;
unsigned i = 0;
for (NSColorWell *well in self.colorWells) {
well.color = [NSColor colorWithRed:theme.colors[i].r / 255.0
green:theme.colors[i].g / 255.0
blue:theme.colors[i].b / 255.0
alpha:1.0];
i++;
}
if (!theme.disabled_lcd_color) {
_colorWell4.color = _colorWell3.color;
}
_brightnessSlider.doubleValue = theme.brightness_bias / (0x40000000 / 128.0) + 128;
_hueSlider.doubleValue = theme.hue_bias / (0x80000000 / 360.0);
_hueStrengthSlider.doubleValue = theme.hue_bias_strength / (0x80000000 / 256.0);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
NSString *baseName = openPanel.URL.lastPathComponent.stringByDeletingPathExtension;
NSString *newName = baseName;
i = 2;
while (themes[newName]) {
newName = [NSString stringWithFormat:@"%@ %d", baseName, i++];
}
[defaults setObject:newName forKey:@"GBCurrentTheme"];
[self savePalette:sender];
[self awakeFromNib];
}
}
- (IBAction)done:(NSButton *)sender
{
[sender.window.sheetParent endSheet:sender.window];
}
- (instancetype)init
{
static id singleton = nil;
if (singleton) return singleton;
return (singleton = [super init]);
}
@end

View File

@ -1,5 +1,6 @@
#import <Cocoa/Cocoa.h>
#import <JoyKit/JoyKit.h>
#import "GBPaletteEditorController.h"
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, JOYListener>
@property (nonatomic, strong) IBOutlet NSTableView *controlsTableView;
@ -29,4 +30,6 @@
@property (weak) IBOutlet NSSlider *volumeSlider;
@property (weak) IBOutlet NSButton *OSDCheckbox;
@property (weak) IBOutlet NSButton *screenshotFilterCheckbox;
@property (weak) IBOutlet GBPaletteEditorController *paletteEditorController;
@property (strong) IBOutlet NSWindow *paletteEditor;
@end

View File

@ -153,8 +153,14 @@
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
{
_colorPalettePopupButton = colorPalettePopupButton;
[self updatePalettesMenu];
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
[_colorPalettePopupButton selectItemAtIndex:mode];
if (mode >= 0) {
[_colorPalettePopupButton selectItemWithTag:mode];
}
else {
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
}
}
- (NSPopUpButton *)colorPalettePopupButton
@ -366,10 +372,51 @@
}
- (void)updatePalettesMenu
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
NSMenu *menu = _colorPalettePopupButton.menu;
while (menu.itemArray.count != 4) {
[menu removeItemAtIndex:4];
}
[menu addItem:[NSMenuItem separatorItem]];
for (NSString *name in [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]) {
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:name action:nil keyEquivalent:@""];
item.tag = -2;
[menu addItem:item];
}
if (themes) {
[menu addItem:[NSMenuItem separatorItem]];
}
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Custom…" action:nil keyEquivalent:@""];
item.tag = -1;
[menu addItem:item];
}
- (IBAction)colorPaletteChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBColorPalette"];
signed tag = [sender selectedItem].tag;
if (tag == -2) {
[[NSUserDefaults standardUserDefaults] setObject:@(-1)
forKey:@"GBColorPalette"];
[[NSUserDefaults standardUserDefaults] setObject:[sender selectedItem].title
forKey:@"GBCurrentTheme"];
}
else if (tag == -1) {
[[NSUserDefaults standardUserDefaults] setObject:@(-1)
forKey:@"GBColorPalette"];
[_paletteEditorController awakeFromNib];
[self beginSheet:_paletteEditor completionHandler:^(NSModalResponse returnCode) {
[self updatePalettesMenu];
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
}];
}
else {
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
forKey:@"GBColorPalette"];
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
}

View File

@ -85,6 +85,8 @@
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
<outlet property="interferenceSlider" destination="FpE-5i-j5L" id="hfH-e8-7cx"/>
<outlet property="paletteEditor" destination="g32-xe-7al" id="WLk-Hh-h5v"/>
<outlet property="paletteEditorController" destination="Zxl-vm-6c9" id="uPS-zn-osL"/>
<outlet property="playerListButton" destination="gWx-7h-0xq" id="zo6-82-JId"/>
<outlet property="preferredJoypadButton" destination="0Az-0R-oNw" id="7JM-tw-BhK"/>
<outlet property="rewindPopupButton" destination="7fg-Ww-JjR" id="Ka2-TP-B1x"/>
@ -257,9 +259,9 @@
<menuItem title="Greyscale" state="on" id="Ajr-5r-iIk">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Lime (Game Boy)" id="snU-ht-fQq"/>
<menuItem title="Olive (Pocket)" id="MQi-yt-nsT"/>
<menuItem title="Teal (Light)" id="xlg-6i-Fhl"/>
<menuItem title="Lime (Game Boy)" tag="1" id="snU-ht-fQq"/>
<menuItem title="Olive (Pocket)" tag="2" id="MQi-yt-nsT"/>
<menuItem title="Teal (Light)" tag="3" id="xlg-6i-Fhl"/>
</items>
</menu>
</popUpButtonCell>
@ -319,7 +321,7 @@
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-176" y="690.5"/>
<point key="canvasLocation" x="-501" y="236.5"/>
</customView>
<customView id="ymk-46-SX7">
<rect key="frame" x="0.0" y="0.0" width="320" height="375"/>
@ -579,7 +581,7 @@
</connections>
</slider>
</subviews>
<point key="canvasLocation" x="-825" y="455.5"/>
<point key="canvasLocation" x="-854" y="627"/>
</customView>
<customView id="8TU-6J-NCg">
<rect key="frame" x="0.0" y="0.0" width="320" height="467"/>
@ -608,7 +610,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
<rect key="frame" x="1" y="1" width="255" height="209"/>
<autoresizingMask key="autoresizingMask"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
<rect key="frame" x="0.0" y="0.0" width="255" height="209"/>
@ -770,7 +772,7 @@
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-159" y="1161.5"/>
<point key="canvasLocation" x="-854" y="260"/>
</customView>
<customView id="ffn-ie-9C3">
<rect key="frame" x="0.0" y="0.0" width="320" height="95"/>
@ -803,14 +805,262 @@
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-825" y="699.5"/>
<point key="canvasLocation" x="-854" y="808"/>
</customView>
<window title="Palette Editor" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="g32-xe-7al">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="283" y="305" width="467" height="341"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<view key="contentView" id="uMa-Yg-exg">
<rect key="frame" x="0.0" y="0.0" width="467" height="341"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DPL-nH-GVw">
<rect key="frame" x="-1" y="24" width="160" height="318"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="5Al-aC-tq8">
<rect key="frame" x="1" y="1" width="158" height="316"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" id="ZVn-bk-duk">
<rect key="frame" x="0.0" y="0.0" width="158" height="316"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="155" minWidth="40" maxWidth="1000" id="qZn-U2-gsp">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="K1k-hc-zcx">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="Zxl-vm-6c9" id="Qpu-7V-i4Q"/>
<outlet property="delegate" destination="Zxl-vm-6c9" id="FoY-pT-glW"/>
</connections>
</tableView>
</subviews>
<nil key="backgroundColor"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="zn7-g9-fNo">
<rect key="frame" x="1" y="274" width="158" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="fa0-wo-kQe">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<colorWell focusRingType="none" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="odC-WD-uUp">
<rect key="frame" x="167" y="266" width="55" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.03129877895116806" green="0.094370834529399872" blue="0.062739767134189606" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<connections>
<action selector="updateAutoColors:" target="Zxl-vm-6c9" id="wmS-sP-YVR"/>
</connections>
</colorWell>
<colorWell focusRingType="none" fixedFrame="YES" tag="1" translatesAutoresizingMaskIntoConstraints="NO" id="mpV-pm-9ZF">
<rect key="frame" x="222" y="266" width="55" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.22354039549827576" green="0.38156121969223022" blue="0.22349703311920166" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<connections>
<action selector="savePalette:" target="Zxl-vm-6c9" id="Ejl-bb-Vd8"/>
</connections>
</colorWell>
<colorWell focusRingType="none" fixedFrame="YES" tag="2" translatesAutoresizingMaskIntoConstraints="NO" id="NEt-gu-zL7">
<rect key="frame" x="277" y="266" width="55" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.51789730787277222" green="0.64901769161224365" blue="0.38817742466926575" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<connections>
<action selector="savePalette:" target="Zxl-vm-6c9" id="lle-OO-YK2"/>
</connections>
</colorWell>
<colorWell focusRingType="none" fixedFrame="YES" tag="3" translatesAutoresizingMaskIntoConstraints="NO" id="fVn-3k-iuN">
<rect key="frame" x="332" y="266" width="55" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.77688723802566528" green="0.87309378385543823" blue="0.54894673824310303" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<connections>
<action selector="updateColor4:" target="Zxl-vm-6c9" id="riQ-Kc-wcG"/>
</connections>
</colorWell>
<colorWell focusRingType="none" fixedFrame="YES" tag="4" translatesAutoresizingMaskIntoConstraints="NO" id="min-mf-sjI">
<rect key="frame" x="392" y="266" width="55" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.82731041736199085" green="0.92976116399242459" blue="0.58457564096374282" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<connections>
<action selector="updateAutoColors:" target="Zxl-vm-6c9" id="Oo4-Dx-wvS"/>
</connections>
</colorWell>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZUa-QG-4ss">
<rect key="frame" x="167" y="243" width="282" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Distinct disabled LCD color" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="cRR-vH-5R2">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="disabledLCDColorCheckboxChanged:" target="Zxl-vm-6c9" id="geJ-mH-o6c"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="l33-w0-rLw">
<rect key="frame" x="167" y="221" width="277" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Manual mode" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Gvn-Rq-LU5">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="manualModeChanged:" target="Zxl-vm-6c9" id="BAF-n1-CiQ"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XFt-AQ-h4m">
<rect key="frame" x="165" y="198" width="284" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Brightness bias:" id="qhH-ls-JEl">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wam-CT-owm">
<rect key="frame" x="177" y="162" width="272" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" continuous="YES" enabled="NO" state="on" alignment="left" maxValue="256" doubleValue="128" tickMarkPosition="above" numberOfTickMarks="3" sliderType="linear" id="ktr-gF-oxu"/>
<connections>
<action selector="updateAutoColors:" target="Zxl-vm-6c9" id="Onm-Bw-sJu"/>
</connections>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Dc6-NT-jgZ">
<rect key="frame" x="167" y="139" width="282" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Hue bias:" id="GzN-2h-xBT">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ImK-kc-EEn">
<rect key="frame" x="177" y="105" width="272" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" continuous="YES" enabled="NO" state="on" alignment="left" maxValue="360" tickMarkPosition="above" sliderType="linear" id="2dt-f1-0bV" customClass="GBHueSliderCell"/>
<connections>
<action selector="updateAutoColors:" target="Zxl-vm-6c9" id="2OR-Nl-jrN"/>
</connections>
</slider>
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ij9-bn-1y8">
<rect key="frame" x="177" y="48" width="272" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" continuous="YES" enabled="NO" state="on" alignment="left" maxValue="256" tickMarkPosition="above" sliderType="linear" id="4WJ-yV-tms"/>
<connections>
<action selector="updateAutoColors:" target="Zxl-vm-6c9" id="Hkn-HH-usp"/>
</connections>
</slider>
<box horizontalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="hcd-Iu-gEm">
<rect key="frame" x="387" y="266" width="5" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="t0j-uz-CIi">
<rect key="frame" x="0.0" y="-1" width="32" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="61Q-ht-htH">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="addTheme:" target="Zxl-vm-6c9" id="lAZ-PU-7Rk"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Qv-3A-Fy9">
<rect key="frame" x="31" y="-1" width="32" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Qax-9H-bwb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="deleteTheme:" target="Zxl-vm-6c9" id="nlR-Hp-4m9"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xtD-ub-MX7">
<rect key="frame" x="62" y="-1" width="49" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="smallSquare" title="Export" bezelStyle="smallSquare" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="RBw-u5-zpv">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
<action selector="export:" target="Zxl-vm-6c9" id="9Fg-bN-W6z"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2E2-qz-P8N">
<rect key="frame" x="110" y="-1" width="49" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="smallSquare" title="Import" bezelStyle="smallSquare" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="u9o-6G-fwU">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
<action selector="import:" target="Zxl-vm-6c9" id="OTl-HS-9Rt"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CZc-qZ-GLV">
<rect key="frame" x="167" y="82" width="277" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Hue bias strength:" id="qLB-J6-2mT">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tyc-KF-YTY">
<rect key="frame" x="372.5" y="19" width="75" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="roundTextured" title="Close" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Vhg-3I-evG">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="done:" target="Zxl-vm-6c9" id="RKB-kM-yzA"/>
</connections>
</button>
</subviews>
</view>
<point key="canvasLocation" x="-62.5" y="222.5"/>
</window>
<customObject id="Zxl-vm-6c9" customClass="GBPaletteEditorController">
<connections>
<outlet property="brightnessSlider" destination="Wam-CT-owm" id="8KP-J6-bwn"/>
<outlet property="colorWell0" destination="odC-WD-uUp" id="Ant-NL-9jb"/>
<outlet property="colorWell1" destination="mpV-pm-9ZF" id="ckr-ci-OFC"/>
<outlet property="colorWell2" destination="NEt-gu-zL7" id="Bzt-Fx-qLu"/>
<outlet property="colorWell3" destination="fVn-3k-iuN" id="7hN-Y8-efH"/>
<outlet property="colorWell4" destination="min-mf-sjI" id="5No-1R-X59"/>
<outlet property="disableLCDColorCheckbox" destination="ZUa-QG-4ss" id="Cn6-Ba-XOm"/>
<outlet property="hueSlider" destination="ImK-kc-EEn" id="bzo-ON-2bZ"/>
<outlet property="hueStrengthSlider" destination="ij9-bn-1y8" id="lcn-4q-27B"/>
<outlet property="manualModeCheckbox" destination="l33-w0-rLw" id="NBU-1I-UzJ"/>
<outlet property="menu" destination="dHJ-3R-Ora" id="SWT-qM-tca"/>
<outlet property="themesList" destination="ZVn-bk-duk" id="S4b-vM-ioi"/>
</connections>
</customObject>
</objects>
<resources>
<image name="AppIcon" width="128" height="128"/>
<image name="CPU" width="32" height="32"/>
<image name="Display" width="32" height="32"/>
<image name="Joypad" width="32" height="32"/>
<image name="NSAddTemplate" width="11" height="11"/>
<image name="NSRemoveTemplate" width="11" height="11"/>
<image name="Speaker" width="32" height="32"/>
</resources>
</document>

View File

@ -81,7 +81,7 @@
#endif
typedef struct {
struct {
struct GB_color_s {
uint8_t r, g, b;
} colors[5];
} GB_palette_t;