SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
iOS/GBPaletteEditor.m
1 #import "GBPaletteEditor.h"
2 #import "GBColorWell.h"
3 #import "GBSlider.h"
4 #import "GBPalettePicker.h"
5
6 static double blend(double from, double to, double position)
7 {
8 return from * (1 - position) + to * position;
9 }
10
11 @implementation GBPaletteEditor
12 {
13 bool _displayingManual;
14 NSString *_paletteName;
15 bool _isCurrent;
16
17 UITableViewCell *_nameCell;
18 UITextField *_nameField;
19
20 UITableViewCell *_colorsCell;
21 UIColorWell *_colorWells[5];
22
23 UITableViewCell *_disabledLCDCell;
24 UISwitch *_disabledLCDSwitch;
25
26 UITableViewCell *_manualCell;
27 UISwitch *_manualSwitch;
28
29 UITableViewCell *_brightnessCell;
30 GBSlider *_brightnessSlider;
31
32 UITableViewCell *_hueCell;
33 GBSlider *_hueSlider;
34
35 UITableViewCell *_hueStrengthCell;
36 UISlider *_hueStrengthSlider;
37 }
38
39
40 - (instancetype)initForPalette:(NSString *)name
41 {
42 self = [self initWithStyle:UITableViewStyleInsetGrouped];
43 _paletteName = name;
44 _isCurrent = [[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] isEqual:name];
45 return self;
46 }
47
48 - (UITableViewCell *)sliderCellWithSlider:(UISlider *)slider
49 {
50 UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
51 CGRect rect = cell.contentView.bounds;
52 rect.size.width -= 24;
53 rect.size.height -= 24;
54 rect.origin.x += 12;
55 rect.origin.y += 12;
56 slider.frame = rect;
57 slider.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
58 [cell.contentView addSubview:slider];
59 cell.selectionStyle = UITableViewCellSelectionStyleNone;
60 [slider addTarget:self action:@selector(updateAutoColors) forControlEvents:UIControlEventValueChanged];
61 return cell;
62 }
63
64 - (void)viewDidLoad
65 {
66 [super viewDidLoad];
67 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
68 NSDictionary *theme = [defaults dictionaryForKey:@"GBThemes"][_paletteName];
69
70 {
71 _nameCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
72 CGRect frame = _nameCell.contentView.bounds;
73 frame.size.width -= - 32;
74 frame.origin.x += 16;
75 _nameField = [[UITextField alloc] initWithFrame:frame];
76 _nameField.font = _nameCell.textLabel.font;
77 _nameField.text = _paletteName;
78 _nameField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
79 [_nameCell.contentView addSubview:_nameField];
80 _nameCell.selectionStyle = UITableViewCellSelectionStyleNone;
81 }
82
83 {
84 static const unsigned wellSize = 36;
85 _colorsCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
86 CGRect frame = _nameCell.contentView.bounds;
87 UIView *view = [[UIView alloc] initWithFrame:CGRectMake((frame.size.width - wellSize * 5) / 2,
88 (frame.size.height - wellSize) / 2,
89 wellSize * 5,
90 wellSize)];
91 NSArray *titles = @[
92 @"Darkest Color",
93 @"Dark Midtone",
94 @"Light Midtone",
95 @"Lightest Color",
96 @"Display Off Color",
97 ];
98 for (unsigned i = 0; i < 5; i++) {
99 _colorWells[i] = [[GBColorWell alloc] initWithFrame:CGRectMake(i * wellSize, 0, wellSize, wellSize)];
100 _colorWells[i].supportsAlpha = false;
101 _colorWells[i].title = titles[i];
102 _colorWells[i].selectedColor = [UIColor colorWithRed:(([theme[@"Colors"][i] unsignedIntValue] >> 0) & 0xFF) / 255.0
103 green:(([theme[@"Colors"][i] unsignedIntValue] >> 8) & 0xFF) / 255.0
104 blue:(([theme[@"Colors"][i] unsignedIntValue] >> 16) & 0xFF) / 255.0
105 alpha:1.0];
106 [_colorWells[i] addTarget:self action:@selector(updateToggles) forControlEvents:UIControlEventValueChanged];
107
108 [view addSubview:_colorWells[i]];
109 }
110 view.autoresizingMask =
111 UIViewAutoresizingFlexibleLeftMargin |
112 UIViewAutoresizingFlexibleRightMargin |
113 UIViewAutoresizingFlexibleTopMargin |
114 UIViewAutoresizingFlexibleBottomMargin;
115 [_colorsCell.contentView addSubview:view];
116 _colorsCell.selectionStyle = UITableViewCellSelectionStyleNone;
117 }
118
119 {
120 _disabledLCDCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
121 _disabledLCDSwitch = [[UISwitch alloc] init];
122 _disabledLCDCell.accessoryView = _disabledLCDSwitch;
123 if ([theme[@"DisabledLCDColor"] boolValue]) {
124 _disabledLCDSwitch.on = true;
125 }
126
127 [_disabledLCDSwitch addTarget:self action:@selector(updateToggles) forControlEvents:UIControlEventValueChanged];
128 _disabledLCDCell.selectionStyle = UITableViewCellSelectionStyleNone;
129 _disabledLCDCell.textLabel.text = @"Distinct Disabled LCD Color";
130 }
131
132 {
133 _manualCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
134 _manualSwitch = [[UISwitch alloc] init];
135 _manualCell.accessoryView = _manualSwitch;
136 if ([theme[@"Manual"] boolValue]) {
137 _manualSwitch.on = true;
138 }
139
140 [_manualSwitch addTarget:self action:@selector(updateToggles) forControlEvents:UIControlEventValueChanged];
141 _manualCell.selectionStyle = UITableViewCellSelectionStyleNone;
142 _manualCell.textLabel.text = @"Manual Mode";
143 }
144
145 {
146 _brightnessSlider = [[GBSlider alloc] init];
147 _brightnessSlider.minimumValue = -1;
148 _brightnessSlider.maximumValue = 1;
149 _brightnessSlider.continuous = true;
150 _brightnessSlider.style = GBSliderStyleTicks;
151 _brightnessSlider.value = [theme[@"BrightnessBias"] doubleValue];
152 _brightnessCell = [self sliderCellWithSlider:_brightnessSlider];
153 }
154
155 {
156 _hueSlider = [[GBSlider alloc] init];
157 _hueSlider.minimumValue = 0;
158 _hueSlider.maximumValue = 360;
159 _hueSlider.continuous = true;
160 _hueSlider.style = GBSliderStyleHue;
161 _hueSlider.value = [theme[@"HueBias"] doubleValue] * 360;
162 _hueCell = [self sliderCellWithSlider:_hueSlider];
163 }
164
165 {
166 _hueStrengthSlider = [[UISlider alloc] init];
167 _hueStrengthSlider.minimumValue = 0;
168 _hueStrengthSlider.maximumValue = 1;
169 _hueStrengthSlider.continuous = true;
170 _hueStrengthSlider.value = [theme[@"HueBiasStrength"] doubleValue];
171 _hueStrengthCell = [self sliderCellWithSlider:_hueStrengthSlider];
172 }
173
174 [self updateToggles];
175 self.title = [NSString stringWithFormat:@"Editing %@", _paletteName];
176 }
177
178 - (UIColor *)autoColorAtPositon:(double)position
179 {
180
181 UIColor *first = _colorWells[0].selectedColor;
182 UIColor *second = _colorWells[4].selectedColor;
183
184 CGFloat firstRed, firstGreen, firstBlue;
185 CGFloat secondRed, secondGreen, secondBlue;
186 [first getRed:&firstRed green:&firstGreen blue:&firstBlue alpha:NULL];
187 [second getRed:&secondRed green:&secondGreen blue:&secondBlue alpha:NULL];
188
189
190 double brightness = 1 / pow(4, _brightnessSlider.value);
191 position = pow(position, brightness);
192 UIColor *hue = _hueSlider.thumbTintColor;
193 double bias = _hueStrengthSlider.value;
194
195 CGFloat red, green, blue;
196 [hue getRed:&red green:&green blue:&blue alpha:NULL];
197 red = 1 / pow(4, (red * 2 - 1) * bias);
198 green = 1 / pow(4, (green * 2 - 1) * bias);
199 blue = 1 / pow(4, (blue * 2 - 1) * bias);
200 UIColor *ret = [UIColor colorWithRed:blend(firstRed, secondRed, pow(position, red))
201 green:blend(firstGreen, secondGreen, pow(position, green))
202 blue:blend(firstBlue, secondBlue, pow(position, blue))
203 alpha:1.0];
204 return ret;
205 }
206
207 - (void)updateAutoColors
208 {
209 if (_disabledLCDSwitch.on) {
210 _colorWells[1].selectedColor = [self autoColorAtPositon:8 / 25.0];
211 _colorWells[2].selectedColor = [self autoColorAtPositon:16 / 25.0];
212 _colorWells[3].selectedColor = [self autoColorAtPositon:24 / 25.0];
213 }
214 else {
215 _colorWells[1].selectedColor = [self autoColorAtPositon:1 / 3.0];
216 _colorWells[2].selectedColor = [self autoColorAtPositon:2 / 3.0];
217 _colorWells[3].selectedColor = _colorWells[4].selectedColor;
218 }
219 [self save];
220 }
221 - (void)updateToggles
222 {
223 if (_manualSwitch.on) {
224 _colorWells[1].enabled = true;
225 _colorWells[2].enabled = true;
226 _colorWells[3].enabled = true;
227 if (!(_colorWells[4].enabled = _disabledLCDSwitch.on)) {
228 _colorWells[4].selectedColor = _colorWells[3].selectedColor;
229 }
230 if (!_displayingManual) {
231 [self.tableView deleteSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 3)]
232 withRowAnimation:UITableViewRowAnimationFade];
233 }
234 [self save];
235 }
236 else {
237 _colorWells[1].enabled = false;
238 _colorWells[2].enabled = false;
239 _colorWells[3].enabled = false;
240 _colorWells[4].enabled = true;
241 if (_displayingManual) {
242 [self.tableView insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 3)]
243 withRowAnimation:UITableViewRowAnimationFade];
244 }
245 [self updateAutoColors];
246 }
247 }
248
249 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
250 {
251 _displayingManual = _manualSwitch.on;
252 return _displayingManual? 2 : 5;
253 }
254
255 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
256 {
257 switch (section) {
258 case 0: return 1; // Name
259 case 1: return 3; // Colors
260 case 2: return 1; // Brightness Bias
261 case 3: return 2; // Hue Bias
262 default: return 0;
263 };
264 }
265
266 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
267 {
268 switch (section) {
269 case 0: return @"Palette Name";
270 case 2: return @"Brightness Bias";
271 case 3: return @"Hue Bias";
272 }
273 return nil;
274 }
275
276 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
277 {
278 switch (indexPath.section) {
279 case 0: return _nameCell;
280 case 1: {
281 switch (indexPath.row) {
282 case 0: return _colorsCell;
283 case 1: return _disabledLCDCell;
284 case 2: return _manualCell;
285 }
286 return nil;
287 }
288 case 2: return _brightnessCell;
289 case 3: return indexPath.row == 0? _hueCell : _hueStrengthCell;
290 }
291 return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
292 }
293
294 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
295 {
296 if (indexPath.section == 2) return 63;
297 return [super tableView:tableView heightForRowAtIndexPath:indexPath];
298 }
299
300 - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
301 {
302 return nil;
303 }
304
305 - (NSNumber *)encodeWell:(unsigned)index
306 {
307 CGFloat r, g, b;
308 [_colorWells[index].selectedColor getRed:&r green:&g blue:&b alpha:NULL];
309 return @((((unsigned)round(r * 255) << 0) |
310 ((unsigned)round(g * 255) << 8) |
311 ((unsigned)round(b * 255) << 16) |
312 0xFF000000));
313 }
314
315 - (void)save
316 {
317 NSMutableDictionary *themes = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBThemes"].mutableCopy;
318 [themes removeObjectForKey:_paletteName];
319 if (![_paletteName isEqual:_nameField.text]) {
320 _paletteName = [GBPalettePicker makeUnique:_nameField.text];
321 }
322
323 themes[_paletteName] = @{
324 @"BrightnessBias": @(_brightnessSlider.value),
325 @"Colors": @[[self encodeWell:0],
326 [self encodeWell:1],
327 [self encodeWell:2],
328 [self encodeWell:3],
329 [self encodeWell:4]],
330 @"DisabledLCDColor": _disabledLCDSwitch.on? @YES : @NO,
331 @"HueBias": @(_hueSlider.value / 360.0),
332 @"HueBiasStrength": @(_hueStrengthSlider.value),
333 @"Manual": _manualSwitch.on? @YES : @NO,
334 };
335
336 [[NSUserDefaults standardUserDefaults] setObject:themes forKey:@"GBThemes"];
337 if (_isCurrent) {
338 [[NSUserDefaults standardUserDefaults] setObject:@"" forKey:@"GBCurrentTheme"]; // Force a reload
339 [[NSUserDefaults standardUserDefaults] setObject:_paletteName forKey:@"GBCurrentTheme"];
340 }
341 }
342 - (void)viewWillDisappear:(BOOL)animated
343 {
344 [super viewWillDisappear:animated];
345 [self save];
346 }
347
348 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.