mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 15:05:25 +00:00
169 lines
6.7 KiB
TypeScript
169 lines
6.7 KiB
TypeScript
import assert from "assert";
|
|
import { describe, it } from "mocha";
|
|
import {
|
|
getDirectColorChannels, encodeDirectColorWord, decodeDirectColorWord,
|
|
convertPaletteFormat, getPaletteLength, Palettizer, SelectablePalette,
|
|
disambiguatePaletteNames, computePaletteSlices, PREDEF_LAYOUTS
|
|
} from "../../src/ide/pixeleditor";
|
|
|
|
describe('Direct-color palette formats', function () {
|
|
|
|
it('should describe channel layout for pal:444', function () {
|
|
assert.deepEqual(getDirectColorChannels({ pal: 444 }),
|
|
{ r: 4, g: 4, b: 4, rshift: 0, gshift: 4, bshift: 8 });
|
|
});
|
|
|
|
it('should describe channel layout for pal:332', function () {
|
|
assert.deepEqual(getDirectColorChannels({ pal: 332 }),
|
|
{ r: 3, g: 3, b: 2, rshift: 0, gshift: 3, bshift: 6 });
|
|
});
|
|
|
|
it('should return null for indexed palettes and non-numeric formats', function () {
|
|
assert.equal(getDirectColorChannels({ pal: "nes" }), null);
|
|
assert.equal(getDirectColorChannels({ pal: "pacman" }), null);
|
|
assert.equal(getDirectColorChannels({}), null);
|
|
});
|
|
|
|
it('should pack and unpack channel values', function () {
|
|
var word = encodeDirectColorWord({ pal: 444 }, 0xF, 0x0, 0xA);
|
|
assert.equal(word, 0xA0F);
|
|
assert.deepEqual(decodeDirectColorWord({ pal: 444 }, word), { r: 0xF, g: 0x0, b: 0xA });
|
|
});
|
|
|
|
it('should round-trip every color in a pal:444 palette', function () {
|
|
var words = getPaletteLength({ pal: 444 });
|
|
assert.equal(words, 4096);
|
|
for (var i = 0; i < words; i++) {
|
|
var c = decodeDirectColorWord({ pal: 444 }, i);
|
|
assert.equal(encodeDirectColorWord({ pal: 444 }, c.r, c.g, c.b), i);
|
|
}
|
|
});
|
|
|
|
it('should match convertPaletteFormat for encoded words', function () {
|
|
var word = encodeDirectColorWord({ pal: 444 }, 0xF, 0x0, 0xA);
|
|
assert.equal(convertPaletteFormat([word], { pal: 444 })[0] >>> 0, 0xffa000f0);
|
|
});
|
|
|
|
it('should round-trip a pal:332 word', function () {
|
|
var word = encodeDirectColorWord({ pal: 332 }, 5, 3, 2);
|
|
var c = decodeDirectColorWord({ pal: 332 }, word);
|
|
assert.deepEqual(c, { r: 5, g: 3, b: 2 });
|
|
});
|
|
|
|
});
|
|
|
|
describe('Palettizer preferred palette', function () {
|
|
var palettes: SelectablePalette[] = [
|
|
{ node: null, name: "Background", palette: new Uint32Array([1, 2, 3, 4]) },
|
|
{ node: null, name: "Sprite", palette: new Uint32Array([5, 6, 7, 8]) },
|
|
];
|
|
var context = {
|
|
setCurrentEditor: () => {},
|
|
getTilemaps: () => [],
|
|
getPalettes: () => palettes,
|
|
} as any;
|
|
|
|
it('should default to the first palette when no name is given', function () {
|
|
var p = new Palettizer(context, { w: 8, h: 8, bpp: 2 });
|
|
p.updateRefs();
|
|
assert.equal(p.palindex, 0);
|
|
assert.deepEqual(Array.from(p.palette), [1, 2, 3, 4]);
|
|
});
|
|
|
|
it('should select the palette named by fmt.palname', function () {
|
|
var p = new Palettizer(context, { w: 8, h: 8, bpp: 2, palname: "Sprite" });
|
|
p.updateRefs();
|
|
assert.equal(p.palindex, 1);
|
|
assert.deepEqual(Array.from(p.palette), [5, 6, 7, 8]);
|
|
});
|
|
|
|
it('should fall back to the first palette when the name does not match', function () {
|
|
var p = new Palettizer(context, { w: 8, h: 8, bpp: 2, palname: "Missing" });
|
|
p.updateRefs();
|
|
assert.equal(p.palindex, 0);
|
|
});
|
|
});
|
|
|
|
describe('Palette name disambiguation', function () {
|
|
function entry(name: string, group?: string): SelectablePalette {
|
|
return { node: null, name, group, palette: new Uint32Array([0, 1, 2, 3]) };
|
|
}
|
|
|
|
it('should leave unique names alone', function () {
|
|
var pals = [entry("Background"), entry("Sprite")];
|
|
disambiguatePaletteNames(pals);
|
|
assert.deepEqual(pals.map((p) => p.name), ["Background", "Sprite"]);
|
|
});
|
|
|
|
it('should prefix only the duplicated slice names with their owner', function () {
|
|
var pals = [
|
|
entry("Background 0", "Palette 1"),
|
|
entry("Sprite 0", "Palette 1"),
|
|
entry("Background 0", "Palette 2"),
|
|
entry("Sprite 0", "Palette 2"),
|
|
];
|
|
disambiguatePaletteNames(pals);
|
|
assert.deepEqual(pals.map((p) => p.name), [
|
|
"Palette 1: Background 0",
|
|
"Palette 1: Sprite 0",
|
|
"Palette 2: Background 0",
|
|
"Palette 2: Sprite 0",
|
|
]);
|
|
});
|
|
|
|
it('should not prefix a unique name even if its owner is set', function () {
|
|
var pals = [entry("Background 0", "Palette 1"), entry("Screen Color", "Palette 1")];
|
|
disambiguatePaletteNames(pals);
|
|
assert.deepEqual(pals.map((p) => p.name), ["Background 0", "Screen Color"]);
|
|
});
|
|
});
|
|
|
|
describe('Palette layouts', function () {
|
|
// 32 distinct colors so slice membership is easy to check.
|
|
function ramp(n: number): Uint32Array {
|
|
var a = new Uint32Array(n);
|
|
for (var i = 0; i < n; i++) a[i] = 0xff000000 | i;
|
|
return a;
|
|
}
|
|
|
|
it('should register sms and gg with the 16+16 mode-4 split', function () {
|
|
assert.deepEqual(PREDEF_LAYOUTS['sms'], [['Background', 0, 16], ['Sprite', 16, 16]]);
|
|
assert.deepEqual(PREDEF_LAYOUTS['gg'], PREDEF_LAYOUTS['sms']);
|
|
});
|
|
|
|
it('should slice an sms palette into background and sprite', function () {
|
|
var pal = ramp(32);
|
|
var slices = computePaletteSlices(pal, PREDEF_LAYOUTS['sms'], 16, null, 'Palette 1');
|
|
assert.deepEqual(slices.map((s) => s.name), ['Background', 'Sprite']);
|
|
assert.deepEqual(slices.map((s) => s.group), ['Palette 1', 'Palette 1']);
|
|
assert.deepEqual(Array.from(slices[0].palette), Array.from(pal.slice(0, 16)));
|
|
assert.deepEqual(Array.from(slices[1].palette), Array.from(pal.slice(16, 32)));
|
|
});
|
|
|
|
it('should drop slices whose size does not match the image pen count', function () {
|
|
// 4bpp images match the 16-color slices; 2bpp images (4 pens) do not.
|
|
assert.equal(computePaletteSlices(ramp(32), PREDEF_LAYOUTS['sms'], 4, null).length, 0);
|
|
});
|
|
|
|
it('should reverse slices with negative length', function () {
|
|
var pal = ramp(8);
|
|
var slices = computePaletteSlices(pal, PREDEF_LAYOUTS['astrocade'], 4, null);
|
|
assert.deepEqual(slices.map((s) => s.name), ['Left', 'Right']);
|
|
assert.deepEqual(Array.from(slices[0].palette), [0xff000003, 0xff000002, 0xff000001, 0xff000000]);
|
|
assert.deepEqual(Array.from(slices[1].palette), [0xff000007, 0xff000006, 0xff000005, 0xff000004]);
|
|
});
|
|
|
|
it('should prepend the shared color for nes backdrop palettes', function () {
|
|
var pal = ramp(64);
|
|
var slices = computePaletteSlices(pal, PREDEF_LAYOUTS['nes'], 4, null);
|
|
var bg0 = slices.find((s) => s.name === 'Background 0');
|
|
assert.deepEqual(Array.from(bg0.palette), [0xff000000, 0xff000001, 0xff000002, 0xff000003]); // shared color 0 + 3 entries
|
|
});
|
|
|
|
it('should ignore slices that start past the end of the palette', function () {
|
|
// 16-entry palette: Background (0-16) is in range, Sprite (16-32) is not.
|
|
var slices = computePaletteSlices(ramp(16), PREDEF_LAYOUTS['sms'], 16, null);
|
|
assert.deepEqual(slices.map((s) => s.name), ['Background']);
|
|
});
|
|
});
|