[poppler] [PATCH] Use cairo transformations in cairo backend

Jeff Muizelaar jeff at infidigm.net
Wed May 31 00:07:08 PDT 2006


This patch changes the cairo backend to use the transformation support
in cairo instead of doing all of the transformations in the
OutputDevice. The primary motivation for this change is correctness but
there are other benefits as well.

Currently, both the splash and cairo backends do all of the
transformations in the OutputDevice and so the actual rasterization
process doesn't know about them at all. The problem with this approach
is that the pen is not transformed. See #6948 for an example of the
problems this causes, or for a quick example, imagine a thickly stroked
curve that has been scaled only on the x-axis.

Also, with this patch, the cairo backend will now for the first time,
afaik, render some pdfs more correctly than the splash backend.

In addition to the improved correctness, this patch also cleans things
up quite a bit as can be seen from the diffstat:

 CairoOutputDev.cc |  194 +++++++++++++++++++++++-------------------------------
 CairoOutputDev.h  |    1 
 2 files changed, 86 insertions(+), 109 deletions(-)


Index: poppler/CairoOutputDev.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/CairoOutputDev.cc,v
retrieving revision 1.38
diff -u -r1.38 CairoOutputDev.cc
--- poppler/CairoOutputDev.cc	29 May 2006 18:44:17 -0000	1.38
+++ poppler/CairoOutputDev.cc	31 May 2006 06:33:22 -0000
@@ -124,9 +124,32 @@
   needFontUpdate = gTrue;
 }
 
+void CairoOutputDev::setDefaultCTM(double *ctm) {
+  cairo_matrix_t matrix;
+  matrix.xx = ctm[0];
+  matrix.yx = ctm[1];
+  matrix.xy = ctm[2];
+  matrix.yy = ctm[3];
+  matrix.x0 = ctm[4];
+  matrix.y0 = ctm[5];
+
+  cairo_set_matrix (cairo, &matrix);
+
+  OutputDev::setDefaultCTM(ctm);
+}
+
 void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12,
 				double m21, double m22,
 				double m31, double m32) {
+  cairo_matrix_t matrix;
+  matrix.xx = m11;
+  matrix.yx = m12;
+  matrix.xy = m21;
+  matrix.yy = m22;
+  matrix.x0 = m31;
+  matrix.y0 = m32;
+
+  cairo_transform (cairo, &matrix);
   updateLineDash(state);
   updateLineJoin(state);
   updateLineCap(state);
@@ -137,20 +160,9 @@
   double *dashPattern;
   int dashLength;
   double dashStart;
-  double *transformedDash;
-  double transformedStart;
-  int i;
 
   state->getLineDash(&dashPattern, &dashLength, &dashStart);
-
-  transformedDash = new double[dashLength];
-  
-  for (i = 0; i < dashLength; ++i) {
-    transformedDash[i] =  state->transformWidth(dashPattern[i]);
-  }
-  transformedStart = state->transformWidth(dashStart);
-  cairo_set_dash (cairo, transformedDash, dashLength, transformedStart);
-  delete [] transformedDash;
+  cairo_set_dash (cairo, dashPattern, dashLength, dashStart);
 }
 
 void CairoOutputDev::updateFlatness(GfxState *state) {
@@ -190,11 +202,11 @@
 }
 
 void CairoOutputDev::updateLineWidth(GfxState *state) {
-  LOG(printf ("line width: %f\n", state->getTransformedLineWidth()));
-  if (state->getTransformedLineWidth() == 0.0) {
+  LOG(printf ("line width: %f\n", state->getLineWidth()));
+  if (state->getLineWidth() == 0.0) {
       cairo_set_line_width (cairo, 72.0/300.0);
   } else {
-      cairo_set_line_width (cairo, state->getTransformedLineWidth());
+      cairo_set_line_width (cairo, state->getLineWidth());
   }
 }
 
@@ -273,43 +285,36 @@
   
   font_face = currentFont->getFontFace();
   cairo_set_font_face (cairo, font_face);
-
-  matrix.xx = m11;
-  matrix.xy = -m21;
-  matrix.yx = m12;
-  matrix.yy = -m22;
-  matrix.x0 = 0;
-  matrix.y0 = 0;
+ 
+  double fontSize = state->getFontSize();
+  double *m = state->getTextMat();
+  matrix.xx = m[0] * fontSize;
+  matrix.yx = m[1] * fontSize;
+  matrix.xy = -m[2] * fontSize;
+  matrix.yy = -m[3] * fontSize;
+  matrix.x0 = m[4];
+  matrix.y0 = m[5];
   cairo_set_font_matrix (cairo, &matrix);
 }
 
 void CairoOutputDev::doPath(GfxState *state, GfxPath *path) {
   GfxSubpath *subpath;
-  double x1, y1, x2, y2, x3, y3;
   int i, j;
-
   for (i = 0; i < path->getNumSubpaths(); ++i) {
     subpath = path->getSubpath(i);
     if (subpath->getNumPoints() > 0) {
-      state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
-      cairo_move_to (cairo, x1, y1);
-      LOG (printf ("move_to %f, %f\n", x1, y1));
-      j = 1;
+      cairo_move_to (cairo, subpath->getX(0), subpath->getY(0));
+         j = 1;
       while (j < subpath->getNumPoints()) {
 	if (subpath->getCurve(j)) {
-	  state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
-	  state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
-	  state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
-	  cairo_curve_to (cairo, 
-			  x1, y1,
-			  x2, y2,
-			  x3, y3);
-	  LOG (printf ("curve_to %f, %f  %f, %f  %f, %f\n", x1, y1, x2, y2, x3, y3));
+	  cairo_curve_to( cairo,
+			  subpath->getX(j), subpath->getY(j),
+			  subpath->getX(j+1), subpath->getY(j+1),
+			  subpath->getX(j+2), subpath->getY(j+2));
+
 	  j += 3;
 	} else {
-	  state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
-	  cairo_line_to (cairo, x1, y1);
-	  LOG(printf ("line_to %f, %f\n", x1, y1));
+	  cairo_line_to (cairo, subpath->getX(j), subpath->getY(j));
 	  ++j;
 	}
       }
@@ -374,15 +379,12 @@
 			      double originX, double originY,
 			      CharCode code, int nBytes, Unicode *u, int uLen)
 {
-  double tx, ty;
-
   if (!currentFont)
     return;
   
   glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen);
-  state->transform(x - originX, y - originY, &tx, &ty);
-  glyphs[glyphCount].x = tx;
-  glyphs[glyphCount].y = ty;
+  glyphs[glyphCount].x = x - originX;
+  glyphs[glyphCount].y = y - originY;
   glyphCount++;
 }
 
@@ -435,7 +437,7 @@
     textClipPath = cairo_copy_path (cairo);
     cairo_new_path (cairo);
   }
