[gegl] jpg-save: store icc profiles in jpgs



commit cab336bb0e423fd47c69eb9db2bb3fd1c5879931
Author: Øyvind Kolås <pippin gimp org>
Date:   Sun Jul 8 23:27:08 2018 +0200

    jpg-save: store icc profiles in jpgs

 operations/external/jpg-save.c | 99 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 2 deletions(-)
---
diff --git a/operations/external/jpg-save.c b/operations/external/jpg-save.c
index b7b281ec5..592139711 100644
--- a/operations/external/jpg-save.c
+++ b/operations/external/jpg-save.c
@@ -139,6 +139,89 @@ close_stream (j_compress_ptr cinfo)
   dest->free_in_buffer = 0;
 }
 
+/*
+ * Since an ICC profile can be larger than the maximum size of a JPEG marker
+ * (64K), we need provisions to split it into multiple markers.  The format
+ * defined by the ICC specifies one or more APP2 markers containing the
+ * following data:
+ *     Identifying string      ASCII "ICC_PROFILE\0"  (12 bytes)
+ *     Marker sequence number  1 for first APP2, 2 for next, etc (1 byte)
+ *     Number of markers       Total number of APP2's used (1 byte)
+ *      Profile data           (remainder of APP2 data)
+ * Decoders should use the marker sequence numbers to reassemble the profile,
+ * rather than assuming that the APP2 markers appear in the correct sequence.
+ */
+
+#define ICC_MARKER  (JPEG_APP0 + 2)    /* JPEG marker code for ICC */
+#define ICC_OVERHEAD_LEN  14           /* size of non-profile data in APP2 */
+#define MAX_BYTES_IN_MARKER  65533     /* maximum data len of a JPEG marker */
+#define MAX_DATA_BYTES_IN_MARKER  (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
+
+/*
+ * This routine writes the given ICC profile data into a JPEG file.
+ * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
+ * the first call to jpeg_write_scanlines().
+ * (This ordering ensures that the APP2 marker(s) will appear after the
+ * SOI and JFIF or Adobe markers, but before all else.)
+ */
+
+static void
+write_icc_profile (j_compress_ptr cinfo,
+                  const JOCTET *icc_data_ptr,
+                  unsigned int icc_data_len)
+{
+  unsigned int num_markers;    /* total number of markers we'll write */
+  int cur_marker = 1;          /* per spec, counting starts at 1 */
+  unsigned int length;         /* number of bytes to write in this marker */
+
+  /* Calculate the number of markers we'll need, rounding up of course */
+  num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
+  if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
+    num_markers++;
+
+  while (icc_data_len > 0) {
+    /* length of profile to put in this marker */
+    length = icc_data_len;
+    if (length > MAX_DATA_BYTES_IN_MARKER)
+      length = MAX_DATA_BYTES_IN_MARKER;
+    icc_data_len -= length;
+
+    /* Write the JPEG marker header (APP2 code and marker length) */
+    jpeg_write_m_header(cinfo, ICC_MARKER,
+                       (unsigned int) (length + ICC_OVERHEAD_LEN));
+
+    /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
+     * We code it in this less-than-transparent way so that the code works
+     * even if the local character set is not ASCII.
+     */
+    jpeg_write_m_byte(cinfo, 0x49);
+    jpeg_write_m_byte(cinfo, 0x43);
+    jpeg_write_m_byte(cinfo, 0x43);
+    jpeg_write_m_byte(cinfo, 0x5F);
+    jpeg_write_m_byte(cinfo, 0x50);
+    jpeg_write_m_byte(cinfo, 0x52);
+    jpeg_write_m_byte(cinfo, 0x4F);
+    jpeg_write_m_byte(cinfo, 0x46);
+    jpeg_write_m_byte(cinfo, 0x49);
+    jpeg_write_m_byte(cinfo, 0x4C);
+    jpeg_write_m_byte(cinfo, 0x45);
+    jpeg_write_m_byte(cinfo, 0x0);
+
+    /* Add the sequencing info */
+    jpeg_write_m_byte(cinfo, cur_marker);
+    jpeg_write_m_byte(cinfo, (int) num_markers);
+
+    /* Add the profile data */
+    while (length--) {
+      jpeg_write_m_byte(cinfo, *icc_data_ptr);
+      icc_data_ptr++;
+    }
+    cur_marker++;
+  }
+}
+
+
+
 static gint
 export_jpg (GeglOperation               *operation,
             GeglBuffer                  *input,
@@ -154,6 +237,8 @@ export_jpg (GeglOperation               *operation,
   gint     width, height;
   JSAMPROW row_pointer[1];
   const Babl *format;
+  const Babl *fmt = gegl_buffer_get_format (input);
+  const Babl *space = babl_format_get_space (fmt);
 
   src_x = result->x;
   src_y = result->y;
@@ -199,14 +284,24 @@ export_jpg (GeglOperation               *operation,
 
   jpeg_start_compress (&cinfo, TRUE);
 
+  {
+    int icc_len;
+    const char *name = babl_get_name (space);
+    char *icc_profile;
+    if (strlen (name) > 10) name = "babl/GEGL";
+    icc_profile = babl_space_to_icc (space, name, NULL, 0, &icc_len);
+    write_icc_profile (&cinfo, (void*)icc_profile, icc_len);
+    free (icc_profile);
+  }
+
   if (!grayscale)
     {
-      format = babl_format ("R'G'B' u8");
+      format = babl_format_with_space ("R'G'B' u8", space);
       row_pointer[0] = g_malloc (width * 3);
     }
   else
     {
-      format = babl_format ("Y' u8");
+      format = babl_format_with_space ("Y' u8", space);
       row_pointer[0] = g_malloc (width);
     }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]