1111import * as fs from 'node:fs' ; // TODO: Remove.
1212import { ByteStream } from '../../io/bytestream.js' ;
1313
14- // https://www.w3.org/TR/2003/REC-PNG-20031110
14+ // https://www.w3.org/TR/png-3/
1515// https://en.wikipedia.org/wiki/PNG#File_format
1616
17- // TODO: Ancillary chunks bKGD, cHRM , hIST, iTXt, pHYs, sPLT, tEXt, tIME, zTXt.
17+ // TODO: Ancillary chunks bKGD, eXIf , hIST, iTXt, pHYs, sPLT, tEXt, tIME, zTXt.
1818
1919// let DEBUG = true;
2020let DEBUG = false ;
@@ -25,6 +25,7 @@ export const PngParseEventType = {
2525 IHDR : 'image_header' ,
2626 gAMA : 'image_gamma' ,
2727 sBIT : 'significant_bits' ,
28+ cHRM : 'chromaticities_white_point' ,
2829 PLTE : 'palette' ,
2930 tRNS : 'transparency' ,
3031 IDAT : 'image_data' ,
@@ -92,6 +93,27 @@ export class PngSignificantBitsEvent extends Event {
9293 }
9394}
9495
96+ /**
97+ * @typedef PngChromaticies
98+ * @property {number } whitePointX
99+ * @property {number } whitePointY
100+ * @property {number } redX
101+ * @property {number } redY
102+ * @property {number } greenX
103+ * @property {number } greenY
104+ * @property {number } blueX
105+ * @property {number } blueY
106+ */
107+
108+ export class PngChromaticitiesEvent extends Event {
109+ /** @param {PngChromaticies } chromaticities */
110+ constructor ( chromaticities ) {
111+ super ( PngParseEventType . cHRM ) ;
112+ /** @type {PngChromaticies } */
113+ this . chromaticities = chromaticities ;
114+ }
115+ }
116+
95117/**
96118 * @typedef PngColor
97119 * @property {number } red
@@ -105,7 +127,7 @@ export class PngSignificantBitsEvent extends Event {
105127 */
106128
107129export class PngPaletteEvent extends Event {
108- /** @param {PngPalette } */
130+ /** @param {PngPalette } palette */
109131 constructor ( palette ) {
110132 super ( PngParseEventType . PLTE ) ;
111133 /** @type {PngPalette } */
@@ -123,7 +145,7 @@ export class PngPaletteEvent extends Event {
123145 */
124146
125147export class PngTransparencyEvent extends Event {
126- /** @param {PngTransparency } */
148+ /** @param {PngTransparency } transparency */
127149 constructor ( transparency ) {
128150 super ( PngParseEventType . tRNS ) ;
129151 /** @type {PngTransparency } */
@@ -137,7 +159,7 @@ export class PngTransparencyEvent extends Event {
137159 */
138160
139161export class PngImageDataEvent extends Event {
140- /** @param {PngImageData } */
162+ /** @param {PngImageData } data */
141163 constructor ( data ) {
142164 super ( PngParseEventType . IDAT ) ;
143165 /** @type {PngImageData } */
@@ -210,6 +232,16 @@ export class PngParser extends EventTarget {
210232 return this ;
211233 }
212234
235+ /**
236+ * Type-safe way to bind a listener for a PngChromaticiesEvent.
237+ * @param {function(PngChromaticiesEvent): void } listener
238+ * @returns {PngParser } for chaining
239+ */
240+ onChromaticities ( listener ) {
241+ super . addEventListener ( PngParseEventType . cHRM , listener ) ;
242+ return this ;
243+ }
244+
213245 /**
214246 * Type-safe way to bind a listener for a PngPaletteEvent.
215247 * @param {function(PngPaletteEvent): void } listener
@@ -261,7 +293,7 @@ export class PngParser extends EventTarget {
261293
262294 const chStream = chunk . chunkStream ;
263295 switch ( chunk . chunkType ) {
264- // https://www.w3.org/TR/2003/REC-PNG-20031110 /#11IHDR
296+ // https://www.w3.org/TR/png-3 /#11IHDR
265297 case 'IHDR' :
266298 if ( this . colorType ) throw `Found multiple IHDR chunks` ;
267299 /** @type {PngImageHeader } */
@@ -292,13 +324,13 @@ export class PngParser extends EventTarget {
292324 this . dispatchEvent ( new PngImageHeaderEvent ( header ) ) ;
293325 break ;
294326
295- // https://www.w3.org/TR/2003/REC-PNG-20031110 /#11gAMA
327+ // https://www.w3.org/TR/png-3 /#11gAMA
296328 case 'gAMA' :
297329 if ( length !== 4 ) throw `Bad length for gAMA: ${ length } ` ;
298330 this . dispatchEvent ( new PngImageGammaEvent ( chStream . readNumber ( 4 ) ) ) ;
299331 break ;
300332
301- // https://www.w3.org/TR/2003/REC-PNG-20031110 /#11sBIT
333+ // https://www.w3.org/TR/png-3 /#11sBIT
302334 case 'sBIT' :
303335 if ( this . colorType === undefined ) throw `sBIT before IHDR` ;
304336 /** @type {PngSignificantBits } */
@@ -329,7 +361,25 @@ export class PngParser extends EventTarget {
329361 this . dispatchEvent ( new PngSignificantBitsEvent ( sigBits ) ) ;
330362 break ;
331363
332- // https://www.w3.org/TR/2003/REC-PNG-20031110/#11PLTE
364+ // https://www.w3.org/TR/png-3/#11cHRM
365+ case 'cHRM' :
366+ if ( length !== 32 ) throw `Weird length for cHRM chunk: ${ length } ` ;
367+
368+ /** @type {PngChromaticies } */
369+ const chromaticities = {
370+ whitePointX : chStream . readNumber ( 4 ) ,
371+ whitePointY : chStream . readNumber ( 4 ) ,
372+ redX : chStream . readNumber ( 4 ) ,
373+ redY : chStream . readNumber ( 4 ) ,
374+ greenX : chStream . readNumber ( 4 ) ,
375+ greenY : chStream . readNumber ( 4 ) ,
376+ blueX : chStream . readNumber ( 4 ) ,
377+ blueY : chStream . readNumber ( 4 ) ,
378+ } ;
379+ this . dispatchEvent ( new PngChromaticitiesEvent ( chromaticities ) ) ;
380+ break ;
381+
382+ // https://www.w3.org/TR/png-3/#11PLTE
333383 case 'PLTE' :
334384 if ( this . colorType === undefined ) throw `PLTE before IHDR` ;
335385 if ( this . colorType === PngColorType . GREYSCALE ||
@@ -354,7 +404,7 @@ export class PngParser extends EventTarget {
354404 this . dispatchEvent ( new PngPaletteEvent ( this . palette ) ) ;
355405 break ;
356406
357- // https://www.w3.org/TR/2003/REC-PNG-20031110 /#11tRNS
407+ // https://www.w3.org/TR/png-3 /#11tRNS
358408 case 'tRNS' :
359409 if ( this . colorType === undefined ) throw `tRNS before IHDR` ;
360410 if ( this . colorType === PngColorType . GREYSCALE_WITH_ALPHA ||
@@ -388,7 +438,7 @@ export class PngParser extends EventTarget {
388438 this . dispatchEvent ( new PngTransparencyEvent ( transparency ) ) ;
389439 break ;
390440
391- // https://www.w3.org/TR/2003/REC-PNG-20031110 /#11IDAT
441+ // https://www.w3.org/TR/png-3 /#11IDAT
392442 case 'IDAT' :
393443 /** @type {PngImageData } */
394444 const data = {
@@ -445,6 +495,9 @@ async function main() {
445495 parser . onSignificantBits ( evt => {
446496 // console.dir(evt.sigBits);
447497 } ) ;
498+ parser . onChromaticities ( evt => {
499+ // console.dir(evt.chromaticities);
500+ } ) ;
448501 parser . onPalette ( evt => {
449502 // console.dir(evt.palette);
450503 } ) ;
0 commit comments