Skip to content

Commit bbfd8fc

Browse files
EscapedGibbonmicroshinetargos
authored
feat: convertIndexedToRgb method (#52)
Closes: #11 Co-authored-by: microshine <microshine@mail.ru> Co-authored-by: Michaël Zasso <targos@protonmail.com>
1 parent 8c78fbd commit bbfd8fc

File tree

5 files changed

+407
-2
lines changed

5 files changed

+407
-2
lines changed
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
import { expect, describe, it } from 'vitest';
2+
3+
import { decode, encode } from '..';
4+
import type { IndexedColors, DecodedPng } from '..';
5+
import { convertIndexedToRgb } from '../convertIndexedToRgb';
6+
7+
import { loadAndDecode } from './decode.test';
8+
9+
describe('rgb', () => {
10+
it('1 bit', () => {
11+
const palette: IndexedColors = [[0, 0, 1]];
12+
const decodedImage: DecodedPng = {
13+
width: 1,
14+
height: 1,
15+
data: new Uint8Array([27]),
16+
depth: 1,
17+
palette,
18+
channels: 1,
19+
text: {},
20+
};
21+
const view = convertIndexedToRgb(decodedImage);
22+
expect(view).toStrictEqual(Uint8Array.from([0, 0, 1]));
23+
});
24+
it('1 bit with multiple rows', () => {
25+
const palette: IndexedColors = [
26+
[0, 0, 1],
27+
[0, 0, 2],
28+
];
29+
const decodedImage: DecodedPng = {
30+
width: 10,
31+
height: 2,
32+
data: new Uint8Array([255, 192, 0, 192]),
33+
depth: 1,
34+
palette,
35+
channels: 1,
36+
text: {},
37+
};
38+
39+
const view = convertIndexedToRgb(decodedImage);
40+
expect(view).toStrictEqual(
41+
Uint8Array.from([
42+
0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2,
43+
0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
44+
0, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 2,
45+
]),
46+
);
47+
});
48+
49+
it('2 bit', () => {
50+
const palette: IndexedColors = [
51+
[0, 0, 1],
52+
[0, 0, 4],
53+
];
54+
const decodedImage: DecodedPng = {
55+
width: 2,
56+
height: 1,
57+
data: new Uint8Array([27]),
58+
depth: 2,
59+
palette,
60+
channels: 1,
61+
text: {},
62+
};
63+
64+
const view = convertIndexedToRgb(decodedImage);
65+
expect(view).toStrictEqual(Uint8Array.from([0, 0, 1, 0, 0, 4]));
66+
});
67+
68+
it('2 bit with multiple rows', () => {
69+
const palette: IndexedColors = [
70+
[0, 0, 1],
71+
[0, 0, 0],
72+
[0, 0, 3],
73+
[0, 0, 4],
74+
];
75+
const decodedImage: DecodedPng = {
76+
width: 5,
77+
height: 2,
78+
data: new Uint8Array([254, 0, 254, 0]),
79+
depth: 2,
80+
palette,
81+
channels: 1,
82+
text: {},
83+
};
84+
85+
const view = convertIndexedToRgb(decodedImage);
86+
expect(view).toStrictEqual(
87+
Uint8Array.from([
88+
0, 0, 4, 0, 0, 4, 0, 0, 4, 0, 0, 3, 0, 0, 1, 0, 0, 4, 0, 0, 4, 0, 0, 4,
89+
0, 0, 3, 0, 0, 1,
90+
]),
91+
);
92+
});
93+
94+
it('4 bit', () => {
95+
const palette: IndexedColors = [
96+
[0, 0, 0],
97+
[0, 0, 1],
98+
[0, 0, 2],
99+
[0, 0, 3],
100+
[0, 0, 4],
101+
[0, 0, 5],
102+
[0, 0, 6],
103+
[0, 0, 7],
104+
[0, 0, 8],
105+
];
106+
107+
const decodedImage: DecodedPng = {
108+
width: 8,
109+
height: 1,
110+
data: new Uint8Array([18, 52, 86, 120]),
111+
depth: 4,
112+
palette,
113+
channels: 1,
114+
text: {},
115+
};
116+
117+
const view = convertIndexedToRgb(decodedImage);
118+
expect(view).toStrictEqual(
119+
Uint8Array.from([
120+
0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5, 0, 0, 6, 0, 0, 7, 0, 0, 8,
121+
]),
122+
);
123+
});
124+
125+
it('4 bit with multiple rows', () => {
126+
const palette: IndexedColors = [
127+
[0, 0, 0],
128+
[0, 0, 1],
129+
[0, 0, 2],
130+
[0, 0, 3],
131+
[0, 0, 4],
132+
[0, 0, 5],
133+
[0, 0, 6],
134+
[0, 0, 7],
135+
[0, 0, 8],
136+
];
137+
138+
const decodedImage: DecodedPng = {
139+
width: 5,
140+
height: 2,
141+
data: new Uint8Array([18, 52, 0, 86, 120, 0]),
142+
depth: 4,
143+
palette,
144+
channels: 1,
145+
text: {},
146+
};
147+
148+
const view = convertIndexedToRgb(decodedImage);
149+
expect(view).toStrictEqual(
150+
Uint8Array.from([
151+
0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 7,
152+
0, 0, 8, 0, 0, 0,
153+
]),
154+
);
155+
});
156+
157+
it('8 bit', () => {
158+
const palette: IndexedColors = [
159+
[0, 0, 0],
160+
[0, 0, 1],
161+
[0, 0, 2],
162+
[0, 0, 3],
163+
[0, 0, 4],
164+
];
165+
const decodedImage: DecodedPng = {
166+
width: 4,
167+
height: 1,
168+
data: new Uint8Array([1, 2, 3, 4]),
169+
depth: 8,
170+
palette,
171+
channels: 1,
172+
text: {},
173+
};
174+
175+
const view = convertIndexedToRgb(decodedImage);
176+
expect(view).toStrictEqual(
177+
Uint8Array.from([0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4]),
178+
);
179+
});
180+
181+
it('convert palette.png tp simple file', () => {
182+
const img = loadAndDecode('palette.png');
183+
184+
expect(img.palette).toBeDefined();
185+
expect(img.depth).toStrictEqual(8);
186+
187+
const data = convertIndexedToRgb(img);
188+
const newImg = encode({
189+
width: img.width,
190+
height: img.height,
191+
channels: 3,
192+
depth: 8,
193+
data,
194+
});
195+
// Uncomment the next line for manual testing
196+
// fs.writeFileSync(path.join(__dirname, "../../img/palette.new.png"), newImg, { flag: "w+" });
197+
198+
const newImageParsed = decode(newImg);
199+
expect(newImageParsed.data.byteLength).toStrictEqual(90000);
200+
});
201+
it('minecraft texture with tRNS ', () => {
202+
const img = loadAndDecode('cocoa_stage2.png');
203+
expect(img.palette).toBeDefined();
204+
expect(img.depth).toStrictEqual(4);
205+
206+
const data = convertIndexedToRgb(img);
207+
const newImg = encode({
208+
width: img.width,
209+
height: img.height,
210+
channels: 4,
211+
depth: 8,
212+
data,
213+
});
214+
// Uncomment the next line for manual testing
215+
// fs.writeFileSync(path.join(__dirname, "../../img/palette.new.png"), newImg, { flag: "w+" });
216+
217+
const newImageParsed = decode(newImg);
218+
219+
expect(newImageParsed.data.byteLength).toStrictEqual(1024);
220+
});
221+
});
222+
223+
describe('rgba', () => {
224+
it('1 bit with RGBA', () => {
225+
const palette: IndexedColors = [
226+
[0, 0, 1, 255],
227+
[0, 0, 2, 255],
228+
];
229+
const decodedImage: DecodedPng = {
230+
width: 8,
231+
height: 2,
232+
data: new Uint8Array([18, 52]),
233+
depth: 1,
234+
palette,
235+
channels: 1,
236+
text: {},
237+
};
238+
239+
const view = convertIndexedToRgb(decodedImage);
240+
expect(view).toStrictEqual(
241+
Uint8Array.from(
242+
[
243+
[
244+
0, 0, 1, 255, 0, 0, 1, 255, 0, 0, 1, 255, 0, 0, 2, 255, 0, 0, 1,
245+
255, 0, 0, 1, 255, 0, 0, 2, 255, 0, 0, 1, 255,
246+
],
247+
[
248+
0, 0, 1, 255, 0, 0, 1, 255, 0, 0, 2, 255, 0, 0, 2, 255, 0, 0, 1,
249+
255, 0, 0, 2, 255, 0, 0, 1, 255, 0, 0, 1, 255,
250+
],
251+
].flat(),
252+
),
253+
);
254+
});
255+
it('4 bit with RGBA', () => {
256+
const palette: IndexedColors = [
257+
[0, 0, 0, 25],
258+
[0, 0, 1, 255],
259+
[0, 0, 2, 90],
260+
[0, 0, 3, 0],
261+
[0, 0, 4, 0],
262+
[0, 0, 5, 255],
263+
[0, 0, 6, 255],
264+
[0, 0, 7, 255],
265+
[0, 0, 8, 255],
266+
];
267+
268+
const decodedImage: DecodedPng = {
269+
width: 8,
270+
height: 1,
271+
data: new Uint8Array([18, 52, 86, 120]),
272+
depth: 4,
273+
palette,
274+
channels: 1,
275+
text: {},
276+
};
277+
278+
const view = convertIndexedToRgb(decodedImage);
279+
expect(view).toStrictEqual(
280+
Uint8Array.from([
281+
0, 0, 1, 255, 0, 0, 2, 90, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 255, 0, 0,
282+
6, 255, 0, 0, 7, 255, 0, 0, 8, 255,
283+
]),
284+
);
285+
});
286+
});
287+
288+
describe('errors', () => {
289+
it('returns an error', () => {
290+
const decodedImage: DecodedPng = {
291+
width: 1,
292+
height: 1,
293+
data: new Uint8Array([27]),
294+
depth: 1,
295+
channels: 1,
296+
text: {},
297+
};
298+
expect(() => {
299+
convertIndexedToRgb(decodedImage);
300+
}).toThrow('Color palette is undefined.');
301+
});
302+
it('throws if data length is not correct', () => {
303+
const palette: IndexedColors = [
304+
[0, 0, 1],
305+
[0, 0, 4],
306+
];
307+
const decodedImage: DecodedPng = {
308+
width: 2,
309+
height: 1,
310+
data: new Uint8Array([27, 22]),
311+
depth: 2,
312+
palette,
313+
channels: 1,
314+
text: {},
315+
};
316+
317+
expect(() => {
318+
convertIndexedToRgb(decodedImage);
319+
}).toThrow(new RangeError('wrong data size. Found 2, expected 1'));
320+
});
321+
});

src/__tests__/decode.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,12 @@ describe('decode', () => {
289289
expect(decodedApng.frames[0].data[0]).toEqual(255);
290290
expect(decodedApng.frames[110].data[1]).toEqual(1);
291291
});
292-
it('APNG RGBA image with multiple data chunks per frame', () => {
292+
it('APNG RGBA image with multiple data chunks per frame', async () => {
293293
const decodedApng = loadAndDecodeApng('rickApng.png');
294294
expect(decodedApng.frames.length).toEqual(decodedApng.numberOfFrames);
295295
expect(decodedApng.width).toEqual(1300);
296296
expect(decodedApng.height).toEqual(1300);
297-
});
297+
}, 6000);
298298
it('decode APNG image as PNG', () => {
299299
const decodedPng = loadAndDecode('beachBallApng.png');
300300
expect(decodedPng.data).toBeDefined();

0 commit comments

Comments
 (0)