1 /* ***** BEGIN LICENSE BLOCK *****
2 * This file is part of openfx-io <https://github.com/MrKepzie/openfx-io>,
3 * Copyright (C) 2013-2018 INRIA
4 *
5 * openfx-io is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * openfx-io is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with openfx-io. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17 * ***** END LICENSE BLOCK ***** */
18
19 /*
20 * OFX PNG reader plugin.
21 * Reads an image in the PNG format
22 */
23
24 #include <cstdio> // fopen, fread...
25 #include <iomanip>
26 #include <locale>
27 #include <cstdlib>
28 #include <cmath>
29 #include <sstream>
30 #include <algorithm>
31 #include <png.h> // includes setjmp.h
32 #include <zlib.h>
33
34 #include "GenericReader.h"
35 #include "GenericOCIO.h"
36 #include "ofxsMacros.h"
37 #include "ofxsFileOpen.h"
38
39 using namespace OFX;
40 using namespace OFX::IO;
41 #ifdef OFX_IO_USING_OCIO
42 namespace OCIO = OCIO_NAMESPACE;
43 #endif
44
45 using std::string;
46 using std::stringstream;
47 using std::vector;
48 using std::map;
49
50 OFXS_NAMESPACE_ANONYMOUS_ENTER
51
52 #define kPluginName "ReadPNG"
53 #define kPluginGrouping "Image/Readers"
54 #define kPluginDescription "Read PNG files."
55 #define kPluginIdentifier "fr.inria.openfx.ReadPNG"
56 #define kPluginVersionMajor 1 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
57 #define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.
58 #define kPluginEvaluation 92 // better than ReadOIIO
59
60 #define kParamShowMetadata "showMetadata"
61 #define kParamShowMetadataLabel "Image Info..."
62 #define kParamShowMetadataHint "Shows information and metadata from the image at current time."
63
64 #define kParamLibraryInfo "libraryInfo"
65 #define kParamLibraryInfoLabel "libpng Info...", "Display information about the underlying library."
66
67 // All PNG images represent RGBA.
68 // Single-channel images are Y
69 // Two-channel images are Y+A
70 // Three-channel images are RGB
71 // Four-channel images are RGBA
72 // RGB may be compacted to Y and A may be removed when writing a PNG image.
73 // User can still use a Shuffle plugin to select just the Alpha channel.
74 //
75 // References:
76 // https://www.w3.org/TR/PNG/#4Concepts.RGBMerging
77 // https://www.w3.org/TR/PNG/#4Concepts.Alpha-indexing
78 // Issue:
79 // https://github.com/MrKepzie/openfx-io/issues/24
80
81 #define kSupportsRGBA true
82 #define kSupportsRGB true
83 #define kSupportsXY false
84 #define kSupportsAlpha false
85 #define kSupportsTiles false
86
87 #define OFX_IO_LIBPNG_VERSION (PNG_LIBPNG_VER_MAJOR * 10000 + PNG_LIBPNG_VER_MINOR * 100 + PNG_LIBPNG_VER_RELEASE)
88
89 // Try to deduce endianness
90 #if (defined(_WIN32) || defined(__i386__) || defined(__x86_64__ ) )
91 # ifndef __LITTLE_ENDIAN__
92 # define __LITTLE_ENDIAN__ 1
93 # undef __BIG_ENDIAN__
94 # endif
95 #endif
96
97 inline bool
littleendian(void)98 littleendian (void)
99 {
100 #if defined(__BIG_ENDIAN__)
101
102 return false;
103 #elif defined(__LITTLE_ENDIAN__)
104
105 return true;
106 #else
107 // Otherwise, do something quick to compute it
108 int i = 1;
109
110 return *( (char *) &i );
111 #endif
112 }
113
114 template<typename T>
115 static inline void
unused(const T &)116 unused(const T&) {}
117
118 // the following ICC check function is from libpng-1.6.26/png.c
119
120 /* Information about the known ICC sRGB profiles */
121 static const struct
122 {
123 png_uint_32 adler, crc, length;
124 png_uint_32 md5[4];
125 png_byte have_md5;
126 png_byte is_broken;
127 png_uint_16 intent;
128
129 # define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0)
130 # define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\
131 { adler, crc, length, md5, broke, intent },
132
133 } png_sRGB_checks[] =
134 {
135 /* This data comes from contrib/tools/checksum-icc run on downloads of
136 * all four ICC sRGB profiles from www.color.org.
137 */
138 /* adler32, crc32, MD5[4], intent, date, length, file-name */
139 PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,
140 PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
141 "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
142
143 /* ICC sRGB v2 perceptual no black-compensation: */
144 PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,
145 PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
146 "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
147
148 PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,
149 PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
150 "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
151
152 /* ICC sRGB v4 perceptual */
153 PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,
154 PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
155 "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
156
157 /* The following profiles have no known MD5 checksum. If there is a match
158 * on the (empty) MD5 the other fields are used to attempt a match and
159 * a warning is produced. The first two of these profiles have a 'cprt' tag
160 * which suggests that they were also made by Hewlett Packard.
161 */
162 PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,
163 PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
164 "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
165
166 /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not
167 * match the D50 PCS illuminant in the header (it is in fact the D65 values,
168 * so the white point is recorded as the un-adapted value.) The profiles
169 * below only differ in one byte - the intent - and are basically the same as
170 * the previous profile except for the mediaWhitePointTag error and a missing
171 * chromaticAdaptationTag.
172 */
173 PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,
174 PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
175 "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
176
177 PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
178 PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
179 "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
180 };
181
182 static int
png_compare_ICC_profile_with_sRGB(png_structp,png_bytep profile,uLong adler)183 png_compare_ICC_profile_with_sRGB(png_structp /*png_ptr*/,
184 png_bytep profile,
185 uLong adler)
186 {
187 /* The quick check is to verify just the MD5 signature and trust the
188 * rest of the data. Because the profile has already been verified for
189 * correctness this is safe. png_colorspace_set_sRGB will check the 'intent'
190 * field too, so if the profile has been edited with an intent not defined
191 * by sRGB (but maybe defined by a later ICC specification) the read of
192 * the profile will fail at that point.
193 */
194
195 png_uint_32 length = 0;
196 png_uint_32 intent = 0x10000; /* invalid */
197 #if PNG_sRGB_PROFILE_CHECKS > 1
198 uLong crc = 0; /* the value for 0 length data */
199 #endif
200 unsigned int i;
201
202 #ifdef PNG_SET_OPTION_SUPPORTED
203 /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */
204 //if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) ==
205 // PNG_OPTION_ON)
206 // return 0;
207 #endif
208
209 for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i)
210 {
211 if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] &&
212 png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] &&
213 png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] &&
214 png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3])
215 {
216 /* This may be one of the old HP profiles without an MD5, in that
217 * case we can only use the length and Adler32 (note that these
218 * are not used by default if there is an MD5!)
219 */
220 # if PNG_sRGB_PROFILE_CHECKS == 0
221 if (png_sRGB_checks[i].have_md5 != 0)
222 return 1+png_sRGB_checks[i].is_broken;
223 # endif
224
225 /* Profile is unsigned or more checks have been configured in. */
226 if (length == 0)
227 {
228 length = png_get_uint_32(profile);
229 intent = png_get_uint_32(profile+64);
230 }
231
232 /* Length *and* intent must match */
233 if (length == (png_uint_32) png_sRGB_checks[i].length &&
234 intent == (png_uint_32) png_sRGB_checks[i].intent)
235 {
236 /* Now calculate the adler32 if not done already. */
237 if (adler == 0)
238 {
239 adler = adler32(0, NULL, 0);
240 adler = adler32(adler, (const Bytef *)profile, length);
241 }
242
243 if (adler == png_sRGB_checks[i].adler)
244 {
245 /* These basic checks suggest that the data has not been
246 * modified, but if the check level is more than 1 perform
247 * our own crc32 checksum on the data.
248 */
249 # if PNG_sRGB_PROFILE_CHECKS > 1
250 if (crc == 0)
251 {
252 crc = crc32(0, NULL, 0);
253 crc = crc32(crc, profile, length);
254 }
255
256 /* So this check must pass for the 'return' below to happen.
257 */
258 if (crc == png_sRGB_checks[i].crc)
259 # endif
260 {
261 if (png_sRGB_checks[i].is_broken != 0)
262 {
263 /* These profiles are known to have bad data that may cause
264 * problems if they are used, therefore attempt to
265 * discourage their use, skip the 'have_md5' warning below,
266 * which is made irrelevant by this error.
267 */
268 //png_chunk_report(png_ptr, "known incorrect sRGB profile",
269 // PNG_CHUNK_ERROR);
270 }
271
272 /* Warn that this being done; this isn't even an error since
273 * the profile is perfectly valid, but it would be nice if
274 * people used the up-to-date ones.
275 */
276 else if (png_sRGB_checks[i].have_md5 == 0)
277 {
278 //png_chunk_report(png_ptr,
279 // "out-of-date sRGB profile with no signature",
280 // PNG_CHUNK_WARNING);
281 }
282
283 return 1+png_sRGB_checks[i].is_broken;
284 }
285 }
286
287 # if PNG_sRGB_PROFILE_CHECKS > 0
288 /* The signature matched, but the profile had been changed in some
289 * way. This probably indicates a data error or uninformed hacking.
290 * Fall through to "no match".
291 */
292 //png_chunk_report(png_ptr,
293 // "Not recognizing known sRGB profile that has been edited",
294 // PNG_CHUNK_WARNING);
295 break;
296 # endif
297 }
298 }
299 }
300
301 return 0; /* no match */
302 }
303
304 /// Helper function - reads background colour.
305 ///
306 inline bool
get_background(png_structp sp,png_infop ip,BitDepthEnum bitdepth,int real_bit_depth,int nChannels,float * red,float * green,float * blue)307 get_background (png_structp sp,
308 png_infop ip,
309 BitDepthEnum bitdepth,
310 int real_bit_depth,
311 int nChannels,
312 float *red,
313 float *green,
314 float *blue)
315 {
316 if ( setjmp ( png_jmpbuf (sp) ) ) {
317 return false;
318 }
319 if ( !png_get_valid (sp, ip, PNG_INFO_bKGD) ) {
320 return false;
321 }
322
323 png_color_16p bg;
324 png_get_bKGD (sp, ip, &bg);
325 if (bitdepth == eBitDepthUShort) {
326 *red = bg->red * (1.f / 65535);
327 *green = bg->green * (1.f / 65535);
328 *blue = bg->blue * (1.f / 65535);
329 } else if ( (nChannels < 3) && (real_bit_depth < 8) ) {
330 if (real_bit_depth == 1) {
331 *red = *green = *blue = (bg->gray ? 1 : 0);
332 } else if (real_bit_depth == 2) {
333 *red = *green = *blue = bg->gray * (1.f / 3);
334 } else { // 4 bits
335 *red = *green = *blue = bg->gray * (1.f / 15);
336 }
337 } else {
338 *red = bg->red * (1.f / 255);
339 *green = bg->green * (1.f / 255);
340 *blue = bg->blue * (1.f / 255);
341 }
342
343 return true;
344 }
345
346 enum PNGColorSpaceEnum
347 {
348 ePNGColorSpaceLinear,
349 ePNGColorSpacesRGB,
350 ePNGColorSpaceRec709,
351 ePNGColorSpaceGammaCorrected
352 };
353
354 /// Read information from a PNG file and fill the ImageSpec accordingly.
355 ///
356 inline void
getPNGInfo(png_structp sp,png_infop ip,int * x1_p,int * y1_p,int * width_p,int * height_p,double * par_p,int * nChannels_p,BitDepthEnum * bit_depth_p,int * real_bit_depth_p,int * color_type_p,PNGColorSpaceEnum * colorspace_p,double * gamma_p,int * interlace_type_p,OfxRGBColourF * bg_p,unsigned char ** iccprofile_data_p,unsigned int * iccprofile_length_p,bool * isResolutionInches_p,double * xResolution_p,double * yResolution_p,string * date_p,map<string,string> * additionalComments_p)357 getPNGInfo(png_structp sp,
358 png_infop ip,
359 int* x1_p,
360 int* y1_p,
361 int* width_p,
362 int* height_p,
363 double* par_p,
364 int* nChannels_p,
365 BitDepthEnum* bit_depth_p,
366 int* real_bit_depth_p,
367 int* color_type_p,
368 PNGColorSpaceEnum* colorspace_p, // optional
369 double* gamma_p, // optional
370 int* interlace_type_p, // optional
371 OfxRGBColourF* bg_p, // optional
372 unsigned char** iccprofile_data_p, // optional
373 unsigned int* iccprofile_length_p, // optional
374 bool* isResolutionInches_p, // optional
375 double* xResolution_p, // optional
376 double* yResolution_p, // optional
377 string* date_p, // optional
378 map<string, string>* additionalComments_p) // optional
379 {
380 png_read_info (sp, ip);
381
382 // Auto-convert 1-, 2-, and 4- bit images to 8 bits, palette to RGB,
383 // and transparency to alpha.
384 png_set_expand (sp);
385
386 /* Expand the grayscale to 24-bit RGB if necessary. */
387 png_set_gray_to_rgb(sp);
388
389 // PNG files are naturally big-endian
390 if ( littleendian() ) {
391 png_set_swap (sp);
392 }
393 png_set_interlace_handling(sp);
394
395 png_read_update_info (sp, ip);
396
397
398 png_uint_32 width, height;
399 png_get_IHDR (sp, ip, &width, &height,
400 real_bit_depth_p, color_type_p, NULL, NULL, NULL);
401 *width_p = width;
402 *height_p = height;
403 *bit_depth_p = *real_bit_depth_p == 16 ? eBitDepthUShort : eBitDepthUByte;
404 png_byte channels = png_get_channels (sp, ip);
405 #ifndef NDEBUG
406 png_byte color_type = png_get_color_type(sp, ip);
407 assert( ( channels == 1 && (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) ) ||
408 ( channels == 2 && (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ) ||
409 ( channels == 3 && (color_type == PNG_COLOR_TYPE_RGB) ) ||
410 ( channels == 4 && (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) ) );
411 #endif
412 *nChannels_p = channels;
413
414 *x1_p = png_get_x_offset_pixels (sp, ip);
415 *y1_p = png_get_y_offset_pixels (sp, ip);
416
417 float aspect = (float)png_get_pixel_aspect_ratio (sp, ip);
418 *par_p = 1.;
419 if ( (aspect != 0) && (aspect != 1) ) {
420 *par_p = aspect;
421 }
422
423 double file_gamma = 1.;
424
425 if (colorspace_p) {
426 bool ping_found_sRGB = false;
427 bool ping_found_gAMA = false;
428 bool ping_found_cHRM = false;
429 bool ping_found_sRGB_cHRM = false;
430 bool ping_found_iCCP = false;
431
432 *colorspace_p = ePNGColorSpaceLinear;
433
434 #ifdef PNG_iCCP_SUPPORTED
435 // Always check the ICC profile, because it may indicate sRGB
436 if ( png_get_valid(sp, ip, PNG_INFO_iCCP) ) {
437 png_charp profile_name = NULL;
438 #if OFX_IO_LIBPNG_VERSION > 10500 /* PNG function signatures changed */
439 png_bytep profile_data = NULL;
440 #else
441 png_charp profile_data = NULL;
442 #endif
443 png_uint_32 profile_length = 0;
444 int compression_type;
445 png_get_iCCP (sp, ip, &profile_name, &compression_type,
446 &profile_data, &profile_length);
447 if (profile_length && profile_data) {
448 ping_found_iCCP = true;
449 if (iccprofile_data_p) {
450 #if OFX_IO_LIBPNG_VERSION > 10500
451 *iccprofile_data_p = profile_data;
452 #else
453 *iccprofile_data_p = reinterpret_cast<unsigned char*>(profile_data);
454 #endif
455 }
456 if (iccprofile_length_p) {
457 *iccprofile_length_p = profile_length;
458 }
459 }
460
461 /* Is this profile one of the known ICC sRGB profiles? If it is, just set
462 * the sRGB information.
463 */
464 if (png_compare_ICC_profile_with_sRGB(sp, (png_bytep)profile_data, 0) != 0) {
465 *colorspace_p = ePNGColorSpacesRGB;
466 file_gamma = 2.2;
467 }
468 }
469 #endif
470
471 #ifdef PNG_GAMMA_SUPPORTED
472 // is there a srgb intent ?
473 if (*colorspace_p == ePNGColorSpaceLinear) {
474 int srgb_intent;
475 if (png_get_sRGB (sp, ip, &srgb_intent) == PNG_INFO_sRGB) {
476 *colorspace_p = ePNGColorSpacesRGB;
477 file_gamma = 2.2;
478 ping_found_sRGB = true;
479 }
480 // srgb_intent possible values:
481 // 0: PerceptualIntent
482 // 1: RelativeIntent
483 // 2: SaturationIntent
484 // 3: AbsoluteIntent
485 }
486 #endif
487
488 // if not is there a gamma ?
489 if (*colorspace_p == ePNGColorSpaceLinear) {
490 if ( png_get_gAMA (sp, ip, &file_gamma) ) {
491 // guard against division by zero
492 file_gamma = file_gamma != 0. ? (1. / file_gamma) : 1.;
493 ping_found_gAMA = true;
494 if (file_gamma > 1.) {
495 *colorspace_p = ePNGColorSpaceGammaCorrected;
496 }
497 }
498
499 double white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
500 blue_y;
501
502 #ifdef PNG_cHRM_SUPPORTED
503 if (png_get_cHRM(sp, ip, &white_x, &white_y, &red_x,
504 &red_y, &green_x, &green_y, &blue_x, &blue_y) == PNG_INFO_cHRM) {
505 ping_found_cHRM = true;
506
507 if (red_x>0.6399f &&
508 red_x<0.6401f &&
509 red_y>0.3299f &&
510 red_y<0.3301f &&
511 green_x>0.2999f &&
512 green_x<0.3001f &&
513 green_y>0.5999f &&
514 green_y<0.6001f &&
515 blue_x>0.1499f &&
516 blue_x<0.1501f &&
517 blue_y>0.0599f &&
518 blue_y<0.0601f &&
519 white_x>0.3126f &&
520 white_x<0.3128f &&
521 white_y>0.3289f &&
522 white_y<0.3291f)
523 ping_found_sRGB_cHRM = true;
524 }
525 #endif
526
527 if (!ping_found_sRGB &&
528 (!ping_found_gAMA ||
529 (file_gamma > 1/.46 && file_gamma < 1/.45)) &&
530 (!ping_found_cHRM ||
531 ping_found_sRGB_cHRM) &&
532 !ping_found_iCCP) {
533 file_gamma = 2.2;
534 *colorspace_p = ePNGColorSpacesRGB;
535 ping_found_sRGB = true;
536 }
537 }
538 unused(ping_found_sRGB);
539
540 // if not, deduce from the bitdepth
541 if ( !ping_found_gAMA && (*colorspace_p == ePNGColorSpaceLinear) ) {
542 *colorspace_p = *bit_depth_p == eBitDepthUByte ? ePNGColorSpacesRGB : ePNGColorSpaceRec709;
543 }
544 }
545 if (gamma_p) {
546 *gamma_p = file_gamma;
547 }
548
549
550 #ifdef PNG_tIME_SUPPORTED
551 png_timep mod_time;
552 if ( date_p && png_get_tIME (sp, ip, &mod_time) ) {
553 stringstream ss;
554 ss << std::setfill('0') << std::setw(4) << mod_time->year << ':' << std::setw(2) << mod_time->month << ':' << mod_time->day << ' ';
555 ss << mod_time->hour << ':' << mod_time->minute << ':' << mod_time->second;
556 *date_p = ss.str();
557 }
558 #endif
559
560 #ifdef PNG_TEXT_SUPPORTED
561 if (additionalComments_p) {
562 png_textp text_ptr;
563 int num_comments = png_get_text (sp, ip, &text_ptr, NULL);
564 if (num_comments) {
565 string comments;
566 for (int i = 0; i < num_comments; ++i) {
567 (*additionalComments_p)[string(text_ptr[i].key)] = string(text_ptr[i].text);
568 }
569 }
570 }
571 #endif
572
573 #ifdef PNG_pHYs_SUPPORTED
574 if (xResolution_p && yResolution_p) {
575 int unit;
576 png_uint_32 resx, resy;
577 if ( png_get_pHYs (sp, ip, &resx, &resy, &unit) ) {
578 float scale = 1;
579 if (unit == PNG_RESOLUTION_METER) {
580 // Convert to inches, to match most other formats
581 scale = 2.54 / 100.0;
582 *isResolutionInches_p = true;
583 } else {
584 *isResolutionInches_p = false;
585 }
586 *xResolution_p = (double)resx * scale;
587 *yResolution_p = (double)resy * scale;
588 }
589 }
590 #endif
591
592 if (bg_p) {
593 get_background (sp, ip, *bit_depth_p, *real_bit_depth_p, *nChannels_p, &bg_p->r, &bg_p->g, &bg_p->b);
594 }
595 if (interlace_type_p) {
596 *interlace_type_p = png_get_interlace_type (sp, ip);
597 }
598 } // getPNGInfo
599
600 class ReadPNGPlugin
601 : public GenericReaderPlugin
602 {
603 public:
604
605 ReadPNGPlugin(OfxImageEffectHandle handle, const vector<string>& extensions);
606
607 virtual ~ReadPNGPlugin();
608
609 private:
610
611 virtual void changedParam(const InstanceChangedArgs &args, const string ¶mName) OVERRIDE FINAL;
isVideoStream(const string &)612 virtual bool isVideoStream(const string& /*filename*/) OVERRIDE FINAL { return false; }
613
614 virtual void decode(const string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds, PixelComponentEnum pixelComponents, int pixelComponentCount, int rowBytes) OVERRIDE FINAL;
615 virtual bool getFrameBounds(const string& filename, OfxTime time, int view, OfxRectI *bounds, OfxRectI *format, double *par, string *error, int* tile_width, int* tile_height) OVERRIDE FINAL;
616
617 /**
618 * @brief Called when the input image/video file changed.
619 *
620 * returns true if file exists and parameters successfully guessed, false in case of error.
621 *
622 * This function is only called once: when the filename is first set.
623 *
624 * Besides returning colorspace, premult, components, and componentcount, if it returns true
625 * this function may also set extra format-specific parameters using Param::setValue.
626 * The parameters must not be animated, since their value must remain the same for a whole sequence.
627 *
628 * You shouldn't do any strong processing as this is called on the main thread and
629 * the getRegionOfDefinition() and decode() should open the file in a separate thread.
630 *
631 * The colorspace may be set if available, else a default colorspace is used.
632 *
633 * You must also return the premultiplication state and pixel components of the image.
634 * When reading an image sequence, this is called only for the first image when the user actually selects the new sequence.
635 **/
636 virtual bool guessParamsFromFilename(const string& filename, string *colorspace, PreMultiplicationEnum *filePremult, PixelComponentEnum *components, int *componentCount) OVERRIDE FINAL;
637 static void openFile(const string& filename,
638 png_structp* png,
639 png_infop* info,
640 std::FILE** file);
641
642 string metadata(const string& filename);
643 };
644
645
ReadPNGPlugin(OfxImageEffectHandle handle,const vector<string> & extensions)646 ReadPNGPlugin::ReadPNGPlugin(OfxImageEffectHandle handle,
647 const vector<string>& extensions)
648 : GenericReaderPlugin(handle, extensions, kSupportsRGBA, kSupportsRGB, kSupportsXY, kSupportsAlpha, kSupportsTiles, false)
649 {
650 }
651
~ReadPNGPlugin()652 ReadPNGPlugin::~ReadPNGPlugin()
653 {
654 }
655
656 #define PNG_BYTES_TO_CHECK 8
657
658 string
metadata(const string & filename)659 ReadPNGPlugin::metadata(const string& filename)
660 {
661 // Open the file
662 std::FILE *image = fopen_utf8(filename.c_str(), "rb");
663 if (image == NULL) {
664 setPersistentMessage(Message::eMessageError, "", string("ReadPNG: cannot open file ") + filename);
665 throwSuiteStatusException(kOfxStatFailed);
666
667 return string();
668 }
669
670 stringstream ss;
671
672 ss << "file: " << filename << std::endl;
673
674 {
675 unsigned char sig[8];
676 // Check that it really is a PNG file
677 /* Read in some of the signature bytes */
678 if ( (std::fread(sig, 1, PNG_BYTES_TO_CHECK, image) != PNG_BYTES_TO_CHECK) ||
679 png_sig_cmp(sig, 0, PNG_BYTES_TO_CHECK) ) {
680 ss << " This file is not a valid PNG file\n";
681 std::fclose (image);
682
683 return ss.str();
684 }
685 }
686
687 // Start decompressing
688 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
689 NULL, NULL);
690 if (png == NULL) {
691 ss << "Could not create a PNG read structure (out of memory?)\n";
692 std::fclose(image);
693
694 return ss.str();
695 }
696 png_infop info = png_create_info_struct(png);
697 if (info == NULL) {
698 ss << "Could not create PNG info structure (out of memory?)\n";
699 png_destroy_read_struct(&png, NULL, NULL);
700 std::fclose(image);
701
702 return ss.str();
703 }
704
705 if ( setjmp( png_jmpbuf(png) ) ) {
706 png_destroy_read_struct(&png, &info, NULL);
707 ss << "Could not set PNG jump value";
708 std::fclose(image);
709
710 return ss.str();
711 }
712 // Get ready for IO and tell the API we have already read the image signature
713 png_init_io (png, image);
714 png_set_sig_bytes (png, PNG_BYTES_TO_CHECK);
715 png_read_info (png, info);
716 png_uint_32 width;
717 png_uint_32 height;
718 int bit_depth;
719 int color_type;
720 png_get_IHDR (png, info, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
721
722 ///////////////////////////////////////////////////////////////////////////
723 // Start displaying information
724 ///////////////////////////////////////////////////////////////////////////
725
726 ss << " Image Width: " << width << " Image Length: " << height << std::endl;
727 int pixel_depth = bit_depth * png_get_channels(png, info);
728 ss << " Bitdepth (Bits/Sample): " << bit_depth << std::endl;
729 ss << " Channels (Samples/Pixel): " << (int)png_get_channels(png, info) << std::endl;
730 ss << " Pixel depth (Pixel Depth): " << pixel_depth << std::endl; // Does this add value?
731
732 // Photometric interp packs a lot of information
733 ss << " Colour Type (Photometric Interpretation): ";
734
735 switch (color_type) {
736 case PNG_COLOR_TYPE_GRAY: {
737 ss << "GRAYSCALE ";
738 png_bytep trans = NULL;
739 int num_trans = 0;
740 if (png_get_tRNS(png, info, &trans, &num_trans, NULL) == PNG_INFO_tRNS) {
741 ss << "(" << num_trans << " transparent) ";
742 }
743 break;
744 }
745 case PNG_COLOR_TYPE_PALETTE: {
746 ss << "PALETTED COLOUR ";
747 int num_palette = 0;
748 png_color *palette = NULL;
749 png_get_PLTE(png, info, &palette, &num_palette);
750 int num_trans = 0;
751 png_bytep trans = NULL;
752 ;
753 ss << "(" << num_palette << " colours";
754 if (png_get_tRNS(png, info, &trans, &num_trans, NULL) == PNG_INFO_tRNS) {
755 ss << ", " << num_trans << " transparent";
756 }
757 ss << ")";
758 break;
759 }
760 case PNG_COLOR_TYPE_RGB: {
761 ss << "RGB ";
762 break;
763 }
764 case PNG_COLOR_TYPE_RGB_ALPHA: {
765 ss << "RGB with alpha channel ";
766 break;
767 }
768 case PNG_COLOR_TYPE_GRAY_ALPHA: {
769 ss << "GRAYSCALE with alpha channel ";
770 break;
771 }
772 default: {
773 ss << "Unknown photometric interpretation!";
774 break;
775 }
776 }
777 ss << std::endl;
778
779 ss << " Image filter: ";
780 switch ( png_get_filter_type(png, info) ) {
781 case PNG_FILTER_TYPE_BASE:
782 ss << "Single row per byte filter ";
783 break;
784
785 case PNG_INTRAPIXEL_DIFFERENCING:
786 ss << "Intrapixel differencing (MNG only) ";
787 break;
788
789 default:
790 ss << "Unknown filter! ";
791 break;
792 }
793 ss << std::endl;
794
795 #if defined(PNG_READ_INTERLACING_SUPPORTED)
796 ss << " Interlacing: ";
797 switch ( png_get_interlace_type(png, info) ) {
798 case PNG_INTERLACE_NONE:
799 ss << "No interlacing ";
800 break;
801
802 case PNG_INTERLACE_ADAM7:
803 ss << "Adam7 interlacing ";
804 break;
805
806 default:
807 ss << "Unknown interlacing ";
808 break;
809 }
810 ss << std::endl;
811 #endif
812
813 ss << " Compression Scheme: ";
814 switch ( png_get_compression_type(png, info) ) {
815 case PNG_COMPRESSION_TYPE_BASE:
816 ss << "Deflate method 8, 32k window";
817 break;
818
819 default:
820 ss << "Unknown compression scheme!";
821 break;
822 }
823 ss << std::endl;
824
825 #ifdef PNG_pHYs_SUPPORTED
826 {
827 png_uint_32 res_x, res_y;
828 int unit_type;
829
830 if (png_get_pHYs(png, info, &res_x, &res_y,
831 &unit_type) == PNG_INFO_pHYs) {
832 ss << " Resolution: " << res_x << ", " << res_y << " ";
833 switch (unit_type) {
834 case PNG_RESOLUTION_UNKNOWN:
835 ss << "(unit unknown)";
836 break;
837
838 case PNG_RESOLUTION_METER:
839 ss << "(pixels per meter)";
840 break;
841
842 default:
843 ss << "(Unknown value for unit stored)";
844 break;
845 }
846 ss << std::endl;
847 }
848 }
849 #endif
850
851 // FillOrder is always msb-to-lsb, big endian
852 //ss << " FillOrder: msb-to-lsb\n Byte Order: Network (Big Endian)\n";
853
854 #ifdef PNG_cHRM_SUPPORTED
855 {
856 double white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
857 blue_y;
858
859 if (png_get_cHRM(png, info, &white_x, &white_y, &red_x,
860 &red_y, &green_x, &green_y, &blue_x, &blue_y) == PNG_INFO_cHRM) {
861 ss << " CIE white point: " << white_x << ", " << white_y << std::endl;
862 ss << " CIE chromaticities: ";
863 ss << "red = " << red_x << ", " << red_y << "; ";
864 ss << "green =" << green_x << ", " << green_y << "; ";
865 ss << "blue =" << blue_x << ", " << blue_y << std::endl;
866 }
867 }
868 #endif
869 #ifdef PNG_gAMA_SUPPORTED
870 {
871 double gamma;
872
873 if (png_get_gAMA(png, info, &gamma) == PNG_INFO_gAMA) {
874 ss << " Gamma: " << gamma << std::endl;
875 }
876 }
877 #endif
878 #ifdef PNG_iCCP_SUPPORTED
879 {
880 png_charp name;
881 #if PNG_LIBPNG_VER_SONUM >= 15
882 png_bytep profile;
883 #else
884 png_charp profile;
885 #endif
886 png_uint_32 proflen;
887 int compression_type;
888
889 if (png_get_iCCP(png, info, &name, &compression_type,
890 &profile, &proflen) == PNG_INFO_iCCP) {
891 ss << " ICC profile: " << name;
892 }
893 }
894 #endif
895 #ifdef PNG_sRGB_SUPPORTED
896 {
897 int intent;
898
899 if (png_get_sRGB(png, info, &intent) == PNG_INFO_sRGB) {
900 ss << " sRGB intent: ";
901 switch (intent) {
902 case PNG_sRGB_INTENT_PERCEPTUAL:
903 ss << "perceptual";
904 break;
905 case PNG_sRGB_INTENT_RELATIVE:
906 ss << "relative";
907 break;
908 case PNG_sRGB_INTENT_SATURATION:
909 ss << "saturation";
910 break;
911 case PNG_sRGB_INTENT_ABSOLUTE:
912 ss << "absolute";
913 break;
914 }
915 ss << std::endl;
916 }
917 }
918 #endif
919 #ifdef PNG_bKGD_SUPPORTED
920 {
921 png_color_16p background;
922
923 if (png_get_bKGD(png, info, &background) == PNG_INFO_bKGD) {
924 ss << " Background color present\n";
925 }
926 }
927 #endif
928 #ifdef PNG_hIST_SUPPORTED
929 {
930 png_uint_16p hist;
931
932 if (png_get_hIST(png, info, &hist) == PNG_INFO_hIST) {
933 ss << " Histogram present\n";
934 }
935 }
936 #endif
937 #ifdef PNG_oFFs_SUPPORTED
938 {
939 png_int_32 offset_x, offset_y;
940 int unit_type;
941
942 if (png_get_oFFs(png, info, &offset_x, &offset_y, &unit_type) == PNG_INFO_oFFs) {
943 // see http://www.libpng.org/pub/png/spec/register/pngext-1.4.0-pdg.html#C.oFFs
944 const char* unit = (unit_type == PNG_OFFSET_PIXEL) ? "px" : "um";
945 ss << " Offset: " << offset_x << unit << ", " << offset_y << unit << std::endl;
946 }
947 }
948 #endif
949 #ifdef PNG_pCAL_SUPPORTED
950 {
951 png_charp purpose, units;
952 png_charpp params;
953 png_int_32 X0, X1;
954 int type, nparams;
955
956 if (png_get_pCAL(png, info, &purpose, &X0, &X1, &type,
957 &nparams, &units, ¶ms) == PNG_INFO_pCAL) {
958 // see http://www.libpng.org/pub/png/spec/register/pngext-1.4.0-pdg.html#C.pCAL
959 ss << " Physical calibration chunk present\n";
960 }
961 }
962 #endif
963 #ifdef PNG_sBIT_SUPPORTED
964 {
965 png_color_8p sig_bit;
966
967 if (png_get_sBIT(png, info, &sig_bit) == PNG_INFO_sBIT) {
968 ss << " Significant bits per channel: " << sig_bit << std::endl;
969 }
970 }
971 #endif
972 #ifdef PNG_sCAL_SUPPORTED
973 {
974 int unit_type;
975 double scal_width, scal_height;
976
977 if (png_get_sCAL(png, info, &unit_type, &scal_width,
978 &scal_height) == PNG_INFO_sCAL) {
979 // see http://www.libpng.org/pub/png/spec/register/pngext-1.4.0-pdg.html#C.sCAL
980 const char* unit = unit_type == PNG_SCALE_UNKNOWN ? "" : (unit_type == PNG_SCALE_METER ? "m" : "rad");
981 ss << " Scale: " << scal_width << unit << " x " << scal_height << unit << std::endl;
982 }
983 }
984 #endif
985
986 #if defined(PNG_TEXT_SUPPORTED)
987 {
988 png_textp text;
989 int num_text = 0;
990
991 if (png_get_text(png, info, &text, &num_text) > 0) {
992 // Text comments
993 ss << " Number of text strings: " << num_text << std::endl;
994
995 for (int i = 0; i < num_text; ++i) {
996 ss << " " << text[i].key << " ";
997
998 switch (text[1].compression) {
999 case -1:
1000 ss << "(tEXt uncompressed)";
1001 break;
1002
1003 case 0:
1004 ss << "(xTXt deflate compressed)";
1005 break;
1006
1007 case 1:
1008 ss << "(iTXt uncompressed)";
1009 break;
1010
1011 case 2:
1012 ss << "(iTXt deflate compressed)";
1013 break;
1014
1015 default:
1016 ss << "(unknown compression)";
1017 break;
1018 }
1019
1020 ss << ": ";
1021 int j = 0;
1022 while (text[i].text[j] != '\0') {
1023 if (text[i].text[j] == '\n') {
1024 ss << std::endl;
1025 } else {
1026 ss << text[i].text[j];
1027 }
1028 j++;
1029 }
1030
1031 ss << std::endl;
1032 }
1033 }
1034 }
1035 #endif // if defined(PNG_TEXT_SUPPORTED)
1036
1037 // This cleans things up for us in the PNG library
1038 std::fclose(image);
1039 png_destroy_read_struct (&png, &info, NULL);
1040
1041 return ss.str();
1042 } // ReadPNGPlugin::metadata
1043
1044 void
changedParam(const InstanceChangedArgs & args,const string & paramName)1045 ReadPNGPlugin::changedParam(const InstanceChangedArgs &args,
1046 const string ¶mName)
1047 {
1048 if (paramName == kParamLibraryInfo) {
1049 string msg = (string() +
1050 "libpng version (compiled with / running with): " + PNG_LIBPNG_VER_STRING + '/' + png_libpng_ver + '\n' +
1051 "zlib version (compiled with / running with): " + ZLIB_VERSION + '/' + zlib_version + '\n' +
1052 png_get_copyright(NULL));
1053 sendMessage(Message::eMessageMessage, "", msg);
1054 } else if (paramName == kParamShowMetadata) {
1055 string filename;
1056 OfxStatus st = getFilenameAtTime(args.time, &filename);
1057 stringstream ss;
1058 if (st == kOfxStatOK) {
1059 ss << metadata(filename);
1060 } else if ( filename.empty() ) {
1061 ss << "Impossible to read image info:\nCould not get filename at time " << args.time << '.';
1062 } else {
1063 ss << "Impossible to read image info:\nCould not read file " << filename << " corresponding to time " << args.time << '.';
1064 }
1065 sendMessage( Message::eMessageMessage, "", ss.str() );
1066 } else {
1067 GenericReaderPlugin::changedParam(args, paramName);
1068 }
1069 }
1070
1071 void
openFile(const string & filename,png_structp * png,png_infop * info,std::FILE ** file)1072 ReadPNGPlugin::openFile(const string& filename,
1073 png_structp* png,
1074 png_infop* info,
1075 std::FILE** file)
1076 {
1077 *png = NULL;
1078 *info = NULL;
1079 *file = fopen_utf8(filename.c_str(), "rb");
1080 if (!*file) {
1081 throw std::runtime_error("Could not open file: " + filename);
1082 }
1083
1084 unsigned char sig[8];
1085 if ( std::fread (sig, 1, sizeof(sig), *file) != sizeof(sig) ) {
1086 throw std::runtime_error("Not a PNG file");
1087 }
1088
1089 if ( png_sig_cmp (sig, 0, 7) ) {
1090 throw std::runtime_error("Not a PNG file");
1091 }
1092
1093 *png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1094 if (!*png) {
1095 std::fclose(*file);
1096 *file = NULL;
1097
1098 throw std::runtime_error("Could not create PNG read structure");
1099 }
1100
1101 *info = png_create_info_struct (*png);
1102 if (!*info) {
1103 png_destroy_read_struct (png, NULL, NULL);
1104 std::fclose(*file);
1105 *file = NULL;
1106
1107 throw std::runtime_error("Could not create PNG info structure");
1108 }
1109 // Must call this setjmp in every function that does PNG reads
1110 if ( setjmp ( png_jmpbuf(*png) ) ) {
1111 png_destroy_read_struct (png, info, NULL);
1112 std::fclose(*file);
1113 *file = NULL;
1114
1115 throw std::runtime_error("PNG library error");
1116 }
1117
1118 png_init_io (*png, *file);
1119 png_set_sig_bytes (*png, 8); // already read 8 bytes
1120 }
1121
1122 void
decode(const string & filename,OfxTime,int,bool,const OfxRectI & renderWindow,float * pixelData,const OfxRectI & bounds,PixelComponentEnum pixelComponents,int,int rowBytes)1123 ReadPNGPlugin::decode(const string& filename,
1124 OfxTime /*time*/,
1125 int /*view*/,
1126 bool /*isPlayback*/,
1127 const OfxRectI& renderWindow,
1128 float *pixelData,
1129 const OfxRectI& bounds,
1130 PixelComponentEnum pixelComponents,
1131 int /*pixelComponentCount*/,
1132 int rowBytes)
1133 {
1134 if ( (pixelComponents != ePixelComponentRGBA) && (pixelComponents != ePixelComponentRGB) && (pixelComponents != ePixelComponentXY) && (pixelComponents != ePixelComponentAlpha) ) {
1135 setPersistentMessage(Message::eMessageError, "", "PNG: can only read RGBA, RGB or Alpha components images");
1136 throwSuiteStatusException(kOfxStatErrFormat);
1137
1138 return;
1139 }
1140
1141 png_structp png;
1142 png_infop info;
1143 FILE* file;
1144
1145 try {
1146 openFile(filename, &png, &info, &file);
1147 } catch (const std::exception& e) {
1148 setPersistentMessage( Message::eMessageError, "", e.what() );
1149 throwSuiteStatusException(kOfxStatFailed);
1150 }
1151
1152 int x1, y1, width, height;
1153 int nChannels;
1154 BitDepthEnum bitdepth;
1155 int realbitdepth;
1156 int colorType;
1157 double par;
1158 getPNGInfo(png, info, &x1, &y1, &width, &height, &par, &nChannels, &bitdepth, &realbitdepth, &colorType, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1159
1160 assert(renderWindow.x1 >= x1 && renderWindow.y1 >= y1 && renderWindow.x2 <= x1 + width && renderWindow.y2 <= y1 + height);
1161
1162 std::size_t pngRowBytes = nChannels * width;
1163 if (bitdepth == eBitDepthUShort) {
1164 pngRowBytes *= sizeof(unsigned short);
1165 }
1166
1167 RamBuffer scratchBuffer(pngRowBytes * height);
1168 unsigned char* tmpData = scratchBuffer.getData();
1169
1170
1171 //if (interlace_type != 0) {
1172 vector<unsigned char *> row_pointers(height);
1173 for (int i = 0; i < height; ++i) {
1174 row_pointers[i] = tmpData + i * pngRowBytes;
1175 }
1176
1177 // Must call this setjmp in every function that does PNG reads
1178 if ( setjmp ( png_jmpbuf (png) ) ) {
1179 png_destroy_read_struct(&png, &info, NULL);
1180 std::fclose(file);
1181 setPersistentMessage(Message::eMessageError, "", "PNG library error");
1182 throwSuiteStatusException(kOfxStatErrFormat);
1183
1184 return;
1185 }
1186 png_read_image(png, &row_pointers[0]);
1187 png_read_end(png, NULL);
1188 /*
1189 } else {
1190 for (int y = 0; y < height; ++y) {
1191 // Must call this setjmp in every function that does PNG reads
1192 if (setjmp (png_jmpbuf (png))) {
1193 png_destroy_read_struct(&png, &info, NULL);
1194 std::fclose(file);
1195 setPersistentMessage(Message::eMessageError, "", "PNG library error");
1196 throwSuiteStatusException(kOfxStatErrFormat);
1197
1198 return;
1199 }
1200 png_read_row (png, (png_bytep)tmpData + y * pngRowBytes, NULL);
1201 }
1202 }
1203 */
1204
1205 png_destroy_read_struct(&png, &info, NULL);
1206 std::fclose(file);
1207 file = NULL;
1208
1209 OfxRectI srcBounds;
1210 srcBounds.x1 = x1;
1211 srcBounds.y1 = y1;
1212 srcBounds.x2 = x1 + width;
1213 srcBounds.y2 = y1 + height;
1214
1215 PixelComponentEnum srcComponents;
1216 switch (nChannels) {
1217 case 1:
1218 srcComponents = ePixelComponentAlpha;
1219 break;
1220 case 2:
1221 srcComponents = ePixelComponentXY;
1222 break;
1223 case 3:
1224 srcComponents = ePixelComponentRGB;
1225 break;
1226 case 4:
1227 srcComponents = ePixelComponentRGBA;
1228 break;
1229 default:
1230 setPersistentMessage(Message::eMessageError, "", "This plug-in only supports images with 1 to 4 channels");
1231 throwSuiteStatusException(kOfxStatErrFormat);
1232
1233 return;
1234 }
1235
1236 convertDepthAndComponents(tmpData, renderWindow, srcBounds, srcComponents, bitdepth, pngRowBytes, pixelData, bounds, pixelComponents, rowBytes);
1237 } // ReadPNGPlugin::decode
1238
1239 bool
getFrameBounds(const string & filename,OfxTime,int,OfxRectI * bounds,OfxRectI * format,double * par,string * error,int * tile_width,int * tile_height)1240 ReadPNGPlugin::getFrameBounds(const string& filename,
1241 OfxTime /*time*/,
1242 int /*view*/,
1243 OfxRectI *bounds,
1244 OfxRectI *format,
1245 double *par,
1246 string *error,
1247 int* tile_width,
1248 int* tile_height)
1249 {
1250 assert(bounds && par);
1251 png_structp png;
1252 png_infop info;
1253 FILE* file;
1254
1255 try {
1256 openFile(filename, &png, &info, &file);
1257 } catch (const std::exception& e) {
1258 *error = e.what();
1259
1260 return false;
1261 }
1262
1263 int x1, y1, width, height;
1264 int nChannels;
1265 BitDepthEnum bitdepth;
1266 int realbitdepth;
1267 int colorType;
1268
1269 getPNGInfo(png, info, &x1, &y1, &width, &height, par, &nChannels, &bitdepth, &realbitdepth, &colorType, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1270 png_destroy_read_struct(&png, &info, NULL);
1271 std::fclose(file);
1272 file = NULL;
1273
1274 bounds->x1 = x1;
1275 bounds->y1 = y1;
1276 bounds->x2 = x1 + width;
1277 bounds->y2 = y1 + height;
1278 *format = *bounds;
1279 *tile_height = *tile_width = 0;
1280
1281 return true;
1282 }
1283
1284 /**
1285 * @brief Called when the input image/video file changed.
1286 *
1287 * returns true if file exists and parameters successfully guessed, false in case of error.
1288 *
1289 * This function is only called once: when the filename is first set.
1290 *
1291 * Besides returning colorspace, premult, components, and componentcount, if it returns true
1292 * this function may also set extra format-specific parameters using Param::setValue.
1293 * The parameters must not be animated, since their value must remain the same for a whole sequence.
1294 *
1295 * You shouldn't do any strong processing as this is called on the main thread and
1296 * the getRegionOfDefinition() and decode() should open the file in a separate thread.
1297 *
1298 * The colorspace may be set if available, else a default colorspace is used.
1299 *
1300 * You must also return the premultiplication state and pixel components of the image.
1301 * When reading an image sequence, this is called only for the first image when the user actually selects the new sequence.
1302 **/
1303 bool
guessParamsFromFilename(const string & filename,string * colorspace,PreMultiplicationEnum * filePremult,PixelComponentEnum * components,int * componentCount)1304 ReadPNGPlugin::guessParamsFromFilename(const string& filename,
1305 string *colorspace,
1306 PreMultiplicationEnum *filePremult,
1307 PixelComponentEnum *components,
1308 int *componentCount)
1309 {
1310 assert(colorspace && filePremult && components && componentCount);
1311 png_structp png;
1312 png_infop info;
1313 FILE* file;
1314 try {
1315 openFile(filename, &png, &info, &file);
1316 } catch (const std::exception& e) {
1317 //setPersistentMessage(Message::eMessageError, "", e.what());
1318
1319 return false;
1320 }
1321
1322 int x1, y1, width, height;
1323 double par;
1324 int nChannels;
1325 BitDepthEnum bitdepth;
1326 int realbitdepth;
1327 int colorType;
1328 double gamma;
1329 PNGColorSpaceEnum pngColorspace;
1330 getPNGInfo(png, info, &x1, &y1, &width, &height, &par, &nChannels, &bitdepth, &realbitdepth, &colorType, &pngColorspace, &gamma, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1331 png_destroy_read_struct(&png, &info, NULL);
1332 std::fclose(file);
1333 file = NULL;
1334
1335 # ifdef OFX_IO_USING_OCIO
1336 switch (pngColorspace) {
1337 case ePNGColorSpaceGammaCorrected:
1338 if (std::fabs(gamma - 1.8) < 0.05) {
1339 if ( _ocio->hasColorspace("Gamma1.8") ) {
1340 // nuke-default
1341 *colorspace = "Gamma1.8";
1342 }
1343 } else if (std::fabs(gamma - 2.2) < 0.05) {
1344 if ( _ocio->hasColorspace("Gamma2.2") ) {
1345 // nuke-default
1346 *colorspace = "Gamma2.2";
1347 } else if ( _ocio->hasColorspace("VD16") ) {
1348 // VD16 in blender
1349 *colorspace = "VD16";
1350 } else if ( _ocio->hasColorspace("vd16") ) {
1351 // vd16 in spi-anim and spi-vfx
1352 *colorspace = "vd16";
1353 } else if ( _ocio->hasColorspace("sRGB") ) {
1354 // nuke-default and blender
1355 *colorspace = "sRGB";
1356 } else if ( _ocio->hasColorspace("sRGB D65") ) {
1357 // blender-cycles
1358 *colorspace = "sRGB D65";
1359 } else if ( _ocio->hasColorspace("sRGB (D60 sim.)") ) {
1360 // out_srgbd60sim or "sRGB (D60 sim.)" in aces 1.0.0
1361 *colorspace = "sRGB (D60 sim.)";
1362 } else if ( _ocio->hasColorspace("out_srgbd60sim") ) {
1363 // out_srgbd60sim or "sRGB (D60 sim.)" in aces 1.0.0
1364 *colorspace = "out_srgbd60sim";
1365 } else if ( _ocio->hasColorspace("rrt_Gamma2.2") ) {
1366 // rrt_Gamma2.2 in aces 0.7.1
1367 *colorspace = "rrt_Gamma2.2";
1368 } else if ( _ocio->hasColorspace("rrt_srgb") ) {
1369 // rrt_srgb in aces 0.1.1
1370 *colorspace = "rrt_srgb";
1371 } else if ( _ocio->hasColorspace("srgb8") ) {
1372 // srgb8 in spi-vfx
1373 *colorspace = "srgb8";
1374 } else if ( _ocio->hasColorspace("vd16") ) {
1375 // vd16 in spi-anim
1376 *colorspace = "vd16";
1377 }
1378 }
1379 break;
1380 case ePNGColorSpacesRGB:
1381 if ( _ocio->hasColorspace("sRGB") ) {
1382 // nuke-default and blender
1383 *colorspace = "sRGB";
1384 } else if ( _ocio->hasColorspace("sRGB D65") ) {
1385 // blender-cycles
1386 *colorspace = "sRGB D65";
1387 } else if ( _ocio->hasColorspace("sRGB (D60 sim.)") ) {
1388 // out_srgbd60sim or "sRGB (D60 sim.)" in aces 1.0.0
1389 *colorspace = "sRGB (D60 sim.)";
1390 } else if ( _ocio->hasColorspace("out_srgbd60sim") ) {
1391 // out_srgbd60sim or "sRGB (D60 sim.)" in aces 1.0.0
1392 *colorspace = "out_srgbd60sim";
1393 } else if ( _ocio->hasColorspace("rrt_Gamma2.2") ) {
1394 // rrt_Gamma2.2 in aces 0.7.1
1395 *colorspace = "rrt_Gamma2.2";
1396 } else if ( _ocio->hasColorspace("rrt_srgb") ) {
1397 // rrt_srgb in aces 0.1.1
1398 *colorspace = "rrt_srgb";
1399 } else if ( _ocio->hasColorspace("srgb8") ) {
1400 // srgb8 in spi-vfx
1401 *colorspace = "srgb8";
1402 } else if ( _ocio->hasColorspace("Gamma2.2") ) {
1403 // nuke-default
1404 *colorspace = "Gamma2.2";
1405 } else if ( _ocio->hasColorspace("srgb8") ) {
1406 // srgb8 in spi-vfx
1407 *colorspace = "srgb8";
1408 } else if ( _ocio->hasColorspace("vd16") ) {
1409 // vd16 in spi-anim
1410 *colorspace = "vd16";
1411 }
1412
1413 break;
1414 case ePNGColorSpaceRec709:
1415 if ( _ocio->hasColorspace("Rec709") ) {
1416 // nuke-default
1417 *colorspace = "Rec709";
1418 } else if ( _ocio->hasColorspace("nuke_rec709") ) {
1419 // blender
1420 *colorspace = "nuke_rec709";
1421 } else if ( _ocio->hasColorspace("Rec.709 - Full") ) {
1422 // out_rec709full or "Rec.709 - Full" in aces 1.0.0
1423 *colorspace = "Rec.709 - Full";
1424 } else if ( _ocio->hasColorspace("out_rec709full") ) {
1425 // out_rec709full or "Rec.709 - Full" in aces 1.0.0
1426 *colorspace = "out_rec709full";
1427 } else if ( _ocio->hasColorspace("rrt_rec709_full_100nits") ) {
1428 // rrt_rec709_full_100nits in aces 0.7.1
1429 *colorspace = "rrt_rec709_full_100nits";
1430 } else if ( _ocio->hasColorspace("rrt_rec709") ) {
1431 // rrt_rec709 in aces 0.1.1
1432 *colorspace = "rrt_rec709";
1433 } else if ( _ocio->hasColorspace("hd10") ) {
1434 // hd10 in spi-anim and spi-vfx
1435 *colorspace = "hd10";
1436 }
1437 break;
1438 case ePNGColorSpaceLinear:
1439 *colorspace = OCIO::ROLE_SCENE_LINEAR;
1440 break;
1441 } // switch
1442 # endif // ifdef OFX_IO_USING_OCIO
1443
1444 switch (nChannels) {
1445 case 1:
1446 assert(false);
1447 *components = ePixelComponentAlpha;
1448 break;
1449 case 2:
1450 assert(false);
1451 *components = ePixelComponentRGBA;
1452 break;
1453 case 3:
1454 *components = ePixelComponentRGB;
1455 break;
1456 case 4:
1457 *components = ePixelComponentRGBA;
1458 break;
1459 default:
1460 break;
1461 }
1462
1463 *componentCount = nChannels;
1464
1465 if ( (*components != ePixelComponentRGBA) && (*components != ePixelComponentAlpha) ) {
1466 *filePremult = eImageOpaque;
1467 } else {
1468 // output is always unpremultiplied
1469 *filePremult = eImageUnPreMultiplied;
1470 }
1471
1472 return true;
1473 } // ReadPNGPlugin::guessParamsFromFilename
1474
1475
1476 mDeclareReaderPluginFactory(ReadPNGPluginFactory, {}, false);
1477 void
load()1478 ReadPNGPluginFactory::load()
1479 {
1480 _extensions.clear();
1481 _extensions.push_back("png");
1482 }
1483
1484 /** @brief The basic describe function, passed a plugin descriptor */
1485 void
describe(ImageEffectDescriptor & desc)1486 ReadPNGPluginFactory::describe(ImageEffectDescriptor &desc)
1487 {
1488 GenericReaderDescribe(desc, _extensions, kPluginEvaluation, kSupportsTiles, false);
1489
1490 // basic labels
1491 desc.setLabel(kPluginName);
1492 desc.setPluginDescription(kPluginDescription);
1493 }
1494
1495 /** @brief The describe in context function, passed a plugin descriptor and a context */
1496 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)1497 ReadPNGPluginFactory::describeInContext(ImageEffectDescriptor &desc,
1498 ContextEnum context)
1499 {
1500 // make some pages and to things in
1501 PageParamDescriptor *page = GenericReaderDescribeInContextBegin(desc, context, isVideoStreamPlugin(),
1502 kSupportsRGBA, kSupportsRGB, kSupportsXY, kSupportsAlpha, kSupportsTiles, true);
1503
1504 {
1505 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamShowMetadata);
1506 param->setLabel(kParamShowMetadataLabel);
1507 param->setHint(kParamShowMetadataHint);
1508 if (page) {
1509 page->addChild(*param);
1510 }
1511 }
1512 {
1513 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamLibraryInfo);
1514 param->setLabelAndHint(kParamLibraryInfoLabel);
1515 if (page) {
1516 page->addChild(*param);
1517 }
1518 }
1519
1520 GenericReaderDescribeInContextEnd(desc, context, page, "sRGB", "scene_linear");
1521 }
1522
1523 /** @brief The create instance function, the plugin must return an object derived from the \ref ImageEffect class */
1524 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)1525 ReadPNGPluginFactory::createInstance(OfxImageEffectHandle handle,
1526 ContextEnum /*context*/)
1527 {
1528 ReadPNGPlugin* ret = new ReadPNGPlugin(handle, _extensions);
1529
1530 ret->restoreStateFromParams();
1531
1532 return ret;
1533 }
1534
1535 static ReadPNGPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
1536 mRegisterPluginFactoryInstance(p)
1537
1538 OFXS_NAMESPACE_ANONYMOUS_EXIT
1539