[gegl] jpg-save: store icc profiles in jpgs
- From: Øyvind "pippin" Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] jpg-save: store icc profiles in jpgs
- Date: Sun, 8 Jul 2018 21:27:51 +0000 (UTC)
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]