@city41/gba-convertpng 0.0.25 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background.d.ts +0 -1
- package/dist/background.js +118 -25
- package/dist/main.js +8 -1
- package/dist/palette.d.ts +3 -1
- package/dist/palette.js +20 -5
- package/dist/tile.js +2 -2
- package/dist/types.d.ts +1 -0
- package/package.json +2 -2
package/dist/background.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ import { BackgroundSpec, ProcessBackgroundResult } from "./types";
|
|
|
2
2
|
declare function isProcessBackgroundResult(obj: unknown): obj is ProcessBackgroundResult;
|
|
3
3
|
declare function processBackground(bg: BackgroundSpec): Promise<ProcessBackgroundResult>;
|
|
4
4
|
export { processBackground, isProcessBackgroundResult };
|
|
5
|
-
export type { ProcessBackgroundResult };
|
package/dist/background.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.processBackground = processBackground;
|
|
7
4
|
exports.isProcessBackgroundResult = isProcessBackgroundResult;
|
|
8
5
|
const canvas_1 = require("./canvas");
|
|
6
|
+
const colors_1 = require("./colors");
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
9
8
|
const palette_1 = require("./palette");
|
|
10
|
-
const tile_1 = require("./tile");
|
|
11
|
-
const isEqual_1 = __importDefault(require("lodash/isEqual"));
|
|
12
9
|
function isProcessBackgroundResult(obj) {
|
|
13
10
|
return (obj !== null &&
|
|
14
11
|
typeof obj === "object" &&
|
|
@@ -17,40 +14,136 @@ function isProcessBackgroundResult(obj) {
|
|
|
17
14
|
obj.background !== null &&
|
|
18
15
|
"file" in obj.background);
|
|
19
16
|
}
|
|
20
|
-
function
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
function convertTileTo15Bit(rawTile) {
|
|
18
|
+
const data15 = [];
|
|
19
|
+
for (let p = 0; p < rawTile.length; p += 4) {
|
|
20
|
+
if (rawTile[p + 3] !== 255) {
|
|
21
|
+
data15.push(palette_1.MAGENTA_15);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
data15.push((0, colors_1.rgbToGBA15)(rawTile[p], rawTile[p + 1], rawTile[p + 2]));
|
|
28
25
|
}
|
|
29
|
-
|
|
26
|
+
}
|
|
27
|
+
return data15;
|
|
28
|
+
}
|
|
29
|
+
function getTileIndex(tilePalette, tile) {
|
|
30
|
+
const index = tilePalette.findIndex(t => {
|
|
31
|
+
return t.join('-') === tile.join('-');
|
|
30
32
|
});
|
|
33
|
+
if (index > -1) {
|
|
34
|
+
return index;
|
|
35
|
+
}
|
|
36
|
+
tilePalette.push([...tile]);
|
|
37
|
+
return tilePalette.length - 1;
|
|
38
|
+
}
|
|
39
|
+
// looks through all the palettes and combines multiple palettes into one
|
|
40
|
+
// based on how much room they have.
|
|
41
|
+
// example, palette-a has 4 colors, palette-b has 6, result is a palette with 10 colors
|
|
42
|
+
// possibly the two palettes share colors, only one copy of each color will be preserved
|
|
43
|
+
//
|
|
44
|
+
// by the way this algoritm works, it also uniqs the palettes
|
|
45
|
+
function combinePalettes(palettes) {
|
|
46
|
+
if (palettes.length <= 1) {
|
|
47
|
+
return palettes;
|
|
48
|
+
}
|
|
49
|
+
const sortedPalettes = (0, lodash_1.sortBy)(palettes, p => p.length);
|
|
50
|
+
let firstPalette = sortedPalettes[0];
|
|
51
|
+
const remainingPalettes = [];
|
|
52
|
+
for (let p = 1; p < sortedPalettes.length; ++p) {
|
|
53
|
+
const otherPalette = sortedPalettes[p];
|
|
54
|
+
const otherPaletteUniqueColors = otherPalette.filter(c => !firstPalette.includes(c));
|
|
55
|
+
if (firstPalette.length + otherPaletteUniqueColors.length < 16) {
|
|
56
|
+
firstPalette = firstPalette.concat(otherPaletteUniqueColors);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
remainingPalettes.push(otherPalette);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const combinedOtherPalettes = combinePalettes(remainingPalettes);
|
|
63
|
+
return [firstPalette].concat(combinedOtherPalettes);
|
|
64
|
+
}
|
|
65
|
+
function buildMap(tiles, bgWidthPx, bgHeightPx) {
|
|
66
|
+
const map = [];
|
|
67
|
+
const bgWidthT = bgWidthPx / 8;
|
|
68
|
+
const bgHeightT = bgHeightPx / 8;
|
|
69
|
+
for (let y = 0; y < bgHeightT; ++y) {
|
|
70
|
+
for (let x = 0; x < bgWidthT; ++x) {
|
|
71
|
+
const tile = tiles[y * bgWidthT + x];
|
|
72
|
+
map.push(tile.paletteIndex << 12 | tile.tileIndex);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
31
75
|
return map;
|
|
32
76
|
}
|
|
77
|
+
function getGBATile(data15, palette) {
|
|
78
|
+
const gbaTile = [];
|
|
79
|
+
for (let p = 0; p < data15.length; p += 2) {
|
|
80
|
+
const highNibble = palette.indexOf(data15[p + 1]);
|
|
81
|
+
const lowNibble = palette.indexOf(data15[p]);
|
|
82
|
+
const byte = (highNibble & 0xf) << 4 | (lowNibble & 0xf);
|
|
83
|
+
gbaTile.push(byte);
|
|
84
|
+
}
|
|
85
|
+
return gbaTile;
|
|
86
|
+
}
|
|
87
|
+
function findMatchingPalette(data15, palettes) {
|
|
88
|
+
const foundPalette = palettes.find(palette => {
|
|
89
|
+
return data15.every(c => palette.includes(c));
|
|
90
|
+
});
|
|
91
|
+
if (!foundPalette) {
|
|
92
|
+
throw new Error('findMatchingPalette: failed to find a palette');
|
|
93
|
+
}
|
|
94
|
+
return foundPalette;
|
|
95
|
+
}
|
|
96
|
+
function padPalette(palette) {
|
|
97
|
+
while (palette.length < 16) {
|
|
98
|
+
palette.push(0);
|
|
99
|
+
}
|
|
100
|
+
return palette;
|
|
101
|
+
}
|
|
33
102
|
async function processBackground(bg) {
|
|
34
|
-
// const canvas = await reduceColors(await createCanvasFromPath(bg.file), 16);
|
|
35
103
|
let canvas = await (0, canvas_1.createCanvasFromPath)(bg.file);
|
|
36
104
|
if (typeof bg.reduceColors === "undefined" || bg.reduceColors === true) {
|
|
37
105
|
canvas = await (0, canvas_1.reduceColors)(canvas, 16);
|
|
38
106
|
}
|
|
39
107
|
canvas = (0, canvas_1.roundUpToTileSize)(canvas);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
108
|
+
const ctx = canvas.getContext('2d');
|
|
109
|
+
const tiles = [];
|
|
110
|
+
const tilePalette = [];
|
|
111
|
+
let palettes = [];
|
|
112
|
+
// first, determine the palettes
|
|
113
|
+
for (let y = 0; y < canvas.height; y += 8) {
|
|
114
|
+
for (let x = 0; x < canvas.width; x += 8) {
|
|
115
|
+
const rawTile = Array.from(ctx.getImageData(x, y, 8, 8).data);
|
|
116
|
+
const data15 = convertTileTo15Bit(rawTile);
|
|
117
|
+
const palette = (0, palette_1.extractPalette15)(data15, false);
|
|
118
|
+
palettes = combinePalettes(palettes.concat([palette]));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// now with palettes in hand, do the rest
|
|
122
|
+
for (let y = 0; y < canvas.height; y += 8) {
|
|
123
|
+
for (let x = 0; x < canvas.width; x += 8) {
|
|
124
|
+
const rawTile = Array.from(ctx.getImageData(x, y, 8, 8).data);
|
|
125
|
+
const data15 = convertTileTo15Bit(rawTile);
|
|
126
|
+
const palette = findMatchingPalette(data15, palettes);
|
|
127
|
+
const gbaTileData = getGBATile(data15, palette);
|
|
128
|
+
const tileIndex = getTileIndex(tilePalette, gbaTileData);
|
|
129
|
+
const paletteIndex = palettes.indexOf(palette);
|
|
130
|
+
tiles.push({
|
|
131
|
+
tileIndex,
|
|
132
|
+
paletteIndex,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
47
135
|
}
|
|
136
|
+
const tileData = tilePalette.flat(1);
|
|
137
|
+
const paletteData = palettes.map(padPalette).flat(1);
|
|
138
|
+
const paletteCount = palettes.length;
|
|
139
|
+
const map = buildMap(tiles, canvas.width, canvas.height);
|
|
48
140
|
return {
|
|
49
|
-
canvas,
|
|
50
141
|
background: bg,
|
|
51
|
-
|
|
52
|
-
palette,
|
|
142
|
+
canvas,
|
|
53
143
|
map,
|
|
144
|
+
palette: paletteData,
|
|
145
|
+
paletteCount,
|
|
146
|
+
tiles: tileData
|
|
54
147
|
};
|
|
55
148
|
}
|
|
56
149
|
//# sourceMappingURL=background.js.map
|
package/dist/main.js
CHANGED
|
@@ -96,6 +96,13 @@ function getBitmapDefines(result) {
|
|
|
96
96
|
return `#define ${name.toUpperCase()}_WIDTH ${result.width}
|
|
97
97
|
#define ${name.toUpperCase()}_HEIGHT ${result.height}`;
|
|
98
98
|
}
|
|
99
|
+
function getPaletteDefines(result, file) {
|
|
100
|
+
if ((0, sprite_1.isProcessBasicSpriteResult)(result)) {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
const name = path.basename(file, ".png");
|
|
104
|
+
return `#define ${name.toUpperCase()}_NUM_PALETTES ${result.paletteCount}`;
|
|
105
|
+
}
|
|
99
106
|
function getTileDefines(result, file) {
|
|
100
107
|
const name = path.basename(file, ".png");
|
|
101
108
|
let tileWidth = result.canvas.width / 8;
|
|
@@ -230,7 +237,7 @@ function toSrcFiles(result, format) {
|
|
|
230
237
|
extension: "c",
|
|
231
238
|
},
|
|
232
239
|
{
|
|
233
|
-
src: (0, c_1.toCh)(result.palette, "w", fileRoot + "_palette"),
|
|
240
|
+
src: (0, c_1.toCh)(result.palette, "w", fileRoot + "_palette", getPaletteDefines(result, file)),
|
|
234
241
|
extension: "h",
|
|
235
242
|
},
|
|
236
243
|
]
|
package/dist/palette.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Canvas } from "canvas";
|
|
2
|
+
declare const MAGENTA_15: number;
|
|
2
3
|
declare function getForcedPalette(c: Canvas): number[];
|
|
3
4
|
declare function extractPalette(c: Canvas, pad?: boolean): number[];
|
|
5
|
+
declare function extractPalette15(data15: number[], pad?: boolean): number[];
|
|
4
6
|
declare function reduceCanvases(canvases: Canvas[]): {
|
|
5
7
|
palette: number[];
|
|
6
8
|
canvas: Canvas;
|
|
7
9
|
};
|
|
8
|
-
export { extractPalette, getForcedPalette, reduceCanvases };
|
|
10
|
+
export { extractPalette, extractPalette15, getForcedPalette, reduceCanvases, MAGENTA_15 };
|
package/dist/palette.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAGENTA_15 = void 0;
|
|
3
4
|
exports.extractPalette = extractPalette;
|
|
5
|
+
exports.extractPalette15 = extractPalette15;
|
|
4
6
|
exports.getForcedPalette = getForcedPalette;
|
|
5
7
|
exports.reduceCanvases = reduceCanvases;
|
|
6
8
|
const canvas_1 = require("canvas");
|
|
7
9
|
const colors_1 = require("./colors");
|
|
8
10
|
const MAGENTA_24 = [255, 0, 255, 255];
|
|
9
|
-
const
|
|
11
|
+
const MAGENTA_15 = (0, colors_1.rgbToGBA15)(255, 0, 255);
|
|
12
|
+
exports.MAGENTA_15 = MAGENTA_15;
|
|
10
13
|
function is24BitMagenta(color) {
|
|
11
14
|
return (color.length === MAGENTA_24.length &&
|
|
12
15
|
color.every((channel, i) => channel === MAGENTA_24[i]));
|
|
@@ -21,9 +24,9 @@ function getForcedPalette(c) {
|
|
|
21
24
|
const gbaColor = (0, colors_1.rgbToGBA15)(r, g, b);
|
|
22
25
|
rawPalette.push(gbaColor);
|
|
23
26
|
}
|
|
24
|
-
const paletteWithoutMangenta = rawPalette.filter((c) => c !==
|
|
27
|
+
const paletteWithoutMangenta = rawPalette.filter((c) => c !== MAGENTA_15);
|
|
25
28
|
// then append magenta as the first color, to become transparent
|
|
26
|
-
const palette = [
|
|
29
|
+
const palette = [MAGENTA_15].concat(paletteWithoutMangenta);
|
|
27
30
|
return palette;
|
|
28
31
|
}
|
|
29
32
|
function extractPalette(c, pad = true) {
|
|
@@ -42,9 +45,21 @@ function extractPalette(c, pad = true) {
|
|
|
42
45
|
}
|
|
43
46
|
const rawPalette = Array.from(gbaColors);
|
|
44
47
|
// make sure there is no magenta in the palette
|
|
45
|
-
const paletteWithoutMangenta = rawPalette.filter((c) => c !==
|
|
48
|
+
const paletteWithoutMangenta = rawPalette.filter((c) => c !== MAGENTA_15);
|
|
46
49
|
// then append magenta as the first color, to become transparent
|
|
47
|
-
const palette = [
|
|
50
|
+
const palette = [MAGENTA_15].concat(paletteWithoutMangenta);
|
|
51
|
+
while (pad && palette.length < 16) {
|
|
52
|
+
palette.push(0);
|
|
53
|
+
}
|
|
54
|
+
return palette;
|
|
55
|
+
}
|
|
56
|
+
function extractPalette15(data15, pad = true) {
|
|
57
|
+
const gbaColors = new Set(data15);
|
|
58
|
+
const rawPalette = Array.from(gbaColors);
|
|
59
|
+
// make sure there is no magenta in the palette
|
|
60
|
+
const paletteWithoutMangenta = rawPalette.filter((c) => c !== MAGENTA_15);
|
|
61
|
+
// then append magenta as the first color, to become transparent
|
|
62
|
+
const palette = [MAGENTA_15].concat(paletteWithoutMangenta);
|
|
48
63
|
while (pad && palette.length < 16) {
|
|
49
64
|
palette.push(0);
|
|
50
65
|
}
|
package/dist/tile.js
CHANGED
|
@@ -6,7 +6,7 @@ const colors_1 = require("./colors");
|
|
|
6
6
|
function extractTile(imageData, palette) {
|
|
7
7
|
const tileData = [];
|
|
8
8
|
for (let p = 0; p < imageData.data.length; p += 8) {
|
|
9
|
-
//
|
|
9
|
+
// second pixel in tile, high nibble
|
|
10
10
|
// if this pixel has any transparency, then use palette index 0
|
|
11
11
|
// which will make it fully transparent on the gba
|
|
12
12
|
let hindex = 0;
|
|
@@ -18,7 +18,7 @@ function extractTile(imageData, palette) {
|
|
|
18
18
|
const hgbaColor = (0, colors_1.rgbToGBA15)(hr, hg, hb);
|
|
19
19
|
hindex = palette.indexOf(hgbaColor);
|
|
20
20
|
}
|
|
21
|
-
//
|
|
21
|
+
// first pixel in tile, low nibble
|
|
22
22
|
// if this pixel has any transparency, then use palette index 0
|
|
23
23
|
// which will make it fully transparent on the gba
|
|
24
24
|
let lindex = 0;
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@city41/gba-convertpng",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "Converts png images to GBA tile format",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "github.com/city41/gba-convertpng",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@types/nearest-color": "^0.4.1",
|
|
26
26
|
"canvas": "^3.2.0",
|
|
27
27
|
"imagemagick": "^0.1.3",
|
|
28
|
-
"lodash": "^4.17.
|
|
28
|
+
"lodash": "^4.17.23",
|
|
29
29
|
"mkdirp": "^3.0.1",
|
|
30
30
|
"nearest-color": "^0.4.4"
|
|
31
31
|
},
|