Easter egg (#123)

* lol zx quant

* Adding ZX option

* Improving colour selection so we don't end up with the same colour twice. Also fixing a bug with the colour conflict resolution.

* Putting it behind a konami code

* Better comments

* Adding comment

* Removing unnecessary malloc.
This commit is contained in:
Jake Archibald
2018-08-06 12:42:23 +01:00
committed by GitHub
parent b52d9d9194
commit ef4094885e
9 changed files with 238 additions and 9 deletions

View File

@@ -9,6 +9,7 @@ interface ModuleAPI {
create_buffer(width: number, height: number): number;
destroy_buffer(pointer: number): void;
quantize(buffer: number, width: number, height: number, numColors: number, dither: number): void;
zx_quantize(buffer: number, width: number, height: number, dither: number): void;
free_result(): void;
get_result_pointer(): number;
}
@@ -51,6 +52,7 @@ export default class ImageQuant {
create_buffer: m.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: m.cwrap('destroy_buffer', '', ['number']),
quantize: m.cwrap('quantize', '', ['number', 'number', 'number', 'number', 'number']),
zx_quantize: m.cwrap('zx_quantize', '', ['number', 'number', 'number', 'number']),
free_result: m.cwrap('free_result', '', []),
get_result_pointer: m.cwrap('get_result_pointer', 'number', []),
};
@@ -63,7 +65,11 @@ export default class ImageQuant {
const p = api.create_buffer(data.width, data.height);
m.HEAP8.set(new Uint8Array(data.data), p);
api.quantize(p, data.width, data.height, opts.maxNumColors, opts.dither);
if (opts.zx) {
api.zx_quantize(p, data.width, data.height, opts.dither);
} else {
api.quantize(p, data.width, data.height, opts.maxNumColors, opts.dither);
}
const resultPointer = api.get_result_pointer();
const resultView = new Uint8Array(
m.HEAP8.buffer,

View File

@@ -1,29 +1,55 @@
import { h, Component } from 'preact';
import { bind, inputFieldValueAsNumber } from '../../lib/util';
import { bind, inputFieldValueAsNumber, konami } from '../../lib/util';
import { QuantizeOptions } from './quantizer';
const konamiPromise = konami();
interface Props {
options: QuantizeOptions;
onChange(newOptions: QuantizeOptions): void;
}
export default class QuantizerOptions extends Component<Props, {}> {
interface State {
extendedSettings: boolean;
}
export default class QuantizerOptions extends Component<Props, State> {
state: State = { extendedSettings: false };
componentDidMount() {
konamiPromise.then(() => {
this.setState({ extendedSettings: true });
});
}
@bind
onChange(event: Event) {
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
const options: QuantizeOptions = {
zx: inputFieldValueAsNumber(form.zx),
maxNumColors: inputFieldValueAsNumber(form.maxNumColors),
dither: inputFieldValueAsNumber(form.dither),
};
this.props.onChange(options);
}
render({ options }: Props) {
render({ options }: Props, { extendedSettings }: State) {
return (
<form>
<label>
Pallette Colors:
<label style={{ display: extendedSettings ? '' : 'none' }}>
Type:
<select
name="zx"
value={'' + options.zx}
onChange={this.onChange}
>
<option value="0">Standard</option>
<option value="1">ZX</option>
</select>
</label>
<label style={{ display: options.zx ? 'none' : '' }}>
Palette Colors:
<input
name="maxNumColors"
type="range"

View File

@@ -6,11 +6,13 @@ export async function quantize(data: ImageData, opts: QuantizeOptions): Promise<
}
export interface QuantizeOptions {
zx: number;
maxNumColors: number;
dither: number;
}
export const defaultOptions: QuantizeOptions = {
zx: 0,
maxNumColors: 256,
dither: 1.0,
};

View File

@@ -159,3 +159,25 @@ export function inputFieldValueAsNumber(field: any): number {
export function inputFieldCheckedAsNumber(field: any): number {
return Number((field as HTMLInputElement).checked);
}
/**
* Creates a promise that resolves when the user types the konami code.
*/
export function konami(): Promise<void> {
return new Promise((resolve) => {
// Keycodes for: ↑ ↑ ↓ ↓ ← → ← → B A
const expectedPattern = '38384040373937396665';
let rollingPattern = '';
const listener = (event: KeyboardEvent) => {
rollingPattern += event.keyCode;
rollingPattern = rollingPattern.slice(0, expectedPattern.length);
if (rollingPattern === expectedPattern) {
window.removeEventListener('keydown', listener);
resolve();
}
};
window.addEventListener('keydown', listener);
});
}