VASTLY improved how I generate tiles and tiledata
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
const process = require('process');
|
||||
const { spawnSync, execSync } = require('child_process');
|
||||
|
||||
execSync(`scp ./build/Penny.gb root@ywbud3:/storage/roms/gb/Penny.gb`);
|
||||
const process = require('process');
|
||||
const { spawnSync, execSync } = require('child_process');
|
||||
|
||||
execSync(`scp ./build/Penny.gb root@ywbud3:/storage/roms/gb/Penny.gb`);
|
||||
execSync(`echo "systemctl stop emustation.service; killall emulationstation; retroarch -L /lib/libretro/gambatte_libretro.so '/storage/roms/gb/Penny.gb';" | ssh root@ywbud3 /bin/bash`);
|
246
scripts/build.js
246
scripts/build.js
@@ -1,124 +1,124 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const process = require('process');
|
||||
const { spawnSync, execSync } = require('child_process');
|
||||
const { png2gb } = require('./png2gb');
|
||||
const { string2gb } = require('./string2gb');
|
||||
|
||||
const DIR_BUILD = path.resolve('build');
|
||||
const DIR_GENERATED = path.resolve(DIR_BUILD, 'generated');
|
||||
const DIR_OBJ = path.resolve(DIR_BUILD, 'obj');
|
||||
const DIR_SRC = path.resolve('src');
|
||||
const DIR_GBDK = path.resolve(process.env['GBDKDIR']);
|
||||
const DIR_ASSETS = path.resolve('assets');
|
||||
const DIR_IMAGES = path.resolve(DIR_ASSETS, 'images');
|
||||
|
||||
const FILE_OUT = path.resolve(DIR_BUILD, 'Penny.gb');
|
||||
const FILE_LINKFILE = path.join(DIR_BUILD, `linkflile.lk`);
|
||||
|
||||
const LCC = path.join(DIR_GBDK, 'bin', 'lcc');
|
||||
const LCCFLAGS = `-I${DIR_GENERATED} -I${DIR_SRC}`;
|
||||
|
||||
const compiledSources = [];
|
||||
|
||||
// Create build dirs
|
||||
[
|
||||
DIR_BUILD, DIR_GENERATED, DIR_OBJ
|
||||
].forEach(d => {
|
||||
if(fs.existsSync(d)) return;
|
||||
fs.mkdirSync(d);
|
||||
});
|
||||
|
||||
// Scandir
|
||||
const buildSourceFiles = directory => {
|
||||
const sources = [];
|
||||
|
||||
fs.readdirSync(directory).forEach(file => {
|
||||
const fullPath = path.join(directory, file);
|
||||
const stats = fs.statSync(fullPath);
|
||||
if(stats.isDirectory()) {
|
||||
sources.push(...buildSourceFiles(fullPath));
|
||||
return;
|
||||
}
|
||||
|
||||
if(file.endsWith('.c')) sources.push(fullPath);
|
||||
});
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
const logOut = (out, buffer) => {
|
||||
const str = [
|
||||
out.stderr, out.stdout
|
||||
].filter(n => n)
|
||||
.map(n => n.toString())
|
||||
.filter(n => n)
|
||||
.join('')
|
||||
;
|
||||
if(!str || !str.length) return;
|
||||
buffer(str);
|
||||
}
|
||||
|
||||
const compileC = (cFile) => {
|
||||
const fileNameOut = path.basename(cFile, '.c') + '.o';
|
||||
const fileOut = path.join(DIR_OBJ, fileNameOut);
|
||||
|
||||
compiledSources.push(path.join(fileNameOut));
|
||||
|
||||
if(fs.existsSync(fileOut)) return;
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = execSync(`${LCC} ${LCCFLAGS} -c -o ${fileOut} ${cFile}`);
|
||||
logOut(result, console.log);
|
||||
} catch(e) {
|
||||
logOut(e, e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate strings
|
||||
// let dataStringH = '#pragma once\n#include "libs.h"\n';
|
||||
// let dataStringC = '#include "STRINGS.h"\n';
|
||||
// Object.entries(GAME_STRINGS).forEach(entry => {
|
||||
// const [ name, str ] = entry;
|
||||
// const { dataH, dataC } = string2gb(str, name);
|
||||
// dataStringH += dataH+'\n', dataStringC += dataC+'\n';
|
||||
// });
|
||||
// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.h'), dataStringH);
|
||||
// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.c'), dataStringC);
|
||||
// compileC(path.join(DIR_GENERATED, 'STRINGS.c'));
|
||||
|
||||
// Gen imagery
|
||||
fs.readdirSync(DIR_IMAGES).forEach(img => {
|
||||
if(!img.endsWith(".png")) return;
|
||||
const { fileC, fileH } = png2gb(
|
||||
path.join(DIR_IMAGES, img), DIR_GENERATED,
|
||||
path.basename(img, '.png').toUpperCase()
|
||||
);
|
||||
compileC(fileC);
|
||||
})
|
||||
|
||||
// Get a list of sources and build each of them prior to linking.
|
||||
const allSources = buildSourceFiles(DIR_SRC);
|
||||
for(let i = 0; i < allSources.length; i++) {
|
||||
compileC(allSources[i]);
|
||||
}
|
||||
|
||||
// Generate a linkfile.
|
||||
fs.writeFileSync(FILE_LINKFILE, compiledSources.map(cs=>{
|
||||
return path.join(DIR_OBJ, cs);
|
||||
}).join('\n'));
|
||||
|
||||
// Compile BIN
|
||||
let result;
|
||||
try {
|
||||
result = execSync(`${LCC} ${LCCFLAGS} -o ${FILE_OUT} -Wl-f${FILE_LINKFILE}`);
|
||||
logOut(result, console.log);
|
||||
} catch(e) {
|
||||
logOut(e, console.error);
|
||||
process.exit(1);
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const process = require('process');
|
||||
const { spawnSync, execSync } = require('child_process');
|
||||
const { png2gb } = require('./png2gb');
|
||||
const { string2gb } = require('./string2gb');
|
||||
|
||||
const DIR_BUILD = path.resolve('build');
|
||||
const DIR_GENERATED = path.resolve(DIR_BUILD, 'generated');
|
||||
const DIR_OBJ = path.resolve(DIR_BUILD, 'obj');
|
||||
const DIR_SRC = path.resolve('src');
|
||||
const DIR_GBDK = path.resolve(process.env['GBDKDIR']);
|
||||
const DIR_ASSETS = path.resolve('assets');
|
||||
const DIR_IMAGES = path.resolve(DIR_ASSETS, 'images');
|
||||
|
||||
const FILE_OUT = path.resolve(DIR_BUILD, 'Penny.gb');
|
||||
const FILE_LINKFILE = path.join(DIR_BUILD, `linkflile.lk`);
|
||||
|
||||
const LCC = path.join(DIR_GBDK, 'bin', 'lcc');
|
||||
const LCCFLAGS = `-I${DIR_GENERATED} -I${DIR_SRC}`;
|
||||
|
||||
const compiledSources = [];
|
||||
|
||||
// Create build dirs
|
||||
[
|
||||
DIR_BUILD, DIR_GENERATED, DIR_OBJ
|
||||
].forEach(d => {
|
||||
if(fs.existsSync(d)) return;
|
||||
fs.mkdirSync(d);
|
||||
});
|
||||
|
||||
// Scandir
|
||||
const buildSourceFiles = directory => {
|
||||
const sources = [];
|
||||
|
||||
fs.readdirSync(directory).forEach(file => {
|
||||
const fullPath = path.join(directory, file);
|
||||
const stats = fs.statSync(fullPath);
|
||||
if(stats.isDirectory()) {
|
||||
sources.push(...buildSourceFiles(fullPath));
|
||||
return;
|
||||
}
|
||||
|
||||
if(file.endsWith('.c')) sources.push(fullPath);
|
||||
});
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
const logOut = (out, buffer) => {
|
||||
const str = [
|
||||
out.stderr, out.stdout
|
||||
].filter(n => n)
|
||||
.map(n => n.toString())
|
||||
.filter(n => n)
|
||||
.join('')
|
||||
;
|
||||
if(!str || !str.length) return;
|
||||
buffer(str);
|
||||
}
|
||||
|
||||
const compileC = (cFile) => {
|
||||
const fileNameOut = path.basename(cFile, '.c') + '.o';
|
||||
const fileOut = path.join(DIR_OBJ, fileNameOut);
|
||||
|
||||
compiledSources.push(path.join(fileNameOut));
|
||||
|
||||
if(fs.existsSync(fileOut)) return;
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = execSync(`${LCC} ${LCCFLAGS} -c -o ${fileOut} ${cFile}`);
|
||||
logOut(result, console.log);
|
||||
} catch(e) {
|
||||
logOut(e, e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate strings
|
||||
// let dataStringH = '#pragma once\n#include "libs.h"\n';
|
||||
// let dataStringC = '#include "STRINGS.h"\n';
|
||||
// Object.entries(GAME_STRINGS).forEach(entry => {
|
||||
// const [ name, str ] = entry;
|
||||
// const { dataH, dataC } = string2gb(str, name);
|
||||
// dataStringH += dataH+'\n', dataStringC += dataC+'\n';
|
||||
// });
|
||||
// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.h'), dataStringH);
|
||||
// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.c'), dataStringC);
|
||||
// compileC(path.join(DIR_GENERATED, 'STRINGS.c'));
|
||||
|
||||
// Gen imagery
|
||||
fs.readdirSync(DIR_IMAGES).forEach(img => {
|
||||
if(!img.endsWith(".png")) return;
|
||||
const { fileC, fileH } = png2gb(
|
||||
path.join(DIR_IMAGES, img), DIR_GENERATED,
|
||||
path.basename(img, '.png').toUpperCase()
|
||||
);
|
||||
compileC(fileC);
|
||||
})
|
||||
|
||||
// Get a list of sources and build each of them prior to linking.
|
||||
const allSources = buildSourceFiles(DIR_SRC);
|
||||
for(let i = 0; i < allSources.length; i++) {
|
||||
compileC(allSources[i]);
|
||||
}
|
||||
|
||||
// Generate a linkfile.
|
||||
fs.writeFileSync(FILE_LINKFILE, compiledSources.map(cs=>{
|
||||
return path.join(DIR_OBJ, cs);
|
||||
}).join('\n'));
|
||||
|
||||
// Compile BIN
|
||||
let result;
|
||||
try {
|
||||
result = execSync(`${LCC} ${LCCFLAGS} -o ${FILE_OUT} -Wl-f${FILE_LINKFILE}`);
|
||||
logOut(result, console.log);
|
||||
} catch(e) {
|
||||
logOut(e, console.error);
|
||||
process.exit(1);
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
console.log('🔥 Cleaning');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
console.log('🔥 Cleaning');
|
||||
rimraf.sync('build');
|
@@ -1,7 +1,7 @@
|
||||
const TILE_WIDTH = 8;
|
||||
const TILE_HEIGHT = 8;
|
||||
|
||||
module.exports = {
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT
|
||||
const TILE_WIDTH = 8;
|
||||
const TILE_HEIGHT = 8;
|
||||
|
||||
module.exports = {
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT
|
||||
}
|
@@ -1,88 +1,88 @@
|
||||
const PNG = require('pngjs').PNG;
|
||||
const fs = require('fs');
|
||||
const {
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT
|
||||
} = require('./common');
|
||||
|
||||
const colorPixel = (id) => {
|
||||
if(id === undefined) id = 3;
|
||||
if(id === 3) return { r: 8, g: 24, b: 32 };
|
||||
if(id === 2) return { r: 52, g: 104, b: 86 };
|
||||
if(id === 1) return { r: 136, g: 192, b: 112 };
|
||||
if(id === 0) return { r: 224, g: 248, b: 208 };
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const gb2png = (DATA, fileOut) => {
|
||||
// Begin
|
||||
const PIXELS = DATA.length / 2 * TILE_WIDTH;
|
||||
const DATA_WIDTH = TILE_WIDTH;
|
||||
const DATA_HEIGHT = PIXELS / DATA_WIDTH;
|
||||
|
||||
// Create output image
|
||||
const imageData = new PNG({
|
||||
width: DATA_WIDTH,
|
||||
height: DATA_HEIGHT
|
||||
});
|
||||
|
||||
// Convert data into pixels
|
||||
const pixelsOut = [];
|
||||
for(let i = 0; i < DATA.length; i += 2) {
|
||||
const low = DATA[i];
|
||||
const high = DATA[i+1];
|
||||
|
||||
for(let j = 0; j < 8; j++) {
|
||||
const mask = 0x80 >> j;
|
||||
const pixel = (low & mask ? 1 : 0) + (high & mask ? 2 : 0);
|
||||
pixelsOut.push(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer data output
|
||||
for(let y = 0; y < DATA_HEIGHT; y++) {
|
||||
for(let x = 0; x < DATA_WIDTH; x++) {
|
||||
const id = (DATA_WIDTH * y + x);
|
||||
const color = colorPixel(pixelsOut[id]);
|
||||
const idx = id << 2;
|
||||
|
||||
imageData.data[idx] = color.r;
|
||||
imageData.data[idx+1] = color.g;
|
||||
imageData.data[idx+2] = color.b;
|
||||
imageData.data[idx+3] = 0xFF;
|
||||
}
|
||||
}
|
||||
const buffer = PNG.sync.write(imageData, { });
|
||||
fs.writeFileSync(fileOut, buffer);
|
||||
}
|
||||
|
||||
// // Now work out tile data
|
||||
// if(TILEMAP.length) {
|
||||
// for(let i = 0; i < TILEMAP.length; i++) {
|
||||
// const tileX = i % TILEMAP_WIDTH;
|
||||
// const tileY = Math.floor(i / TILEMAP_WIDTH);
|
||||
// const tile = TILEMAP[i];
|
||||
|
||||
// for(let j = 0; j < TILE_WIDTH*TILE_HEIGHT; j++) {
|
||||
// const outI = (
|
||||
// (tileX * TILE_WIDTH) + (tileY * TILE_HEIGHT * TILEMAP_PIXEL_WIDTH) +
|
||||
// ((j % TILE_WIDTH) + (Math.floor(j / TILE_WIDTH) * TILEMAP_PIXEL_WIDTH))
|
||||
// );
|
||||
// const idx = outI << 2;
|
||||
// const pixelI = (tile * TILE_WIDTH * TILE_HEIGHT) + j;
|
||||
// const color = colorPixel(pixelsOut[pixelI]);
|
||||
|
||||
// tileData.data[idx] = color.r;
|
||||
// tileData.data[idx+1] = color.g;
|
||||
// tileData.data[idx+2] = color.b;
|
||||
// tileData.data[idx+3] = 0xFF;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const buffer2 = PNG.sync.write(tileData, { });
|
||||
// fs.writeFileSync('out.png', buffer2);
|
||||
// }
|
||||
|
||||
module.exports = {
|
||||
gb2png
|
||||
const PNG = require('pngjs').PNG;
|
||||
const fs = require('fs');
|
||||
const {
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT
|
||||
} = require('./common');
|
||||
|
||||
const colorPixel = (id) => {
|
||||
if(id === undefined) id = 3;
|
||||
if(id === 3) return { r: 8, g: 24, b: 32 };
|
||||
if(id === 2) return { r: 52, g: 104, b: 86 };
|
||||
if(id === 1) return { r: 136, g: 192, b: 112 };
|
||||
if(id === 0) return { r: 224, g: 248, b: 208 };
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const gb2png = (DATA, fileOut) => {
|
||||
// Begin
|
||||
const PIXELS = DATA.length / 2 * TILE_WIDTH;
|
||||
const DATA_WIDTH = TILE_WIDTH;
|
||||
const DATA_HEIGHT = PIXELS / DATA_WIDTH;
|
||||
|
||||
// Create output image
|
||||
const imageData = new PNG({
|
||||
width: DATA_WIDTH,
|
||||
height: DATA_HEIGHT
|
||||
});
|
||||
|
||||
// Convert data into pixels
|
||||
const pixelsOut = [];
|
||||
for(let i = 0; i < DATA.length; i += 2) {
|
||||
const low = DATA[i];
|
||||
const high = DATA[i+1];
|
||||
|
||||
for(let j = 0; j < 8; j++) {
|
||||
const mask = 0x80 >> j;
|
||||
const pixel = (low & mask ? 1 : 0) + (high & mask ? 2 : 0);
|
||||
pixelsOut.push(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer data output
|
||||
for(let y = 0; y < DATA_HEIGHT; y++) {
|
||||
for(let x = 0; x < DATA_WIDTH; x++) {
|
||||
const id = (DATA_WIDTH * y + x);
|
||||
const color = colorPixel(pixelsOut[id]);
|
||||
const idx = id << 2;
|
||||
|
||||
imageData.data[idx] = color.r;
|
||||
imageData.data[idx+1] = color.g;
|
||||
imageData.data[idx+2] = color.b;
|
||||
imageData.data[idx+3] = 0xFF;
|
||||
}
|
||||
}
|
||||
const buffer = PNG.sync.write(imageData, { });
|
||||
fs.writeFileSync(fileOut, buffer);
|
||||
}
|
||||
|
||||
// // Now work out tile data
|
||||
// if(TILEMAP.length) {
|
||||
// for(let i = 0; i < TILEMAP.length; i++) {
|
||||
// const tileX = i % TILEMAP_WIDTH;
|
||||
// const tileY = Math.floor(i / TILEMAP_WIDTH);
|
||||
// const tile = TILEMAP[i];
|
||||
|
||||
// for(let j = 0; j < TILE_WIDTH*TILE_HEIGHT; j++) {
|
||||
// const outI = (
|
||||
// (tileX * TILE_WIDTH) + (tileY * TILE_HEIGHT * TILEMAP_PIXEL_WIDTH) +
|
||||
// ((j % TILE_WIDTH) + (Math.floor(j / TILE_WIDTH) * TILEMAP_PIXEL_WIDTH))
|
||||
// );
|
||||
// const idx = outI << 2;
|
||||
// const pixelI = (tile * TILE_WIDTH * TILE_HEIGHT) + j;
|
||||
// const color = colorPixel(pixelsOut[pixelI]);
|
||||
|
||||
// tileData.data[idx] = color.r;
|
||||
// tileData.data[idx+1] = color.g;
|
||||
// tileData.data[idx+2] = color.b;
|
||||
// tileData.data[idx+3] = 0xFF;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const buffer2 = PNG.sync.write(tileData, { });
|
||||
// fs.writeFileSync('out.png', buffer2);
|
||||
// }
|
||||
|
||||
module.exports = {
|
||||
gb2png
|
||||
};
|
@@ -1,247 +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);
|
||||
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);
|
||||
// }
|
@@ -1,93 +1,101 @@
|
||||
const PNG = require('pngjs').PNG;
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { arrayToString } = require('./util');
|
||||
const {
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT
|
||||
} = require('./common');
|
||||
|
||||
|
||||
const getPixelValue = (pixel) => {
|
||||
if(pixel.g === 188) return 0;
|
||||
if(pixel.g === 172) return 1;
|
||||
if(pixel.g === 98) return 2;
|
||||
if(pixel.g === 56) return 3;
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const png2gb = (fileIn, dirOut, name) => {
|
||||
const data = fs.readFileSync(fileIn);
|
||||
const png = PNG.sync.read(data);
|
||||
|
||||
// Convert PNG pixels into 0x00-0x03
|
||||
const pixels = [];
|
||||
for(let y = 0; y < png.height; y++) {
|
||||
for(let x = 0; x < png.width; x++) {
|
||||
const id = x + (y * png.width);
|
||||
const idx = id << 2;
|
||||
const r = png.data[idx];
|
||||
const g = png.data[idx+1];
|
||||
const b = png.data[idx+2];
|
||||
const value = getPixelValue({ r, g, b });
|
||||
pixels.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Now take these raw pixels and extract the tiles themselves
|
||||
let rearranged = [];
|
||||
const columns = (png.width / TILE_WIDTH);
|
||||
const rows = (png.height / TILE_HEIGHT);
|
||||
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++] = 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);
|
||||
}
|
||||
|
||||
let outH = '';
|
||||
outH += `#include "libs.h"\n\n`
|
||||
outH += `#define ${name}_IMAGE_WIDTH ${png.width}\n`;
|
||||
outH += `#define ${name}_IMAGE_HEIGHT ${png.height}\n`;
|
||||
outH += `#define ${name}_IMAGE_COLUMNS ${png.width / TILE_WIDTH}\n`;
|
||||
outH += `#define ${name}_IMAGE_ROWS ${png.height / TILE_HEIGHT}\n`;
|
||||
outH += `#define ${name}_IMAGE_TILES ${columns * rows}\n`;
|
||||
outH += `extern const uint8_t ${name}_IMAGE[];`;
|
||||
|
||||
let outC = `#include "${name}.h"\n`;
|
||||
outC += `\nconst uint8_t ${name}_IMAGE[] = {\n${arrayToString(bits)}};`;
|
||||
|
||||
const fileH = path.join(dirOut, name + '.h');
|
||||
const fileC = path.join(dirOut, name + '.c');
|
||||
|
||||
fs.writeFileSync(fileH, outH);
|
||||
fs.writeFileSync(fileC, outC);
|
||||
return { fileH, fileC };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
png2gb
|
||||
}
|
||||
|
||||
const PNG = require('pngjs').PNG;
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { arrayToString } = require('./util');
|
||||
const {
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT
|
||||
} = require('./common');
|
||||
|
||||
|
||||
const getPixelValue = (pixel) => {
|
||||
if(pixel.g === 188) return 0;
|
||||
if(pixel.g === 172) return 1;
|
||||
if(pixel.g === 98 || pixel.g === 145) return 2;
|
||||
if(pixel.g === 56) return 3;
|
||||
if(pixel.a === 0) return 0;
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const png2gb = (fileIn, dirOut, name) => {
|
||||
const data = fs.readFileSync(fileIn);
|
||||
const png = PNG.sync.read(data);
|
||||
|
||||
// Convert PNG pixels into 0x00-0x03
|
||||
const pixels = [];
|
||||
for(let y = 0; y < png.height; y++) {
|
||||
for(let x = 0; x < png.width; x++) {
|
||||
const id = x + (y * png.width);
|
||||
const idx = id << 2;
|
||||
const r = png.data[idx];
|
||||
const g = png.data[idx+1];
|
||||
const b = png.data[idx+2];
|
||||
const a = png.data[idx+3];
|
||||
const pixel = { r, g, b, a };
|
||||
try {
|
||||
const value = getPixelValue(pixel);
|
||||
pixels.push(value);
|
||||
} catch(e) {
|
||||
console.error(`Failed to get color for `, x, y, idx, fileIn, pixel);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now take these raw pixels and extract the tiles themselves
|
||||
let rearranged = [];
|
||||
const columns = (png.width / TILE_WIDTH);
|
||||
const rows = (png.height / TILE_HEIGHT);
|
||||
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++] = 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);
|
||||
}
|
||||
|
||||
let outH = '';
|
||||
outH += `#include "libs.h"\n\n`
|
||||
outH += `#define ${name}_IMAGE_WIDTH ${png.width}\n`;
|
||||
outH += `#define ${name}_IMAGE_HEIGHT ${png.height}\n`;
|
||||
outH += `#define ${name}_IMAGE_COLUMNS ${png.width / TILE_WIDTH}\n`;
|
||||
outH += `#define ${name}_IMAGE_ROWS ${png.height / TILE_HEIGHT}\n`;
|
||||
outH += `#define ${name}_IMAGE_TILES ${columns * rows}\n`;
|
||||
outH += `extern const uint8_t ${name}_IMAGE[];`;
|
||||
|
||||
let outC = `#include "${name}.h"\n`;
|
||||
outC += `\nconst uint8_t ${name}_IMAGE[] = {\n${arrayToString(bits)}};`;
|
||||
|
||||
const fileH = path.join(dirOut, name + '.h');
|
||||
const fileC = path.join(dirOut, name + '.c');
|
||||
|
||||
fs.writeFileSync(fileH, outH);
|
||||
fs.writeFileSync(fileC, outC);
|
||||
return { fileH, fileC };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
png2gb
|
||||
}
|
||||
|
||||
// convert('images/sm.png', 'out.c', 'PENNY');
|
@@ -1,32 +1,32 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { arrayToString } = require('./util');
|
||||
|
||||
const FONT_CHARACTER_FIRST = 33;
|
||||
const FONT_DATA_POSITION = 4;
|
||||
|
||||
const getCodeFrom = l => {
|
||||
const cc = l.charCodeAt(0)
|
||||
if(l == '\n' || l == ' ') return cc;
|
||||
return cc - FONT_CHARACTER_FIRST + FONT_DATA_POSITION
|
||||
}
|
||||
|
||||
const string2gb = (string, name) => {
|
||||
const letters = [];
|
||||
for(let i = 0; i < string.length; i++) {
|
||||
letters.push(getCodeFrom(string[i]));
|
||||
}
|
||||
|
||||
let dataH = `#define STR_${name}_LENGTH ${string.length}\n`;
|
||||
dataH += `extern const uint8_t STR_${name}_DATA[];`;
|
||||
|
||||
let dataC = `const uint8_t STR_${name}_DATA[] = {\n`;
|
||||
dataC += arrayToString(letters);
|
||||
dataC += `\n};`;
|
||||
|
||||
return { dataH, dataC };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
string2gb
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { arrayToString } = require('./util');
|
||||
|
||||
const FONT_CHARACTER_FIRST = 33;
|
||||
const FONT_DATA_POSITION = 4;
|
||||
|
||||
const getCodeFrom = l => {
|
||||
const cc = l.charCodeAt(0)
|
||||
if(l == '\n' || l == ' ') return cc;
|
||||
return cc - FONT_CHARACTER_FIRST + FONT_DATA_POSITION
|
||||
}
|
||||
|
||||
const string2gb = (string, name) => {
|
||||
const letters = [];
|
||||
for(let i = 0; i < string.length; i++) {
|
||||
letters.push(getCodeFrom(string[i]));
|
||||
}
|
||||
|
||||
let dataH = `#define STR_${name}_LENGTH ${string.length}\n`;
|
||||
dataH += `extern const uint8_t STR_${name}_DATA[];`;
|
||||
|
||||
let dataC = `const uint8_t STR_${name}_DATA[] = {\n`;
|
||||
dataC += arrayToString(letters);
|
||||
dataC += `\n};`;
|
||||
|
||||
return { dataH, dataC };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
string2gb
|
||||
};
|
@@ -1,21 +1,21 @@
|
||||
const arrayToString = arr => {
|
||||
const b = arr.map(n => {
|
||||
return '0x' + (n.toString(16).padStart(2, '0').toUpperCase());
|
||||
});
|
||||
|
||||
let str = '';
|
||||
for(let i = 0; i < b.length; i += 16) {
|
||||
str += ' ';
|
||||
for(let x = i; x < Math.min(i+16, b.length); x++) {
|
||||
str += b[x];
|
||||
str += ',';
|
||||
}
|
||||
str += '\n';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
arrayToString
|
||||
const arrayToString = arr => {
|
||||
const b = arr.map(n => {
|
||||
return '0x' + (n.toString(16).padStart(2, '0').toUpperCase());
|
||||
});
|
||||
|
||||
let str = '';
|
||||
for(let i = 0; i < b.length; i += 16) {
|
||||
str += ' ';
|
||||
for(let x = i; x < Math.min(i+16, b.length); x++) {
|
||||
str += b[x];
|
||||
str += ',';
|
||||
}
|
||||
str += '\n';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
arrayToString
|
||||
}
|
Reference in New Issue
Block a user