[poppler] poppler/CairoOutputDev.cc poppler/CairoRescaleBox.cc poppler/CairoRescaleBox.h

Adrian Johnson ajohnson at kemper.freedesktop.org
Sun Nov 18 04:14:23 PST 2012


 poppler/CairoOutputDev.cc  |  278 +++++++++++++++++++++++----------------------
 poppler/CairoRescaleBox.cc |  177 +++++++++++++++-------------
 poppler/CairoRescaleBox.h  |   21 ++-
 3 files changed, 258 insertions(+), 218 deletions(-)

New commits:
commit 87fd5275514b63f13622b79a8fcfe443ccc4f45d
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Nov 11 18:53:12 2012 +1030

    cairo: make drawImage work with images > 32767 in width/height
    
    Cairo images are limited to 32767 in width and height due to the
    16.16 format used by pixman. Make drawImage work with large images
    by scaling down the image before a cairo image is created.
    
    CairoRescaleBox.cc has been turned into a class with a virtual
    function to get the next row of the source image. This allows the
    rescale code to access the source data one row at a time to avoid
    needing to allocate an image the size of the source image.
    
    A RescaleDrawImage class derived from CairoRescaleBox has been
    written to create the cairo source image to be used by drawImage. The
    code from drawImage that created the cairo source image has been moved
    into RescaleDrawImage::getSourceImage and RescaleDrawImage::getRow.
    
    Bug 56858

diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 2cd67c9..0f33d4e 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -1750,49 +1750,6 @@ void CairoOutputDev::getScaledSize(int  orig_width,
   }
 }
 
-cairo_surface_t *CairoOutputDev::downscaleSurface(cairo_surface_t *orig_surface) {
-  cairo_surface_t *dest_surface;
-  unsigned char *dest_buffer;
-  int dest_stride;
-  unsigned char *orig_buffer;
-  int orig_width, orig_height;
-  int orig_stride;
-  int scaledHeight;
-  int scaledWidth;
-  GBool res;
-
-  if (printing)
-    return NULL;
-
-  orig_width = cairo_image_surface_get_width (orig_surface);
-  orig_height = cairo_image_surface_get_height (orig_surface);
-  getScaledSize (orig_width, orig_height, &scaledWidth, &scaledHeight);
-  if (scaledWidth >= orig_width || scaledHeight >= orig_height)
-    return NULL;
-
-  dest_surface = cairo_surface_create_similar (orig_surface,
-					       cairo_surface_get_content (orig_surface),
-					       scaledWidth, scaledHeight);
-  dest_buffer = cairo_image_surface_get_data (dest_surface);
-  dest_stride = cairo_image_surface_get_stride (dest_surface);
-
-  orig_buffer = cairo_image_surface_get_data (orig_surface);
-  orig_stride = cairo_image_surface_get_stride (orig_surface);
-
-  res = downscale_box_filter((uint32_t *)orig_buffer,
-			     orig_stride, orig_width, orig_height,
-			     scaledWidth, scaledHeight, 0, 0,
-			     scaledWidth, scaledHeight,
-			     (uint32_t *)dest_buffer, dest_stride);
-  if (!res) {
-    cairo_surface_destroy (dest_surface);
-    return NULL;
-  }
-
-  return dest_surface;
-
-}
-
 cairo_filter_t
 CairoOutputDev::getFilterForSurface(cairo_surface_t *image,
 				    GBool interpolate)
@@ -2738,63 +2695,119 @@ void CairoOutputDev::setMimeData(Stream *str, Object *ref, cairo_surface_t *imag
   }
 }
 
