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

Adrian Johnson ajohnson at kemper.freedesktop.org
Mon Oct 17 01:33:49 PDT 2011


 poppler/CairoOutputDev.cc |   97 ++++++++++++++++++++++++++++------------------
 poppler/CairoOutputDev.h  |    6 +-
 2 files changed, 65 insertions(+), 38 deletions(-)

New commits:
commit 4bb34757dbbff780baba053371274c05b29771e1
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Mon Sep 19 21:11:44 2011 +0930

    cairo: fix setSoftMask bugs
    
    - Getting the clip extents in device space requires transforming all
      four corners of the clip extents and translating by the group device
      offset other wise the device extents will not be correct for rotated
      ctm.
    
    - Adjust matrix to include translation of the clip extents origin
      since the mask surface does not start at (0,0).
    
    - the ctm when called cairo_mask() needs to be the same as the ctm when
      the mask was created.
    
    - implement transfer function in setSoftMask
    
    Bug 41005

diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 30c69f1..fae0136 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -78,6 +78,11 @@ static inline void printMatrix(cairo_matrix_t *matrix){
 			matrix->x0, matrix->y0);
 }
 
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+
 //------------------------------------------------------------------------
 // CairoImage
 //------------------------------------------------------------------------
@@ -261,6 +266,7 @@ void CairoOutputDev::saveState(GfxState *state) {
 
   MaskStack *ms = new MaskStack;
   ms->mask = cairo_pattern_reference(mask);
+  ms->mask_matrix = mask_matrix;
   ms->next = maskStack;
   maskStack = ms;
 }
@@ -284,6 +290,7 @@ void CairoOutputDev::restoreState(GfxState *state) {
     if (mask)
       cairo_pattern_destroy(mask);
     mask = ms->mask;
+    mask_matrix = ms->mask_matrix;
     maskStack = ms->next;
     delete ms;
   }
@@ -410,8 +417,6 @@ void CairoOutputDev::updateMiterLimit(GfxState *state) {
     cairo_set_miter_limit (cairo_shape, state->getMiterLimit());
 }
 
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-
 void CairoOutputDev::updateLineWidth(GfxState *state) {
   LOG(printf ("line width: %f\n", state->getLineWidth()));
   adjusted_stroke_width = gFalse;
@@ -725,7 +730,10 @@ void CairoOutputDev::fill(GfxState *state) {
   //XXX: how do we get the path
   if (mask) {
     cairo_clip (cairo);
+    cairo_save (cairo);
+    cairo_set_matrix (cairo, &mask_matrix);
     cairo_mask (cairo, mask);
+    cairo_restore (cairo);
   } else if (strokePathClip) {
     fillToStrokePathClip();
   } else {
@@ -1477,7 +1485,10 @@ void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbo
     if (status)
       printf("BAD status: %s\n", cairo_status_to_string(status));
   } else {
+    cairo_save(cairo);
+    cairo_set_matrix(cairo, &mask_matrix);
     cairo_mask(cairo, mask);
+    cairo_restore(cairo);
 
     cairo_pattern_destroy(mask);
     mask = NULL;
@@ -1486,14 +1497,14 @@ void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbo
   popTransparencyGroup();
 }
 
-static uint32_t luminocity(uint32_t x)
+static int luminocity(uint32_t x)
 {
   int r = (x >> 16) & 0xff;
   int g = (x >>  8) & 0xff;
   int b = (x >>  0) & 0xff;
   // an arbitrary integer approximation of .3*r + .59*g + .11*b
   int y = (r*19661+g*38666+b*7209 + 32829)>>16;
-  return y << 24;
+  return y;
 }
 
 
@@ -1509,24 +1520,39 @@ void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
      * So we paint the group to an image surface convert it to a luminocity map
      * and then use that as the mask. */
 
-    double x1, y1, x2, y2, tmp;
+    /* Get clip extents in device space */
+    double x1, y1, x2, y2, x_min, y_min, x_max, y_max;
     cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
     cairo_user_to_device(cairo, &x1, &y1);
     cairo_user_to_device(cairo, &x2, &y2);
-    if (x1 > x2) {
-      tmp = x1;
-      x1 = x2;
-      x2 = tmp;
-    }
+    x_min = MIN(x1, x2);
+    y_min = MIN(y1, y2);
+    x_max = MAX(x1, x2);
+    y_max = MAX(y1, y2);
+    cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
+    cairo_user_to_device(cairo, &x1, &y2);
+    cairo_user_to_device(cairo, &x2, &y1);
+    x_min = MIN(x_min,MIN(x1, x2));
+    y_min = MIN(y_min,MIN(y1, y2));
+    x_max = MAX(x_max,MAX(x1, x2));
+    y_max = MAX(y_max,MAX(y1, y2));
+
+    int width = (int)(ceil(x_max) - floor(x_min));
+    int height = (int)(ceil(y_max) - floor(y_min));
 
-    if (y1 > y2) {
-      tmp = y1;
-      y1 = y2;
-      y2 = tmp;
+    /* Get group device offset */
+    double x_offset, y_offset;
+    if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
+      cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset);
+    } else {
+      cairo_surface_t *pats;
+      cairo_pattern_get_surface(group, &pats);
+      cairo_surface_get_device_offset(pats, &x_offset, &y_offset);
     }
 
-    int width = (int)(ceil(x2) - floor(x1));
-    int height = (int)(ceil(y2) - floor(y1));
+    /* Adjust extents by group offset */
+    x_min += x_offset;
+    y_min += y_offset;
 
     cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
     cairo_t *maskCtx = cairo_create(source);
@@ -1541,19 +1567,15 @@ void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
 			 colToDbl(backdropColorRGB.b));
     cairo_paint(maskCtx);
 
