1 /* Eye Of MATE -- PNG Metadata Reader
2  *
3  * Copyright (C) 2008 The Free Software Foundation
4  *
5  * Author: Felix Riemann <friemann@svn.gnome.org>
6  *
7  * Based on the old EomMetadataReader code.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <math.h>
29 #include <string.h>
30 #include <zlib.h>
31 #include <gdk/gdkx.h>
32 
33 #include "eom-metadata-reader.h"
34 #include "eom-metadata-reader-png.h"
35 #include "eom-debug.h"
36 
37 typedef enum {
38 	EMR_READ_MAGIC,
39 	EMR_READ_SIZE_HIGH_HIGH_BYTE,
40 	EMR_READ_SIZE_HIGH_LOW_BYTE,
41 	EMR_READ_SIZE_LOW_HIGH_BYTE,
42 	EMR_READ_SIZE_LOW_LOW_BYTE,
43 	EMR_READ_CHUNK_NAME,
44 	EMR_SKIP_BYTES,
45 	EMR_CHECK_CRC,
46 	EMR_SKIP_CRC,
47 	EMR_READ_XMP_ITXT,
48 	EMR_READ_ICCP,
49 	EMR_READ_SRGB,
50 	EMR_READ_CHRM,
51 	EMR_READ_GAMA,
52 	EMR_FINISHED
53 } EomMetadataReaderPngState;
54 
55 #if 0
56 #define IS_FINISHED(priv) (priv->icc_chunk  != NULL && \
57                            priv->xmp_chunk  != NULL)
58 #endif
59 
60 struct _EomMetadataReaderPngPrivate {
61 	EomMetadataReaderPngState  state;
62 
63 	/* data fields */
64 	guint32  icc_len;
65 	gpointer icc_chunk;
66 
67 	gpointer xmp_chunk;
68 	guint32  xmp_len;
69 
70 	guint32	 sRGB_len;
71 	gpointer sRGB_chunk;
72 
73 	gpointer cHRM_chunk;
74 	guint32	 cHRM_len;
75 
76 	guint32	 gAMA_len;
77 	gpointer gAMA_chunk;
78 
79 	/* management fields */
80 	gsize      size;
81 	gsize      bytes_read;
82 	guint	   sub_step;
83 	guchar	   chunk_name[4];
84 	gpointer   *crc_chunk;
85 	guint32	   *crc_len;
86 	guint32    target_crc;
87 	gboolean   hasIHDR;
88 };
89 
90 static void
91 eom_metadata_reader_png_init_emr_iface (gpointer g_iface, gpointer iface_data);
92 
G_DEFINE_TYPE_WITH_CODE(EomMetadataReaderPng,eom_metadata_reader_png,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (EOM_TYPE_METADATA_READER,eom_metadata_reader_png_init_emr_iface)G_ADD_PRIVATE (EomMetadataReaderPng))93 G_DEFINE_TYPE_WITH_CODE (EomMetadataReaderPng, eom_metadata_reader_png,
94 			 G_TYPE_OBJECT,
95 			 G_IMPLEMENT_INTERFACE (EOM_TYPE_METADATA_READER,
96 			                        eom_metadata_reader_png_init_emr_iface) \
97 			                        G_ADD_PRIVATE(EomMetadataReaderPng))
98 
99 static void
100 eom_metadata_reader_png_dispose (GObject *object)
101 {
102 	EomMetadataReaderPng *emr = EOM_METADATA_READER_PNG (object);
103 	EomMetadataReaderPngPrivate *priv = emr->priv;
104 
105 	g_free (priv->xmp_chunk);
106 	priv->xmp_chunk = NULL;
107 
108 	g_free (priv->icc_chunk);
109 	priv->icc_chunk = NULL;
110 
111 	g_free (priv->sRGB_chunk);
112 	priv->sRGB_chunk = NULL;
113 
114 	g_free (priv->cHRM_chunk);
115 	priv->cHRM_chunk = NULL;
116 
117 	g_free (priv->gAMA_chunk);
118 	priv->gAMA_chunk = NULL;
119 
120 	G_OBJECT_CLASS (eom_metadata_reader_png_parent_class)->dispose (object);
121 }
122 
123 static void
eom_metadata_reader_png_init(EomMetadataReaderPng * emr)124 eom_metadata_reader_png_init (EomMetadataReaderPng *emr)
125 {
126 	EomMetadataReaderPngPrivate *priv;
127 
128 	priv = emr->priv =  eom_metadata_reader_png_get_instance_private (emr);
129 	priv->icc_chunk = NULL;
130 	priv->icc_len = 0;
131 	priv->xmp_chunk = NULL;
132 	priv->xmp_len = 0;
133 	priv->sRGB_chunk = NULL;
134 	priv->sRGB_len = 0;
135 	priv->cHRM_chunk = NULL;
136 	priv->cHRM_len = 0;
137 	priv->gAMA_chunk = NULL;
138 	priv->gAMA_len = 0;
139 
140 	priv->sub_step = 0;
141 	priv->state = EMR_READ_MAGIC;
142 	priv->hasIHDR = FALSE;
143 }
144 
145 static void
eom_metadata_reader_png_class_init(EomMetadataReaderPngClass * klass)146 eom_metadata_reader_png_class_init (EomMetadataReaderPngClass *klass)
147 {
148 	GObjectClass *object_class = (GObjectClass*) klass;
149 
150 	object_class->dispose = eom_metadata_reader_png_dispose;
151 }
152 
153 static gboolean
eom_metadata_reader_png_finished(EomMetadataReaderPng * emr)154 eom_metadata_reader_png_finished (EomMetadataReaderPng *emr)
155 {
156 	g_return_val_if_fail (EOM_IS_METADATA_READER_PNG (emr), TRUE);
157 
158 	return (emr->priv->state == EMR_FINISHED);
159 }
160 
161 
162 static void
eom_metadata_reader_png_get_next_block(EomMetadataReaderPngPrivate * priv,guchar * chunk,int * i,const guchar * buf,int len,EomMetadataReaderPngState state)163 eom_metadata_reader_png_get_next_block (EomMetadataReaderPngPrivate* priv,
164 				    	guchar *chunk,
165 					int* i,
166 					const guchar *buf,
167 					int len,
168 					EomMetadataReaderPngState state)
169 {
170 	if (*i + priv->size < len) {
171 		/* read data in one block */
172 		memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[*i], priv->size);
173 		priv->state = EMR_CHECK_CRC;
174 		*i = *i + priv->size - 1; /* the for-loop consumes the other byte */
175 		priv->size = 0;
176 	} else {
177 		int chunk_len = len - *i;
178 		memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[*i], chunk_len);
179 		priv->bytes_read += chunk_len; /* bytes already read */
180 		priv->size = (*i + priv->size) - len; /* remaining data to read */
181 		*i = len - 1;
182 		priv->state = state;
183 	}
184 }
185 
186 static void
eom_metadata_reader_png_consume(EomMetadataReaderPng * emr,const guchar * buf,guint len)187 eom_metadata_reader_png_consume (EomMetadataReaderPng *emr, const guchar *buf, guint len)
188 {
189 	EomMetadataReaderPngPrivate *priv;
190  	int i;
191 	guint32 chunk_crc;
192 	static const gchar PNGMAGIC[8] = "\x89PNG\x0D\x0A\x1a\x0A";
193 
194 	g_return_if_fail (EOM_IS_METADATA_READER_PNG (emr));
195 
196 	priv = emr->priv;
197 
198 	if (priv->state == EMR_FINISHED) return;
199 
200 	for (i = 0; (i < len) && (priv->state != EMR_FINISHED); i++) {
201 
202 		switch (priv->state) {
203 		case EMR_READ_MAGIC:
204 			/* Check PNG magic string */
205 			if (priv->sub_step < 8 &&
206 			    (gchar)buf[i] == PNGMAGIC[priv->sub_step]) {
207 			    	if (priv->sub_step == 7)
208 					priv->state = EMR_READ_SIZE_HIGH_HIGH_BYTE;
209 				priv->sub_step++;
210 			} else {
211 				priv->state = EMR_FINISHED;
212 			}
213 			break;
214 		case EMR_READ_SIZE_HIGH_HIGH_BYTE:
215 			/* Read the high byte of the size's high word */
216 			priv->size |= (buf[i] & 0xFF) << 24;
217 			priv->state = EMR_READ_SIZE_HIGH_LOW_BYTE;
218 			break;
219 		case EMR_READ_SIZE_HIGH_LOW_BYTE:
220 			/* Read the low byte of the size's high word */
221 			priv->size |= (buf[i] & 0xFF) << 16;
222 			priv->state = EMR_READ_SIZE_LOW_HIGH_BYTE;
223 			break;
224 		case EMR_READ_SIZE_LOW_HIGH_BYTE:
225 			/* Read the high byte of the size's low word */
226 			priv->size |= (buf [i] & 0xff) << 8;
227 			priv->state = EMR_READ_SIZE_LOW_LOW_BYTE;
228 			break;
229 		case EMR_READ_SIZE_LOW_LOW_BYTE:
230 			/* Read the high byte of the size's low word */
231 			priv->size |= (buf [i] & 0xff);
232 			/* The maximum chunk length is 2^31-1 */
233 			if (G_LIKELY (priv->size <= (guint32) 0x7fffffff)) {
234 				priv->state = EMR_READ_CHUNK_NAME;
235 				/* Make sure sub_step is 0 before next step */
236 				priv->sub_step = 0;
237 			} else {
238 				priv->state = EMR_FINISHED;
239 				eom_debug_message (DEBUG_IMAGE_DATA,
240 						   "chunk size larger than "
241 						   "2^31-1; stopping parser");
242 			}
243 
244 			break;
245 		case EMR_READ_CHUNK_NAME:
246 			/* Read the 4-byte chunk name */
247 			if (priv->sub_step > 3)
248 				g_assert_not_reached ();
249 
250 			priv->chunk_name[priv->sub_step] = buf[i];
251 
252 			if (priv->sub_step++ != 3)
253 				break;
254 
255 			if (G_UNLIKELY (!priv->hasIHDR)) {
256 				/* IHDR should be the first chunk in a PNG */
257 				if (priv->size == 13
258 				    && memcmp (priv->chunk_name, "IHDR", 4) == 0){
259 					priv->hasIHDR = TRUE;
260 				} else {
261 					/* Stop parsing if it is not */
262 					priv->state = EMR_FINISHED;
263 				}
264 			}
265 
266 			/* Try to identify the chunk by its name.
267 			 * Already do some sanity checks where possible */
268 			if (memcmp (priv->chunk_name, "iTXt", 4) == 0 &&
269 			    priv->size > (22 + 54) && priv->xmp_chunk == NULL) {
270 				priv->state = EMR_READ_XMP_ITXT;
271 			} else if (memcmp (priv->chunk_name, "iCCP", 4) == 0 &&
272 				   priv->icc_chunk == NULL) {
273 				priv->state = EMR_READ_ICCP;
274 			} else if (memcmp (priv->chunk_name, "sRGB", 4) == 0 &&
275 				   priv->sRGB_chunk == NULL && priv->size == 1) {
276 				priv->state = EMR_READ_SRGB;
277 			} else if (memcmp (priv->chunk_name, "cHRM", 4) == 0 &&
278 				   priv->cHRM_chunk == NULL && priv->size == 32) {
279 				priv->state = EMR_READ_CHRM;
280 			} else if (memcmp (priv->chunk_name, "gAMA", 4) == 0 &&
281 				   priv->gAMA_chunk == NULL && priv->size == 4) {
282 				priv->state = EMR_READ_GAMA;
283 			} else if (memcmp (priv->chunk_name, "IEND", 4) == 0) {
284 				priv->state = EMR_FINISHED;
285 			} else {
286 				/* Skip chunk + 4-byte CRC32 value */
287 				priv->size += 4;
288 				priv->state = EMR_SKIP_BYTES;
289 			}
290 			priv->sub_step = 0;
291 			break;
292 		case EMR_SKIP_CRC:
293 			/* Skip the 4-byte CRC32 value following every chunk */
294 			priv->size = 4;
295 		case EMR_SKIP_BYTES:
296 		/* Skip chunk and start reading the size of the next one */
297 			eom_debug_message (DEBUG_IMAGE_DATA,
298 					   "Skip bytes: %" G_GSIZE_FORMAT,
299 					   priv->size);
300 
301 			if (i + priv->size < len) {
302 				i = i + priv->size - 1; /* the for-loop consumes the other byte */
303 				priv->size = 0;
304 				priv->state = EMR_READ_SIZE_HIGH_HIGH_BYTE;
305 			}
306 			else {
307 				priv->size = (i + priv->size) - len;
308 				i = len - 1;
309 			}
310 			break;
311 		case EMR_CHECK_CRC:
312 			/* Read the chunks CRC32 value from the file,... */
313 			if (priv->sub_step == 0)
314 				priv->target_crc = 0;
315 
316 			priv->target_crc |= buf[i] << ((3 - priv->sub_step) * 8);
317 
318 			if (priv->sub_step++ != 3)
319 				break;
320 
321 			/* ...generate the chunks CRC32,... */
322 			chunk_crc = crc32 (crc32 (0L, Z_NULL, 0), priv->chunk_name, 4);
323 			chunk_crc = crc32 (chunk_crc, *priv->crc_chunk, *priv->crc_len);
324 
325 			eom_debug_message (DEBUG_IMAGE_DATA, "Checking CRC: Chunk: 0x%X - Target: 0x%X", chunk_crc, priv->target_crc);
326 
327 			/* ...and check if they match. If they don't throw
328 			 * the chunk away and stop parsing. */
329 			if (priv->target_crc == chunk_crc) {
330 				priv->state = EMR_READ_SIZE_HIGH_HIGH_BYTE;
331 			} else {
332 				g_free (*priv->crc_chunk);
333 				*priv->crc_chunk = NULL;
334 				*priv->crc_len = 0;
335 				/* Stop parsing for security reasons */
336 				priv->state = EMR_FINISHED;
337 			}
338 			priv->sub_step = 0;
339 			break;
340 		case EMR_READ_XMP_ITXT:
341 			/* Extract an iTXt chunk possibly containing
342 			 * an XMP packet */
343 			eom_debug_message (DEBUG_IMAGE_DATA,
344 					   "Read XMP Chunk - size: %"
345 					   G_GSIZE_FORMAT, priv->size);
346 
347 			if (priv->xmp_chunk == NULL) {
348 				priv->xmp_chunk = g_new0 (guchar, priv->size);
349 				priv->xmp_len = priv->size;
350 				priv->crc_len = &priv->xmp_len;
351 				priv->bytes_read = 0;
352 				priv->crc_chunk = &priv->xmp_chunk;
353 			}
354 			eom_metadata_reader_png_get_next_block (priv,
355 							    priv->xmp_chunk,
356 							    &i, buf, len,
357 							    EMR_READ_XMP_ITXT);
358 
359 			if (priv->state == EMR_CHECK_CRC) {
360 				/* Check if it is actually an XMP chunk.
361 				 * Throw it away if not.
362 				 * The check has 4 extra \0's to check
363 				 * if the chunk is configured correctly. */
364 				if (memcmp (priv->xmp_chunk, "XML:com.adobe.xmp\0\0\0\0\0", 22) != 0) {
365 					priv->state = EMR_SKIP_CRC;
366 					g_free (priv->xmp_chunk);
367 					priv->xmp_chunk = NULL;
368 					priv->xmp_len = 0;
369 				}
370 			}
371 			break;
372 		case EMR_READ_ICCP:
373 			/* Extract an iCCP chunk containing a
374 			 * deflated ICC profile. */
375 			eom_debug_message (DEBUG_IMAGE_DATA,
376 					   "Read ICC Chunk - size: %"
377 					   G_GSIZE_FORMAT, priv->size);
378 
379 			if (priv->icc_chunk == NULL) {
380 				priv->icc_chunk = g_new0 (guchar, priv->size);
381 				priv->icc_len = priv->size;
382 				priv->crc_len = &priv->icc_len;
383 				priv->bytes_read = 0;
384 				priv->crc_chunk = &priv->icc_chunk;
385 			}
386 
387 			eom_metadata_reader_png_get_next_block (priv,
388 							    priv->icc_chunk,
389 							    &i, buf, len,
390 							    EMR_READ_ICCP);
391 			break;
392 		case EMR_READ_SRGB:
393 			/* Extract the sRGB chunk. Marks the image data as
394 			 * being in sRGB colorspace. */
395 			eom_debug_message (DEBUG_IMAGE_DATA,
396 					   "Read sRGB Chunk - value: %u", *(buf+i));
397 
398 			if (priv->sRGB_chunk == NULL) {
399 				priv->sRGB_chunk = g_new0 (guchar, priv->size);
400 				priv->sRGB_len = priv->size;
401 				priv->crc_len = &priv->sRGB_len;
402 				priv->bytes_read = 0;
403 				priv->crc_chunk = &priv->sRGB_chunk;
404 			}
405 
406 			eom_metadata_reader_png_get_next_block (priv,
407 							    priv->sRGB_chunk,
408 							    &i, buf, len,
409 							    EMR_READ_SRGB);
410 			break;
411 		case EMR_READ_CHRM:
412 			/* Extract the cHRM chunk. Contains the coordinates of
413 			 * the image's whitepoint and primary chromacities. */
414 			eom_debug_message (DEBUG_IMAGE_DATA,
415 					   "Read cHRM Chunk - size: %"
416 					   G_GSIZE_FORMAT, priv->size);
417 
418 			if (priv->cHRM_chunk == NULL) {
419 				priv->cHRM_chunk = g_new0 (guchar, priv->size);
420 				priv->cHRM_len = priv->size;
421 				priv->crc_len = &priv->cHRM_len;
422 				priv->bytes_read = 0;
423 				priv->crc_chunk = &priv->cHRM_chunk;
424 			}
425 
426 			eom_metadata_reader_png_get_next_block (priv,
427 							    priv->cHRM_chunk,
428 							    &i, buf, len,
429 							    EMR_READ_ICCP);
430 			break;
431 		case EMR_READ_GAMA:
432 			/* Extract the gAMA chunk containing the
433 			 * image's gamma value */
434 			eom_debug_message (DEBUG_IMAGE_DATA,
435 					   "Read gAMA-Chunk - size: %"
436 					   G_GSIZE_FORMAT, priv->size);
437 
438 			if (priv->gAMA_chunk == NULL) {
439 				priv->gAMA_chunk = g_new0 (guchar, priv->size);
440 				priv->gAMA_len = priv->size;
441 				priv->crc_len = &priv->gAMA_len;
442 				priv->bytes_read = 0;
443 				priv->crc_chunk = &priv->gAMA_chunk;
444 			}
445 
446 			eom_metadata_reader_png_get_next_block (priv,
447 							    priv->gAMA_chunk,
448 							    &i, buf, len,
449 							    EMR_READ_ICCP);
450 			break;
451 		default:
452 			g_assert_not_reached ();
453 		}
454 	}
455 }
456 
457 #ifdef HAVE_EXEMPI
458 
459 /* skip the chunk ID */
460 #define EOM_XMP_OFFSET (22)
461 
462 static gpointer
eom_metadata_reader_png_get_xmp_data(EomMetadataReaderPng * emr)463 eom_metadata_reader_png_get_xmp_data (EomMetadataReaderPng *emr )
464 {
465 	EomMetadataReaderPngPrivate *priv;
466 	XmpPtr xmp = NULL;
467 
468 	g_return_val_if_fail (EOM_IS_METADATA_READER_PNG (emr), NULL);
469 
470 	priv = emr->priv;
471 
472 	if (priv->xmp_chunk != NULL) {
473 		xmp = xmp_new (priv->xmp_chunk+EOM_XMP_OFFSET,
474 			       priv->xmp_len-EOM_XMP_OFFSET);
475 	}
476 
477 	return (gpointer) xmp;
478 }
479 #endif
480 
481 #if defined(HAVE_LCMS) && defined(GDK_WINDOWING_X11)
482 
483 #define EXTRACT_DOUBLE_UINT_BLOCK_OFFSET(chunk,offset,divider) \
484 		(double)(GUINT32_FROM_BE(*((guint32*)((chunk)+((offset)*4))))/(double)(divider))
485 
486 /* This is the amount of memory the inflate output buffer gets increased by
487  * while decompressing the ICC profile */
488 #define EOM_ICC_INFLATE_BUFFER_STEP 1024
489 
490 /* I haven't seen ICC profiles larger than 1MB yet.
491  * A maximum output buffer of 5MB should be enough. */
492 #define EOM_ICC_INFLATE_BUFFER_LIMIT (1024*1024*5)
493 
494 /* Apparently an sRGB profile saved in cHRM and gAMA chunks does not compute
495  * a profile that exactly matches the built-in sRGB profile and thus could
496  * cause a slight color deviation. Try catching this case to allow fallback
497  * to the built-in profile instead.
498  */
499 static gboolean
_chrm_matches_srgb(const cmsCIExyY * whitepoint,const cmsCIExyYTRIPLE * primaries,gdouble gammaValue)500 _chrm_matches_srgb(const cmsCIExyY       *whitepoint,
501 		   const cmsCIExyYTRIPLE *primaries,
502 		         gdouble          gammaValue)
503 {
504 	/* PNGs gAMA value for 2.2 is only accurate to the 4th decimal point */
505 #define DOUBLE_EQUAL_MAX_DIFF 1e-4
506 #define DOUBLE_EQUAL(a,b) (fabs (a - b) < DOUBLE_EQUAL_MAX_DIFF)
507 
508 	return (DOUBLE_EQUAL(gammaValue, 2.2)
509 		&& DOUBLE_EQUAL(whitepoint->x, 0.3127)
510 		&& DOUBLE_EQUAL(whitepoint->y, 0.329)
511 		&& DOUBLE_EQUAL(primaries->Red.x, 0.64)
512 		&& DOUBLE_EQUAL(primaries->Red.y, 0.33)
513 		&& DOUBLE_EQUAL(primaries->Green.x, 0.3)
514 		&& DOUBLE_EQUAL(primaries->Green.y, 0.6)
515 		&& DOUBLE_EQUAL(primaries->Blue.x, 0.15)
516 		&& DOUBLE_EQUAL(primaries->Blue.y, 0.06));
517 }
518 
519 static gpointer
eom_metadata_reader_png_get_icc_profile(EomMetadataReaderPng * emr)520 eom_metadata_reader_png_get_icc_profile (EomMetadataReaderPng *emr)
521 {
522 	EomMetadataReaderPngPrivate *priv;
523 	cmsHPROFILE profile = NULL;
524 
525 	g_return_val_if_fail (EOM_IS_METADATA_READER_PNG (emr), NULL);
526 
527 	priv = emr->priv;
528 
529 	if (priv->icc_chunk) {
530 		gpointer outbuf;
531 		gsize offset = 0;
532 		z_stream zstr;
533 		int z_ret;
534 
535 		/* Use default allocation functions */
536 		zstr.zalloc = Z_NULL;
537 		zstr.zfree = Z_NULL;
538 		zstr.opaque = Z_NULL;
539 
540 		/* Skip the name of the ICC profile */
541 		while (*((guchar*)priv->icc_chunk+offset) != '\0')
542 			offset++;
543 		/* Ensure the compression method (deflate) */
544 		if (*((guchar*)priv->icc_chunk+(++offset)) != '\0')
545 			return NULL;
546 		++offset; //offset now points to the start of the deflated data
547 
548 		/* Prepare the zlib data structure for decompression */
549 		zstr.next_in = priv->icc_chunk + offset;
550 		zstr.avail_in = priv->icc_len - offset;
551 		if (inflateInit (&zstr) != Z_OK) {
552 			return NULL;
553 		}
554 
555 		/* Prepare output buffer and make zlib aware of it */
556 		outbuf = g_malloc (EOM_ICC_INFLATE_BUFFER_STEP);
557 		zstr.next_out = outbuf;
558 		zstr.avail_out = EOM_ICC_INFLATE_BUFFER_STEP;
559 
560 		do {
561 			if (zstr.avail_out == 0) {
562 				/* The output buffer was not large enough to
563 				 * hold all the decompressed data. Increase its
564 				 * size and continue decompression. */
565 				gsize new_size = zstr.total_out + EOM_ICC_INFLATE_BUFFER_STEP;
566 
567 				if (G_UNLIKELY (new_size > EOM_ICC_INFLATE_BUFFER_LIMIT)) {
568 					/* Enforce a memory limit for the output
569 					 * buffer to avoid possible OOM cases */
570 					inflateEnd (&zstr);
571 					g_free (outbuf);
572 					eom_debug_message (DEBUG_IMAGE_DATA, "ICC profile is too large. Ignoring.");
573 					return NULL;
574 				}
575 				outbuf = g_realloc(outbuf, new_size);
576 				zstr.avail_out = EOM_ICC_INFLATE_BUFFER_STEP;
577 				zstr.next_out = outbuf + zstr.total_out;
578 			}
579 			z_ret = inflate (&zstr, Z_SYNC_FLUSH);
580 		} while (z_ret == Z_OK);
581 
582 		if (G_UNLIKELY (z_ret != Z_STREAM_END)) {
583 			eom_debug_message (DEBUG_IMAGE_DATA, "Error while inflating ICC profile: %s (%d)", zstr.msg, z_ret);
584 			inflateEnd (&zstr);
585 			g_free (outbuf);
586 			return NULL;
587 		}
588 
589 		if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
590 			profile = cmsOpenProfileFromMem(outbuf, zstr.total_out);
591 		}
592 		inflateEnd (&zstr);
593 		g_free (outbuf);
594 
595 		eom_debug_message (DEBUG_LCMS, "PNG has %s ICC profile", profile ? "valid" : "invalid");
596 	}
597 
598 	if (!profile && priv->sRGB_chunk) {
599 		eom_debug_message (DEBUG_LCMS, "PNG is sRGB");
600 		/* If the file has an sRGB chunk the image data is in the sRGB
601 		 * colorspace. lcms has a built-in sRGB profile. */
602 
603 		if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
604 			profile = cmsCreate_sRGBProfile ();
605 		}
606 	}
607 
608 	if (!profile && priv->cHRM_chunk && priv->gAMA_chunk) {
609 		cmsCIExyY whitepoint;
610 		cmsCIExyYTRIPLE primaries;
611 		cmsToneCurve *gamma[3];
612 		double gammaValue;
613 
614 		/* This uglyness extracts the chromacity and whitepoint values
615 		 * from a PNG's cHRM chunk. These can be accurate up to the
616 		 * 5th decimal point.
617 		 * They are saved as integer values multiplied by 100000. */
618 
619 		eom_debug_message (DEBUG_LCMS, "Trying to calculate color profile");
620 
621 		whitepoint.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 0, 100000);
622 		whitepoint.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 1, 100000);
623 
624 		primaries.Red.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 2, 100000);
625 		primaries.Red.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 3, 100000);
626 		primaries.Green.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 4, 100000);
627 		primaries.Green.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 5, 100000);
628 		primaries.Blue.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 6, 100000);
629 		primaries.Blue.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 7, 100000);
630 
631 		whitepoint.Y = primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0;
632 
633 		gammaValue = (double) 1.0/EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->gAMA_chunk, 0, 100000);
634 		eom_debug_message (DEBUG_LCMS, "Gamma %.5lf", gammaValue);
635 
636 		/* Catch SRGB in cHRM/gAMA chunks and use accurate built-in
637 		 * profile instead of computing one that "gets close". */
638 		if(_chrm_matches_srgb (&whitepoint, &primaries, gammaValue)) {
639 			eom_debug_message (DEBUG_LCMS, "gAMA and cHRM match sRGB");
640 			if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
641 				profile = cmsCreate_sRGBProfile ();
642 			}
643 		} else {
644 			gamma[0] = gamma[1] = gamma[2] =
645 				cmsBuildGamma (NULL, gammaValue);
646 			if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
647 				profile = cmsCreateRGBProfile (&whitepoint, &primaries,
648 						gamma);
649 				cmsFreeToneCurve(gamma[0]);
650 			}
651 		}
652 	}
653 
654 	return profile;
655 }
656 #endif
657 
658 static void
eom_metadata_reader_png_init_emr_iface(gpointer g_iface,gpointer iface_data)659 eom_metadata_reader_png_init_emr_iface (gpointer g_iface, gpointer iface_data)
660 {
661 	EomMetadataReaderInterface *iface;
662 
663 	iface = (EomMetadataReaderInterface*) g_iface;
664 
665 	iface->consume =
666 		(void (*) (EomMetadataReader *self, const guchar *buf, guint len))
667 			eom_metadata_reader_png_consume;
668 	iface->finished =
669 		(gboolean (*) (EomMetadataReader *self))
670 			eom_metadata_reader_png_finished;
671 #if defined(HAVE_LCMS) && defined(GDK_WINDOWING_X11)
672 	iface->get_icc_profile =
673 		(cmsHPROFILE (*) (EomMetadataReader *self))
674 			eom_metadata_reader_png_get_icc_profile;
675 #endif
676 #ifdef HAVE_EXEMPI
677 	iface->get_xmp_ptr =
678 		(gpointer (*) (EomMetadataReader *self))
679 			eom_metadata_reader_png_get_xmp_data;
680 #endif
681 }
682