Skip to content

Commit

Permalink
Allow to define custom palette colors
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscodelahoz committed Apr 9, 2024
1 parent dcd2c67 commit cc843a9
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 56 deletions.
94 changes: 65 additions & 29 deletions src/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -166,35 +166,71 @@ <h2 class="configuration-title">Compatibility Presets</h2>
</section>
<section class="configuration-group">
<h2 class="configuration-title">Appearance</h2>
<ul class="quirks-list">
<li>
<label>
Color Palette
<select name="color-palettes-select" id="color-palettes-select">
<option value="default">Default</option>
<option value="black_and_white">Black and white</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="angry_orange">Angry Orange</option>
<option value="octo">Octo</option>
<option value="arctic_horizon">Arctic Horizon</option>
<option value="harbor_mist">Harbor Mist</option>
<option value="autumn">Autumn</option>
</select>
</label>
</li>
</ul>
<ul class="quirks-list">
<li>
<label>
Font appearance
<select name="font-appearance-select" id="font-appearance-select">
<option value="chip8">Chip8</option>
<option value="schip">SCHIP</option>
<option value="octo">Octo</option>
</select>
</label>
</li>
</ul>
<section class="configuration-subsection">
<h3 class="configuration-subtitle">Color settings</h3>
<label>
Color palette presets
<select name="color-palettes-select" id="color-palettes-select">
<option value="custom" disabled>Custom</option>
<option value="default">Default</option>
<option value="black_and_white">Black and white</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="angry_orange">Angry Orange</option>
<option value="octo">Octo</option>
<option value="arctic_horizon">Arctic Horizon</option>
<option value="harbor_mist">Harbor Mist</option>
<option value="autumn">Autumn</option>
<option value="gameboy">Game Boy</option>
</select>
</label>
<table class="configuration-table" id="configuration-table">
<tbody>
<tr>
<td>Background</td>
<td class="color-value"></td>
<td class="color-swatch-container">
<input type="color" class="color-swatch" value="#000000">
<div class="color-overlay"></div>
</td>
</tr>
<tr>
<td>Foreground 1</td>
<td class="color-value"></td>
<td class="color-swatch-container">
<input type="color" class="color-swatch" value="#000000">
<div class="color-overlay"></div>
</td>
</tr>
<tr>
<td>Foreground 2</td>
<td class="color-value"></td>
<td class="color-swatch-container">
<input type="color" class="color-swatch" value="#000000">
<div class="color-overlay"></div>
</td>
</tr>
<tr>
<td>Blend</td>
<td class="color-value"></td>
<td class="color-swatch-container">
<input type="color" class="color-swatch" value="#000000">
<div class="color-overlay"></div>
</td>
</tr>
</tbody>
</table>
</section>
<section class="configuration-subsection">
<h3 class="configuration-subtitle">Font settings</h3>
<label>
Font appearance
<select name="font-appearance-select" id="font-appearance-select">
<option value="chip8">Chip8</option>
<option value="schip">SCHIP</option>
<option value="octo">Octo</option>
</select>
</label>
</section>
</section>
</div>
</aside>
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/constants/chip8.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export const defaultFontAppearance: EmulatorFontAppearance = 'octo';

export const defaultColorPalette: EmulatorColorPalette = 'default';

export const customColorPaletteKeyName = 'custom';

export enum Chip8CpuEvents {
EXIT = 'exit',
}
8 changes: 7 additions & 1 deletion src/scripts/constants/color-palettes.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,11 @@ export const colorPalettes = {
'#CBD5E1',
'#F1F5F9',
'#64748B',
]
],
gameboy: [
'#E0F8D0',
'#88C070',
'#346856',
'#081820',
],
}
8 changes: 8 additions & 0 deletions src/scripts/constants/emulator.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const emulatorConfigurationsKeys = {
palette_keys: [
'background',
'foreground1',
'foreground2',
'blend'
]
}
13 changes: 8 additions & 5 deletions src/scripts/emulator/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,6 @@ export class Chip8Emulator {
return this.cpuInstance.getMemorySize();
}

