This repository has been archived on 2024-11-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Dawn-GB/scripts/gbc_old.js

247 lines
7.5 KiB
JavaScript

const fs = require('fs');
const { PNG } = require('pngjs');
const path = require('path');
const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 };
const WHITE = { r: 255, g: 255, b : 255, a: 255 };
const RED = { r: 255, g: 0, b: 0, a: 255 };
const TILE_WIDTH = 8;
const TILE_HEIGHT = 8;
// Helpers
const pixelIsSame = (left, right, alpha) => {
if(left.r !== right.r) return false;
if(left.g !== right.g) return false;
if(left.b !== right.b) return false;
if(!alpha) return true;
return left.a === right.a;
}
const imageOut = (pixels, width, fileName) => {
const png = new PNG({
width,
height: pixels.length / width
});
pixels.forEach((pixel, i) => {
const x = i % width;
const y = (i - x) / width;
const idx = (width * y + x) << 2;
png.data[idx] = pixel.r;
png.data[idx+1] = pixel.g;
png.data[idx+2] = pixel.b;
png.data[idx+3] = pixel.a;
});
if(!fs.existsSync('out')) fs.mkdirSync('out');
const out = PNG.sync.write(png);
fs.writeFileSync(path.join('out', fileName), out);
}
const imageIn = fileName => {
const data = fs.readFileSync(fileName);
const png = PNG.sync.read(data);
const pixels = [];
for(let y = 0; y < png.height; y++) {
for (let x = 0; x < png.width; x++) {
let idx = (png.width * y + x) << 2;
const r = png.data[idx];
const g = png.data[idx+1];
const b = png.data[idx+2];
const a = png.data[idx+3];
let pixel = { r, g, b, a };
if(a === 0) {
pixel = { ...WHITE };
} else {
pixel.a = 255;
}
pixels.push(pixel);
}
}
return { pixels, width: png.width, height: png.height };
}
const tileFromPixel = (x, y, original) => {
const byEightX = Math.floor(x / 8);
const byEightY = Math.floor(y / 8);
const byEightWidth = Math.floor(original.width / 8);
const byEightId = byEightX + (byEightY * byEightWidth);
return { x: byEightX, y: byEightY, columns: byEightWidth, id: byEightId };
}
// Read Input File
const original = imageIn('bruh.png');
const columns = (original.width / TILE_WIDTH);
const rows = (original.height / TILE_HEIGHT);
// Foreach pixel
const palette = [];
const paletteByEight = [];
const withPaletteOverflows = [];
for(let y = 0; y < original.height; y++) {
for(let x = 0; x < original.width; x++) {
const id = x + (y * original.width);
const pixel = original.pixels[id];
let errorPixel = { ...pixel };
const tile = tileFromPixel(x, y, original);
// Handle palettes
if(pixel.a != 0) {
const pb8 = (paletteByEight[tile.id] = paletteByEight[tile.id] || []);
if(!pb8.some(p => pixelIsSame(pixel, p))) {
pb8.push(pixel);
}
// Handle palette overflow
if(pb8.length > 4) errorPixel = { ...RED };
// Append to palette
if(!palette.some(p => pixelIsSame(pixel, p))) palette.push(pixel);
}
withPaletteOverflows.push(errorPixel);
}
}
// Generate the palette set image
const outPaletteByEight = [];
let outByEightWidth = 1;
paletteByEight.forEach((pal,y) => {
pal.forEach((p,x) => {
outByEightWidth = Math.max(outByEightWidth, x+1);
})
});
paletteByEight.forEach((pal,y) => {
for(let x = 0; x < outByEightWidth; x++) {
outPaletteByEight.push(x >= pal.length ? TRANSPARENT : pal[x]);
}
});
// Now determine for each TILE what palette to use.
const paletteGroups = [];
const gbVersion = [];
const paletteImage = [];
for(let y = 0; y < original.height; y++) {
for(let x = 0; x < original.width; x++) {
const id = x + (y * original.width);
const pixel = original.pixels[id];
const tile = tileFromPixel(x, y, original);
// Get the palette
const paletteSet = paletteByEight[tile.id];
// Check for matching
let palId = paletteGroups.findIndex(pg => {
// Check for cases where one of the pallet group palettes may have
// less pixels than the current set we're checking, e.g. we do a tile that
// has only two colors, then we iterate over a tile with 4 colors that has
// two colors shared with that other tile. In that case we just add our
// two extra colors.
if(paletteSet.length > pg.length) {
return pg.every(p => paletteSet.some(pss => pixelIsSame(pss, p)));
} else {
return paletteSet.every(p => pg.some(pgs => pixelIsSame(pgs, p)));
}
});
if(palId === -1) {
palId = paletteGroups.length;
paletteGroups.push(paletteSet);
}
const paletteGroupSet = paletteGroups[palId];
// This is where we correct the missing pixels if we share that tileset from
// earlier
paletteSet.forEach(ps => {
const existing = paletteGroupSet.some(pgs => pixelIsSame(pgs, ps));
if(existing) return;
paletteGroupSet.push(ps);
});
// Sort the paletteGroupSet...
const pgsGetWeight = thing => {
return thing.r + thing.g + thing.b;
}
paletteGroupSet.sort((l,r) => {
return pgsGetWeight(l) - pgsGetWeight(r);
});
const examples = [
/* 0 */{ r: 0, g: 0, b: 0, a: 255 },
/* 1 */{ r: 255, g: 0, b: 0, a: 255 },
/* 2 */{ r: 0, g: 255, b: 0, a: 255 },
/* 3 */{ r: 0, g: 0, b: 255, a: 255 },
/* 4 */{ r: 255, g: 255, b: 0, a: 255 },
/* 5 */{ r: 255, g: 0, b: 255, a: 255 },
/* 6 */{ r: 0, g: 255, b: 255, a: 255 },
/* 7 */{ r: 255, g: 255, b: 255, a: 255 },
/* S */{ r: 100, g: 0, b: 0, a: 255 },
/* S */{ r: 0, g: 100, b: 0, a: 255 },
/* S */{ r: 0, g: 0, b: 100, a: 255 },
/* S */{ r: 100, g: 100, b: 0, a: 255 },
/* S */{ r: 0, g: 100, b: 100, a: 255 },
/* S */{ r: 100, g: 0, b: 100, a: 255 },
/* S */{ r: 100, g: 100, b: 100, a: 255 },
];
paletteImage.push(examples[palId]);
const pixelIndex = paletteGroupSet.findIndex(ps => pixelIsSame(ps, pixel));
const nonColor = [
{ r: 8, g: 24, b: 32, a: 255 },
{ r: 52, g: 104, b: 86, a: 255 },
{ r: 136, g: 192, b: 112, a: 255 },
{ r: 224, g: 248, b: 208, a: 255 }
];
gbVersion.push(nonColor[pixelIndex % nonColor.length]);
}
}
console.log('Found', paletteGroups.length, 'palettes');
imageOut(original.pixels, original.width, 'original.png');
imageOut(withPaletteOverflows, original.width, 'errors.png');
imageOut(palette, palette.length, 'palette.png');
imageOut(outPaletteByEight, outByEightWidth, 'paletteByEight.png');
imageOut(paletteImage, original.width, 'palettes.png');
imageOut(gbVersion, original.width, 'gameboy.png');
// Now generate the GB files
// let rearranged = [];
// let n = 0;
// for(let i = 0; i < columns * rows; i++) {
// const tileX = i % columns;
// const tileY = Math.floor(i / columns) % rows;
// for(let y = 0; y < TILE_HEIGHT; y++) {
// for(let x = 0; x < TILE_WIDTH; x++) {
// const px = (tileX * TILE_WIDTH) + x;
// const py = (tileY * TILE_HEIGHT) + y;
// const pi = (py * png.width) + px;
// rearranged[n++] = original.pixels[pi];
// }
// }
// }
// // Now turn into a tileset
// const bits = [];
// for(let i = 0; i < rearranged.length; i += TILE_WIDTH) {
// let lowBits = 0x00;
// let highBits = 0x00;
// for(let j = 0; j < TILE_WIDTH; j++) {
// const pixel = rearranged[i + j];
// lowBits = lowBits | ((pixel & 0x01) << (7-j));
// highBits = highBits | ((pixel & 0x02) >> 1 << (7-j));
// }
// bits.push(lowBits, highBits);
// }