-  
+
   gfree (glyphs);
   glyphs = NULL;
 }
@@ -443,10 +445,24 @@
 GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y,
 				      double dx, double dy,
 				      CharCode code, Unicode *u, int uLen) {
+
+  cairo_save (cairo);
+  double *ctm;
+  cairo_matrix_t matrix;
+
+  ctm = state->getCTM();
+  matrix.xx = ctm[0];
+  matrix.yx = ctm[1];
+  matrix.xy = ctm[2];
+  matrix.yy = ctm[3];
+  matrix.x0 = ctm[4];
+  matrix.y0 = ctm[5];
+  cairo_set_matrix(cairo, &matrix);
   return gFalse;
 }
 
 void CairoOutputDev::endType3Char(GfxState *state) {
+  cairo_restore (cairo);
 }
 
 void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) {
@@ -478,31 +494,21 @@
   int x, y;
   ImageStream *imgStr;
   Guchar *pix;
-  double *ctm;
   cairo_matrix_t matrix;
   int invert_bit;
   int row_stride;
-
-  ctm = state->getCTM();
-  LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n",
-	       width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]));
-  matrix.xx = ctm[0] / width;
-  matrix.xy = -ctm[2] / height;
-  matrix.yx = ctm[1] / width;
-  matrix.yy = -ctm[3] / height;
-  matrix.x0 = ctm[2] + ctm[4];
-  matrix.y0 = ctm[3] + ctm[5];
-
+  
   /* work around a cairo bug when scaling 1x1 surfaces */
+
   if (width == 1 && height == 1) {
     cairo_save (cairo);
-    cairo_set_matrix (cairo, &matrix);
-    cairo_rectangle (cairo, 0., 0., 1., 1.);
+    cairo_rectangle (cairo, 0., 0., width, height);
     cairo_fill (cairo);
     cairo_restore (cairo);
     return;
   }
 
+
   row_stride = (width + 3) & ~3;
   buffer = (unsigned char *) malloc (height * row_stride);
   if (buffer == NULL) {
@@ -540,7 +546,9 @@
     return;
   }
 
