Skip to content

Commit

Permalink
PngParser: Add support for bKGD chunk
Browse files Browse the repository at this point in the history
  • Loading branch information
codedread committed Jan 18, 2024
1 parent af72e19 commit 9f5b3a4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
56 changes: 55 additions & 1 deletion image/parsers/png.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ByteStream } from '../../io/bytestream.js';
// https://www.w3.org/TR/png-3/
// https://en.wikipedia.org/wiki/PNG#File_format

// TODO: Ancillary chunks bKGD, eXIf, hIST, pHYs, sPLT, tIME.
// TODO: Ancillary chunks eXIf, hIST, pHYs, sPLT, tIME.

// let DEBUG = true;
let DEBUG = false;
Expand All @@ -28,6 +28,7 @@ export const PngParseEventType = {
PLTE: 'palette',

// Ancillary chunks.
bKGD: 'background_color',
cHRM: 'chromaticities_white_point',
gAMA: 'image_gamma',
iTXt: 'intl_text_data',
Expand Down Expand Up @@ -223,6 +224,24 @@ export class PngIntlTextualDataEvent extends Event {
}
}

/**
* @typedef PngBackgroundColor
* @property {number=} greyscale Only for color types 0 and 4.
* @property {number=} red Only for color types 2 and 6.
* @property {number=} green Only for color types 2 and 6.
* @property {number=} blue Only for color types 2 and 6.
* @property {number=} paletteIndex Only for color type 3.
*/

export class PngBackgroundColorEvent extends Event {
/** @param {PngBackgroundColor} backgroundColor */
constructor(backgroundColor) {
super(PngParseEventType.bKGD);
/** @type {PngBackgroundColor} */
this.backgroundColor = backgroundColor;
}
}

/**
* @typedef PngChunk Internal use only.
* @property {number} length
Expand Down Expand Up @@ -258,6 +277,16 @@ export class PngParser extends EventTarget {
this.bstream.setBigEndian();
}

/**
* Type-safe way to bind a listener for a PngBackgroundColorEvent.
* @param {function(PngBackgroundColorEvent): void} listener
* @returns {PngParser} for chaining
*/
onBackgroundColor(listener) {
super.addEventListener(PngParseEventType.bKGD, listener);
return this;
}

/**
* Type-safe way to bind a listener for a PngChromaticiesEvent.
* @param {function(PngChromaticiesEvent): void} listener
Expand Down Expand Up @@ -416,6 +445,28 @@ export class PngParser extends EventTarget {
this.dispatchEvent(new PngImageGammaEvent(chStream.readNumber(4)));
break;

// https://www.w3.org/TR/png-3/#11bKGD
case 'bKGD':
if (this.colorType === undefined) throw `bKGD before IHDR`;
if (this.colorType === PngColorType.INDEXED_COLOR && !this.palette) throw `bKGD before PLTE`;
/** @type {PngBackgroundColor} */
const bkgdColor = {};

if (this.colorType === PngColorType.GREYSCALE ||
this.colorType === PngColorType.GREYSCALE_WITH_ALPHA) {
bkgdColor.greyscale = chStream.readNumber(2);
} else if (this.colorType === PngColorType.TRUE_COLOR ||
this.colorType === PngColorType.TRUE_COLOR_WITH_ALPHA) {
bkgdColor.red = chStream.readNumber(2);
bkgdColor.green = chStream.readNumber(2);
bkgdColor.blue = chStream.readNumber(2);
} else if (this.colorType === PngColorType.INDEXED_COLOR) {
bkgdColor.paletteIndex = chStream.readNumber(1);
}

this.dispatchEvent(new PngBackgroundColorEvent(bkgdColor));
break;

// https://www.w3.org/TR/png-3/#11sBIT
case 'sBIT':
if (this.colorType === undefined) throw `sBIT before IHDR`;
Expand Down Expand Up @@ -651,6 +702,9 @@ async function main() {
parser.onIntlTextualData(evt => {
// console.dir(evt.intlTextualdata);
});
parser.onBackgroundColor(evt => {
// console.dir(evt.backgroundColor);
})

try {
await parser.start();
Expand Down
32 changes: 32 additions & 0 deletions tests/image-parsers-png.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'mocha';
import { expect } from 'chai';
import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/png.js';

/** @typedef {import('../image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */
/** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */
/** @typedef {import('../image/parsers/png.js').PngCompressedTextualData} PngCompressedTextualData */
/** @typedef {import('../image/parsers/png.js').PngImageData} PngImageData */
Expand Down Expand Up @@ -201,4 +202,35 @@ describe('bitjs.image.parsers.PngParser', () => {
expect(data[5].keyword).equals('Disclaimer');
// TODO: Test this better!
});

describe('bKGD', () => {
it('greyscale', async () => {
/** @type {PngBackgroundColor} */
let bc;
await getPngParser('tests/image-testfiles/bggn4a16.png')
.onBackgroundColor(evt => { bc = evt.backgroundColor })
.start();
expect(bc.greyscale).equals(43908);
});

it('rgb', async () => {
/** @type {PngBackgroundColor} */
let bc;
await getPngParser('tests/image-testfiles/tbrn2c08.png')
.onBackgroundColor(evt => { bc = evt.backgroundColor })
.start();
expect(bc.red).equals(255);
expect(bc.green).equals(0);
expect(bc.blue).equals(0);
});

it('paletteIndex', async () => {
/** @type {PngBackgroundColor} */
let bc;
await getPngParser('tests/image-testfiles/tbbn3p08.png')
.onBackgroundColor(evt => { bc = evt.backgroundColor })
.start();
expect(bc.paletteIndex).equals(245);
});
});
});
Binary file added tests/image-testfiles/bggn4a16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9f5b3a4

Please # to comment.