public setColorPalette(palette: EmulatorColorPalette) {
this.displayInstance.setColorPalette(palette);
this.cpuInstance.resetRom();
}

public async startEmulation(event: GenericEvent<HTMLInputElement>) {
const romData = await this.readFile(event as GenericEvent<HTMLInputElement>);
this.stopEmulatorLoop();
Expand Down Expand Up @@ -154,6 +149,14 @@ export class Chip8Emulator {
});
}

public resetRom() {
this.cpuInstance.resetRom();
}

public setPaletteColor(index: number, color: string) {
this.displayInstance.setPaletteColor(index, color);
}

public setFontAppearance(fontAppearance: EmulatorFontAppearance) {
this.cpuInstance.setFontAppearance(fontAppearance);
this.cpuInstance.resetRom();
Expand Down
20 changes: 6 additions & 14 deletions src/scripts/emulator/interfaces/display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ export class DisplayInterface {

private displayHeight: number;

private foregroundColor: string;

private planeColors: string[];

private bitPlane: number = 1;
Expand Down Expand Up @@ -50,8 +48,11 @@ export class DisplayInterface {
this.context.canvas.width = this.displayWidth;
this.context.canvas.height = this.displayHeight;

this.foregroundColor = colorPalettes.default[0];
this.planeColors = colorPalettes.default;
this.planeColors = [ ...colorPalettes.default ];
}

setPaletteColor(index: number, color: string) {
this.planeColors[index] = color;
}

createDisplayBuffer() {
Expand Down Expand Up @@ -86,15 +87,6 @@ export class DisplayInterface {
];
}

public setColorPalette(palette: EmulatorColorPalette) {
if (!colorPalettes[palette]) {
throw new Error('Invalid color palette name.');
}

this.foregroundColor = colorPalettes[palette][0];
this.planeColors = colorPalettes[palette];
}

clearDisplayBuffer() {
for (let plane = 0; plane < 2; plane += 1) {
if (!(this.bitPlane & (plane + 1))) continue;
Expand Down Expand Up @@ -130,7 +122,7 @@ export class DisplayInterface {
}

clearCanvas() {
this.context.fillStyle = this.foregroundColor;
this.context.fillStyle = this.planeColors[0];
this.context.fillRect(0, 0, this.displayWidth, this.displayHeight);
}

Expand Down
116 changes: 109 additions & 7 deletions src/scripts/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { maximumAudioGain } from './constants/audio.constants';

import {
Chip8Quirks,
customColorPaletteKeyName,
defaultColorPalette,
defaultFontAppearance,
defaultMemorySize,
Expand All @@ -11,6 +12,8 @@ import {
xoChipMemorySize,
xoChipQuirkConfigurations
} from './constants/chip8.constants';
import { colorPalettes } from './constants/color-palettes.constants';
import { emulatorConfigurationsKeys } from './constants/emulator.constants';
import { Chip8Emulator } from './emulator/emulator';
import { EmulatorColorPalette, EmulatorFontAppearance } from './types/emulator';

Expand Down Expand Up @@ -39,6 +42,8 @@ const xoChipProfileBtn = document.getElementById('xo-chip-profile') as HTMLEleme
const colorPaletteSelect = document.getElementById('color-palettes-select') as HTMLSelectElement | null;
const fontAppearanceSelect = document.getElementById('font-appearance-select') as HTMLSelectElement | null;

const colorPaletteTable = document.getElementById('configuration-table');

const emulatorInstance = new Chip8Emulator({ canvas });

function closeSideMenu() {
Expand Down Expand Up @@ -120,19 +125,116 @@ function setMemorySizeFromProfile(memorySize: number) {
}
}

function getColorValueFromLocalStorage(colorIndex: number) {
const storedColorValue = window.localStorage.getItem(emulatorConfigurationsKeys.palette_keys[colorIndex]);

if (!storedColorValue) {
return colorPalettes[defaultColorPalette][colorIndex].toUpperCase();
}

return storedColorValue.toUpperCase();
}

function storeColorInLocalStorage(colorIndex: number, colorValue: string) {
window.localStorage.setItem(emulatorConfigurationsKeys.palette_keys[colorIndex], colorValue);
}

function setColorPaletteInLocalStorage(colorPaletteName: EmulatorColorPalette) {
colorPalettes[colorPaletteName].forEach((color, index) => {
storeColorInLocalStorage(index, color);
});
}

function setColorPaletteInTable(colorPaletteName?: EmulatorColorPalette) {
colorPaletteTable?.querySelectorAll(`tr`)?.forEach((element, index) => {
const colorInput = element.querySelector('.color-value') as HTMLElement;
const colorSwatch = element.querySelector('.color-swatch') as HTMLInputElement;
const colorOverlay = element.querySelector('.color-overlay') as HTMLElement;

const colorValue = colorPaletteName ? colorPalettes[colorPaletteName][index]
: getColorValueFromLocalStorage(index);

if (colorInput) {
colorInput.innerText = colorValue;
}

if (colorSwatch) {
colorSwatch.value = colorValue;
}

if (colorOverlay) {
colorOverlay.style.backgroundColor = colorValue;
}

});
}

function setColorPaletteInSelect() {
for (const key in colorPalettes) {
const allColorsMatch = colorPalettes[(key as EmulatorColorPalette)].every((color, index) => {
return color === getColorValueFromLocalStorage(index);
});

if (colorPaletteSelect) {
if (allColorsMatch) {
colorPaletteSelect.value = key as EmulatorColorPalette;
break;
} else {
colorPaletteSelect.value = customColorPaletteKeyName;
}
}
}
}

function setColorPaletteInEmulator() {
emulatorConfigurationsKeys.palette_keys.forEach((_, index) => {
const colorValue = getColorValueFromLocalStorage(index);
emulatorInstance.setPaletteColor(index, colorValue);
});
}

function setInitialColorPaletteSelectState() {
const storedColorPalette = window.localStorage.getItem('colorPalette') as EmulatorColorPalette | null;
setColorPaletteInTable();
setColorPaletteInSelect();

if (colorPaletteSelect) {
colorPaletteSelect.value = storedColorPalette || defaultColorPalette;
setColorPaletteInEmulator();

if (colorPaletteSelect) {
colorPaletteSelect.addEventListener('change', () => {
const colorPalette = colorPaletteSelect.value;
emulatorInstance.setColorPalette((colorPalette as EmulatorColorPalette));
window.localStorage.setItem('colorPalette', colorPalette);
const selectedValue = (colorPaletteSelect.value as EmulatorColorPalette);

const colorPaletteName = colorPalettes[selectedValue] ? colorPaletteSelect.value
: defaultColorPalette;

colorPaletteSelect.value = colorPaletteName;

setColorPaletteInLocalStorage(colorPaletteName as EmulatorColorPalette);
setColorPaletteInTable(colorPaletteName as EmulatorColorPalette);

setColorPaletteInEmulator();
emulatorInstance.resetRom();
});
}

emulatorInstance.setColorPalette(storedColorPalette || defaultColorPalette);
if (colorPaletteTable) {
colorPaletteTable.querySelectorAll(`tr .color-swatch-container`).forEach((element, index) => {
const colorSwatch = element.querySelector('.color-swatch') as HTMLInputElement;

element.addEventListener('click', () => {
colorSwatch.click();
});

colorSwatch?.addEventListener('change', (element) => {
const colorValue = (element.target as HTMLInputElement).value.toUpperCase();

storeColorInLocalStorage(index, colorValue);
setColorPaletteInSelect();
setColorPaletteInTable();

setColorPaletteInEmulator();
emulatorInstance.resetRom();
});
});
}
}

Expand Down
Loading

0 comments on commit cc843a9

Please sign in to comment.