Home | History | Annotate | Download | only in iccjpeg
      1 /*
      2  * iccprofile.c
      3  *
      4  * This file provides code to read and write International Color Consortium
      5  * (ICC) device profiles embedded in JFIF JPEG image files.  The ICC has
      6  * defined a standard format for including such data in JPEG "APP2" markers.
      7  * The code given here does not know anything about the internal structure
      8  * of the ICC profile data; it just knows how to put the profile data into
      9  * a JPEG file being written, or get it back out when reading.
     10  *
     11  * This code depends on new features added to the IJG JPEG library as of
     12  * IJG release 6b; it will not compile or work with older IJG versions.
     13  *
     14  * NOTE: this code would need surgery to work on 16-bit-int machines
     15  * with ICC profiles exceeding 64K bytes in size.  If you need to do that,
     16  * change all the "unsigned int" variables to "INT32".  You'll also need
     17  * to find a malloc() replacement that can allocate more than 64K.
     18  */
     19 
     20 #include "iccjpeg.h"
     21 #include <stdlib.h>			/* define malloc() */
     22 
     23 
     24 /*
     25  * Since an ICC profile can be larger than the maximum size of a JPEG marker
     26  * (64K), we need provisions to split it into multiple markers.  The format
     27  * defined by the ICC specifies one or more APP2 markers containing the
     28  * following data:
     29  *	Identifying string	ASCII "ICC_PROFILE\0"  (12 bytes)
     30  *	Marker sequence number	1 for first APP2, 2 for next, etc (1 byte)
     31  *	Number of markers	Total number of APP2's used (1 byte)
     32  *      Profile data		(remainder of APP2 data)
     33  * Decoders should use the marker sequence numbers to reassemble the profile,
     34  * rather than assuming that the APP2 markers appear in the correct sequence.
     35  */
     36 
     37 #define ICC_MARKER  (JPEG_APP0 + 2)	/* JPEG marker code for ICC */
     38 #define ICC_OVERHEAD_LEN  14		/* size of non-profile data in APP2 */
     39 #define MAX_BYTES_IN_MARKER  65533	/* maximum data len of a JPEG marker */
     40 #define MAX_DATA_BYTES_IN_MARKER  (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
     41 
     42 
     43 /*
     44  * This routine writes the given ICC profile data into a JPEG file.
     45  * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
     46  * the first call to jpeg_write_scanlines().
     47  * (This ordering ensures that the APP2 marker(s) will appear after the
     48  * SOI and JFIF or Adobe markers, but before all else.)
     49  */
     50 
     51 void
     52 write_icc_profile (j_compress_ptr cinfo,
     53 		   const JOCTET *icc_data_ptr,
     54 		   unsigned int icc_data_len)
     55 {
     56   unsigned int num_markers;	/* total number of markers we'll write */
     57   int cur_marker = 1;		/* per spec, counting starts at 1 */
     58   unsigned int length;		/* number of bytes to write in this marker */
     59 
     60   /* Calculate the number of markers we'll need, rounding up of course */
     61   num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
     62   if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
     63     num_markers++;
     64 
     65   while (icc_data_len > 0) {
     66     /* length of profile to put in this marker */
     67     length = icc_data_len;
     68     if (length > MAX_DATA_BYTES_IN_MARKER)
     69       length = MAX_DATA_BYTES_IN_MARKER;
     70     icc_data_len -= length;
     71 
     72     /* Write the JPEG marker header (APP2 code and marker length) */
     73     jpeg_write_m_header(cinfo, ICC_MARKER,
     74 			(unsigned int) (length + ICC_OVERHEAD_LEN));
     75 
     76     /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
     77      * We code it in this less-than-transparent way so that the code works
     78      * even if the local character set is not ASCII.
     79      */
     80     jpeg_write_m_byte(cinfo, 0x49);
     81     jpeg_write_m_byte(cinfo, 0x43);
     82     jpeg_write_m_byte(cinfo, 0x43);
     83     jpeg_write_m_byte(cinfo, 0x5F);
     84     jpeg_write_m_byte(cinfo, 0x50);
     85     jpeg_write_m_byte(cinfo, 0x52);
     86     jpeg_write_m_byte(cinfo, 0x4F);
     87     jpeg_write_m_byte(cinfo, 0x46);
     88     jpeg_write_m_byte(cinfo, 0x49);
     89     jpeg_write_m_byte(cinfo, 0x4C);
     90     jpeg_write_m_byte(cinfo, 0x45);
     91     jpeg_write_m_byte(cinfo, 0x0);
     92 
     93     /* Add the sequencing info */
     94     jpeg_write_m_byte(cinfo, cur_marker);
     95     jpeg_write_m_byte(cinfo, (int) num_markers);
     96 
     97     /* Add the profile data */
     98     while (length--) {
     99       jpeg_write_m_byte(cinfo, *icc_data_ptr);
    100       icc_data_ptr++;
    101     }
    102     cur_marker++;
    103   }
    104 }
    105 
    106 
    107 /*
    108  * Prepare for reading an ICC profile
    109  */
    110 
    111 void
    112 setup_read_icc_profile (j_decompress_ptr cinfo)
    113 {
    114   /* Tell the library to keep any APP2 data it may find */
    115   jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF);
    116 }
    117 
    118 
    119 /*
    120  * Handy subroutine to test whether a saved marker is an ICC profile marker.
    121  */
    122 
    123 static boolean
    124 marker_is_icc (jpeg_saved_marker_ptr marker)
    125 {
    126   return
    127     marker->marker == ICC_MARKER &&
    128     marker->data_length >= ICC_OVERHEAD_LEN &&
    129     /* verify the identifying string */
    130     GETJOCTET(marker->data[0]) == 0x49 &&
    131     GETJOCTET(marker->data[1]) == 0x43 &&
    132     GETJOCTET(marker->data[2]) == 0x43 &&
    133     GETJOCTET(marker->data[3]) == 0x5F &&
    134     GETJOCTET(marker->data[4]) == 0x50 &&
    135     GETJOCTET(marker->data[5]) == 0x52 &&
    136     GETJOCTET(marker->data[6]) == 0x4F &&
    137     GETJOCTET(marker->data[7]) == 0x46 &&
    138     GETJOCTET(marker->data[8]) == 0x49 &&
    139     GETJOCTET(marker->data[9]) == 0x4C &&
    140     GETJOCTET(marker->data[10]) == 0x45 &&
    141     GETJOCTET(marker->data[11]) == 0x0;
    142 }
    143 
    144 
    145 /*
    146  * See if there was an ICC profile in the JPEG file being read;
    147  * if so, reassemble and return the profile data.
    148  *
    149  * TRUE is returned if an ICC profile was found, FALSE if not.
    150  * If TRUE is returned, *icc_data_ptr is set to point to the
    151  * returned data, and *icc_data_len is set to its length.
    152  *
    153  * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
    154  * and must be freed by the caller with free() when the caller no longer
    155  * needs it.  (Alternatively, we could write this routine to use the
    156  * IJG library's memory allocator, so that the data would be freed implicitly
    157  * at jpeg_finish_decompress() time.  But it seems likely that many apps
    158  * will prefer to have the data stick around after decompression finishes.)
    159  *
    160  * NOTE: if the file contains invalid ICC APP2 markers, we just silently
    161  * return FALSE.  You might want to issue an error message instead.
    162  */
    163 
    164 boolean
    165 read_icc_profile (j_decompress_ptr cinfo,
    166 		  JOCTET **icc_data_ptr,
    167 		  unsigned int *icc_data_len)
    168 {
    169   jpeg_saved_marker_ptr marker;
    170   int num_markers = 0;
    171   int seq_no;
    172   JOCTET *icc_data;
    173   unsigned int total_length;
    174 #define MAX_SEQ_NO  255		/* sufficient since marker numbers are bytes */
    175   char marker_present[MAX_SEQ_NO+1];	  /* 1 if marker found */
    176   unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
    177   unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
    178 
    179   *icc_data_ptr = NULL;		/* avoid confusion if FALSE return */
    180   *icc_data_len = 0;
    181 
    182   /* This first pass over the saved markers discovers whether there are
    183    * any ICC markers and verifies the consistency of the marker numbering.
    184    */
    185 
    186   for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
    187     marker_present[seq_no] = 0;
    188 
    189   for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
    190     if (marker_is_icc(marker)) {
    191       if (num_markers == 0)
    192 	num_markers = GETJOCTET(marker->data[13]);
    193       else if (num_markers != GETJOCTET(marker->data[13]))
    194 	return FALSE;		/* inconsistent num_markers fields */
    195       seq_no = GETJOCTET(marker->data[12]);
    196       if (seq_no <= 0 || seq_no > num_markers)
    197 	return FALSE;		/* bogus sequence number */
    198       if (marker_present[seq_no])
    199 	return FALSE;		/* duplicate sequence numbers */
    200       marker_present[seq_no] = 1;
    201       data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
    202     }
    203   }
    204 
    205   if (num_markers == 0)
    206     return FALSE;
    207 
    208   /* Check for missing markers, count total space needed,
    209    * compute offset of each marker's part of the data.
    210    */
    211 
    212   total_length = 0;
    213   for (seq_no = 1; seq_no <= num_markers; seq_no++) {
    214     if (marker_present[seq_no] == 0)
    215       return FALSE;		/* missing sequence number */
    216     data_offset[seq_no] = total_length;
    217     total_length += data_length[seq_no];
    218   }
    219 
    220   if (total_length <= 0)
    221     return FALSE;		/* found only empty markers? */
    222 
    223   /* Allocate space for assembled data */
    224   icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
    225   if (icc_data == NULL)
    226     return FALSE;		/* oops, out of memory */
    227 
    228   /* and fill it in */
    229   for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
    230     if (marker_is_icc(marker)) {
    231       JOCTET FAR *src_ptr;
    232       JOCTET *dst_ptr;
    233       unsigned int length;
    234       seq_no = GETJOCTET(marker->data[12]);
    235       dst_ptr = icc_data + data_offset[seq_no];
    236       src_ptr = marker->data + ICC_OVERHEAD_LEN;
    237       length = data_length[seq_no];
    238       while (length--) {
    239 	*dst_ptr++ = *src_ptr++;
    240       }
    241     }
    242   }
    243 
    244   *icc_data_ptr = icc_data;
    245   *icc_data_len = total_length;
    246 
    247   return TRUE;
    248 }
    249