[cairo-commit] src/cairo-image-surface.c src/cairoint.h src/cairo-pdf-surface.c src/cairo-types-private.h

Adrian Johnson ajohnson at kemper.freedesktop.org
Tue Aug 2 06:54:20 PDT 2011


 src/cairo-image-surface.c |   59 ++++++++++++++++++++++++++++++
 src/cairo-pdf-surface.c   |   90 ++++++++++++++++++++++++++++++++++------------
 src/cairo-types-private.h |    8 ++++
 src/cairoint.h            |    4 ++
 4 files changed, 138 insertions(+), 23 deletions(-)

New commits:
commit 3f2126f092d86d3a217fe256df682bb45ee6ab2a
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Aug 2 23:10:17 2011 +0930

    pdf: check if images are grayscale or monochrome and encode as such
    
    Printing PDFs with large monochrome or grayscale images would result
    in the images being blown up to 24-bit color images. Some printers are
    very slow to print huge color images.

diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 293191d..8389465 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -168,6 +168,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t		*pixman_image,
     surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
     surface->owns_data = FALSE;
     surface->transparency = CAIRO_IMAGE_UNKNOWN;
+    surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
 
     surface->width = width;
     surface->height = height;
@@ -4908,3 +4909,61 @@ _cairo_image_analyze_transparency (cairo_image_surface_t      *image)
 
     return image->transparency;
 }