-void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
-			       int width, int height,
-			       GfxImageColorMap *colorMap,
-			       GBool interpolate,
-			       int *maskColors, GBool inlineImg)
-{
-  cairo_surface_t *image;
-  cairo_pattern_t *pattern, *maskPattern;
+class RescaleDrawImage : public CairoRescaleBox {
+private:
   ImageStream *imgStr;
-  cairo_matrix_t matrix;
-  unsigned char *buffer;
-  int stride, i;
-  GfxRGB *lookup = NULL;
-  cairo_filter_t filter = CAIRO_FILTER_BILINEAR;
-
-  /* TODO: Do we want to cache these? */
-  imgStr = new ImageStream(str, width,
-			   colorMap->getNumPixelComps(),
-			   colorMap->getBits());
-  imgStr->reset();
+  GfxRGB *lookup;
+  int width;
+  GfxImageColorMap *colorMap;
+  int *maskColors;
+  int current_row;
+
+public:
+  cairo_surface_t *getSourceImage(Stream *str,
+                                  int widthA, int height,
+                                  int scaledWidth, int scaledHeight,
+                                  GBool printing,
+                                  GfxImageColorMap *colorMapA,
+                                  int *maskColorsA) {
+    cairo_surface_t *image = NULL;
+    int i;
+
+    lookup = NULL;
+    colorMap = colorMapA;
+    maskColors = maskColorsA;
+    width = widthA;
+    current_row = -1;
+
+    /* TODO: Do we want to cache these? */
+    imgStr = new ImageStream(str, width,
+                             colorMap->getNumPixelComps(),
+                             colorMap->getBits());
+    imgStr->reset();
 
 #if 0
-  /* ICCBased color space doesn't do any color correction
-   * so check its underlying color space as well */
-  int is_identity_transform;
-  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
-		  (colorMap->getColorSpace()->getMode() == csICCBased &&
-		   ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
+    /* ICCBased color space doesn't do any color correction
+     * so check its underlying color space as well */
+    int is_identity_transform;
+    is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
+      (colorMap->getColorSpace()->getMode() == csICCBased &&
+       ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
 #endif
 
-  image = cairo_image_surface_create (maskColors ?
-				      CAIRO_FORMAT_ARGB32 :
-				      CAIRO_FORMAT_RGB24,
-				      width, height);
-  if (cairo_surface_status (image))
-    goto cleanup;
+    // special case for one-channel (monochrome/gray/separation) images:
+    // build a lookup table here
+    if (colorMap->getNumPixelComps() == 1) {
+      int n;
+      Guchar pix;
 
-  // special case for one-channel (monochrome/gray/separation) images:
-  // build a lookup table here
-  if (colorMap->getNumPixelComps() == 1) {
-    int n;
-    Guchar pix;
+      n = 1 << colorMap->getBits();
+      lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB));
+      for (i = 0; i < n; ++i) {
+        pix = (Guchar)i;
 
-    n = 1 << colorMap->getBits();
-    lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB));
-    for (i = 0; i < n; ++i) {
-      pix = (Guchar)i;
+        colorMap->getRGB(&pix, &lookup[i]);
+      }
+    }
 
-      colorMap->getRGB(&pix, &lookup[i]);
+    if (printing || scaledWidth >= width || scaledHeight >= height) {
+      // No downscaling. Create cairo image containing the source image data.
+      unsigned char *buffer;
+      int stride;
+
+      image = cairo_image_surface_create (maskColors ?
+                                          CAIRO_FORMAT_ARGB32 :
+                                          CAIRO_FORMAT_RGB24,
+                                          width, height);
+      if (cairo_surface_status (image))
+        goto cleanup;
+
+      buffer = cairo_image_surface_get_data (image);
+      stride = cairo_image_surface_get_stride (image);
+      for (int y = 0; y < height; y++) {
+        uint32_t *dest = (uint32_t *) (buffer + y * stride);
+        getRow(y, dest);
+      }
+    } else {
+      // // Downscaling required. Create cairo image the size of the
+      // rescaled image and // downscale the source image data into
+      // the cairo image. downScaleImage() will call getRow() to read
+      // source image data from the image stream. This avoids having
+      // to create an image the size of the source image which may
+      // exceed cairo's 32676x32767 image size limit (and also saves a
+      // lot of memory).
+      image = cairo_image_surface_create (maskColors ?
+                                          CAIRO_FORMAT_ARGB32 :
+                                          CAIRO_FORMAT_RGB24,
+                                          scaledWidth, scaledHeight);
+      if (cairo_surface_status (image))
+        goto cleanup;
+
+      downScaleImage(width, height,
+                     scaledWidth, scaledHeight,
+                     0, 0, scaledWidth, scaledHeight,
+                     image);
     }
+    cairo_surface_mark_dirty (image);
+
+  cleanup:
+    gfree(lookup);
+    imgStr->close();
+    delete imgStr;
+    return image;
   }
 
-  buffer = cairo_image_surface_get_data (image);
-  stride = cairo_image_surface_get_stride (image);
-  for (int y = 0; y < height; y++) {
-    uint32_t *dest = (uint32_t *) (buffer + y * stride);
-    Guchar *pix = imgStr->getLine();
+  void getRow(int row_num, uint32_t *row_data) {
+    int i;
+    Guchar *pix;
+
+    if (row_num == current_row)
+      return;
+
+    while (current_row  < row_num) {
+      pix = imgStr->getLine();
+      current_row++;
+    }
 
     if (lookup) {
       Guchar *p = pix;
@@ -2802,54 +2815,63 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
       for (i = 0; i < width; i++) {
         rgb = lookup[*p];
-        dest[i] =
-		((int) colToByte(rgb.r) << 16) |
-		((int) colToByte(rgb.g) << 8) |
-		((int) colToByte(rgb.b) << 0);
-	p++;
+        row_data[i] =
+          ((int) colToByte(rgb.r) << 16) |
+          ((int) colToByte(rgb.g) << 8) |
+          ((int) colToByte(rgb.b) << 0);
+        p++;
       }
     } else {
-      colorMap->getRGBLine (pix, dest, width);
+      colorMap->getRGBLine (pix, row_data, width);
     }
 
     if (maskColors) {
       for (int x = 0; x < width; x++) {
-	bool is_opaque = false;
-	for (int i = 0; i < colorMap->getNumPixelComps(); ++i) {
-	  if (pix[i] < maskColors[2*i] ||
-	      pix[i] > maskColors[2*i+1]) {
-	    is_opaque = true;
-	    break;
-	  }
-	}
-	if (is_opaque)
-	  *dest |= 0xff000000;
-	else
-	  *dest = 0;
-	dest++;
-	pix += colorMap->getNumPixelComps();
+        bool is_opaque = false;
+        for (int i = 0; i < colorMap->getNumPixelComps(); ++i) {
+          if (pix[i] < maskColors[2*i] ||
+              pix[i] > maskColors[2*i+1]) {
+            is_opaque = true;
+            break;
+          }
+        }
+        if (is_opaque)
+          *row_data |= 0xff000000;
+        else
+          *row_data = 0;
+        row_data++;
+        pix += colorMap->getNumPixelComps();
       }
     }
   }
-  gfree(lookup);
 
-  LOG (printf ("drawImage %dx%d\n", width, height));
+};
+
+void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+			       int widthA, int heightA,
+			       GfxImageColorMap *colorMap,
+			       GBool interpolate,
+			       int *maskColors, GBool inlineImg)
+{
+  cairo_surface_t *image;
+  cairo_pattern_t *pattern, *maskPattern;
+  cairo_matrix_t matrix;
+  int width, height;
+  int scaledWidth, scaledHeight;
+  cairo_filter_t filter = CAIRO_FILTER_BILINEAR;
+  RescaleDrawImage rescale;
+
+  LOG (printf ("drawImage %dx%d\n", widthA, heightA));
 
-  cairo_surface_t *scaled_surface;
+  getScaledSize (widthA, heightA, &scaledWidth, &scaledHeight);
+  image = rescale.getSourceImage(str, widthA, heightA, scaledWidth, scaledHeight, printing, colorMap, maskColors);
+  if (!image)
+    return;
 
-  scaled_surface = downscaleSurface (image);
-  if (scaled_surface) {
-    if (cairo_surface_status (scaled_surface))
-      goto cleanup;
-    cairo_surface_destroy (image);
-    image = scaled_surface;
-    width = cairo_image_surface_get_width (image);
-    height = cairo_image_surface_get_height (image);
-  } else {
+  width = cairo_image_surface_get_width (image);
+  height = cairo_image_surface_get_height (image);
+  if (width == widthA && height == heightA)
     filter = getFilterForSurface (image, interpolate);
-  }
-
-  cairo_surface_mark_dirty (image);
 
   if (!inlineImg) /* don't read stream twice if it is an inline image */
     setMimeData(str, ref, image);
@@ -2857,7 +2879,7 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   pattern = cairo_pattern_create_for_surface (image);
   cairo_surface_destroy (image);
   if (cairo_pattern_status (pattern))
-    goto cleanup;
+    return;
 
   cairo_pattern_set_filter (pattern, filter);
 
@@ -2869,7 +2891,7 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   cairo_pattern_set_matrix (pattern, &matrix);
   if (cairo_pattern_status (pattern)) {
     cairo_pattern_destroy (pattern);
-    goto cleanup;
+    return;
   }
 
   if (!mask && fill_opacity != 1.0) {
@@ -2912,10 +2934,6 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   }
 
   cairo_pattern_destroy (pattern);
-
-cleanup:
-  imgStr->close();
-  delete imgStr;
 }
 
 
diff --git a/poppler/CairoRescaleBox.cc b/poppler/CairoRescaleBox.cc
index 68a0c4c..e2682a4 100644
--- a/poppler/CairoRescaleBox.cc
+++ b/poppler/CairoRescaleBox.cc
@@ -57,6 +57,7 @@
 /* we work in fixed point where 1. == 1 << 24 */
 #define FIXED_SHIFT 24
 
+
 static void downsample_row_box_filter (
         int start, int width,
         uint32_t *src, uint32_t *dest,
@@ -261,106 +262,116 @@ static int compute_coverage (int coverage[], int src_length, int dest_length)
     return ratio;
 }
 
-GBool downscale_box_filter(uint32_t *orig, int orig_stride, unsigned orig_width, unsigned orig_height,
-			   signed scaled_width, signed scaled_height,
-			   uint16_t start_column, uint16_t start_row,
-			   uint16_t width, uint16_t height,
-			   uint32_t *dest, int dst_stride)
-{
-    int pixel_coverage_x, pixel_coverage_y;
-    int dest_y;
-    int src_y = 0;
-    uint32_t *scanline = orig;
-    int *x_coverage = NULL;
-    int *y_coverage = NULL;
-    uint32_t *temp_buf = NULL;
-    GBool retval = gFalse;
-
-    x_coverage = (int *)gmallocn3 (orig_width, 1, sizeof(int));
-    y_coverage = (int *)gmallocn3 (orig_height, 1, sizeof(int));
-
-    /* we need to allocate enough room for ceil(src_height/dest_height)+1
-      Example:
-      src_height = 140
-      dest_height = 50
-      src_height/dest_height = 2.8
-
-      |-------------|       2.8 pixels
-      |----|----|----|----| 4 pixels
-      need to sample 3 pixels
-
-         |-------------|       2.8 pixels
-      |----|----|----|----| 4 pixels
-      need to sample 4 pixels
-    */
 
-    temp_buf = (uint32_t *)gmallocn3 ((orig_height + scaled_height-1)/scaled_height+1, scaled_width, sizeof(uint32_t));
+GBool CairoRescaleBox::downScaleImage(unsigned orig_width, unsigned orig_height,
+                                      signed scaled_width, signed scaled_height,
+                                      unsigned short int start_column, unsigned short int start_row,
+                                      unsigned short int width, unsigned short int height,
+                                      cairo_surface_t *dest_surface) {
+  int pixel_coverage_x, pixel_coverage_y;
+  int dest_y;
+  int src_y = 0;
+  uint32_t *scanline;
+  int *x_coverage = NULL;
+  int *y_coverage = NULL;
+  uint32_t *temp_buf = NULL;
+  GBool retval = gFalse;
+  unsigned int *dest;
+  int dst_stride;
+
+  dest = (unsigned int *)cairo_image_surface_get_data (dest_surface);
+  dst_stride = cairo_image_surface_get_stride (dest_surface);
+
+  scanline = (uint32_t*)gmallocn3 (orig_width, 1, sizeof(int));
+
+  x_coverage = (int *)gmallocn3 (orig_width, 1, sizeof(int));
+  y_coverage = (int *)gmallocn3 (orig_height, 1, sizeof(int));
+
+  /* we need to allocate enough room for ceil(src_height/dest_height)+1
+     Example:
+     src_height = 140
+     dest_height = 50
+     src_height/dest_height = 2.8
 
-    if (!x_coverage || !y_coverage || !scanline || !temp_buf)
-	goto cleanup;
+     |-------------|       2.8 pixels
+     |----|----|----|----| 4 pixels
+     need to sample 3 pixels
 
-    pixel_coverage_x = compute_coverage (x_coverage, orig_width, scaled_width);
-    pixel_coverage_y = compute_coverage (y_coverage, orig_height, scaled_height);
+     |-------------|       2.8 pixels
+     |----|----|----|----| 4 pixels
+     need to sample 4 pixels
+  */
 
-    assert (width + start_column <= scaled_width);
+  temp_buf = (uint32_t *)gmallocn3 ((orig_height + scaled_height-1)/scaled_height+1, scaled_width, sizeof(uint32_t));
 
-    /* skip the rows at the beginning */
-    for (dest_y = 0; dest_y < start_row; dest_y++)
+  if (!x_coverage || !y_coverage || !scanline || !temp_buf)
+    goto cleanup;
+
+  pixel_coverage_x = compute_coverage (x_coverage, orig_width, scaled_width);
+  pixel_coverage_y = compute_coverage (y_coverage, orig_height, scaled_height);
+
+  assert (width + start_column <= scaled_width);
+
+
+
+  /* skip the rows at the beginning */
+  for (dest_y = 0; dest_y < start_row; dest_y++)
+  {
+    int box = 1 << FIXED_SHIFT;
+    int start_coverage_y = y_coverage[dest_y];
+    box -= start_coverage_y;
+    src_y++;
+    while (box >= pixel_coverage_y)
     {
-        int box = 1 << FIXED_SHIFT;
-        int start_coverage_y = y_coverage[dest_y];
-        box -= start_coverage_y;
-        src_y++;
-        while (box >= pixel_coverage_y)
-        {
-            box -= pixel_coverage_y;
-            src_y++;
-        }
+      box -= pixel_coverage_y;
+      src_y++;
     }
+  }
 
-    for (; dest_y < start_row + height; dest_y++)
-    {
-        int columns = 0;
-        int box = 1 << FIXED_SHIFT;
-        int start_coverage_y = y_coverage[dest_y];
+  for (; dest_y < start_row + height; dest_y++)
+  {
+    int columns = 0;
+    int box = 1 << FIXED_SHIFT;
+    int start_coverage_y = y_coverage[dest_y];
 
-	scanline = orig + src_y * orig_stride / 4;
-        downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x);
-        columns++;
-        src_y++;
-        box -= start_coverage_y;
+    getRow(src_y, scanline);
+    downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x);
+    columns++;
+    src_y++;
+    box -= start_coverage_y;
 
-        while (box >= pixel_coverage_y)
-        {
-	    scanline = orig + src_y * orig_stride / 4;
-            downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x);
-            columns++;
-            src_y++;
-            box -= pixel_coverage_y;
-        }
+    while (box >= pixel_coverage_y)
+    {
+      getRow(src_y, scanline);
+      downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x);
+      columns++;
+      src_y++;
+      box -= pixel_coverage_y;
+    }
 
-        /* downsample any leftovers */
-        if (box > 0)
-        {
-	    scanline = orig + src_y * orig_stride / 4;
-            downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x);
-            columns++;
-        }
+    /* downsample any leftovers */
+    if (box > 0)
+    {
+      getRow(src_y, scanline);
+      downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x);
+      columns++;
+    }
 
