From 91a5299c98a68fb763b089e7327636ad844429a1 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 26 Apr 2022 22:28:21 -0700 Subject: [PATCH] Added gbc script I'm never using again --- scripts/gbc_old.js | 247 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 scripts/gbc_old.js diff --git a/scripts/gbc_old.js b/scripts/gbc_old.js new file mode 100644 index 0000000..5b4a16a --- /dev/null +++ b/scripts/gbc_old.js @@ -0,0 +1,247 @@ +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); +// } \ No newline at end of file