+
+cairo_image_color_t
+_cairo_image_analyze_color (cairo_image_surface_t      *image)
+{
+    int x, y;
+
+    if (image->transparency != CAIRO_IMAGE_UNKNOWN_COLOR)
+	return image->color;
+
+    if (image->format == CAIRO_FORMAT_A1 || image->format == CAIRO_FORMAT_A8)
+	return image->color = CAIRO_IMAGE_IS_MONOCHROME;
+
+    if (image->format == CAIRO_FORMAT_ARGB32) {
+	image->color = CAIRO_IMAGE_IS_MONOCHROME;
+	for (y = 0; y < image->height; y++) {
+	    uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+	    for (x = 0; x < image->width; x++, pixel++) {
+		int a = (*pixel & 0xff000000) >> 24;
+		int r = (*pixel & 0x00ff0000) >> 16;
+		int g = (*pixel & 0x0000ff00) >> 8;
+		int b = (*pixel & 0x000000ff);
+		if (a == 0) {
+		    r = g = b = 0;
+		} else {
+		    r = (r * 255 + a / 2) / a;
+		    g = (g * 255 + a / 2) / a;
+		    b = (b * 255 + a / 2) / a;
+		}
+		if (!(r == g && g == b))
+		    return image->color = CAIRO_IMAGE_IS_COLOR;
+		else if (r > 0 && r < 255)
+		    image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+	    }
+	}
+	return image->color;
+    }
+
+    if (image->format == CAIRO_FORMAT_RGB24) {
+	image->color = CAIRO_IMAGE_IS_MONOCHROME;
+	for (y = 0; y < image->height; y++) {
+	    uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+	    for (x = 0; x < image->width; x++, pixel++) {
+		int r = (*pixel & 0x00ff0000) >> 16;
+		int g = (*pixel & 0x0000ff00) >>  8;
+		int b = (*pixel & 0x000000ff);
+		if (!(r == g && g == b))
+		    return image->color = CAIRO_IMAGE_IS_COLOR;
+		else if (r > 0 && r < 255)
+		    image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+	    }
+	}
+	return image->color;
+    }
+
+    return image->color = CAIRO_IMAGE_IS_COLOR;
+}
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 9b5bed2..b2e6a1d 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1967,13 +1967,14 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 			       cairo_bool_t             mask)
 {
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
-    char *rgb;
-    unsigned long rgb_size;
+    char *data;
+    unsigned long data_size;
     uint32_t *pixel;
-    int i, x, y;
+    int i, x, y, bit;
     cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
     cairo_bool_t need_smask;
     const char *interpolate = "true";
+    cairo_image_color_t color;
 
     /* These are the only image formats we currently support, (which
      * makes things a lot simpler here). This is enforced through
@@ -1988,18 +1989,36 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
     if (mask)
 	return _cairo_pdf_surface_emit_imagemask (surface, image, image_res);
 
-    rgb_size = image->height * image->width * 3;
-    rgb = _cairo_malloc_abc (image->width, image->height, 3);
-    if (unlikely (rgb == NULL)) {
+    color = _cairo_image_analyze_color (image);
+    switch (color) {
+	case CAIRO_IMAGE_IS_COLOR:
+	case CAIRO_IMAGE_UNKNOWN_COLOR:
+	    data_size = image->height * image->width * 3;
+	    data = _cairo_malloc_abc (image->width, image->height, 3);
+	    break;
+
+	case CAIRO_IMAGE_IS_GRAYSCALE:
+	    data_size = image->height * image->width;
+	    data = _cairo_malloc_ab (image->width, image->height);
+	    break;
+	case CAIRO_IMAGE_IS_MONOCHROME:
+	    data_size = (image->width + 7) / 8 * image->height;
+	    data = _cairo_malloc_ab ((image->width+7) / 8, image->height);
+	    break;
+    }
+    if (unlikely (data == NULL)) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto CLEANUP;
     }
 
     i = 0;
+    bit = 7;
     for (y = 0; y < image->height; y++) {
 	pixel = (uint32_t *) (image->data + y * image->stride);
 
 	for (x = 0; x < image->width; x++, pixel++) {
+	    int r, g, b;
+
 	    /* XXX: We're un-premultiplying alpha here. My reading of the PDF
 	     * specification suggests that we should be able to avoid having
 	     * to do this by filling in the SMask's Matte dictionary
@@ -2009,22 +2028,43 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 		uint8_t a;
 		a = (*pixel & 0xff000000) >> 24;
 		if (a == 0) {
-		    rgb[i++] = 0;
-		    rgb[i++] = 0;
-		    rgb[i++] = 0;
+		    r = g = b = 0;
 		} else {
-		    rgb[i++] = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
-		    rgb[i++] = (((*pixel & 0x00ff00) >>  8) * 255 + a / 2) / a;
-		    rgb[i++] = (((*pixel & 0x0000ff) >>  0) * 255 + a / 2) / a;
+		    r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
+		    g = (((*pixel & 0x00ff00) >>  8) * 255 + a / 2) / a;
+		    b = (((*pixel & 0x0000ff) >>  0) * 255 + a / 2) / a;
 		}
 	    } else if (image->format == CAIRO_FORMAT_RGB24) {
-		rgb[i++] = (*pixel & 0x00ff0000) >> 16;
-		rgb[i++] = (*pixel & 0x0000ff00) >>  8;
-		rgb[i++] = (*pixel & 0x000000ff) >>  0;
+		r = (*pixel & 0x00ff0000) >> 16;
+		g = (*pixel & 0x0000ff00) >>  8;
+		b = (*pixel & 0x000000ff) >>  0;
 	    } else {
-		rgb[i++] = 0;
-		rgb[i++] = 0;
-		rgb[i++] = 0;
+		r = g = b = 0;
+	    }
+
+	    switch (color) {
+		case CAIRO_IMAGE_IS_COLOR:
+		case CAIRO_IMAGE_UNKNOWN_COLOR:
+		    data[i++] = r;
+		    data[i++] = g;
+		    data[i++] = b;
+		    break;
+
+		case CAIRO_IMAGE_IS_GRAYSCALE:
+		    data[i++] = r;
+		    break;
+
+		case CAIRO_IMAGE_IS_MONOCHROME:
+		    if (bit == 7)
+			data[i] = 0;
+		    if (r != 0)
+			data[i] |= (1 << bit);
+		    bit--;
+		    if (bit < 0) {
+			bit = 7;
+			i++;
+		    }
+		    break;
 	    }
 	}
     }
@@ -2058,9 +2098,9 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 				"   /Subtype /Image\n"	\
 				"   /Width %d\n"		\
 				"   /Height %d\n"		\
-				"   /ColorSpace /DeviceRGB\n"	\
+				"   /ColorSpace %s\n"	\
 	                        "   /Interpolate %s\n" \
-				"   /BitsPerComponent 8\n"
+				"   /BitsPerComponent %d\n"
 
     if (need_smask)
 	status = _cairo_pdf_surface_open_stream (surface,
@@ -2069,7 +2109,9 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 						 IMAGE_DICTIONARY
 						 "   /SMask %d 0 R\n",
 						 image->width, image->height,
+						 color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
 						 interpolate,
+						 color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
 						 smask.id);
     else
 	status = _cairo_pdf_surface_open_stream (surface,
@@ -2077,17 +2119,19 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 						 TRUE,
 						 IMAGE_DICTIONARY,
 						 image->width, image->height,
-						 interpolate);
+						 color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+						 interpolate,
+						 color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8);
     if (unlikely (status))
 	goto CLEANUP_RGB;
 
 #undef IMAGE_DICTIONARY
 
-    _cairo_output_stream_write (surface->output, rgb, rgb_size);
+    _cairo_output_stream_write (surface->output, data, data_size);
     status = _cairo_pdf_surface_close_stream (surface);
 
 CLEANUP_RGB:
-    free (rgb);
+    free (data);
 CLEANUP:
     return status;
 }
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index c797c1a..b1b0db3 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -377,6 +377,14 @@ typedef enum _cairo_image_transparency {
     CAIRO_IMAGE_UNKNOWN
 } cairo_image_transparency_t;
 
+typedef enum _cairo_image_color {
+    CAIRO_IMAGE_IS_COLOR,
+    CAIRO_IMAGE_IS_GRAYSCALE,
+    CAIRO_IMAGE_IS_MONOCHROME,
+    CAIRO_IMAGE_UNKNOWN_COLOR
+} cairo_image_color_t;
+
+
 struct _cairo_mime_data {
     cairo_reference_count_t ref_count;
     unsigned char *data;
diff --git a/src/cairoint.h b/src/cairoint.h
index 33c9a49..e47e14b 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -964,6 +964,7 @@ struct _cairo_image_surface {
 
     unsigned owns_data : 1;
     unsigned transparency : 2;
+    unsigned color : 2;
 };
 
 extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
@@ -2028,6 +2029,9 @@ _cairo_image_surface_span_render_row (int				 y,
 cairo_private cairo_image_transparency_t
 _cairo_image_analyze_transparency (cairo_image_surface_t      *image);
 
+cairo_private cairo_image_color_t
+_cairo_image_analyze_color (cairo_image_surface_t      *image);
+
 cairo_private cairo_bool_t
 _cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure;
 


More information about the cairo-commit mailing list