-  cairo_matrix_invert (&matrix);
+  cairo_matrix_init_translate (&matrix, 0, height);
+  cairo_matrix_scale (&matrix, width, -height);
+
   cairo_pattern_set_matrix (pattern, &matrix);
 
   /* we should actually be using CAIRO_FILTER_NEAREST here. However,
@@ -605,7 +613,6 @@
   ImageStream *imgStr;
   GfxRGB rgb;
   int alpha, i;
-  double *ctm;
   cairo_matrix_t matrix;
   cairo_matrix_t maskMatrix;
   int is_identity_transform;
@@ -644,25 +651,14 @@
     return;
   }
 
-  ctm = state->getCTM();
-  LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n",
-	       width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]));
-  matrix.xx = ctm[0] / width;
-  matrix.xy = -ctm[2] / height;
-  matrix.yx = ctm[1] / width;
-  matrix.yy = -ctm[3] / height;
-  matrix.x0 = ctm[2] + ctm[4];
-  matrix.y0 = ctm[3] + ctm[5];
-
-  maskMatrix.xx = ctm[0] / maskWidth;
-  maskMatrix.xy = -ctm[2] / maskHeight;
-  maskMatrix.yx = ctm[1] / maskWidth;
-  maskMatrix.yy = -ctm[3] / maskHeight;
-  maskMatrix.x0 = ctm[2] + ctm[4];
-  maskMatrix.y0 = ctm[3] + ctm[5];
+  LOG (printf ("drawMaskedImage %dx%d\n", width, height));
+
+  cairo_matrix_init_translate (&matrix, 0, height);
+  cairo_matrix_scale (&matrix, width, -height);
+
+  cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
+  cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
 
-  cairo_matrix_invert (&matrix);
-  cairo_matrix_invert (&maskMatrix);
 
   cairo_pattern_set_matrix (pattern, &matrix);
   cairo_pattern_set_matrix (maskPattern, &maskMatrix);
@@ -720,7 +716,6 @@
   ImageStream *imgStr;
   GfxRGB rgb;
   int alpha, i;
-  double *ctm;
   cairo_matrix_t matrix;
   cairo_matrix_t maskMatrix;
   int is_identity_transform;
@@ -759,25 +754,13 @@
     return;
   }
 
-  ctm = state->getCTM();
-  LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n",
-	       width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]));
-  matrix.xx = ctm[0] / width;
-  matrix.xy = -ctm[2] / height;
-  matrix.yx = ctm[1] / width;
-  matrix.yy = -ctm[3] / height;
-  matrix.x0 = ctm[2] + ctm[4];
-  matrix.y0 = ctm[3] + ctm[5];
-
-  maskMatrix.xx = ctm[0] / maskWidth;
-  maskMatrix.xy = -ctm[2] / maskHeight;
-  maskMatrix.yx = ctm[1] / maskWidth;
-  maskMatrix.yy = -ctm[3] / maskHeight;
-  maskMatrix.x0 = ctm[2] + ctm[4];
-  maskMatrix.y0 = ctm[3] + ctm[5];
+  LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height));
+
+  cairo_matrix_init_translate (&matrix, 0, height);
+  cairo_matrix_scale (&matrix, width, -height);
 
-  cairo_matrix_invert (&matrix);
-  cairo_matrix_invert (&maskMatrix);
+  cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
+  cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
 
   cairo_pattern_set_matrix (pattern, &matrix);
   cairo_pattern_set_matrix (maskPattern, &maskMatrix);
@@ -808,7 +791,6 @@
   Guchar *pix;
   GfxRGB rgb;
   int alpha, i;
-  double *ctm;
   cairo_matrix_t matrix;
   int is_identity_transform;
   
@@ -870,17 +852,11 @@
     return;
   }
 
-  ctm = state->getCTM();
-  LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n",
-	       width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]));
-  matrix.xx = ctm[0] / width;
-  matrix.xy = -ctm[2] / height;
-  matrix.yx = ctm[1] / width;
-  matrix.yy = -ctm[3] / height;
-  matrix.x0 = ctm[2] + ctm[4];
-  matrix.y0 = ctm[3] + ctm[5];
+  LOG (printf ("drawImageMask %dx%d\n", width, height));
+  
+  cairo_matrix_init_translate (&matrix, 0, height);
+  cairo_matrix_scale (&matrix, width, -height);
 
-  cairo_matrix_invert (&matrix);
   cairo_pattern_set_matrix (pattern, &matrix);
 
   cairo_pattern_set_filter (pattern, CAIRO_FILTER_BILINEAR);
Index: poppler/CairoOutputDev.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/CairoOutputDev.h,v
retrieving revision 1.16
diff -u -r1.16 CairoOutputDev.h
--- poppler/CairoOutputDev.h	27 May 2006 17:27:37 -0000	1.16
+++ poppler/CairoOutputDev.h	31 May 2006 06:33:22 -0000
@@ -71,6 +71,7 @@
 
   //----- update graphics state
   virtual void updateAll(GfxState *state);
+  virtual void setDefaultCTM(double *ctm);
   virtual void updateCTM(GfxState *state, double m11, double m12,
 			 double m21, double m22, double m31, double m32);
   virtual void updateLineDash(GfxState *state);


More information about the poppler mailing list