[patch] gdk-pixbuf loaders crash on CMYK JPEGs



Hi all,

currently io-jpeg.c simply crashes on CMYK JPEGs (or any JPEG with
n_components != 1 or 3). The attached patch does the following:

- removes GREY->RGB conversion routine
- uses the libjpeg built-in ability to promote everything to either
  RGB or CMYK
- adds CMYK->RGB conversion.
- refuses to load files with unknown colorspace.

I guess it should be comitted to both branches.

OK to commit or is it too much of a change and should go to
Bugzilla first?

ciao,
--mitch

Index: gdk-pixbuf/io-jpeg.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/io-jpeg.c,v
retrieving revision 1.43
diff -u -p -r1.43 io-jpeg.c
--- gdk-pixbuf/io-jpeg.c	11 Apr 2002 21:18:40 -0000	1.43
+++ gdk-pixbuf/io-jpeg.c	29 Jul 2002 10:59:01 -0000
@@ -86,6 +86,8 @@ typedef struct {
 	GdkPixbuf                *pixbuf;
 	guchar                   *dptr;   /* current position in pixbuf */
 
+        guchar                   *cmyk_buf;  /*  buffer for CMYK data  */
+
 	gboolean                 did_prescan;  /* are we in image data yet? */
 	gboolean                 got_header;  /* have we loaded jpeg header? */
 	gboolean                 src_initialized;/* TRUE when jpeg lib initialized */
@@ -140,32 +142,35 @@ output_message_handler (j_common_ptr cin
   /* do nothing */
 }
 
-/* explode gray image data from jpeg library into rgb components in pixbuf */
+/* implode CMYK image data from jpeg library into rgb components in pixbuf */
 static void
-explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
-		       guchar **lines) 
+convert_cmyk_lines (struct jpeg_decompress_struct *cinfo,
+                    guchar **cmyk_lines,
+                    guchar **lines) 
 {
 	gint i, j;
-	guint w;
+        gint w;
 
 	g_return_if_fail (cinfo != NULL);
-	g_return_if_fail (cinfo->output_components == 1);
+	g_return_if_fail (cinfo->out_color_space == JCS_CMYK);
 
-	/* Expand grey->colour.  Expand from the end of the
-	 * memory down, so we can use the same buffer.
-	 */
 	w = cinfo->image_width;
-	for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
+	for (i = 0; i < cinfo->rec_outbuf_height; i++) {
 		guchar *from, *to;
 		
-		from = lines[i] + w - 1;
-		to = lines[i] + (w - 1) * 3;
-		for (j = w - 1; j >= 0; j--) {
-			to[0] = from[0];
-			to[1] = from[0];
-			to[2] = from[0];
-			to -= 3;
-			from--;
+		from = cmyk_lines[i];
+		to = lines[i];
+		for (j = 0; j < w; j++) {
+                        guint r_c, g_m, b_y, k;
+
+                        r_c = *from++;
+                        g_m = *from++;
+                        b_y = *from++;
+                        k   = *from++;
+
+                        *to++ = (r_c * k) / 255;
+                        *to++ = (g_m * k) / 255;
+                        *to++ = (b_y * k) / 255;
 		}
 	}
 }
@@ -240,6 +245,7 @@ gdk_pixbuf__jpeg_image_load (FILE *f, GE
 {
 	gint i;
 	GdkPixbuf * volatile pixbuf = NULL;
+	guchar * volatile cmyk_buf = NULL;
 	guchar *dptr;
 	guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
                            * from the header file: 
@@ -263,6 +269,9 @@ gdk_pixbuf__jpeg_image_load (FILE *f, GE
 		if (pixbuf)
 			g_object_unref (pixbuf);
 
+                if (cmyk_buf)
+                        g_free (cmyk_buf);
+
 		jpeg_destroy_decompress (&cinfo);
 		return NULL;
 	}
@@ -292,11 +301,37 @@ gdk_pixbuf__jpeg_image_load (FILE *f, GE
 	cinfo.do_fancy_upsampling = FALSE;
 	cinfo.do_block_smoothing = FALSE;
 
+        switch (cinfo.out_color_space) {
+                case JCS_UNKNOWN:
+                        g_set_error (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
+                                     _("JPEG file has unknown colorspace"));
+                        return NULL;
+
+                case JCS_GRAYSCALE:
+                case JCS_RGB:
+                case JCS_YCbCr:
+                        cinfo.out_color_space = JCS_RGB;
+                        break;
+
+                case JCS_CMYK:
+                case JCS_YCCK:
+                        cinfo.out_color_space = JCS_CMYK;
+                        cmyk_buf = g_new (guchar, (cinfo.output_width *
+                                                   cinfo.output_components *
+                                                   cinfo.rec_outbuf_height));
+                        break;
+        }
+
 	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, cinfo.output_width, cinfo.output_height);
  
 	if (!pixbuf) {
 		jpeg_destroy_decompress (&cinfo);
 
+                if (cmyk_buf)
+                        g_free (cmyk_buf);
+
                 /* broken check for *error == NULL for robustness against
                  * crappy JPEG library
                  */
@@ -321,15 +356,32 @@ gdk_pixbuf__jpeg_image_load (FILE *f, GE
 			dptr += pixbuf->rowstride;
 		}
 
-		jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
+                if (cinfo.out_color_space == JCS_CMYK) {
+                        guchar *cmyk_lines[4];
+
+                        for (i = 0; i < cinfo.rec_outbuf_height; i++) {
+                              cmyk_lines[i] = (cmyk_buf +
+                                               i * (cinfo.output_width *
+                                                    cinfo.output_components));
+                        }
+
+                        jpeg_read_scanlines (&cinfo, cmyk_lines,
+                                             cinfo.rec_outbuf_height);
+
+                        convert_cmyk_lines (&cinfo, cmyk_lines, lines);
+                } else {
+                        jpeg_read_scanlines (&cinfo, lines,
+                                             cinfo.rec_outbuf_height);
 
-		if (cinfo.output_components == 1)
-			explode_gray_into_buf (&cinfo, lines);
+                }
 	}
 
 	jpeg_finish_decompress (&cinfo);
 	jpeg_destroy_decompress (&cinfo);
 
+        if (cmyk_buf)
+                g_free (cmyk_buf);
+
 	return pixbuf;
 }
 
@@ -459,6 +511,9 @@ gdk_pixbuf__jpeg_image_stop_load (gpoint
 	if (context->pixbuf)
 		g_object_unref (context->pixbuf);
 
+        if (context->cmyk_buf)
+                g_free (context->cmyk_buf);
+
 	/* if we have an error? */
 	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
 		jpeg_destroy_decompress (&context->cinfo);
@@ -618,6 +673,29 @@ gdk_pixbuf__jpeg_image_load_increment (g
 			cinfo->do_fancy_upsampling = FALSE;
 			cinfo->do_block_smoothing = FALSE;
 			
+                        switch (cinfo->out_color_space) {
+                                case JCS_UNKNOWN:
+                                        g_set_error (error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
+                                                     _("JPEG file has unknown colorspace"));
+                                        return FALSE;
+
+                                case JCS_GRAYSCALE:
+                                case JCS_RGB:
+                                case JCS_YCbCr:
+                                        cinfo->out_color_space = JCS_RGB;
+                                        break;
+
+                                case JCS_CMYK:
+                                case JCS_YCCK:
+                                        cinfo->out_color_space = JCS_CMYK;
+                                        context->cmyk_buf = g_new (guchar, (cinfo->output_width *
+                                                                            cinfo->output_components *
+                                                                            cinfo->rec_outbuf_height));
+                                        break;
+                        }
+
 			if (rc == JPEG_SUSPENDED)
 				continue;
 
@@ -647,16 +725,29 @@ gdk_pixbuf__jpeg_image_load_increment (g
 						*lptr++ = rowptr;
 						rowptr += context->pixbuf->rowstride;
 					}
-					
-					nlines = jpeg_read_scanlines (cinfo, lines,
-								      cinfo->rec_outbuf_height);
+
+                                        if (cinfo->out_color_space == JCS_CMYK) {
+                                                guchar *cmyk_lines[4];
+
+                                                for (i = 0; i < cinfo->rec_outbuf_height; i++) {
+                                                        cmyk_lines[i] = (context->cmyk_buf +
+                                                                         i * (cinfo->output_width *
+                                                                              cinfo->output_components));
+                                                }
+
+                                                nlines = jpeg_read_scanlines (cinfo, cmyk_lines,
+                                                                              cinfo->rec_outbuf_height);
+
+                                                if (nlines > 0)
+                                                        convert_cmyk_lines (cinfo, cmyk_lines, lines);
+                                        } else {
+                                                nlines = jpeg_read_scanlines (cinfo, lines,
+                                                                              cinfo->rec_outbuf_height);
+                                        }
+
 					if (nlines == 0)
 						break;
 
-				        /* handle gray */
-					if (cinfo->output_components == 1)
-						explode_gray_into_buf (cinfo, lines);
-					
 					context->dptr += nlines * context->pixbuf->rowstride;
 					
 				        /* send updated signal */


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