From: glennrp Date: Tue, 14 May 2013 22:50:32 +0000 (+0000) Subject: Recognize ICC profiles that are sRGB and use PNG sRGB chunk instead X-Git-Tag: 7.0.1-0~3677 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ecab7d7b7fc598881ff3c72240381caa5c444a56;p=imagemagick Recognize ICC profiles that are sRGB and use PNG sRGB chunk instead --- diff --git a/coders/png.c b/coders/png.c index dd6c82853..0df284a1c 100644 --- a/coders/png.c +++ b/coders/png.c @@ -129,12 +129,56 @@ ((color).blue == (target).blue)) #endif +/* Table of recognized sRGB ICC profiles */ +struct sRGB_info_struct +{ + png_uint_32 len; + png_uint_32 crc; + png_byte intent; +}; + +const struct sRGB_info_struct sRGB_info[] = +{ + /* ICC v2 perceptual no black compensation + * sRGB_IEC61966-2-1_black_scaled.icc + */ + { 3048, 0x3b8772b9UL, 0}, + + /* ICC v2 relative no black compensation + * sRGB_IEC61966-2-1_no_black_scaling.icc + */ + { 3052, 0x427ebb21UL, 1}, + + /* ICC v4 perceptual + * sRGB_v4_ICC_preference_displayclass.icc + */ + {60988, 0x306fd8aeUL, 0}, + + /* ICC v4 perceptual + * sRGB_v4_ICC_preference.icc perceptual + */ + {60960, 0xbbef7812UL, 0}, + + /* HP? sRGB v2 media-relative + * sRGB_IEC61966-2-1_noBPC.icc + */ + { 3024, 0x5d5129ceUL, 1}, + + /* HP-Microsoft sRGB v2 perceptual */ + { 3144, 0x182ea552UL, 0}, + + /* HP-Microsoft sRGB v2 media-relative */ + { 3144, 0xf29e526dUL, 1}, + + /* Not recognized */ + { 0, 0x00000000UL, 0}, +}; + /* Macros for left-bit-replication to ensure that pixels * and PixelInfos all have the same image->depth, and for use * in PNG8 quantization. */ - /* LBR01: Replicate top bit */ #define LBR01PacketRed(pixelpacket) \ @@ -934,7 +978,9 @@ typedef struct _MngInfo ping_exclude_vpAg, ping_exclude_zCCP, /* hex-encoded iCCP */ ping_exclude_zTXt, - ping_preserve_colormap; + ping_preserve_colormap, + /* Added at version 6.8.5-7 */ + ping_preserve_iCCP; } MngInfo; #endif /* VER */ @@ -2062,6 +2108,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, ping_found_iCCP, ping_found_sRGB, ping_found_sRGB_cHRM, + ping_preserve_iCCP, status; PixelInfo @@ -2239,6 +2286,20 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, ping_found_gAMA = MagickFalse; ping_found_iCCP = MagickFalse; ping_found_sRGB = MagickFalse; + ping_preserve_iCCP = MagickFalse; + + { + const char + *value; + + value=GetImageOption(image_info,"png:preserve-iCCP"); + + if (value == NULL) + value=GetImageArtifact(image,"png:preserve-iCCP"); + + if (value != NULL) + ping_preserve_iCCP=MagickTrue; + } /* Allocate the PNG structures @@ -2456,6 +2517,14 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, ping_interlace_method,ping_filter_method); } + if (png_get_valid(ping,ping_info, PNG_INFO_iCCP)) + { + ping_found_iCCP=MagickTrue; + if (logging != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " Found PNG iCCP chunk."); + } + if (png_get_valid(ping,ping_info,PNG_INFO_gAMA)) { ping_found_gAMA=MagickTrue; @@ -2472,23 +2541,26 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, " Found PNG cHRM chunk."); } - if (png_get_valid(ping,ping_info,PNG_INFO_iCCP)) + if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info, + PNG_INFO_sRGB)) { - ping_found_iCCP=MagickTrue; + ping_found_sRGB=MagickTrue; if (logging != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " Found PNG iCCP chunk."); + " Found PNG sRGB chunk."); } - if (png_get_valid(ping,ping_info,PNG_INFO_sRGB)) +#ifdef PNG_READ_iCCP_SUPPORTED + if (ping_found_iCCP !=MagickTrue && + ping_found_sRGB != MagickTrue && + png_get_valid(ping,ping_info, PNG_INFO_iCCP)) { - ping_found_sRGB=MagickTrue; + ping_found_iCCP=MagickTrue; if (logging != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " Found PNG sRGB chunk."); + " Found PNG iCCP chunk."); } -#ifdef PNG_READ_iCCP_SUPPORTED if (png_get_valid(ping,ping_info,PNG_INFO_iCCP)) { int @@ -2519,39 +2591,113 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, if (logging != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG iCCP chunk."); + profile=BlobToStringInfo(info,profile_length); + if (profile == (StringInfo *) NULL) + { + png_warning(ping, "ICC profile is NULL"); + profile=DestroyStringInfo(profile); + } + else + { + if (ping_preserve_iCCP == MagickFalse) { - png_warning(ping, "ICC profile is NULL"); - profile=DestroyStringInfo(profile); + int + icheck, + got_crc=0; + + + png_uint_32 + length, + profile_crc=0; + + unsigned char + *data; + + length=(png_uint_32) GetStringInfoLength(profile); + + for (icheck=0; sRGB_info[icheck].len > 0; icheck++) + { + if (length == sRGB_info[icheck].len) + { + if (got_crc == 0) + { + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " Got a %lu-byte ICC profile (potentially sRGB)", + (unsigned long) length); + + data=GetStringInfoDatum(profile); + profile_crc=crc32(0,data,length); + + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " with crc=%8x",(unsigned int) profile_crc); + got_crc++; + } + + if (profile_crc == sRGB_info[icheck].crc) + { + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " It is sRGB with rendering intent = %s", + Magick_RenderingIntentString_from_PNG_RenderingIntent( + sRGB_info[icheck].intent)); + if (image->rendering_intent==UndefinedIntent) + { + image->rendering_intent= + Magick_RenderingIntent_from_PNG_RenderingIntent( + sRGB_info[icheck].intent); + } + break; + } + } + } + if (sRGB_info[icheck].len == 0) + { + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " Got a %lu-byte ICC profile not recognized as sRGB", + (unsigned long) length); + (void) SetImageProfile(image,"icc",profile,exception); + } } - else + else /* Preserve-iCCP */ { - (void) SetImageProfile(image,"icc",profile,exception); - profile=DestroyStringInfo(profile); + (void) SetImageProfile(image,"icc",profile,exception); } + + profile=DestroyStringInfo(profile); + } } } #endif + #if defined(PNG_READ_sRGB_SUPPORTED) { - if (mng_info->have_global_srgb) - { - image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent - (mng_info->global_srgb_intent); - } - - if (png_get_sRGB(ping,ping_info,&intent)) + if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info, + PNG_INFO_sRGB)) + { + if (png_get_sRGB(ping,ping_info,&intent)) { - image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent - (intent); + if (image->rendering_intent == UndefinedIntent) + image->rendering_intent= + Magick_RenderingIntent_from_PNG_RenderingIntent (intent); if (logging != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG sRGB chunk: rendering_intent: %d",intent); } + } + + else if (mng_info->have_global_srgb) + { + if (image->rendering_intent == UndefinedIntent) + image->rendering_intent= + Magick_RenderingIntent_from_PNG_RenderingIntent + (mng_info->global_srgb_intent); + } } #endif + + { if (!png_get_gAMA(ping,ping_info,&file_gamma)) if (mng_info->have_global_gama) @@ -2594,6 +2740,8 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, &image->chromaticity.blue_primary.x, &image->chromaticity.blue_primary.y); + ping_found_cHRM=MagickTrue; + if (image->chromaticity.red_primary.x>0.6399f && image->chromaticity.red_primary.x<0.6401f && image->chromaticity.red_primary.y>0.3299f && @@ -2611,8 +2759,6 @@ static Image *ReadOnePNGImage(MngInfo *mng_info, image->chromaticity.white_point.y>0.3289f && image->chromaticity.white_point.y<0.3291f) ping_found_sRGB_cHRM=MagickTrue; - - ping_found_cHRM=MagickTrue; } if (image->rendering_intent != UndefinedIntent) @@ -7639,7 +7785,7 @@ Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping, (char *) profile_type, (double) length); } -#if PNG_LIBPNG_VER >= 14000 +#if PNG_LIBPNG_VER >= 10400 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text)); #else text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text)); @@ -7647,7 +7793,7 @@ Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping, description_length=(png_uint_32) strlen((const char *) profile_description); allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20 + description_length); -#if PNG_LIBPNG_VER >= 14000 +#if PNG_LIBPNG_VER >= 10400 text[0].text=(png_charp) png_malloc(ping, (png_alloc_size_t) allocated_length); text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80); @@ -7826,6 +7972,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, ping_exclude_zTXt, ping_preserve_colormap, + ping_preserve_iCCP, ping_need_colortype_warning, status, @@ -7985,6 +8132,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, ping_exclude_zTXt=mng_info->ping_exclude_zTXt; ping_preserve_colormap = mng_info->ping_preserve_colormap; + ping_preserve_iCCP = mng_info->ping_preserve_iCCP; ping_need_colortype_warning = MagickFalse; /* Recognize the ICC sRGB profile and convert it to the sRGB chunk, @@ -7992,8 +8140,19 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, * Note that this will not involve any changes to the actual pixels * but merely passes information to applications that read the resulting * PNG image. + * + * To do: recognize other variants of the sRGB profile, using the CRC to + * verify all recognized variants including the 7 already known. + * + * Work around libpng16+ rejecting some "known invalid sRGB profiles". + * + * Use something other than image->rendering_intent to record the fact + * that the sRGB profile was found. + * + * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC + * profile. Record the Blackpoint Compensation, if any. */ - if (ping_exclude_sRGB == MagickFalse) + if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse) { char *name; @@ -8009,56 +8168,63 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, if (profile != (StringInfo *) NULL) { if ((LocaleCompare(name,"ICC") == 0) || - (LocaleCompare(name,"ICM") == 0)) - { + (LocaleCompare(name,"ICM") == 0)) + + { int - icheck; + icheck, + got_crc=0; - /* 0: not a known sRGB profile - * 1: HP-Microsoft sRGB v2 - * 2: ICC sRGB v4 perceptual - * 3: ICC sRGB v2 perceptual no black-compensation - */ - png_uint_32 - check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL}, - check_len[4] = {0, 3144, 60960, 3052}; png_uint_32 length, - profile_crc; + profile_crc=0; unsigned char *data; length=(png_uint_32) GetStringInfoLength(profile); - for (icheck=3; icheck > 0; icheck--) + for (icheck=0; sRGB_info[icheck].len > 0; icheck++) { - if (length == check_len[icheck]) + if (length == sRGB_info[icheck].len) { - (void) LogMagickEvent(CoderEvent,GetMagickModule(), + if (got_crc == 0) + { + (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Got a %lu-byte ICC profile (potentially sRGB)", (unsigned long) length); - data=GetStringInfoDatum(profile); - profile_crc=crc32(0,data,length); + data=GetStringInfoDatum(profile); + profile_crc=crc32(0,data,length); - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " with crc=%8x",(unsigned int) profile_crc); + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " with crc=%8x",(unsigned int) profile_crc); + got_crc++; + } - if (profile_crc == check_crc[icheck]) + if (profile_crc == sRGB_info[icheck].crc) { (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " It is sRGB."); + " It is sRGB with rendering intent = %s", + Magick_RenderingIntentString_from_PNG_RenderingIntent( + sRGB_info[icheck].intent)); if (image->rendering_intent==UndefinedIntent) - image->rendering_intent=PerceptualIntent; + { + image->rendering_intent= + Magick_RenderingIntent_from_PNG_RenderingIntent( + sRGB_info[icheck].intent); + } + ping_exclude_iCCP = MagickTrue; + ping_exclude_zCCP = MagickTrue; + ping_have_sRGB = MagickTrue; break; } } } - if (icheck == 0) + if (sRGB_info[icheck].len == 0) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " Got a %lu-byte ICC profile", + " Got a %lu-byte ICC profile not recognized as sRGB", (unsigned long) length); } } @@ -10450,6 +10616,9 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, if (ping_exclude_iCCP == MagickFalse) { + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " Setting up iCCP chunk"); + png_set_iCCP(ping,ping_info,(png_charp) name,0, #if (PNG_LIBPNG_VER < 10500) (png_charp) GetStringInfoDatum(profile), @@ -10465,10 +10634,13 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, #endif if (ping_exclude_zCCP == MagickFalse) { + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " Setting up zTXT chunk with uuencoded ICC"); Magick_png_write_raw_profile(image_info,ping,ping_info, (unsigned char *) name,(unsigned char *) name, GetStringInfoDatum(profile), (png_uint_32) GetStringInfoLength(profile)); + ping_have_iCCP = MagickTrue; } } @@ -10483,7 +10655,9 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, #if defined(PNG_WRITE_sRGB_SUPPORTED) if ((mng_info->have_write_global_srgb == 0) && - (png_get_valid(ping,ping_info,PNG_INFO_sRGB))) + ping_have_iCCP != MagickTrue && + (ping_have_sRGB != MagickFalse || + png_get_valid(ping,ping_info,PNG_INFO_sRGB))) { if (ping_exclude_sRGB == MagickFalse) { @@ -11078,7 +11252,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info, if (value != (const char *) NULL) { -#if PNG_LIBPNG_VER >= 14000 +#if PNG_LIBPNG_VER >= 10400 text=(png_textp) png_malloc(ping, (png_alloc_size_t) sizeof(png_text)); #else @@ -11696,7 +11870,15 @@ static MagickBooleanType WritePNGImage(const ImageInfo *image_info, if (value != NULL) mng_info->ping_preserve_colormap=MagickTrue; - /* Thes compression-level, compression-strategy, and compression-filter + mng_info->ping_preserve_iCCP=MagickFalse; + + value=GetImageOption(image_info,"png:preserve-iCCP"); + if (value == NULL) + value=GetImageArtifact(image,"png:preserve-iCCP"); + if (value != NULL) + mng_info->ping_preserve_iCCP=MagickTrue; + + /* These compression-level, compression-strategy, and compression-filter * defines take precedence over values from the -quality option. */ value=GetImageOption(image_info,"png:compression-level");