Skip to content

Commit 9f5b3a4

Browse files
committed
PngParser: Add support for bKGD chunk
1 parent af72e19 commit 9f5b3a4

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

image/parsers/png.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { ByteStream } from '../../io/bytestream.js';
1414
// https://www.w3.org/TR/png-3/
1515
// https://en.wikipedia.org/wiki/PNG#File_format
1616

17-
// TODO: Ancillary chunks bKGD, eXIf, hIST, pHYs, sPLT, tIME.
17+
// TODO: Ancillary chunks eXIf, hIST, pHYs, sPLT, tIME.
1818

1919
// let DEBUG = true;
2020
let DEBUG = false;
@@ -28,6 +28,7 @@ export const PngParseEventType = {
2828
PLTE: 'palette',
2929

3030
// Ancillary chunks.
31+
bKGD: 'background_color',
3132
cHRM: 'chromaticities_white_point',
3233
gAMA: 'image_gamma',
3334
iTXt: 'intl_text_data',
@@ -223,6 +224,24 @@ export class PngIntlTextualDataEvent extends Event {
223224
}
224225
}
225226

227+
/**
228+
* @typedef PngBackgroundColor
229+
* @property {number=} greyscale Only for color types 0 and 4.
230+
* @property {number=} red Only for color types 2 and 6.
231+
* @property {number=} green Only for color types 2 and 6.
232+
* @property {number=} blue Only for color types 2 and 6.
233+
* @property {number=} paletteIndex Only for color type 3.
234+
*/
235+
236+
export class PngBackgroundColorEvent extends Event {
237+
/** @param {PngBackgroundColor} backgroundColor */
238+
constructor(backgroundColor) {
239+
super(PngParseEventType.bKGD);
240+
/** @type {PngBackgroundColor} */
241+
this.backgroundColor = backgroundColor;
242+
}
243+
}
244+
226245
/**
227246
* @typedef PngChunk Internal use only.
228247
* @property {number} length
@@ -258,6 +277,16 @@ export class PngParser extends EventTarget {
258277
this.bstream.setBigEndian();
259278
}
260279

280+
/**
281+
* Type-safe way to bind a listener for a PngBackgroundColorEvent.
282+
* @param {function(PngBackgroundColorEvent): void} listener
283+
* @returns {PngParser} for chaining
284+
*/
285+
onBackgroundColor(listener) {
286+
super.addEventListener(PngParseEventType.bKGD, listener);
287+
return this;
288+
}
289+
261290
/**
262291
* Type-safe way to bind a listener for a PngChromaticiesEvent.
263292
* @param {function(PngChromaticiesEvent): void} listener
@@ -416,6 +445,28 @@ export class PngParser extends EventTarget {
416445
this.dispatchEvent(new PngImageGammaEvent(chStream.readNumber(4)));
417446
break;
418447

448+
// https://www.w3.org/TR/png-3/#11bKGD
449+
case 'bKGD':
450+
if (this.colorType === undefined) throw `bKGD before IHDR`;
451+
if (this.colorType === PngColorType.INDEXED_COLOR && !this.palette) throw `bKGD before PLTE`;
452+
/** @type {PngBackgroundColor} */
453+
const bkgdColor = {};
454+
455+
if (this.colorType === PngColorType.GREYSCALE ||
456+
this.colorType === PngColorType.GREYSCALE_WITH_ALPHA) {
457+
bkgdColor.greyscale = chStream.readNumber(2);
458+
} else if (this.colorType === PngColorType.TRUE_COLOR ||
459+
this.colorType === PngColorType.TRUE_COLOR_WITH_ALPHA) {
460+
bkgdColor.red = chStream.readNumber(2);
461+
bkgdColor.green = chStream.readNumber(2);
462+
bkgdColor.blue = chStream.readNumber(2);
463+
} else if (this.colorType === PngColorType.INDEXED_COLOR) {
464+
bkgdColor.paletteIndex = chStream.readNumber(1);
465+
}
466+
467+
this.dispatchEvent(new PngBackgroundColorEvent(bkgdColor));
468+
break;
469+
419470
// https://www.w3.org/TR/png-3/#11sBIT
420471
case 'sBIT':
421472
if (this.colorType === undefined) throw `sBIT before IHDR`;
@@ -651,6 +702,9 @@ async function main() {
651702
parser.onIntlTextualData(evt => {
652703
// console.dir(evt.intlTextualdata);
653704
});
705+
parser.onBackgroundColor(evt => {
706+
// console.dir(evt.backgroundColor);
707+
})
654708

655709
try {
656710
await parser.start();

tests/image-parsers-png.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'mocha';
33
import { expect } from 'chai';
44
import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/png.js';
55

6+
/** @typedef {import('../image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */
67
/** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */
78
/** @typedef {import('../image/parsers/png.js').PngCompressedTextualData} PngCompressedTextualData */
89
/** @typedef {import('../image/parsers/png.js').PngImageData} PngImageData */
@@ -201,4 +202,35 @@ describe('bitjs.image.parsers.PngParser', () => {
201202
expect(data[5].keyword).equals('Disclaimer');
202203
// TODO: Test this better!
203204
});
205+
206+
describe('bKGD', () => {
207+
it('greyscale', async () => {
208+
/** @type {PngBackgroundColor} */
209+
let bc;
210+
await getPngParser('tests/image-testfiles/bggn4a16.png')
211+
.onBackgroundColor(evt => { bc = evt.backgroundColor })
212+
.start();
213+
expect(bc.greyscale).equals(43908);
214+
});
215+
216+
it('rgb', async () => {
217+
/** @type {PngBackgroundColor} */
218+
let bc;
219+
await getPngParser('tests/image-testfiles/tbrn2c08.png')
220+
.onBackgroundColor(evt => { bc = evt.backgroundColor })
221+
.start();
222+
expect(bc.red).equals(255);
223+
expect(bc.green).equals(0);
224+
expect(bc.blue).equals(0);
225+
});
226+
227+
it('paletteIndex', async () => {
228+
/** @type {PngBackgroundColor} */
229+
let bc;
230+
await getPngParser('tests/image-testfiles/tbbn3p08.png')
231+
.onBackgroundColor(evt => { bc = evt.backgroundColor })
232+
.start();
233+
expect(bc.paletteIndex).equals(245);
234+
});
235+
});
204236
});

tests/image-testfiles/bggn4a16.png

2.17 KB
Loading

0 commit comments

Comments
 (0)