]> granicus.if.org Git - imagemagick/commitdiff
Recognize ICC profiles that are sRGB and use PNG sRGB chunk instead
authorglennrp <glennrp@git.imagemagick.org>
Tue, 14 May 2013 22:50:32 +0000 (22:50 +0000)
committerglennrp <glennrp@git.imagemagick.org>
Tue, 14 May 2013 22:50:32 +0000 (22:50 +0000)
coders/png.c

index dd6c828530bd2ef1190e5496cc6665862f408d80..0df284a1c5c1039fb36955dc961727dafd10093a 100644 (file)
         ((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");