-    cairo_matrix_t mat;
+    /* Copy source ctm to mask ctm and translate origin so that the
+     * mask appears it the same location on the source surface.  */
+    cairo_matrix_t mat, tmat;
+    cairo_matrix_init_translate(&tmat, -x_min, -y_min);
     cairo_get_matrix(cairo, &mat);
+    cairo_matrix_multiply(&mat, &mat, &tmat);
     cairo_set_matrix(maskCtx, &mat);
 
     /* make the device offset of the new mask match that of the group */
-    double x_offset, y_offset;
-    if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
-      cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset);
-    } else {
-      cairo_surface_t *pats;
-      cairo_pattern_get_surface(group, &pats);
-      cairo_surface_get_device_offset(pats, &x_offset, &y_offset);
-    }
     cairo_surface_set_device_offset(source, x_offset, y_offset);
 
     /* paint the group */
@@ -1569,37 +1591,37 @@ void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
     int stride = cairo_image_surface_get_stride(source)/4;
     for (int y=0; y<height; y++) {
       for (int x=0; x<width; x++) {
-	source_data[y*stride + x] = luminocity(source_data[y*stride + x]);
-
-#if 0
-	here is how splash deals with the transferfunction we should deal with this
-	  at some point
+	int lum;
+	lum = luminocity(source_data[y*stride + x]);
 	if (transferFunc) {
-	  transferFunc->transform(&lum, &lum2);
-	} else {
-	  lum2 = lum;
+	  double lum_in, lum_out;
+	  lum_in = lum/256.0;
+	  transferFunc->transform(&lum_in, &lum_out);
+	  lum = (int)(lum_out * 255.0 + 0.5);
 	}
-	p[x] = (int)(lum2 * 255.0 + 0.5);
-#endif
-
+	source_data[y*stride + x] = lum << 24;
       }
     }
     cairo_surface_mark_dirty (source);
 
     /* setup the new mask pattern */
     mask = cairo_pattern_create_for_surface(source);
+    cairo_get_matrix(cairo, &mask_matrix);
 
     if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
       cairo_pattern_set_matrix(mask, &mat);
     } else {
       cairo_matrix_t patMatrix;
       cairo_pattern_get_matrix(group, &patMatrix);
+      /* Apply x_min, y_min offset to it appears in the same location as source. */
+      cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat);
       cairo_pattern_set_matrix(mask, &patMatrix);
     }
 
     cairo_surface_destroy(source);
   } else {
     mask = cairo_pattern_reference(group);
+    cairo_get_matrix(cairo, &mask_matrix);
   }
 
   popTransparencyGroup();
@@ -1818,6 +1840,7 @@ void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
     if (mask)
       cairo_pattern_destroy (mask);
     mask = cairo_pop_group (cairo);
+    cairo_get_matrix (cairo, &mask_matrix);
   }
 }
 
@@ -1897,6 +1920,7 @@ void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *
 
   if (state->getFillColorSpace()->getMode() == csPattern) {
     mask = cairo_pattern_reference (pattern);
+    cairo_get_matrix (cairo, &mask_matrix);
   } else if (!printing) {
     cairo_save (cairo);
     cairo_rectangle (cairo, 0., 0., 1., 1.);
@@ -2169,6 +2193,7 @@ void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream
     }
 
     mask = cairo_pattern_reference (pattern);
+    cairo_get_matrix (cairo, &mask_matrix);
   } else {
     cairo_save (cairo);
 
diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h
index 0f202a5..949d459 100644
--- a/poppler/CairoOutputDev.h
+++ b/poppler/CairoOutputDev.h
@@ -341,6 +341,7 @@ protected:
   cairo_pattern_t *group;
   cairo_pattern_t *shape;
   cairo_pattern_t *mask;
+  cairo_matrix_t mask_matrix;
   cairo_surface_t *cairo_shape_surface;
   cairo_t *cairo_shape;
   int knockoutCount;
@@ -351,8 +352,9 @@ protected:
   } * groupColorSpaceStack;
 
   struct MaskStack {
-      cairo_pattern_t *mask;
-      struct MaskStack *next;
+    cairo_pattern_t *mask;
+    cairo_matrix_t mask_matrix;
+    struct MaskStack *next;
   } *maskStack;
 
   GBool haveCSPattern;	// set if text has been drawn with a


More information about the poppler mailing list