-        /* now scale the rows we just downsampled in the y direction */
-        downsample_columns_box_filter (width, start_coverage_y, pixel_coverage_y, temp_buf, dest);
-        dest += dst_stride / 4;
+    /* now scale the rows we just downsampled in the y direction */
+    downsample_columns_box_filter (width, start_coverage_y, pixel_coverage_y, temp_buf, dest);
+    dest += dst_stride / 4;
 
 //        assert(width*columns <= ((orig_height + scaled_height-1)/scaled_height+1) * width);
-    }
+  }
 //    assert (src_y<=orig_height);
 
-    retval = gTrue;
+  retval = gTrue;
 
 cleanup:
-    free (x_coverage);
-    free (y_coverage);
-    free (temp_buf);
+  free (x_coverage);
+  free (y_coverage);
+  free (temp_buf);
+  free (scanline);
 
-    return retval;
+  return retval;
 }
diff --git a/poppler/CairoRescaleBox.h b/poppler/CairoRescaleBox.h
index 5349c87..6187d68 100644
--- a/poppler/CairoRescaleBox.h
+++ b/poppler/CairoRescaleBox.h
@@ -2,11 +2,22 @@
 #define CAIRO_RESCALE_BOX_H
 
 #include "goo/gtypes.h"
+#include <cairo.h>
 
-GBool downscale_box_filter(unsigned int *orig, int orig_stride, unsigned orig_width, unsigned orig_height,
-			   signed scaled_width, signed scaled_height,
-			   unsigned short int start_column, unsigned short int start_row,
-			   unsigned short int width, unsigned short int height,
-			   unsigned int *dest, int dst_stride);
+class CairoRescaleBox {
+public:
+
+  CairoRescaleBox() {};
+  virtual ~CairoRescaleBox() {};
+
+  virtual GBool downScaleImage(unsigned orig_width, unsigned orig_height,
+                               signed scaled_width, signed scaled_height,
+                               unsigned short int start_column, unsigned short int start_row,
+                               unsigned short int width, unsigned short int height,
+                               cairo_surface_t *dest_surface);
+
+  virtual void getRow(int row_num, uint32_t *row_data) = 0;
+
+};
 
 #endif /* CAIRO_RESCALE_BOX_H */


More information about the poppler mailing list