[poppler] Branch 'xpdf303merge' - 5 commits - poppler/CairoOutputDev.cc poppler/CairoOutputDev.h poppler/Gfx.cc poppler/Gfx.h poppler/GfxState.cc poppler/GfxState.h poppler/GlobalParams.cc poppler/GlobalParams.h poppler/OutputDev.h poppler/PreScanOutputDev.cc poppler/PreScanOutputDev.h poppler/PSOutputDev.cc poppler/PSOutputDev.h poppler/SplashOutputDev.cc poppler/SplashOutputDev.h splash/Splash.cc splash/SplashFontEngine.cc splash/SplashFontEngine.h splash/SplashFTFont.cc splash/SplashFTFontEngine.cc splash/SplashFTFontEngine.h splash/SplashFTFont.h splash/Splash.h splash/SplashXPath.cc utils/ImageOutputDev.cc utils/ImageOutputDev.h

Albert Astals Cid aacid at kemper.freedesktop.org
Tue Jan 10 14:39:14 PST 2012


 poppler/CairoOutputDev.cc    |    2 
 poppler/CairoOutputDev.h     |    4 
 poppler/Gfx.cc               |   45 
 poppler/Gfx.h                |   15 
 poppler/GfxState.cc          |    5 
 poppler/GfxState.h           |    7 
 poppler/GlobalParams.cc      |   17 
 poppler/GlobalParams.h       |    3 
 poppler/OutputDev.h          |    3 
 poppler/PSOutputDev.cc       |    2 
 poppler/PSOutputDev.h        |    2 
 poppler/PreScanOutputDev.cc  |   15 
 poppler/PreScanOutputDev.h   |    2 
 poppler/SplashOutputDev.cc   |   11 
 poppler/SplashOutputDev.h    |    4 
 splash/Splash.cc             | 2824 +++++++++++++++++++++++++++++--------------
 splash/Splash.h              |   60 
 splash/SplashFTFont.cc       |   34 
 splash/SplashFTFont.h        |    1 
 splash/SplashFTFontEngine.cc |    7 
 splash/SplashFTFontEngine.h  |    5 
 splash/SplashFontEngine.cc   |    3 
 splash/SplashFontEngine.h    |    1 
 splash/SplashXPath.cc        |   22 
 utils/ImageOutputDev.cc      |    2 
 utils/ImageOutputDev.h       |    2 
 26 files changed, 2116 insertions(+), 982 deletions(-)

New commits:
commit 52d190d8ff962a57a59218f6871c3a63a443ea53
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Jan 10 23:33:40 2012 +0100

    [xpdf303] tiling "merges" from Thomas, using mostly our "old" code instead of xpdf's

diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 2ab9a11..295eb94 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -751,7 +751,7 @@ void CairoOutputDev::eoFill(GfxState *state) {
 
 }
 
-GBool CairoOutputDev::tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *cat, Object *str,
 					double *pmat, int paintType, int /*tilingType*/, Dict *resDict,
 					double *mat, double *bbox,
 					int x0, int y0, int x1, int y1,
diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h
index 2d67ae1..b00ab96 100644
--- a/poppler/CairoOutputDev.h
+++ b/poppler/CairoOutputDev.h
@@ -159,7 +159,7 @@ public:
   virtual void fill(GfxState *state);
   virtual void eoFill(GfxState *state);
   virtual void clipToStrokePath(GfxState *state);
-  virtual GBool tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+  virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
@@ -432,7 +432,7 @@ public:
   virtual void stroke(GfxState *state) { }
   virtual void fill(GfxState *state) { }
   virtual void eoFill(GfxState *state) { }
-  virtual GBool tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+  virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index cbdb1a3..9ac8fa9 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -1985,7 +1985,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
   GfxPatternColorSpace *patCS;
   GfxColorSpace *cs;
   GfxColor color;
-  GfxPath *savedPath;
+  GfxState *savedState;
   double xMin, yMin, xMax, yMax, x, y, x1, y1;
   double cxMin, cyMin, cxMax, cyMax;
   int xi0, yi0, xi1, yi1, xi, yi;
@@ -2036,12 +2036,13 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
 
   // save current graphics state
-  savedPath = state->getPath()->copy();
-  saveState();
+  savedState = saveStateStack();
 
   // set underlying color space (for uncolored tiling patterns); set
   // various other parameters (stroke color, line width) to match
   // Adobe's behavior
+  state->setFillPattern(NULL);
+  state->setStrokePattern(NULL);
   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
     state->setFillColorSpace(cs->copy());
     out->updateFillColorSpace(state);
@@ -2052,6 +2053,8 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
     } else {
 	state->setStrokeColor(state->getFillColor());
     }
+    out->updateFillColor(state);
+    out->updateStrokeColor(state);
   } else {
     cs = new GfxDeviceGrayColorSpace();
     state->setFillColorSpace(cs);
@@ -2062,10 +2065,10 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
     state->setStrokeColor(&color);
     out->updateStrokeColorSpace(state);
   }
-  state->setFillPattern(NULL);
-  out->updateFillColor(state);
-  state->setStrokePattern(NULL);
-  out->updateStrokeColor(state);
+  if (!stroke) {
+    state->setLineWidth(0);
+    out->updateLineWidth(state);
+  }
 
   // clip to current path
   if (stroke) {
@@ -2080,8 +2083,6 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
     }
   }
   state->clearPath();
-  state->setLineWidth(0);
-  out->updateLineWidth(state);
 
   // get the clip region, check for empty
   state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
@@ -2145,7 +2146,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
     m1[4] = m[4];
     m1[5] = m[5];
     if (out->useTilingPatternFill() &&
-    	out->tilingPatternFill(state, catalog, tPat->getContentStream(),
+    	out->tilingPatternFill(state, this, catalog, tPat->getContentStream(),
 			       tPat->getMatrix(), tPat->getPaintType(), tPat->getTilingType(),
 			       tPat->getResDict(), m1, tPat->getBBox(),
 			       xi0, yi0, xi1, yi1, xstep, ystep)) {
@@ -2166,8 +2167,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
 
   // restore graphics state
  restore:
-  restoreState();
-  state->setPath(savedPath);
+  restoreStateStack(savedState);
 }
 
 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
@@ -5100,6 +5100,27 @@ void Gfx::restoreState() {
   stackHeight--;
 }
 
+// Create a new state stack, and initialize it with a copy of the
+// current state.
+GfxState *Gfx::saveStateStack() {
+  GfxState *oldState;
+
+  out->saveState(state);
+  oldState = state;
+  state = state->copy(gTrue);
+  return oldState;
+}
+
+// Switch back to the previous state stack.
+void Gfx::restoreStateStack(GfxState *oldState) {
+  while (state->hasSaves()) {
+    restoreState();
+  }
+  delete state;
+  state = oldState;
+  out->restoreState(state);
+}
+
 void Gfx::pushResources(Dict *resDict) {
   res = new GfxResources(xref, resDict, res);
 }
diff --git a/poppler/Gfx.h b/poppler/Gfx.h
index 2db341b..32fe9cc 100644
--- a/poppler/Gfx.h
+++ b/poppler/Gfx.h
@@ -181,6 +181,13 @@ public:
   // Get the current graphics state object.
   GfxState *getState() { return state; }
 
+  void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
+	       GBool transpGroup = gFalse, GBool softMask = gFalse,
+	       GfxColorSpace *blendingColorSpace = NULL,
+	       GBool isolated = gFalse, GBool knockout = gFalse,
+	       GBool alpha = gFalse, Function *transferFunc = NULL,
+	       GfxColor *backdropColor = NULL);
+
   void pushResources(Dict *resDict);
   void popResources();
   
@@ -347,12 +354,6 @@ private:
   void opXObject(Object args[], int numArgs);
   void doImage(Object *ref, Stream *str, GBool inlineImg);
   void doForm(Object *str);
-  void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
-	       GBool transpGroup = gFalse, GBool softMask = gFalse,
-	       GfxColorSpace *blendingColorSpace = NULL,
-	       GBool isolated = gFalse, GBool knockout = gFalse,
-	       GBool alpha = gFalse, Function *transferFunc = NULL,
-	       GfxColor *backdropColor = NULL);
 
   // in-line image operators
   void opBeginImage(Object args[], int numArgs);
@@ -372,6 +373,8 @@ private:
   void opBeginMarkedContent(Object args[], int numArgs);
   void opEndMarkedContent(Object args[], int numArgs);
   void opMarkPoint(Object args[], int numArgs);
+  GfxState *saveStateStack();
+  void restoreStateStack(GfxState *oldState);
   GBool contentIsHidden();
   void pushMarkedContent();
   void popMarkedContent();
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index 224b9cf..7475ab4 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -5580,7 +5580,7 @@ GfxState::~GfxState() {
 }
 
 // Used for copy();
-GfxState::GfxState(GfxState *state) {
+GfxState::GfxState(GfxState *state, GBool copyPath) {
   int i;
 
   memcpy(this, state, sizeof(GfxState));
@@ -5608,6 +5608,9 @@ GfxState::GfxState(GfxState *state) {
   if (font)
     font->incRefCnt();
 
+  if (copyPath) {
+    path = state->path->copy();
+  }
   saved = NULL;
 }
 
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index 632acc1..c8995e3 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -1312,8 +1312,9 @@ public:
   // Destructor.
   ~GfxState();
 
-  // Copy.
-  GfxState *copy() { return new GfxState(this); }
+  // Copy.
+  GfxState *copy(GBool copyPath = gFalse)
+    { return new GfxState(this, copyPath); }
 
   // Accessors.
   double getHDPI() { return hDPI; }
@@ -1539,7 +1540,7 @@ private:
 
   GfxState *saved;		// next GfxState on stack
 
-  GfxState(GfxState *state);
+  GfxState(GfxState *state, GBool copyPath);
 };
 
 #endif
diff --git a/poppler/OutputDev.h b/poppler/OutputDev.h
index 75f2bd0..03e1613 100644
--- a/poppler/OutputDev.h
+++ b/poppler/OutputDev.h
@@ -43,6 +43,7 @@ class Dict;
 class GooHash;
 class GooString;
 class GfxState;
+class Gfx;
 struct GfxColor;
 class GfxColorSpace;
 class GfxImageColorMap;
@@ -196,7 +197,7 @@ public:
   virtual void stroke(GfxState * /*state*/) {}
   virtual void fill(GfxState * /*state*/) {}
   virtual void eoFill(GfxState * /*state*/) {}
-  virtual GBool tilingPatternFill(GfxState * /*state*/, Catalog * /*cat*/, Object * /*str*/,
+  virtual GBool tilingPatternFill(GfxState * /*state*/, Gfx * /*gfx*/, Catalog * /*cat*/, Object * /*str*/,
 				  double * /*pmat*/, int /*paintType*/, int /*tilingType*/, Dict * /*resDict*/,
 				  double * /*mat*/, double * /*bbox*/,
 				  int /*x0*/, int /*y0*/, int /*x1*/, int /*y1*/,
diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc
index 37e4bb6..5db5a31 100644
--- a/poppler/PSOutputDev.cc
+++ b/poppler/PSOutputDev.cc
@@ -4124,7 +4124,7 @@ GBool PSOutputDev::tilingPatternFillL2(GfxState *state, Catalog *cat, Object *st
   return gTrue;
 }
 
-GBool PSOutputDev::tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+GBool PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				     double *pmat, int paintType, int tilingType, Dict *resDict,
 				     double *mat, double *bbox,
 				     int x0, int y0, int x1, int y1,
diff --git a/poppler/PSOutputDev.h b/poppler/PSOutputDev.h
index 57ede3f..11ebbb8 100644
--- a/poppler/PSOutputDev.h
+++ b/poppler/PSOutputDev.h
@@ -209,7 +209,7 @@ public:
   virtual void stroke(GfxState *state);
   virtual void fill(GfxState *state);
   virtual void eoFill(GfxState *state);
-  virtual GBool tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+  virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
diff --git a/poppler/PreScanOutputDev.cc b/poppler/PreScanOutputDev.cc
index b82828d..1c5d09a 100644
--- a/poppler/PreScanOutputDev.cc
+++ b/poppler/PreScanOutputDev.cc
@@ -81,18 +81,17 @@ void PreScanOutputDev::eoFill(GfxState *state) {
 	state->getFillOpacity(), state->getBlendMode());
 }
 
-GBool PreScanOutputDev::tilingPatternFill(GfxState *state, Catalog *catalog, Object *str,
+GBool PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, Object *str,
 					  double *pmat, int paintType, int /*tilingType*/, Dict *resDict,
 					double *mat, double *bbox,
 					int x0, int y0, int x1, int y1,
 					double xStep, double yStep) {
-  PDFRectangle box;
-  Gfx *gfx;
-  box.x1 = bbox[0]; box.y1 = bbox[1];
-  box.x2 = bbox[2]; box.y2 = bbox[3];
-  gfx = new Gfx(doc, this, resDict, &box, NULL);
-  gfx->display(str);
-  delete gfx;
+  if (paintType == 1) {
+    gfx->doForm1(str, resDict, mat, bbox);
+  } else {
+    check(state->getFillColorSpace(), state->getFillColor(),
+	  state->getFillOpacity(), state->getBlendMode());
+  }
   return gTrue;
 }
 
diff --git a/poppler/PreScanOutputDev.h b/poppler/PreScanOutputDev.h
index d94afaa..ac147f3 100644
--- a/poppler/PreScanOutputDev.h
+++ b/poppler/PreScanOutputDev.h
@@ -80,7 +80,7 @@ public:
   virtual void stroke(GfxState *state);
   virtual void fill(GfxState *state);
   virtual void eoFill(GfxState *state);
-  virtual GBool tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+  virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index 020808e..58382ca 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -1359,6 +1359,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
   textClipPath = NULL;
   haveCSPattern = gFalse;
   transpGroupStack = NULL;
+  nestCount = 0;
 }
 
 void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
@@ -2384,6 +2385,7 @@ void SplashOutputDev::endType3Char(GfxState *state) {
   double *ctm;
 
   if (t3GlyphStack->cacheTag) {
+    --nestCount;
     memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
 	   t3GlyphStack->cache->glyphSize);
     delete bitmap;
@@ -2525,6 +2527,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
 		-t3Font->glyphX, -t3Font->glyphY);
   updateCTM(state, 0, 0, 0, 0, 0, 0);
+  ++nestCount;
 }
 
 void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
@@ -3681,10 +3684,12 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
   transpGroup->tBitmap = bitmap;
   state->shiftCTM(-tx, -ty);
   updateCTM(state, 0, 0, 0, 0, 0, 0);
+  ++nestCount;
 }
 
 void SplashOutputDev::endTransparencyGroup(GfxState *state) {
   // restore state
+  --nestCount;
   delete splash;
   bitmap = transpGroupStack->origBitmap;
   splash = transpGroupStack->origSplash;
@@ -3896,7 +3901,7 @@ void SplashOutputDev::setFreeTypeHinting(GBool enable, GBool enableSlightHinting
   enableSlightHinting = enableSlightHintingA;
 }
 
-GBool SplashOutputDev::tilingPatternFill(GfxState *state, Catalog *catalog, Object *str,
+GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *catalog, Object *str,
 					double *ptm, int paintType, int /*tilingType*/, Dict *resDict,
 					double *mat, double *bbox,
 					int x0, int y0, int x1, int y1,
@@ -4056,7 +4061,6 @@ GBool SplashOutputDev::tilingPatternFill(GfxState *state, Catalog *catalog, Obje
   matc[1] = ctm[1];
   matc[2] = ctm[2];
   matc[3] = ctm[3];
-  splash->setOverprintMask(0xffffffff);
   splash->drawImage(&tilingBitmapSrc, &imgData, colorMode, gTrue, result_width, result_height, matc);
   delete tBitmap;
   delete gfx;
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index c06359a..8975c65 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -273,7 +273,7 @@ public:
   virtual void stroke(GfxState *state);
   virtual void fill(GfxState *state);
   virtual void eoFill(GfxState *state);
-  virtual GBool tilingPatternFill(GfxState *state, Catalog *catalog, Object *str,
+  virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
@@ -441,6 +441,7 @@ private:
   SplashTransparencyGroup *	// transparency group stack
     transpGroupStack;
   SplashBitmap *maskBitmap; // for image masks in pattern colorspace
+  int nestCount;
 };
 
 #endif
diff --git a/splash/Splash.cc b/splash/Splash.cc
index 7c65f77..a188f34 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -2636,7 +2636,7 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
       blitMask(scaledMask, x0, y0, clipRes);
       delete scaledMask;
     }
-    
+
   // scaling plus vertical flip
   } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
     x0 = imgCoordMungeLowerC(mat[4], glyphMode);
@@ -3412,7 +3412,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
       blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
       delete scaledImg;
     }
-    
+
   // scaling plus vertical flip
   } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
     x0 = imgCoordMungeLower(mat[4]);
@@ -3467,7 +3467,7 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData
   SplashClipResult clipRes, clipRes2;
   SplashPipe pipe;
   SplashColor pixel;
-  int scaledWidth, scaledHeight, t0, t1;
+  int scaledWidth, scaledHeight, t0, t1, th;
   SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
   SplashCoord vx[4], vy[4];
   int xMin, yMin, xMax, yMax;
@@ -3524,13 +3524,29 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData
   scaledWidth = t0 > t1 ? t0 : t1;
   if (mat[2] >= 0) {
     t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
+    if (splashAbs(mat[1]) >= 1) {
+      th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]);
+	  if (th > t0) t0 = th;
+    }
   } else {
     t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
+    if (splashAbs(mat[1]) >= 1) {
+      th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]);
+      if (th > t0) t0 = th;
+    }
   }
   if (mat[3] >= 0) {
     t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
+    if (splashAbs(mat[0]) >= 1) {
+      th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]);
+	  if (th > t1) t1 = th;
+    }
   } else {
     t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
+    if (splashAbs(mat[0]) >= 1) {
+      th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]);
+	  if (th > t1) t1 = th;
+    }
   }
   scaledHeight = t0 > t1 ? t0 : t1;
   if (scaledWidth == 0) {
@@ -3901,7 +3917,7 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
 	*destPtr++ = (Guchar)pix2;
 	*destPtr++ = (Guchar)pix1;
 	*destPtr++ = (Guchar)pix0;
-	*destPtr++ = (Guchar)255;	
+	*destPtr++ = (Guchar)255;
 	break;
 
       case splashModeBGR8:
@@ -4081,7 +4097,7 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
 	  *destPtr++ = (Guchar)pix[2];
 	  *destPtr++ = (Guchar)pix[1];
 	  *destPtr++ = (Guchar)pix[0];
-	  *destPtr++ = (Guchar)255;	  
+	  *destPtr++ = (Guchar)255;
 	}
 	break;
       case splashModeBGR8:
@@ -4222,7 +4238,7 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
 	  *destPtr++ = (Guchar)pix[2];
 	  *destPtr++ = (Guchar)pix[1];
 	  *destPtr++ = (Guchar)pix[0];
-	  *destPtr++ = (Guchar)255;	  
+	  *destPtr++ = (Guchar)255;
 	}
 	break;
       case splashModeBGR8:
@@ -4365,7 +4381,7 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
 	    *destPtr++ = (Guchar)pix[2];
 	    *destPtr++ = (Guchar)pix[1];
 	    *destPtr++ = (Guchar)pix[0];
-	    *destPtr++ = (Guchar)255;	    
+	    *destPtr++ = (Guchar)255;
 	  }
 	}
 	break;
@@ -5243,7 +5259,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
 
 SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
 				    GBool flatten) {
-  SplashPath *pathIn, *dashPath, *pathOut;
+SplashPath *pathIn, *dashPath, *pathOut;
   SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
   SplashCoord crossprod, dotprod, miter, m;
   GBool first, last, closed;
diff --git a/utils/ImageOutputDev.cc b/utils/ImageOutputDev.cc
index b35869f..dd855bb 100644
--- a/utils/ImageOutputDev.cc
+++ b/utils/ImageOutputDev.cc
@@ -70,7 +70,7 @@ void ImageOutputDev::setFilename(const char *fileExt) {
   }
 }
 
-GBool ImageOutputDev::tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+GBool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
diff --git a/utils/ImageOutputDev.h b/utils/ImageOutputDev.h
index 1e43308..fabe6b5 100644
--- a/utils/ImageOutputDev.h
+++ b/utils/ImageOutputDev.h
@@ -84,7 +84,7 @@ public:
   virtual GBool useDrawChar() { return gFalse; }
 
   //----- path painting
-  virtual GBool tilingPatternFill(GfxState *state, Catalog *cat, Object *str,
+  virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str,
 				  double *pmat, int paintType, int tilingType, Dict *resDict,
 				  double *mat, double *bbox,
 				  int x0, int y0, int x1, int y1,
commit bf75a957650dd5208ecf1f6db1555a3d00b7949c
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Jan 10 23:31:27 2012 +0100

    [xpdf303] Splash::blitTransparent merges from Thomas

diff --git a/splash/Splash.cc b/splash/Splash.cc
index 0ef97ed..7c65f77 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -5151,7 +5151,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
 				    int xDest, int yDest, int w, int h) {
   SplashColorPtr p, sp;
   Guchar *q;
-  int x, y, mask;
+  int x, y, mask, srcMask;
 
   if (src->mode != bitmap->mode) {
     return splashErrModeMismatch;
@@ -5161,10 +5161,11 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
   case splashModeMono1:
     for (y = 0; y < h; ++y) {
       p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
-      sp = &src->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)];
       mask = 0x80 >> (xDest & 7);
+      sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
+      srcMask = 0x80 >> (xSrc & 7);
       for (x = 0; x < w; ++x) {
-	if (sp[0] & (0x80 >> (x & 7))) {
+	if (*sp & srcMask) {
 	  *p |= mask;
 	} else {
 	  *p &= ~mask;
@@ -5172,6 +5173,9 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
 	if (!(mask >>= 1)) {
 	  mask = 0x80;
 	  ++p;
+	}
+	if (!(srcMask >>= 1)) {
+	  srcMask = 0x80;
 	  ++sp;
 	}
       }
commit 296244ab74e56b2781daae0a664617d1da30527c
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Jan 10 23:30:34 2012 +0100

    [xpdf303] Merge xpath Splash stuff from Thomas

diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc
index c3cb62c..1d6119c 100644
--- a/splash/SplashXPath.cc
+++ b/splash/SplashXPath.cc
@@ -71,8 +71,7 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
   SplashXPathPoint *pts;
   SplashXPathAdjust *adjusts, *adjust;
   SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp;
-  SplashCoord adj0, adj1, w;
-  int ww;
+  SplashCoord adj0, adj1;
   int curSubpath, i, j;
 
   // transform the points
@@ -114,19 +113,24 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
 	adj0 = adj1;
 	adj1 = x0;
       }
-      w = adj1 - adj0;
-      ww = splashRound(w);
-      if (ww == 0) {
-	ww = 1;
-      }
       adjusts[i].x0a = adj0 - 0.01;
       adjusts[i].x0b = adj0 + 0.01;
       adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01;
       adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01;
       adjusts[i].x1a = adj1 - 0.01;
       adjusts[i].x1b = adj1 + 0.01;
-      adjusts[i].x0 = (SplashCoord)splashRound(adj0);
-      adjusts[i].x1 = adjusts[i].x0 + ww - 0.01;
+      // rounding both edge coordinates can result in lines of
+      // different widths (e.g., adj=10.1, adj1=11.3 --> x0=10, x1=11;
+      // adj0=10.4, adj1=11.6 --> x0=10, x1=12), but it has the
+      // benefit of making adjacent strokes/fills line up without any
+      // gaps between them
+      x0 = splashRound(adj0);
+      x1 = splashRound(adj1);
+      if (x1 == x0) {
+	x1 = x1 + 1;
+      }
+      adjusts[i].x0 = (SplashCoord)x0;
+      adjusts[i].x1 = (SplashCoord)x1 - 0.01;
       adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
       adjusts[i].firstPt = hint->firstPt;
       adjusts[i].lastPt = hint->lastPt;
commit e1ae7b900b01db7c7703da68ad94aa9bda1938f6
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Jan 10 23:29:38 2012 +0100

    [xpdf303] Merge splash font stuff from Thomas

diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc
index 5757acc..a6c6ace 100644
--- a/poppler/GlobalParams.cc
+++ b/poppler/GlobalParams.cc
@@ -685,6 +685,7 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir)
   textKeepTinyChars = gFalse;
   fontDirs = new GooList();
   enableFreeType = gTrue;
+  disableFreeTypeHinting = gFalse;
   antialias = gTrue;
   vectorAntialias = gTrue;
   strokeAdjust = gTrue;
@@ -1509,6 +1510,14 @@ GBool GlobalParams::getEnableFreeType() {
   return f;
 }
 
+GBool GlobalParams::getDisableFreeTypeHinting() {
+  GBool f;
+
+  lockGlobalParams;
+  f = disableFreeTypeHinting;
+  unlockGlobalParams;
+  return f;
+}
 
 GBool GlobalParams::getAntialias() {
   GBool f;
@@ -1855,6 +1864,14 @@ GBool GlobalParams::setEnableFreeType(char *s) {
   return ok;
 }
 
+GBool GlobalParams::setDisableFreeTypeHinting(char *s) {
+  GBool ok;
+
+  lockGlobalParams;
+  ok = parseYesNo2(s, &disableFreeTypeHinting);
+  unlockGlobalParams;
+  return ok;
+}
 
 GBool GlobalParams::setAntialias(char *s) {
   GBool ok;
diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h
index f4cb6c1..57b18f1 100644
--- a/poppler/GlobalParams.h
+++ b/poppler/GlobalParams.h
@@ -204,6 +204,7 @@ public:
   GBool getTextKeepTinyChars();
   GooString *findFontFile(GooString *fontName, const char **exts);
   GBool getEnableFreeType();
+  GBool getDisableFreeTypeHinting();
   GBool getAntialias();
   GBool getVectorAntialias();
   GBool getStrokeAdjust();
@@ -251,6 +252,7 @@ public:
   void setTextPageBreaks(GBool pageBreaks);
   void setTextKeepTinyChars(GBool keep);
   GBool setEnableFreeType(char *s);
+  GBool setDisableFreeTypeHinting(char *s);
   GBool setAntialias(char *s);
   GBool setVectorAntialias(char *s);
   void setStrokeAdjust(GBool strokeAdjust);
@@ -338,6 +340,7 @@ private:
   GBool textKeepTinyChars;	// keep all characters in text output
   GooList *fontDirs;		// list of font dirs [GooString]
   GBool enableFreeType;		// FreeType enable flag
+  GBool disableFreeTypeHinting;	// FreeType disable hinting flag
   GBool antialias;		// anti-aliasing enable flag
   GBool vectorAntialias;	// vector anti-aliasing enable flag
   GBool strokeAdjust;		// stroke adjustment enable flag
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index 52e2bef..020808e 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -1330,6 +1330,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
   vectorAntialias = allowAntialias &&
 		      globalParams->getVectorAntialias() &&
 		      colorMode != splashModeMono1;
+  enableAutoHinting = !globalParams->getDisableFreeTypeHinting();
   enableFreeTypeHinting = gFalse;
   enableSlightHinting = gFalse;
   setupScreenParams(72.0, 72.0);
@@ -1441,6 +1442,7 @@ void SplashOutputDev::startDoc(PDFDoc *docA) {
 #endif
 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
 				    globalParams->getEnableFreeType(),
+				    enableAutoHinting,
 				    enableFreeTypeHinting,
 				    enableSlightHinting,
 #endif
@@ -3889,6 +3891,7 @@ void SplashOutputDev::setVectorAntialias(GBool vaa) {
 
 void SplashOutputDev::setFreeTypeHinting(GBool enable, GBool enableSlightHintingA)
 {
+  enableAutoHinting = gFalse;
   enableFreeTypeHinting = enable;
   enableSlightHinting = enableSlightHintingA;
 }
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index 8cfdbb1..c06359a 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -416,6 +416,7 @@ private:
   GBool bitmapTopDown;
   GBool allowAntialias;
   GBool vectorAntialias;
+  GBool enableAutoHinting;
   GBool enableFreeTypeHinting;
   GBool enableSlightHinting;
   GBool reverseVideo;		// reverse video mode
diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc
index 9b5958c..b511c96 100644
--- a/splash/SplashFTFont.cc
+++ b/splash/SplashFTFont.cc
@@ -58,6 +58,7 @@ static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
 SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
 			   SplashCoord *textMatA):
   SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa), 
+  enableAutoHinting(fontFileA->engine->enableAutoHinting),
   enableFreeTypeHinting(fontFileA->engine->enableFreeTypeHinting),
   enableSlightHinting(fontFileA->engine->enableSlightHinting)
 {
@@ -232,13 +233,28 @@ GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
   return SplashFont::getGlyph(c, xFrac, 0, bitmap, x0, y0, clip, clipRes);
 }
 
-static FT_Int32 getFTLoadFlags(GBool aa, GBool enableFreeTypeHinting, GBool enableSlightHinting)
+static FT_Int32 getFTLoadFlags(GBool type1, GBool trueType, GBool aa, 
+							   GBool enableAutoHinting, GBool enableFreeTypeHinting, GBool enableSlightHinting)
 {
   int ret = FT_LOAD_DEFAULT;
   if (aa)
     ret |= FT_LOAD_NO_BITMAP;
   
-  if (enableFreeTypeHinting) {
+  if (enableAutoHinting) {
+    if (trueType) {
+	  // FT2's autohinting doesn't always work very well (especially with
+      // font subsets), so turn it off if anti-aliasing is enabled; if
+      // anti-aliasing is disabled, this seems to be a tossup - some fonts
+      // look better with hinting, some without, so leave hinting on
+      if (aa) {
+        ret |= FT_LOAD_NO_AUTOHINT;
+      }
+    } else if (type1) {
+      // Type 1 fonts seem to look better with 'light' hinting mode
+      ret |= FT_LOAD_TARGET_LIGHT;
+    }
+
+  } else if (enableFreeTypeHinting) {
     if (enableSlightHinting)
       ret |= FT_LOAD_TARGET_LIGHT;
   } else {
@@ -271,7 +287,7 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
     gid = (FT_UInt)c;
   }
 
-  if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(aa, enableFreeTypeHinting, enableSlightHinting))) {
+  if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting))) {
     return gFalse;
   }
 
@@ -298,6 +314,12 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
     return gFalse;
   }
 
+  if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) {
+    // this can happen if (a) the glyph is really tiny or (b) the
+    // metrics in the TrueType file are broken
+    return gFalse;
+  }
+
   bitmap->x = -slot->bitmap_left;
   bitmap->y = slot->bitmap_top;
   bitmap->w = slot->bitmap.width;
@@ -354,7 +376,7 @@ double SplashFTFont::getGlyphAdvance(int c)
     return -1;
   }
 
-  if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(aa, enableFreeTypeHinting, enableSlightHinting))) {
+  if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting))) {
     return -1;
   }
 
@@ -398,11 +420,11 @@ SplashPath *SplashFTFont::getGlyphPath(int c) {
   } else {
     gid = (FT_UInt)c;
   }
-  if (ff->trueType && gid == 0) {
+  if (ff->trueType && gid < 0) {
     // skip the TrueType notdef glyph
     return NULL;
   }
-  if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(aa, enableFreeTypeHinting, enableSlightHinting))) {
+  if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting))) {
     return NULL;
   }
   if (FT_Get_Glyph(slot, &glyph)) {
diff --git a/splash/SplashFTFont.h b/splash/SplashFTFont.h
index f49d7b1..1745807 100644
--- a/splash/SplashFTFont.h
+++ b/splash/SplashFTFont.h
@@ -71,6 +71,7 @@ private:
   FT_Matrix textMatrix;
   SplashCoord textScale;
   int size;
+  GBool enableAutoHinting;
   GBool enableFreeTypeHinting;
   GBool enableSlightHinting;
 };
diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc
index 7a4568d..dee5728 100644
--- a/splash/SplashFTFontEngine.cc
+++ b/splash/SplashFTFontEngine.cc
@@ -59,11 +59,12 @@ static void fileWrite(void *stream, const char *data, int len) {
 // SplashFTFontEngine
 //------------------------------------------------------------------------
 
-SplashFTFontEngine::SplashFTFontEngine(GBool aaA, GBool enableFreeTypeHintingA,
+SplashFTFontEngine::SplashFTFontEngine(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA,
 				       GBool enableSlightHintingA, FT_Library libA) {
   FT_Int major, minor, patch;
 
   aa = aaA;
+  enableAutoHinting = enableAutoHintingA;
   enableFreeTypeHinting = enableFreeTypeHintingA;
   enableSlightHinting = enableSlightHintingA;
   lib = libA;
@@ -74,14 +75,14 @@ SplashFTFontEngine::SplashFTFontEngine(GBool aaA, GBool enableFreeTypeHintingA,
             (major == 2 && (minor > 1 || (minor == 1 && patch > 7)));
 }
 
-SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA, GBool enableFreeTypeHintingA,
+SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA,
 					     GBool enableSlightHintingA) {
   FT_Library libA;
 
   if (FT_Init_FreeType(&libA)) {
     return NULL;
   }
-  return new SplashFTFontEngine(aaA, enableFreeTypeHintingA, enableSlightHintingA, libA);
+  return new SplashFTFontEngine(aaA, enableAutoHintingA, enableFreeTypeHintingA, enableSlightHintingA, libA);
 }
 
 SplashFTFontEngine::~SplashFTFontEngine() {
diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h
index aa1ad5f..d236e4f 100644
--- a/splash/SplashFTFontEngine.h
+++ b/splash/SplashFTFontEngine.h
@@ -45,7 +45,7 @@ class SplashFontSrc;
 class SplashFTFontEngine {
 public:
 
-  static SplashFTFontEngine *init(GBool aaA, GBool enableFreeTypeHintingA, GBool enableSlightHinting);
+  static SplashFTFontEngine *init(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA, GBool enableSlightHinting);
 
   ~SplashFTFontEngine();
 
@@ -61,9 +61,10 @@ public:
 
 private:
 
-  SplashFTFontEngine(GBool aaA, GBool enableFreeTypeHintingA, GBool enableSlightHintingA, FT_Library libA);
+  SplashFTFontEngine(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA, GBool enableSlightHintingA, FT_Library libA);
 
   GBool aa;
+  GBool enableAutoHinting;
   GBool enableFreeTypeHinting;
   GBool enableSlightHinting;
   FT_Library lib;
diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc
index 2e74f5a..ab9beb9 100644
--- a/splash/SplashFontEngine.cc
+++ b/splash/SplashFontEngine.cc
@@ -69,6 +69,7 @@ SplashFontEngine::SplashFontEngine(
 #endif
 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
 				   GBool enableFreeType,
+				   GBool enableAutoHinting,
 				   GBool enableFreeTypeHinting,
 				   GBool enableSlightHinting,
 #endif
@@ -88,7 +89,7 @@ SplashFontEngine::SplashFontEngine(
 #endif
 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
   if (enableFreeType) {
-    ftEngine = SplashFTFontEngine::init(aa, enableFreeTypeHinting, enableSlightHinting);
+    ftEngine = SplashFTFontEngine::init(aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting);
   } else {
     ftEngine = NULL;
   }
diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h
index 54926b4..f0340a5 100644
--- a/splash/SplashFontEngine.h
+++ b/splash/SplashFontEngine.h
@@ -58,6 +58,7 @@ public:
 #endif
 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
 		   GBool enableFreeType,
+		   GBool enabbleAutoHinting,
 		   GBool enableFreeTypeHinting,
 		   GBool enableSlightHinting,
 #endif
commit d46b673c46a72132fb3918b64733be552e35952f
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Jan 10 23:28:43 2012 +0100

    [xpdf303] More merges from Thomas

diff --git a/splash/Splash.cc b/splash/Splash.cc
index 5091edb..0ef97ed 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -71,6 +71,47 @@ static inline Guchar clip255(int x) {
 template<typename T>
 inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; }
 
+// The PDF spec says that all pixels whose *centers* lie within the
+// image target region get painted, so we want to round n+0.5 down to
+// n.  But this causes problems, e.g., with PDF files that fill a
+// rectangle with black and then draw an image to the exact same
+// rectangle, so we instead use the fill scan conversion rule.
+// However, the correct rule works better for glyphs, so we also
+// provide that option in fillImageMask.
+#if 0
+static inline int imgCoordMungeLower(SplashCoord x) {
+  return splashCeil(x + 0.5) - 1;
+}
+static inline int imgCoordMungeUpper(SplashCoord x) {
+  return splashCeil(x + 0.5) - 1;
+}
+#else
+static inline int imgCoordMungeLower(SplashCoord x) {
+  return splashFloor(x);
+}
+static inline int imgCoordMungeUpper(SplashCoord x) {
+  return splashFloor(x) + 1;
+}
+static inline int imgCoordMungeLowerC(SplashCoord x, GBool glyphMode) {
+  return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x);
+}
+static inline int imgCoordMungeUpperC(SplashCoord x, GBool glyphMode) {
+  return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1);
+}
+#endif
+
+// Used by drawImage and fillImageMask to divide the target
+// quadrilateral into sections.
+struct ImageSection {
+  int y0, y1;				// actual y range
+  int ia0, ia1;				// vertex indices for edge A
+  int ib0, ib1;				// vertex indices for edge A
+  SplashCoord xa0, ya0, xa1, ya1;	// edge A
+  SplashCoord dxdya;			// slope of edge A
+  SplashCoord xb0, yb0, xb1, yb1;	// edge B
+  SplashCoord dxdyb;			// slope of edge B
+};
+
 //------------------------------------------------------------------------
 // SplashPipe
 //------------------------------------------------------------------------
@@ -2416,7 +2457,8 @@ void Splash::fillGlyph(SplashCoord x, SplashCoord y,
 
 void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
   SplashPipe pipe;
-  int alpha0, alpha;
+  int alpha0;
+  Guchar alpha;
   Guchar *p;
   int x1, y1, xx, xx1, yy;
 
@@ -2547,22 +2589,11 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip)
 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
 				  int w, int h, SplashCoord *mat,
 				  GBool glyphMode) {
-  SplashPipe pipe;
-  GBool rot;
-  SplashCoord xScale, yScale, xShear, yShear, yShear1;
-  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
-  int ulx, uly, llx, lly, urx, ury, lrx, lry;
-  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
-  int xMin, xMax, yMin, yMax;
-  SplashClipResult clipRes, clipRes2;
-  int yp, yq, yt, yStep, lastYStep;
-  int xp, xq, xt, xStep, xSrc;
-  int k1, spanXMin, spanXMax, spanY;
-  SplashColorPtr pixBuf, p;
-  int pixAcc;
-  int x, y, x1, x2, y2;
-  SplashCoord y1;
-  int n, m, i, j;
+  SplashBitmap *scaledMask;
+  SplashClipResult clipRes;
+  GBool minorAxisZero;
+  int x0, y0, x1, y1, scaledWidth, scaledHeight;
+  int yp;
 
   if (debugMode) {
     printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
@@ -2573,289 +2604,738 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
   if (w == 0 && h == 0) return splashErrZeroImage;
 
   // check for singular matrix
-  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
+  if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
     return splashErrSingularMatrix;
   }
 
-  // compute scale, shear, rotation, translation parameters
-  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
-  if (rot) {
-    xScale = -mat[1];
-    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
-    xShear = -mat[3] / yScale;
-    yShear = -mat[0] / mat[1];
-  } else {
-    xScale = mat[0];
-    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
-    xShear = mat[2] / yScale;
-    yShear = mat[1] / mat[0];
-  }
-  // Note 1: The PDF spec says that all pixels whose *centers* lie
-  // within the region get painted -- but that doesn't seem to match
-  // up with what Acrobat actually does: it ends up leaving gaps
-  // between image stripes.  So we use the same rule here as for
-  // fills: any pixel that overlaps the region gets painted.
-  // Note 2: The "glyphMode" flag is a kludge: it switches back to
-  // "correct" behavior (matching the spec), for use in rendering Type
-  // 3 fonts.
-  // Note 3: The +/-0.01 in these computations is to avoid floating
-  // point precision problems which can lead to gaps between image
-  // stripes (it can cause image stripes to overlap, but that's a much
-  // less visible problem).
-  if (glyphMode) {
-    if (xScale >= 0) {
-      tx = splashRound(mat[4]);
-      tx2 = splashRound(mat[4] + xScale) - 1;
-    } else {
-      tx = splashRound(mat[4]) - 1;
-      tx2 = splashRound(mat[4] + xScale);
+  minorAxisZero = mat[1] == 0 && mat[2] == 0;
+
+  // scaling only
+  if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
+    x0 = imgCoordMungeLowerC(mat[4], glyphMode);
+    y0 = imgCoordMungeLowerC(mat[5], glyphMode);
+    x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
+    y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode);
+    // make sure narrow images cover at least one pixel
+    if (x0 == x1) {
+      ++x1;
     }
-  } else {
-    if (xScale >= 0) {
-      tx = splashFloor(mat[4] - 0.01);
-      tx2 = splashFloor(mat[4] + xScale + 0.01);
-    } else {
-      tx = splashFloor(mat[4] + 0.01);
-      tx2 = splashFloor(mat[4] + xScale - 0.01);
+    if (y0 == y1) {
+      ++y1;
     }
-  }
-  scaledWidth = abs(tx2 - tx) + 1;
-  if (glyphMode) {
-    if (yScale >= 0) {
-      ty = splashRound(mat[5]);
-      ty2 = splashRound(mat[5] + yScale) - 1;
-    } else {
-      ty = splashRound(mat[5]) - 1;
-      ty2 = splashRound(mat[5] + yScale);
+    clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+    opClipRes = clipRes;
+    if (clipRes != splashClipAllOutside) {
+      scaledWidth = x1 - x0;
+      scaledHeight = y1 - y0;
+      yp = h / scaledHeight;
+      if (yp < 0 || yp > INT_MAX - 1) {
+        return splashErrBadArg;
+      }
+      scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
+      blitMask(scaledMask, x0, y0, clipRes);
+      delete scaledMask;
     }
-  } else {
-    if (yScale >= 0) {
-      ty = splashFloor(mat[5] - 0.01);
-      ty2 = splashFloor(mat[5] + yScale + 0.01);
-    } else {
-      ty = splashFloor(mat[5] + 0.01);
-      ty2 = splashFloor(mat[5] + yScale - 0.01);
+    
+  // scaling plus vertical flip
+  } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
+    x0 = imgCoordMungeLowerC(mat[4], glyphMode);
+    y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
+    x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
+    y1 = imgCoordMungeUpperC(mat[5], glyphMode);
+    // make sure narrow images cover at least one pixel
+    if (x0 == x1) {
+      ++x1;
+    }
+    if (y0 == y1) {
+      ++y1;
     }
+    clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+    opClipRes = clipRes;
+    if (clipRes != splashClipAllOutside) {
+      scaledWidth = x1 - x0;
+      scaledHeight = y1 - y0;
+      yp = h / scaledHeight;
+      if (yp < 0 || yp > INT_MAX - 1) {
+        return splashErrBadArg;
+      }
+      scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
+      vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
+      blitMask(scaledMask, x0, y0, clipRes);
+      delete scaledMask;
+    }
+
+  // all other cases
+  } else {
+    arbitraryTransformMask(src, srcData, w, h, mat, glyphMode);
   }
-  scaledHeight = abs(ty2 - ty) + 1;
-  xSign = (xScale < 0) ? -1 : 1;
-  ySign = (yScale < 0) ? -1 : 1;
-  yShear1 = (SplashCoord)xSign * yShear;
+
+  return splashOk;
+}
+
+void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
+				    int srcWidth, int srcHeight,
+				    SplashCoord *mat, GBool glyphMode) {
+  SplashBitmap *scaledMask;
+  SplashClipResult clipRes, clipRes2;
+  SplashPipe pipe;
+  int scaledWidth, scaledHeight, t0, t1;
+  SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
+  SplashCoord vx[4], vy[4];
+  int xMin, yMin, xMax, yMax;
+  ImageSection section[3];
+  int nSections;
+  int y, xa, xb, x, i, xx, yy;
+
+  // compute the four vertices of the target quadrilateral
+  vx[0] = mat[4];                    vy[0] = mat[5];
+  vx[1] = mat[2] + mat[4];           vy[1] = mat[3] + mat[5];
+  vx[2] = mat[0] + mat[2] + mat[4];  vy[2] = mat[1] + mat[3] + mat[5];
+  vx[3] = mat[0] + mat[4];           vy[3] = mat[1] + mat[5];
 
   // clipping
-  ulx1 = 0;
-  uly1 = 0;
-  urx1 = xSign * (scaledWidth - 1);
-  ury1 = (int)(yShear * urx1);
-  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
-  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
-  lrx1 = xSign * (scaledWidth - 1) +
-           splashRound(xShear * ySign * (scaledHeight - 1));
-  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
-  if (rot) {
-    ulx = tx + uly1;    uly = ty - ulx1;
-    urx = tx + ury1;    ury = ty - urx1;
-    llx = tx + lly1;    lly = ty - llx1;
-    lrx = tx + lry1;    lry = ty - lrx1;
-  } else {
-    ulx = tx + ulx1;    uly = ty + uly1;
-    urx = tx + urx1;    ury = ty + ury1;
-    llx = tx + llx1;    lly = ty + lly1;
-    lrx = tx + lrx1;    lry = ty + lry1;
-  }
-  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
-                                   : (llx < lrx) ? llx : lrx
-		     : (urx < llx) ? (urx < lrx) ? urx : lrx
-                                   : (llx < lrx) ? llx : lrx;
-  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
-                                   : (llx > lrx) ? llx : lrx
-		     : (urx > llx) ? (urx > lrx) ? urx : lrx
-                                   : (llx > lrx) ? llx : lrx;
-  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
-                                   : (lly < lry) ? lly : lry
-		     : (ury < lly) ? (ury < lry) ? ury : lry
-                                   : (lly < lry) ? lly : lry;
-  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
-                                   : (lly > lry) ? lly : lry
-		     : (ury > lly) ? (ury > lry) ? ury : lry
-                                   : (lly > lry) ? lly : lry;
-  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
+  xMin = imgCoordMungeLowerC(vx[0], glyphMode);
+  xMax = imgCoordMungeUpperC(vx[0], glyphMode);
+  yMin = imgCoordMungeLowerC(vy[0], glyphMode);
+  yMax = imgCoordMungeUpperC(vy[0], glyphMode);
+  for (i = 1; i < 4; ++i) {
+    t0 = imgCoordMungeLowerC(vx[i], glyphMode);
+    if (t0 < xMin) {
+      xMin = t0;
+    }
+    t0 = imgCoordMungeUpperC(vx[i], glyphMode);
+    if (t0 > xMax) {
+      xMax = t0;
+    }
+    t1 = imgCoordMungeLowerC(vy[i], glyphMode);
+    if (t1 < yMin) {
+      yMin = t1;
+    }
+    t1 = imgCoordMungeUpperC(vy[i], glyphMode);
+    if (t1 > yMax) {
+      yMax = t1;
+    }
+  }
+  clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
   opClipRes = clipRes;
+  if (clipRes == splashClipAllOutside) {
+    return;
+  }
 
-  // compute Bresenham parameters for x and y scaling
-  yp = h / scaledHeight;
-  yq = h % scaledHeight;
-  xp = w / scaledWidth;
-  xq = w % scaledWidth;
+  // compute the scale factors
+  if (mat[0] >= 0) {
+    t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) -
+         imgCoordMungeLowerC(mat[4], glyphMode);
+  } else {
+    t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
+         imgCoordMungeLowerC(mat[0] + mat[4], glyphMode);
+  }
+  if (mat[1] >= 0) {
+    t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) -
+         imgCoordMungeLowerC(mat[5], glyphMode);
+  } else {
+    t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
+         imgCoordMungeLowerC(mat[1] + mat[5], glyphMode);
+  }
+  scaledWidth = t0 > t1 ? t0 : t1;
+  if (mat[2] >= 0) {
+    t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) -
+         imgCoordMungeLowerC(mat[4], glyphMode);
+  } else {
+    t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
+         imgCoordMungeLowerC(mat[2] + mat[4], glyphMode);
+  }
+  if (mat[3] >= 0) {
+    t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) -
+         imgCoordMungeLowerC(mat[5], glyphMode);
+  } else {
+    t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
+         imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
+  }
+  scaledHeight = t0 > t1 ? t0 : t1;
+  if (scaledWidth == 0) {
+    scaledWidth = 1;
+  }
+  if (scaledHeight == 0) {
+    scaledHeight = 1;
+  }
 
-  // allocate pixel buffer
-  if (yp < 0 || yp > INT_MAX - 1) {
-    return splashErrBadArg;
+  // compute the inverse transform (after scaling) matrix
+  r00 = mat[0] / scaledWidth;
+  r01 = mat[1] / scaledWidth;
+  r10 = mat[2] / scaledHeight;
+  r11 = mat[3] / scaledHeight;
+  det = r00 * r11 - r01 * r10;
+  if (splashAbs(det) < 1e-6) {
+    // this should be caught by the singular matrix check in fillImageMask
+    return;
+  }
+  ir00 = r11 / det;
+  ir01 = -r01 / det;
+  ir10 = -r10 / det;
+  ir11 = r00 / det;
+
+  // scale the input image
+  scaledMask = scaleMask(src, srcData, srcWidth, srcHeight,
+			 scaledWidth, scaledHeight);
+
+  // construct the three sections
+  i = (vy[2] <= vy[3]) ? 2 : 3;
+  if (vy[1] <= vy[i]) {
+    i = 1;
+  }
+  if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) {
+    i = 0;
+  }
+  if (vy[i] == vy[(i+1) & 3]) {
+    section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
+    section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
+    if (vx[i] < vx[(i+1) & 3]) {
+      section[0].ia0 = i;
+      section[0].ia1 = (i+3) & 3;
+      section[0].ib0 = (i+1) & 3;
+      section[0].ib1 = (i+2) & 3;
+    } else {
+      section[0].ia0 = (i+1) & 3;
+      section[0].ia1 = (i+2) & 3;
+      section[0].ib0 = i;
+      section[0].ib1 = (i+3) & 3;
+    }
+    nSections = 1;
+  } else {
+    section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
+    section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
+    section[0].ia0 = section[0].ib0 = i;
+    section[2].ia1 = section[2].ib1 = (i+2) & 3;
+    if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
+      section[0].ia1 = section[2].ia0 = (i+1) & 3;
+      section[0].ib1 = section[2].ib0 = (i+3) & 3;
+    } else {
+      section[0].ia1 = section[2].ia0 = (i+3) & 3;
+      section[0].ib1 = section[2].ib0 = (i+1) & 3;
+    }
+    if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
+      section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode);
+      section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode);
+      if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
+	section[1].ia0 = (i+1) & 3;
+	section[1].ia1 = (i+2) & 3;
+	section[1].ib0 = i;
+	section[1].ib1 = (i+3) & 3;
+      } else {
+	section[1].ia0 = i;
+	section[1].ia1 = (i+3) & 3;
+	section[1].ib0 = (i+1) & 3;
+	section[1].ib1 = (i+2) & 3;
+      }
+    } else {
+      section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode);
+      section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode);
+      if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
+	section[1].ia0 = i;
+	section[1].ia1 = (i+1) & 3;
+	section[1].ib0 = (i+3) & 3;
+	section[1].ib1 = (i+2) & 3;
+      } else {
+	section[1].ia0 = (i+3) & 3;
+	section[1].ia1 = (i+2) & 3;
+	section[1].ib0 = i;
+	section[1].ib1 = (i+1) & 3;
+      }
+    }
+    section[0].y1 = section[1].y0 - 1;
+    section[1].y1 = section[2].y0 - 1;
+    nSections = 3;
+  }
+  for (i = 0; i < nSections; ++i) {
+    section[i].xa0 = vx[section[i].ia0];
+    section[i].ya0 = vy[section[i].ia0];
+    section[i].xa1 = vx[section[i].ia1];
+    section[i].ya1 = vy[section[i].ia1];
+    section[i].xb0 = vx[section[i].ib0];
+    section[i].yb0 = vy[section[i].ib0];
+    section[i].xb1 = vx[section[i].ib1];
+    section[i].yb1 = vy[section[i].ib1];
+    section[i].dxdya = (section[i].xa1 - section[i].xa0) /
+                       (section[i].ya1 - section[i].ya0);
+    section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
+                       (section[i].yb1 - section[i].yb0);
   }
-  pixBuf = (SplashColorPtr)gmallocn((yp + 1), w);
 
   // initialize the pixel pipe
-  pipeInit(&pipe, 0, 0, state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255),
-	   gTrue, gFalse);
+  pipeInit(&pipe, 0, 0, state->fillPattern, NULL,
+	   (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
   if (vectorAntialias) {
     drawAAPixelInit();
   }
 
+  // make sure narrow images cover at least one pixel
+  if (nSections == 1) {
+    if (section[0].y0 == section[0].y1) {
+      ++section[0].y1;
+      clipRes = opClipRes = splashClipPartial;
+    }
+  } else {
+    if (section[0].y0 == section[2].y1) {
+      ++section[1].y1;
+      clipRes = opClipRes = splashClipPartial;
+    }
+  }
+
+  // scan all pixels inside the target region
+  for (i = 0; i < nSections; ++i) {
+    for (y = section[i].y0; y <= section[i].y1; ++y) {
+      xa = imgCoordMungeLowerC(section[i].xa0 +
+			         ((SplashCoord)y + 0.5 - section[i].ya0) *
+			           section[i].dxdya,
+			       glyphMode);
+      xb = imgCoordMungeUpperC(section[i].xb0 +
+			         ((SplashCoord)y + 0.5 - section[i].yb0) *
+			           section[i].dxdyb,
+			       glyphMode);
+      // make sure narrow images cover at least one pixel
+      if (xa == xb) {
+	++xb;
+      }
+      if (clipRes != splashClipAllInside) {
+	clipRes2 = state->clip->testSpan(xa, xb - 1, y);
+      } else {
+	clipRes2 = clipRes;
+      }
+      for (x = xa; x < xb; ++x) {
+	// map (x+0.5, y+0.5) back to the scaled image
+	xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
+			 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
+	yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
+			 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
+	// xx should always be within bounds, but floating point
+	// inaccuracy can cause problems
+	if (xx < 0) {
+	  xx = 0;
+	} else if (xx >= scaledWidth) {
+	  xx = scaledWidth - 1;
+	}
+	if (yy < 0) {
+	  yy = 0;
+	} else if (yy >= scaledHeight) {
+	  yy = scaledHeight - 1;
+	}
+	pipe.shape = scaledMask->data[yy * scaledWidth + xx];
+	if (vectorAntialias && clipRes2 != splashClipAllInside) {
+	  drawAAPixel(&pipe, x, y);
+	} else {
+	  drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
+	}
+      }
+    }
+  }
+
+  delete scaledMask;
+}
+
+// Scale an image mask into a SplashBitmap.
+SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData,
+				int srcWidth, int srcHeight,
+				int scaledWidth, int scaledHeight) {
+  SplashBitmap *dest;
+
+  dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8,
+			  gFalse);
+  if (scaledHeight < srcHeight) {
+    if (scaledWidth < srcWidth) {
+      scaleMaskYdXd(src, srcData, srcWidth, srcHeight,
+		    scaledWidth, scaledHeight, dest);
+    } else {
+      scaleMaskYdXu(src, srcData, srcWidth, srcHeight,
+		    scaledWidth, scaledHeight, dest);
+    }
+  } else {
+    if (scaledWidth < srcWidth) {
+      scaleMaskYuXd(src, srcData, srcWidth, srcHeight,
+		    scaledWidth, scaledHeight, dest);
+    } else {
+      scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
+		    scaledWidth, scaledHeight, dest);
+    }
+  }
+  return dest;
+}
+
+void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
+			   int srcWidth, int srcHeight,
+			   int scaledWidth, int scaledHeight,
+			   SplashBitmap *dest) {
+  Guchar *lineBuf;
+  Guint *pixBuf;
+  Guint pix;
+  Guchar *destPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = srcHeight / scaledHeight;
+  yq = srcHeight % scaledHeight;
+
+  // Bresenham parameters for x scale
+  xp = srcWidth / scaledWidth;
+  xq = srcWidth % scaledWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmalloc(srcWidth);
+  pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
+
   // init y scale Bresenham
   yt = 0;
-  lastYStep = 1;
 
+  destPtr = dest->data;
   for (y = 0; y < scaledHeight; ++y) {
 
     // y scale Bresenham
-    yStep = yp;
-    yt += yq;
-    if (yt >= scaledHeight) {
+    if ((yt += yq) >= scaledHeight) {
       yt -= scaledHeight;
-      ++yStep;
+      yStep = yp + 1;
+    } else {
+      yStep = yp;
     }
 
-    // read row(s) from image
-    n = (yp > 0) ? yStep : lastYStep;
-    if (n > 0) {
-      p = pixBuf;
-      for (i = 0; i < n; ++i) {
-	(*src)(srcData, p);
-	p += w;
+    // read rows from image
+    memset(pixBuf, 0, srcWidth * sizeof(int));
+    for (i = 0; i < yStep; ++i) {
+      (*src)(srcData, lineBuf);
+      for (j = 0; j < srcWidth; ++j) {
+	pixBuf[j] += lineBuf[j];
       }
     }
-    lastYStep = yStep;
 
-    // loop-invariant constants
-    k1 = splashRound(xShear * ySign * y);
+    // init x scale Bresenham
+    xt = 0;
+    d0 = (255 << 23) / (yStep * xp);
+    d1 = (255 << 23) / (yStep * (xp + 1));
+
+    xx = 0;
+    for (x = 0; x < scaledWidth; ++x) {
 
-    // clipping test
-    if (clipRes != splashClipAllInside &&
-	!rot &&
-	(int)(yShear * k1) ==
-	  (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
-      if (xSign > 0) {
-	spanXMin = tx + k1;
-	spanXMax = spanXMin + (scaledWidth - 1);
+      // x scale Bresenham
+      if ((xt += xq) >= scaledWidth) {
+	xt -= scaledWidth;
+	xStep = xp + 1;
+	d = d1;
       } else {
-	spanXMax = tx + k1;
-	spanXMin = spanXMax - (scaledWidth - 1);
+	xStep = xp;
+	d = d0;
       }
-      spanY = ty + ySign * y + (int)(yShear * k1);
-      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
-      if (clipRes2 == splashClipAllOutside) {
-	continue;
+
+      // compute the final pixel
+      pix = 0;
+      for (i = 0; i < xStep; ++i) {
+	pix += pixBuf[xx++];
       }
+      // (255 * pix) / xStep * yStep
+      pix = (pix * d) >> 23;
+
+      // store the pixel
+      *destPtr++ = (Guchar)pix;
+    }
+  }
+
+  gfree(pixBuf);
+  gfree(lineBuf);
+}
+
+void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
+			   int srcWidth, int srcHeight,
+			   int scaledWidth, int scaledHeight,
+			   SplashBitmap *dest) {
+  Guchar *lineBuf;
+  Guint *pixBuf;
+  Guint pix;
+  Guchar *destPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = srcHeight / scaledHeight;
+  yq = srcHeight % scaledHeight;
+
+  // Bresenham parameters for x scale
+  xp = scaledWidth / srcWidth;
+  xq = scaledWidth % srcWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmalloc(srcWidth);
+  pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
+
+  // init y scale Bresenham
+  yt = 0;
+
+  destPtr = dest->data;
+  for (y = 0; y < scaledHeight; ++y) {
+
+    // y scale Bresenham
+    if ((yt += yq) >= scaledHeight) {
+      yt -= scaledHeight;
+      yStep = yp + 1;
     } else {
-      clipRes2 = clipRes;
+      yStep = yp;
+    }
+
+    // read rows from image
+    memset(pixBuf, 0, srcWidth * sizeof(int));
+    for (i = 0; i < yStep; ++i) {
+      (*src)(srcData, lineBuf);
+      for (j = 0; j < srcWidth; ++j) {
+	pixBuf[j] += lineBuf[j];
+      }
     }
 
     // init x scale Bresenham
     xt = 0;
-    xSrc = 0;
+    d = (255 << 23) / yStep;
+
+    for (x = 0; x < srcWidth; ++x) {
+
+      // x scale Bresenham
+      if ((xt += xq) >= srcWidth) {
+	xt -= srcWidth;
+	xStep = xp + 1;
+      } else {
+	xStep = xp;
+      }
 
-    // x shear
-    x1 = k1;
+      // compute the final pixel
+      pix = pixBuf[x];
+      // (255 * pix) / yStep
+      pix = (pix * d) >> 23;
 
-    // y shear
-    y1 = (SplashCoord)ySign * y + yShear * x1;
-    // this is a kludge: if yShear1 is negative, then (int)y1 would
-    // change immediately after the first pixel, which is not what we
-    // want
-    if (yShear1 < 0) {
-      y1 += 0.999;
+      // store the pixel
+      for (i = 0; i < xStep; ++i) {
+	*destPtr++ = (Guchar)pix;
+      }
     }
+  }
 
-    // loop-invariant constants
-    n = yStep > 0 ? yStep : 1;
+  gfree(pixBuf);
+  gfree(lineBuf);
+}
 
+void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
+			   int srcWidth, int srcHeight,
+			   int scaledWidth, int scaledHeight,
+			   SplashBitmap *dest) {
+  Guchar *lineBuf;
+  Guint pix;
+  Guchar *destPtr0, *destPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
+  int i;
+
+  // Bresenham parameters for y scale
+  yp = scaledHeight / srcHeight;
+  yq = scaledHeight % srcHeight;
+
+  // Bresenham parameters for x scale
+  xp = srcWidth / scaledWidth;
+  xq = srcWidth % scaledWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmalloc(srcWidth);
+
+  // init y scale Bresenham
+  yt = 0;
+
+  destPtr0 = dest->data;
+  for (y = 0; y < srcHeight; ++y) {
+
+    // y scale Bresenham
+    if ((yt += yq) >= srcHeight) {
+      yt -= srcHeight;
+      yStep = yp + 1;
+    } else {
+      yStep = yp;
+    }
+
+    // read row from image
+    (*src)(srcData, lineBuf);
+
+    // init x scale Bresenham
+    xt = 0;
+    d0 = (255 << 23) / xp;
+    d1 = (255 << 23) / (xp + 1);
+
+    xx = 0;
     for (x = 0; x < scaledWidth; ++x) {
 
       // x scale Bresenham
-      xStep = xp;
-      xt += xq;
-      if (xt >= scaledWidth) {
+      if ((xt += xq) >= scaledWidth) {
 	xt -= scaledWidth;
-	++xStep;
+	xStep = xp + 1;
+	d = d1;
+      } else {
+	xStep = xp;
+	d = d0;
       }
 
-      // rotation
-      if (rot) {
-	x2 = (int)y1;
-	y2 = -x1;
-      } else {
-	x2 = x1;
-	y2 = (int)y1;
-      }
-
-      // compute the alpha value for (x,y) after the x and y scaling
-      // operations
-      m = xStep > 0 ? xStep : 1;
-      p = pixBuf + xSrc;
-      pixAcc = 0;
-      for (i = 0; i < n; ++i) {
-	for (j = 0; j < m; ++j) {
-	  pixAcc += *p++;
-	}
-	p += w - m;
+      // compute the final pixel
+      pix = 0;
+      for (i = 0; i < xStep; ++i) {
+	pix += lineBuf[xx++];
       }
+      // (255 * pix) / xStep
+      pix = (pix * d) >> 23;
 
-      // blend fill color with background
-      if (pixAcc != 0) {
-	pipe.shape = ((pixAcc == n * m)
-	                 ? (SplashCoord)1
-	                 : (SplashCoord)pixAcc / (SplashCoord)(n * m)) * 255;
-	if (vectorAntialias && clipRes2 != splashClipAllInside) {
-	  drawAAPixel(&pipe, tx + x2, ty + y2);
-	} else {
-	  drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);
-	}
+      // store the pixel
+      for (i = 0; i < yStep; ++i) {
+	destPtr = destPtr0 + i * scaledWidth + x;
+	*destPtr = (Guchar)pix;
       }
+    }
+
+    destPtr0 += yStep * scaledWidth;
+  }
+
+  gfree(lineBuf);
+}
+
+void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
+			   int srcWidth, int srcHeight,
+			   int scaledWidth, int scaledHeight,
+			   SplashBitmap *dest) {
+  Guchar *lineBuf;
+  Guint pix;
+  Guchar *destPtr0, *destPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = scaledHeight / srcHeight;
+  yq = scaledHeight % srcHeight;
+
+  // Bresenham parameters for x scale
+  xp = scaledWidth / srcWidth;
+  xq = scaledWidth % srcWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmalloc(srcWidth);
+
+  // init y scale Bresenham
+  yt = 0;
+
+  destPtr0 = dest->data;
+  for (y = 0; y < srcHeight; ++y) {
+
+    // y scale Bresenham
+    if ((yt += yq) >= srcHeight) {
+      yt -= srcHeight;
+      yStep = yp + 1;
+    } else {
+      yStep = yp;
+    }
+
+    // read row from image
+    (*src)(srcData, lineBuf);
+
+    // init x scale Bresenham
+    xt = 0;
+
+    xx = 0;
+    for (x = 0; x < srcWidth; ++x) {
 
       // x scale Bresenham
-      xSrc += xStep;
+      if ((xt += xq) >= srcWidth) {
+	xt -= srcWidth;
+	xStep = xp + 1;
+      } else {
+	xStep = xp;
+      }
+
+      // compute the final pixel
+      pix = lineBuf[x] ? 255 : 0;
 
-      // x shear
-      x1 += xSign;
+      // store the pixel
+      for (i = 0; i < yStep; ++i) {
+	for (j = 0; j < xStep; ++j) {
+	  destPtr = destPtr0 + i * scaledWidth + xx + j;
+	  *destPtr++ = (Guchar)pix;
+	}
+      }
 
-      // y shear
-      y1 += yShear1;
+      xx += xStep;
     }
+
+    destPtr0 += yStep * scaledWidth;
   }
 
-  // free memory
-  gfree(pixBuf);
+  gfree(lineBuf);
+}
 
-  return splashOk;
+void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
+		      SplashClipResult clipRes) {
+  SplashPipe pipe;
+  Guchar *p;
+  int w, h, x, y;
+
+  w = src->getWidth();
+  h = src->getHeight();
+  if (vectorAntialias && clipRes != splashClipAllInside) {
+    pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
+	     (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
+    drawAAPixelInit();
+    p = src->getDataPtr();
+    for (y = 0; y < h; ++y) {
+      for (x = 0; x < w; ++x) {
+	pipe.shape = *p++;
+	drawAAPixel(&pipe, xDest + x, yDest + y);
+      }
+    }
+  } else {
+    pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
+	     (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
+    p = src->getDataPtr();
+    if (clipRes == splashClipAllInside) {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	for (x = 0; x < w; ++x) {
+	  if (*p) {
+	    pipe.shape = *p;
+	    (this->*pipe.run)(&pipe);
+	  } else {
+	    pipeIncX(&pipe);
+	  }
+	  ++p;
+	}
+      }
+      updateModX(xDest);
+      updateModX(xDest + w - 1);
+      updateModY(yDest);
+      updateModY(yDest + h - 1);
+    } else {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	for (x = 0; x < w; ++x) {
+	  if (*p && state->clip->test(xDest + x, yDest + y)) {
+	    pipe.shape = *p;
+	    (this->*pipe.run)(&pipe);
+	    updateModX(xDest + x);
+	    updateModY(yDest + y);
+	  } else {
+	    pipeIncX(&pipe);
+	  }
+	  ++p;
+	}
+      }
+    }
+  }
 }
 
 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
 			      SplashColorMode srcMode, GBool srcAlpha,
 			      int w, int h, SplashCoord *mat, SplashPattern *opImagePattern) {
-  SplashPipe pipe;
-  GBool ok, rot;
-  SplashCoord xScale, yScale, xShear, yShear, yShear1;
-  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
-  int ulx, uly, llx, lly, urx, ury, lrx, lry;
-  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
-  int xMin, xMax, yMin, yMax;
-  SplashClipResult clipRes, clipRes2;
-  int yp, yq, yt, yStep, lastYStep;
-  int xp, xq, xt, xStep, xSrc;
-  int k1, spanXMin, spanXMax, spanY;
-  SplashColorPtr colorBuf, p;
-  SplashColor pix;
-  Guchar *alphaBuf, *q;
-#if SPLASH_CMYK
-  int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
-#else
-  int pixAcc0, pixAcc1, pixAcc2;
-#endif
-  int alphaAcc;
-  SplashCoord pixMul, alphaMul, alpha;
-  int x, y, x1, x2, y2;
-  SplashCoord y1;
-  int nComps, n, m, i, j;
+  GBool ok;
+  SplashBitmap *scaledImg;
+  SplashClipResult clipRes;
+  GBool minorAxisZero;
+  int x0, y0, x1, y1, scaledWidth, scaledHeight;
+  int nComps;
+  int yp;
 
   if (debugMode) {
     printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
@@ -2890,799 +3370,1242 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
     nComps = 4;
     break;
 #endif
+  default:
+    ok = gFalse;
+    break;
   }
   if (!ok) {
     return splashErrModeMismatch;
   }
 
   // check for singular matrix
-  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
+  if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
     return splashErrSingularMatrix;
   }
 
-  // compute scale, shear, rotation, translation parameters
-  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
-  if (rot) {
-    xScale = -mat[1];
-    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
-    xShear = -mat[3] / yScale;
-    yShear = -mat[0] / mat[1];
-  } else {
-    xScale = mat[0];
-    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
-    xShear = mat[2] / yScale;
-    yShear = mat[1] / mat[0];
-  }
-  // Note 1: The PDF spec says that all pixels whose *centers* lie
-  // within the region get painted -- but that doesn't seem to match
-  // up with what Acrobat actually does: it ends up leaving gaps
-  // between image stripes.  So we use the same rule here as for
-  // fills: any pixel that overlaps the region gets painted.
-  // Note 2: The +/-0.01 in these computations is to avoid floating
-  // point precision problems which can lead to gaps between image
-  // stripes (it can cause image stripes to overlap, but that's a much
-  // less visible problem).
-  if (xScale >= 0) {
-    tx = splashFloor(mat[4] - 0.01);
-    tx2 = splashFloor(mat[4] + xScale + 0.01);
-  } else {
-    tx = splashFloor(mat[4] + 0.01);
-    tx2 = splashFloor(mat[4] + xScale - 0.01);
-  }
-  scaledWidth = abs(tx2 - tx) + 1;
-  if (yScale >= 0) {
-    ty = splashFloor(mat[5] - 0.01);
-    ty2 = splashFloor(mat[5] + yScale + 0.01);
+  minorAxisZero = mat[1] == 0 && mat[2] == 0;
+
+  // scaling only
+  if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
+    x0 = imgCoordMungeLower(mat[4]);
+    y0 = imgCoordMungeLower(mat[5]);
+    x1 = imgCoordMungeUpper(mat[0] + mat[4]);
+    y1 = imgCoordMungeUpper(mat[3] + mat[5]);
+    // make sure narrow images cover at least one pixel
+    if (x0 == x1) {
+      ++x1;
+    }
+    if (y0 == y1) {
+      ++y1;
+    }
+    clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+    opClipRes = clipRes;
+    if (clipRes != splashClipAllOutside) {
+      scaledWidth = x1 - x0;
+      scaledHeight = y1 - y0;
+      yp = h / scaledHeight;
+      if (yp < 0 || yp > INT_MAX - 1) {
+        return splashErrBadArg;
+      }
+      scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
+			     scaledWidth, scaledHeight);
+      blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
+      delete scaledImg;
+    }
+    
+  // scaling plus vertical flip
+  } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
+    x0 = imgCoordMungeLower(mat[4]);
+    y0 = imgCoordMungeLower(mat[3] + mat[5]);
+    x1 = imgCoordMungeUpper(mat[0] + mat[4]);
+    y1 = imgCoordMungeUpper(mat[5]);
+    if (x0 == x1) {
+      if (mat[4] + mat[0] * 0.5 < x0) {
+	--x0;
+      } else {
+	++x1;
+      }
+    }
+    if (y0 == y1) {
+      if (mat[5] + mat[1] * 0.5 < y0) {
+	--y0;
+      } else {
+	++y1;
+      }
+    }
+    clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+    opClipRes = clipRes;
+    if (clipRes != splashClipAllOutside) {
+      scaledWidth = x1 - x0;
+      scaledHeight = y1 - y0;
+      yp = h / scaledHeight;
+      if (yp < 0 || yp > INT_MAX - 1) {
+        return splashErrBadArg;
+      }
+      scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
+			     scaledWidth, scaledHeight);
+      vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
+      blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
+      delete scaledImg;
+    }
+
+  // all other cases
   } else {
-    ty = splashFloor(mat[5] + 0.01);
-    ty2 = splashFloor(mat[5] + yScale - 0.01);
+    return arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha,
+			    w, h, mat);
   }
-  scaledHeight = abs(ty2 - ty) + 1;
-  xSign = (xScale < 0) ? -1 : 1;
-  ySign = (yScale < 0) ? -1 : 1;
-  yShear1 = (SplashCoord)xSign * yShear;
+
+  return splashOk;
+}
+
+SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
+				     SplashColorMode srcMode, int nComps,
+				     GBool srcAlpha,
+				     int srcWidth, int srcHeight,
+				     SplashCoord *mat) {
+  SplashBitmap *scaledImg;
+  SplashClipResult clipRes, clipRes2;
+  SplashPipe pipe;
+  SplashColor pixel;
+  int scaledWidth, scaledHeight, t0, t1;
+  SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
+  SplashCoord vx[4], vy[4];
+  int xMin, yMin, xMax, yMax;
+  ImageSection section[3];
+  int nSections;
+  int y, xa, xb, x, i, xx, yy, yp;
+
+  // compute the four vertices of the target quadrilateral
+  vx[0] = mat[4];                    vy[0] = mat[5];
+  vx[1] = mat[2] + mat[4];           vy[1] = mat[3] + mat[5];
+  vx[2] = mat[0] + mat[2] + mat[4];  vy[2] = mat[1] + mat[3] + mat[5];
+  vx[3] = mat[0] + mat[4];           vy[3] = mat[1] + mat[5];
 
   // clipping
-  ulx1 = 0;
-  uly1 = 0;
-  urx1 = xSign * (scaledWidth - 1);
-  ury1 = (int)(yShear * urx1);
-  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
-  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
-  lrx1 = xSign * (scaledWidth - 1) +
-           splashRound(xShear * ySign * (scaledHeight - 1));
-  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
-  if (rot) {
-    ulx = tx + uly1;    uly = ty - ulx1;
-    urx = tx + ury1;    ury = ty - urx1;
-    llx = tx + lly1;    lly = ty - llx1;
-    lrx = tx + lry1;    lry = ty - lrx1;
-  } else {
-    ulx = tx + ulx1;    uly = ty + uly1;
-    urx = tx + urx1;    ury = ty + ury1;
-    llx = tx + llx1;    lly = ty + lly1;
-    lrx = tx + lrx1;    lry = ty + lry1;
-  }
-  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
-                                   : (llx < lrx) ? llx : lrx
-		     : (urx < llx) ? (urx < lrx) ? urx : lrx
-                                   : (llx < lrx) ? llx : lrx;
-  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
-                                   : (llx > lrx) ? llx : lrx
-		     : (urx > llx) ? (urx > lrx) ? urx : lrx
-                                   : (llx > lrx) ? llx : lrx;
-  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
-                                   : (lly < lry) ? lly : lry
-		     : (ury < lly) ? (ury < lry) ? ury : lry
-                                   : (lly < lry) ? lly : lry;
-  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
-                                   : (lly > lry) ? lly : lry
-		     : (ury > lly) ? (ury > lry) ? ury : lry
-                                   : (lly > lry) ? lly : lry;
-  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
+  xMin = imgCoordMungeLower(vx[0]);
+  xMax = imgCoordMungeUpper(vx[0]);
+  yMin = imgCoordMungeLower(vy[0]);
+  yMax = imgCoordMungeUpper(vy[0]);
+  for (i = 1; i < 4; ++i) {
+    t0 = imgCoordMungeLower(vx[i]);
+    if (t0 < xMin) {
+      xMin = t0;
+    }
+    t0 = imgCoordMungeUpper(vx[i]);
+    if (t0 > xMax) {
+      xMax = t0;
+    }
+    t1 = imgCoordMungeLower(vy[i]);
+    if (t1 < yMin) {
+      yMin = t1;
+    }
+    t1 = imgCoordMungeUpper(vy[i]);
+    if (t1 > yMax) {
+      yMax = t1;
+    }
+  }
+  clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
   opClipRes = clipRes;
   if (clipRes == splashClipAllOutside) {
     return splashOk;
   }
 
-  // compute Bresenham parameters for x and y scaling
-  yp = h / scaledHeight;
-  yq = h % scaledHeight;
-  xp = w / scaledWidth;
-  xq = w % scaledWidth;
+  // compute the scale factors
+  if (mat[0] >= 0) {
+    t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]);
+  } else {
+    t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]);
+  }
+  if (mat[1] >= 0) {
+    t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]);
+  } else {
+    t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]);
+  }
+  scaledWidth = t0 > t1 ? t0 : t1;
+  if (mat[2] >= 0) {
+    t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
+  } else {
+    t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
+  }
+  if (mat[3] >= 0) {
+    t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
+  } else {
+    t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
+  }
+  scaledHeight = t0 > t1 ? t0 : t1;
+  if (scaledWidth == 0) {
+    scaledWidth = 1;
+  }
+  if (scaledHeight == 0) {
+    scaledHeight = 1;
+  }
 
-  // allocate pixel buffers
+  // compute the inverse transform (after scaling) matrix
+  r00 = mat[0] / scaledWidth;
+  r01 = mat[1] / scaledWidth;
+  r10 = mat[2] / scaledHeight;
+  r11 = mat[3] / scaledHeight;
+  det = r00 * r11 - r01 * r10;
+  if (splashAbs(det) < 1e-6) {
+    // this should be caught by the singular matrix check in drawImage
+    return splashErrBadArg;
+  }
+  ir00 = r11 / det;
+  ir01 = -r01 / det;
+  ir10 = -r10 / det;
+  ir11 = r00 / det;
+
+  // scale the input image
+  yp = srcHeight / scaledHeight;
   if (yp < 0 || yp > INT_MAX - 1) {
     return splashErrBadArg;
   }
-  colorBuf = (SplashColorPtr)gmallocn3((yp + 1), w, nComps);
-  if (srcAlpha) {
-    alphaBuf = (Guchar *)gmallocn((yp + 1), w);
+  scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
+			 srcWidth, srcHeight, scaledWidth, scaledHeight);
+
+  // construct the three sections
+  i = 0;
+  if (vy[1] < vy[i]) {
+    i = 1;
+  }
+  if (vy[2] < vy[i]) {
+    i = 2;
+  }
+  if (vy[3] < vy[i]) {
+    i = 3;
+  }
+  // NB: if using fixed point, 0.000001 will be truncated to zero,
+  // so these two comparisons must be <=, not <
+  if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
+      vy[(i-1) & 3] < vy[(i+1) & 3]) {
+    i = (i-1) & 3;
+  }
+  if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
+    section[0].y0 = imgCoordMungeLower(vy[i]);
+    section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
+    if (vx[i] < vx[(i+1) & 3]) {
+      section[0].ia0 = i;
+      section[0].ia1 = (i+3) & 3;
+      section[0].ib0 = (i+1) & 3;
+      section[0].ib1 = (i+2) & 3;
+    } else {
+      section[0].ia0 = (i+1) & 3;
+      section[0].ia1 = (i+2) & 3;
+      section[0].ib0 = i;
+      section[0].ib1 = (i+3) & 3;
+    }
+    nSections = 1;
   } else {
-    alphaBuf = NULL;
+    section[0].y0 = imgCoordMungeLower(vy[i]);
+    section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
+    section[0].ia0 = section[0].ib0 = i;
+    section[2].ia1 = section[2].ib1 = (i+2) & 3;
+    if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
+      section[0].ia1 = section[2].ia0 = (i+1) & 3;
+      section[0].ib1 = section[2].ib0 = (i+3) & 3;
+    } else {
+      section[0].ia1 = section[2].ia0 = (i+3) & 3;
+      section[0].ib1 = section[2].ib0 = (i+1) & 3;
+    }
+    if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
+      section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]);
+      section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]);
+      if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
+	section[1].ia0 = (i+1) & 3;
+	section[1].ia1 = (i+2) & 3;
+	section[1].ib0 = i;
+	section[1].ib1 = (i+3) & 3;
+      } else {
+	section[1].ia0 = i;
+	section[1].ia1 = (i+3) & 3;
+	section[1].ib0 = (i+1) & 3;
+	section[1].ib1 = (i+2) & 3;
+      }
+    } else {
+      section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]);
+      section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]);
+      if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
+	section[1].ia0 = i;
+	section[1].ia1 = (i+1) & 3;
+	section[1].ib0 = (i+3) & 3;
+	section[1].ib1 = (i+2) & 3;
+      } else {
+	section[1].ia0 = (i+3) & 3;
+	section[1].ia1 = (i+2) & 3;
+	section[1].ib0 = i;
+	section[1].ib1 = (i+1) & 3;
+      }
+    }
+    section[0].y1 = section[1].y0 - 1;
+    section[1].y1 = section[2].y0 - 1;
+    nSections = 3;
+  }
+  for (i = 0; i < nSections; ++i) {
+    section[i].xa0 = vx[section[i].ia0];
+    section[i].ya0 = vy[section[i].ia0];
+    section[i].xa1 = vx[section[i].ia1];
+    section[i].ya1 = vy[section[i].ia1];
+    section[i].xb0 = vx[section[i].ib0];
+    section[i].yb0 = vy[section[i].ib0];
+    section[i].xb1 = vx[section[i].ib1];
+    section[i].yb1 = vy[section[i].ib1];
+    section[i].dxdya = (section[i].xa1 - section[i].xa0) /
+                       (section[i].ya1 - section[i].ya0);
+    section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
+                       (section[i].yb1 - section[i].yb0);
   }
 
-  pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy
-#if SPLASH_CMYK
-  pixAcc3 = 0; // make gcc happy
-#endif
-
   // initialize the pixel pipe
-  pipeInit(&pipe, 0, 0, NULL, pix, (Guchar)splashRound(state->fillAlpha * 255),
+  pipeInit(&pipe, 0, 0, NULL, pixel,
+	   (Guchar)splashRound(state->fillAlpha * 255),
 	   srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
 	   gFalse);
   if (vectorAntialias) {
     drawAAPixelInit();
   }
 
-  if (srcAlpha) {
-
-    // init y scale Bresenham
-    yt = 0;
-    lastYStep = 1;
-
-    for (y = 0; y < scaledHeight; ++y) {
+  // make sure narrow images cover at least one pixel
+  if (nSections == 1) {
+    if (section[0].y0 == section[0].y1) {
+      ++section[0].y1;
+      clipRes = opClipRes = splashClipPartial;
+    }
+  } else {
+    if (section[0].y0 == section[2].y1) {
+      ++section[1].y1;
+      clipRes = opClipRes = splashClipPartial;
+    }
+  }
 
-      // y scale Bresenham
-      yStep = yp;
-      yt += yq;
-      if (yt >= scaledHeight) {
-	yt -= scaledHeight;
-	++yStep;
-      }
-
-      // read row(s) from image
-      n = (yp > 0) ? yStep : lastYStep;
-      if (n > 0) {
-	p = colorBuf;
-	q = alphaBuf;
-	for (i = 0; i < n; ++i) {
-	  (*src)(srcData, p, q);
-	  p += w * nComps;
-	  q += w;
-	}
+  // scan all pixels inside the target region
+  for (i = 0; i < nSections; ++i) {
+    for (y = section[i].y0; y <= section[i].y1; ++y) {
+      xa = imgCoordMungeLower(section[i].xa0 +
+			      ((SplashCoord)y + 0.5 - section[i].ya0) *
+			        section[i].dxdya);
+      xb = imgCoordMungeUpper(section[i].xb0 +
+			      ((SplashCoord)y + 0.5 - section[i].yb0) *
+			        section[i].dxdyb);
+      // make sure narrow images cover at least one pixel
+      if (xa == xb) {
+	++xb;
       }
-      lastYStep = yStep;
-
-      // loop-invariant constants
-      k1 = splashRound(xShear * ySign * y);
-
-      // clipping test
-      if (clipRes != splashClipAllInside &&
-	  !rot &&
-	  (int)(yShear * k1) ==
-	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
-	if (xSign > 0) {
-	  spanXMin = tx + k1;
-	  spanXMax = spanXMin + (scaledWidth - 1);
+      if (clipRes != splashClipAllInside) {
+	clipRes2 = state->clip->testSpan(xa, xb - 1, y);
+      } else {
+	clipRes2 = clipRes;
+      }
+      for (x = xa; x < xb; ++x) {
+	// map (x+0.5, y+0.5) back to the scaled image
+	xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
+			 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
+	yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
+			 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
+	// xx should always be within bounds, but floating point
+	// inaccuracy can cause problems
+	if (xx < 0) {
+	  xx = 0;
+	} else if (xx >= scaledWidth) {
+	  xx = scaledWidth - 1;
+	}
+	if (yy < 0) {
+	  yy = 0;
+	} else if (yy >= scaledHeight) {
+	  yy = scaledHeight - 1;
+	}
+	scaledImg->getPixel(xx, yy, pixel);
+	if (srcAlpha) {
+	  pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
 	} else {
-	  spanXMax = tx + k1;
-	  spanXMin = spanXMax - (scaledWidth - 1);
+	  pipe.shape = 255;
 	}
-	spanY = ty + ySign * y + (int)(yShear * k1);
-	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
-	if (clipRes2 == splashClipAllOutside) {
-	  continue;
+	if (vectorAntialias && clipRes2 != splashClipAllInside) {
+	  drawAAPixel(&pipe, x, y);
+	} else {
+	  drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
 	}
-      } else {
-	clipRes2 = clipRes;
       }
+    }
+  }
 
-      // init x scale Bresenham
-      xt = 0;
-      xSrc = 0;
+  delete scaledImg;
+  return splashOk;
+}
 
-      // x shear
-      x1 = k1;
+// Scale an image into a SplashBitmap.
+SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
+				 SplashColorMode srcMode, int nComps,
+				 GBool srcAlpha, int srcWidth, int srcHeight,
+				 int scaledWidth, int scaledHeight) {
+  SplashBitmap *dest;
+
+  dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha);
+  if (scaledHeight < srcHeight) {
+    if (scaledWidth < srcWidth) {
+      scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha,
+		     srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+    } else {
+      scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha,
+		     srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+    }
+  } else {
+    if (scaledWidth < srcWidth) {
+      scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
+		     srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+    } else {
+      scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
+		     srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+    }
+  }
+  return dest;
+}
 
-      // y shear
-      y1 = (SplashCoord)ySign * y + yShear * x1;
-      // this is a kludge: if yShear1 is negative, then (int)y1 would
-      // change immediately after the first pixel, which is not what
-      // we want
-      if (yShear1 < 0) {
-	y1 += 0.999;
-      }
+void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
+			    SplashColorMode srcMode, int nComps,
+			    GBool srcAlpha, int srcWidth, int srcHeight,
+			    int scaledWidth, int scaledHeight,
+			    SplashBitmap *dest) {
+  Guchar *lineBuf, *alphaLineBuf;
+  Guint *pixBuf, *alphaPixBuf;
+  Guint pix0, pix1, pix2;
+#if SPLASH_CMYK
+  Guint pix3;
+#endif
+  Guint alpha;
+  Guchar *destPtr, *destAlphaPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = srcHeight / scaledHeight;
+  yq = srcHeight % scaledHeight;
+
+  // Bresenham parameters for x scale
+  xp = srcWidth / scaledWidth;
+  xq = srcWidth % scaledWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
+  pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
+  if (srcAlpha) {
+    alphaLineBuf = (Guchar *)gmalloc(srcWidth);
+    alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
+  } else {
+    alphaLineBuf = NULL;
+    alphaPixBuf = NULL;
+  }
 
-      // loop-invariant constants
-      n = yStep > 0 ? yStep : 1;
+  // init y scale Bresenham
+  yt = 0;
 
-      switch (srcMode) {
+  destPtr = dest->data;
+  destAlphaPtr = dest->alpha;
+  for (y = 0; y < scaledHeight; ++y) {
 
-      case splashModeMono1:
-      case splashModeMono8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
+    // y scale Bresenham
+    if ((yt += yq) >= scaledHeight) {
+      yt -= scaledHeight;
+      yStep = yp + 1;
+    } else {
+      yStep = yp;
+    }
 
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
+    // read rows from image
+    memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
+    if (srcAlpha) {
+      memset(alphaPixBuf, 0, srcWidth * sizeof(int));
+    }
+    for (i = 0; i < yStep; ++i) {
+      (*src)(srcData, lineBuf, alphaLineBuf);
+      for (j = 0; j < srcWidth * nComps; ++j) {
+	pixBuf[j] += lineBuf[j];
+      }
+      if (srcAlpha) {
+	for (j = 0; j < srcWidth; ++j) {
+	  alphaPixBuf[j] += alphaLineBuf[j];
+	}
+      }
+    }
 
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  alphaAcc = 0;
-	  p = colorBuf + xSrc;
-	  q = alphaBuf + xSrc;
-	  pixAcc0 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      alphaAcc += *q++;
-	    }
-	    p += w - m;
-	    q += w - m;
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
-	  alphaMul = pixMul * (1.0 / 255.0);
-	  alpha = (SplashCoord)alphaAcc * pixMul;
+    // init x scale Bresenham
+    xt = 0;
+    d0 = (1 << 23) / (yStep * xp);
+    d1 = (1 << 23) / (yStep * (xp + 1));
 
-	  if (alpha > 0) {
-	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
+    xx = xxa = 0;
+    for (x = 0; x < scaledWidth; ++x) {
 
-	    // set pixel
-	    pipe.shape = alpha;
-	    if (vectorAntialias && clipRes != splashClipAllInside) {
-	      drawAAPixel(&pipe, tx + x2, ty + y2);
-	    } else {
-	      drawPixel(&pipe, tx + x2, ty + y2,
-			clipRes2 == splashClipAllInside);
-	    }
-	  }
+      // x scale Bresenham
+      if ((xt += xq) >= scaledWidth) {
+	xt -= scaledWidth;
+	xStep = xp + 1;
+	d = d1;
+      } else {
+	xStep = xp;
+	d = d0;
+      }
 
-	  // x scale Bresenham
-	  xSrc += xStep;
+      switch (srcMode) {
 
-	  // x shear
-	  x1 += xSign;
+      case splashModeMono8:
 
-	  // y shear
-	  y1 += yShear1;
+	// compute the final pixel
+	pix0 = 0;
+	for (i = 0; i < xStep; ++i) {
+	  pix0 += pixBuf[xx++];
 	}
+	// pix / xStep * yStep
+	pix0 = (pix0 * d) >> 23;
+
+	// store the pixel
+	*destPtr++ = (Guchar)pix0;
 	break;
 
       case splashModeRGB8:
-      case splashModeBGR8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
-
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
-
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  alphaAcc = 0;
-	  p = colorBuf + xSrc * 3;
-	  q = alphaBuf + xSrc;
-	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      pixAcc1 += *p++;
-	      pixAcc2 += *p++;
-	      alphaAcc += *q++;
-	    }
-	    p += 3 * (w - m);
-	    q += w - m;
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
-	  alphaMul = pixMul * (1.0 / 255.0);
-	  alpha = (SplashCoord)alphaAcc * pixMul;
-
-	  if (alpha > 0) {
-	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
-	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
-	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
-
-	    // set pixel
-	    pipe.shape = alpha;
-	    if (vectorAntialias && clipRes != splashClipAllInside) {
-	      drawAAPixel(&pipe, tx + x2, ty + y2);
-	    } else {
-	      drawPixel(&pipe, tx + x2, ty + y2,
-			clipRes2 == splashClipAllInside);
-	    }
-	  }
-
-	  // x scale Bresenham
-	  xSrc += xStep;
 
-	  // x shear
-	  x1 += xSign;
-
-	  // y shear
-	  y1 += yShear1;
+	// compute the final pixel
+	pix0 = pix1 = pix2 = 0;
+	for (i = 0; i < xStep; ++i) {
+	  pix0 += pixBuf[xx];
+	  pix1 += pixBuf[xx+1];
+	  pix2 += pixBuf[xx+2];
+	  xx += 3;
 	}
+	// pix / xStep * yStep
+	pix0 = (pix0 * d) >> 23;
+	pix1 = (pix1 * d) >> 23;
+	pix2 = (pix2 * d) >> 23;
+
+	// store the pixel
+	*destPtr++ = (Guchar)pix0;
+	*destPtr++ = (Guchar)pix1;
+	*destPtr++ = (Guchar)pix2;
 	break;
 
       case splashModeXBGR8:
-	for (x = 0; x < scaledWidth; ++x) {
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
-
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
-
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  alphaAcc = 0;
-	  p = colorBuf + xSrc * 4;
-	  q = alphaBuf + xSrc;
-	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      pixAcc1 += *p++;
-	      pixAcc2 += *p++;
-	      p++;
-	      alphaAcc += *q++;
-	    }
-	    p += 4 * (w - m);
-	    q += w - m;
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
-	  alphaMul = pixMul * (1.0 / 255.0);
-	  alpha = (SplashCoord)alphaAcc * pixMul;
-
-	  if (alpha > 0) {
-	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
-	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
-	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
-	    pix[3] = 255;
-
-	    // set pixel
-	    pipe.shape = alpha;
-	    if (vectorAntialias && clipRes != splashClipAllInside) {
-	      drawAAPixel(&pipe, tx + x2, ty + y2);
-	    } else {
-	      drawPixel(&pipe, tx + x2, ty + y2,
-			clipRes2 == splashClipAllInside);
-	    }
-	  }
 
-	  // x scale Bresenham
-	  xSrc += xStep;
+	// compute the final pixel
+	pix0 = pix1 = pix2 = 0;
+	for (i = 0; i < xStep; ++i) {
+	  pix0 += pixBuf[xx];
+	  pix1 += pixBuf[xx+1];
+	  pix2 += pixBuf[xx+2];
+	  xx += 4;
+	}
+	// pix / xStep * yStep
+	pix0 = (pix0 * d) >> 23;
+	pix1 = (pix1 * d) >> 23;
+	pix2 = (pix2 * d) >> 23;
+
+	// store the pixel
+	*destPtr++ = (Guchar)pix2;
+	*destPtr++ = (Guchar)pix1;
+	*destPtr++ = (Guchar)pix0;
+	*destPtr++ = (Guchar)255;	
+	break;
 
-	  // x shear
-	  x1 += xSign;
+      case splashModeBGR8:
 
-	  // y shear
-	  y1 += yShear1;
+	// compute the final pixel
+	pix0 = pix1 = pix2 = 0;
+	for (i = 0; i < xStep; ++i) {
+	  pix0 += pixBuf[xx];
+	  pix1 += pixBuf[xx+1];
+	  pix2 += pixBuf[xx+2];
+	  xx += 3;
 	}
+	// pix / xStep * yStep
+	pix0 = (pix0 * d) >> 23;
+	pix1 = (pix1 * d) >> 23;
+	pix2 = (pix2 * d) >> 23;
+
+	// store the pixel
+	*destPtr++ = (Guchar)pix2;
+	*destPtr++ = (Guchar)pix1;
+	*destPtr++ = (Guchar)pix0;
 	break;
 
-
 #if SPLASH_CMYK
       case splashModeCMYK8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
 
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
-
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  alphaAcc = 0;
-	  p = colorBuf + xSrc * 4;
-	  q = alphaBuf + xSrc;
-	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      pixAcc1 += *p++;
-	      pixAcc2 += *p++;
-	      pixAcc3 += *p++;
-	      alphaAcc += *q++;
-	    }
-	    p += 4 * (w - m);
-	    q += w - m;
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
-	  alphaMul = pixMul * (1.0 / 255.0);
-	  alpha = (SplashCoord)alphaAcc * pixMul;
-
-	  if (alpha > 0) {
-	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
-	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
-	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
-	    pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
-
-	    // set pixel
-	    pipe.shape = alpha;
-	    if (vectorAntialias && clipRes != splashClipAllInside) {
-	      drawAAPixel(&pipe, tx + x2, ty + y2);
-	    } else {
-	      drawPixel(&pipe, tx + x2, ty + y2,
-			clipRes2 == splashClipAllInside);
-	    }
-	  }
+	// compute the final pixel
+	pix0 = pix1 = pix2 = pix3 = 0;
+	for (i = 0; i < xStep; ++i) {
+	  pix0 += pixBuf[xx];
+	  pix1 += pixBuf[xx+1];
+	  pix2 += pixBuf[xx+2];
+	  pix3 += pixBuf[xx+3];
+	  xx += 4;
+	}
+	// pix / xStep * yStep
+	pix0 = (pix0 * d) >> 23;
+	pix1 = (pix1 * d) >> 23;
+	pix2 = (pix2 * d) >> 23;
+	pix3 = (pix3 * d) >> 23;
+
+	// store the pixel
+	*destPtr++ = (Guchar)pix0;
+	*destPtr++ = (Guchar)pix1;
+	*destPtr++ = (Guchar)pix2;
+	*destPtr++ = (Guchar)pix3;
+	break;
+#endif
 
-	  // x scale Bresenham
-	  xSrc += xStep;
 
-	  // x shear
-	  x1 += xSign;
+      case splashModeMono1: // mono1 is not allowed
+      default:
+	break;
+      }
 
-	  // y shear
-	  y1 += yShear1;
+      // process alpha
+      if (srcAlpha) {
+	alpha = 0;
+	for (i = 0; i < xStep; ++i, ++xxa) {
+	  alpha += alphaPixBuf[xxa];
 	}
-	break;
-#endif // SPLASH_CMYK
+	// alpha / xStep * yStep
+	alpha = (alpha * d) >> 23;
+	*destAlphaPtr++ = (Guchar)alpha;
       }
     }
+  }
 
+  gfree(alphaPixBuf);
+  gfree(alphaLineBuf);
+  gfree(pixBuf);
+  gfree(lineBuf);
+}
+
+void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
+			    SplashColorMode srcMode, int nComps,
+			    GBool srcAlpha, int srcWidth, int srcHeight,
+			    int scaledWidth, int scaledHeight,
+			    SplashBitmap *dest) {
+  Guchar *lineBuf, *alphaLineBuf;
+  Guint *pixBuf, *alphaPixBuf;
+  Guint pix[splashMaxColorComps];
+  Guint alpha;
+  Guchar *destPtr, *destAlphaPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = srcHeight / scaledHeight;
+  yq = srcHeight % scaledHeight;
+
+  // Bresenham parameters for x scale
+  xp = scaledWidth / srcWidth;
+  xq = scaledWidth % srcWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
+  pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
+  if (srcAlpha) {
+    alphaLineBuf = (Guchar *)gmalloc(srcWidth);
+    alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
   } else {
+    alphaLineBuf = NULL;
+    alphaPixBuf = NULL;
+  }
 
-    // init y scale Bresenham
-    yt = 0;
-    lastYStep = 1;
+  // init y scale Bresenham
+  yt = 0;
 
-    for (y = 0; y < scaledHeight; ++y) {
+  destPtr = dest->data;
+  destAlphaPtr = dest->alpha;
+  for (y = 0; y < scaledHeight; ++y) {
 
-      // y scale Bresenham
+    // y scale Bresenham
+    if ((yt += yq) >= scaledHeight) {
+      yt -= scaledHeight;
+      yStep = yp + 1;
+    } else {
       yStep = yp;
-      yt += yq;
-      if (yt >= scaledHeight) {
-	yt -= scaledHeight;
-	++yStep;
-      }
-
-      // read row(s) from image
-      n = (yp > 0) ? yStep : lastYStep;
-      if (n > 0) {
-	p = colorBuf;
-	for (i = 0; i < n; ++i) {
-	  (*src)(srcData, p, NULL);
-	  p += w * nComps;
+    }
+
+    // read rows from image
+    memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
+    if (srcAlpha) {
+      memset(alphaPixBuf, 0, srcWidth * sizeof(int));
+    }
+    for (i = 0; i < yStep; ++i) {
+      (*src)(srcData, lineBuf, alphaLineBuf);
+      for (j = 0; j < srcWidth * nComps; ++j) {
+	pixBuf[j] += lineBuf[j];
+      }
+      if (srcAlpha) {
+	for (j = 0; j < srcWidth; ++j) {
+	  alphaPixBuf[j] += alphaLineBuf[j];
 	}
       }
-      lastYStep = yStep;
+    }
 
-      // loop-invariant constants
-      k1 = splashRound(xShear * ySign * y);
+    // init x scale Bresenham
+    xt = 0;
+    d = (1 << 23) / yStep;
 
-      // clipping test
-      if (clipRes != splashClipAllInside &&
-	  !rot &&
-	  (int)(yShear * k1) ==
-	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
-	if (xSign > 0) {
-	  spanXMin = tx + k1;
-	  spanXMax = spanXMin + (scaledWidth - 1);
-	} else {
-	  spanXMax = tx + k1;
-	  spanXMin = spanXMax - (scaledWidth - 1);
-	}
-	spanY = ty + ySign * y + (int)(yShear * k1);
-	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
-	if (clipRes2 == splashClipAllOutside) {
-	  continue;
-	}
+    for (x = 0; x < srcWidth; ++x) {
+
+      // x scale Bresenham
+      if ((xt += xq) >= srcWidth) {
+	xt -= srcWidth;
+	xStep = xp + 1;
       } else {
-	clipRes2 = clipRes;
+	xStep = xp;
       }
 
-      // init x scale Bresenham
-      xt = 0;
-      xSrc = 0;
+      // compute the final pixel
+      for (i = 0; i < nComps; ++i) {
+	// pixBuf[] / yStep
+	pix[i] = (pixBuf[x * nComps + i] * d) >> 23;
+      }
 
-      // x shear
-      x1 = k1;
+      // store the pixel
+      switch (srcMode) {
+      case splashModeMono1: // mono1 is not allowed
+	break;
+      case splashModeMono8:
+	for (i = 0; i < xStep; ++i) {
+	  *destPtr++ = (Guchar)pix[0];
+	}
+	break;
+      case splashModeRGB8:
+	for (i = 0; i < xStep; ++i) {
+	  *destPtr++ = (Guchar)pix[0];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[2];
+	}
+	break;
+      case splashModeXBGR8:
+	for (i = 0; i < xStep; ++i) {
+	  *destPtr++ = (Guchar)pix[2];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[0];
+	  *destPtr++ = (Guchar)255;	  
+	}
+	break;
+      case splashModeBGR8:
+	for (i = 0; i < xStep; ++i) {
+	  *destPtr++ = (Guchar)pix[2];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[0];
+	}
+	break;
+#if SPLASH_CMYK
+      case splashModeCMYK8:
+	for (i = 0; i < xStep; ++i) {
+	  *destPtr++ = (Guchar)pix[0];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[2];
+	  *destPtr++ = (Guchar)pix[3];
+	}
+	break;
+#endif
+      }
 
-      // y shear
-      y1 = (SplashCoord)ySign * y + yShear * x1;
-      // this is a kludge: if yShear1 is negative, then (int)y1 would
-      // change immediately after the first pixel, which is not what
-      // we want
-      if (yShear1 < 0) {
-	y1 += 0.999;
+      // process alpha
+      if (srcAlpha) {
+	// alphaPixBuf[] / yStep
+	alpha = (alphaPixBuf[x] * d) >> 23;
+	for (i = 0; i < xStep; ++i) {
+	  *destAlphaPtr++ = (Guchar)alpha;
+	}
       }
+    }
+  }
 
-      // loop-invariant constants
-      n = yStep > 0 ? yStep : 1;
+  gfree(alphaPixBuf);
+  gfree(alphaLineBuf);
+  gfree(pixBuf);
+  gfree(lineBuf);
+}
 
-      switch (srcMode) {
+void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
+			    SplashColorMode srcMode, int nComps,
+			    GBool srcAlpha, int srcWidth, int srcHeight,
+			    int scaledWidth, int scaledHeight,
+			    SplashBitmap *dest) {
+  Guchar *lineBuf, *alphaLineBuf;
+  Guint pix[splashMaxColorComps];
+  Guint alpha;
+  Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = scaledHeight / srcHeight;
+  yq = scaledHeight % srcHeight;
+
+  // Bresenham parameters for x scale
+  xp = srcWidth / scaledWidth;
+  xq = srcWidth % scaledWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
+  if (srcAlpha) {
+    alphaLineBuf = (Guchar *)gmalloc(srcWidth);
+  } else {
+    alphaLineBuf = NULL;
+  }
 
-      case splashModeMono1:
-      case splashModeMono8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
+  // init y scale Bresenham
+  yt = 0;
 
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
+  destPtr0 = dest->data;
+  destAlphaPtr0 = dest->alpha;
+  for (y = 0; y < srcHeight; ++y) {
 
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  p = colorBuf + xSrc;
-	  pixAcc0 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	    }
-	    p += w - m;
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
+    // y scale Bresenham
+    if ((yt += yq) >= srcHeight) {
+      yt -= srcHeight;
+      yStep = yp + 1;
+    } else {
+      yStep = yp;
+    }
 
-	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
+    // read row from image
+    (*src)(srcData, lineBuf, alphaLineBuf);
 
-	  // set pixel
-	  if (vectorAntialias && clipRes != splashClipAllInside) {
-	    pipe.shape = (SplashCoord)255;
-	    drawAAPixel(&pipe, tx + x2, ty + y2);
-	  } else {
-	    drawPixel(&pipe, tx + x2, ty + y2,
-		      clipRes2 == splashClipAllInside);
-	  }
+    // init x scale Bresenham
+    xt = 0;
+    d0 = (1 << 23) / xp;
+    d1 = (1 << 23) / (xp + 1);
 
-	  // x scale Bresenham
-	  xSrc += xStep;
+    xx = xxa = 0;
+    for (x = 0; x < scaledWidth; ++x) {
 
-	  // x shear
-	  x1 += xSign;
+      // x scale Bresenham
+      if ((xt += xq) >= scaledWidth) {
+	xt -= scaledWidth;
+	xStep = xp + 1;
+	d = d1;
+      } else {
+	xStep = xp;
+	d = d0;
+      }
 
-	  // y shear
-	  y1 += yShear1;
+      // compute the final pixel
+      for (i = 0; i < nComps; ++i) {
+	pix[i] = 0;
+      }
+      for (i = 0; i < xStep; ++i) {
+	for (j = 0; j < nComps; ++j, ++xx) {
+	  pix[j] += lineBuf[xx];
 	}
-	break;
+      }
+      for (i = 0; i < nComps; ++i) {
+	// pix[] / xStep
+	pix[i] = (pix[i] * d) >> 23;
+      }
 
+      // store the pixel
+      switch (srcMode) {
+      case splashModeMono1: // mono1 is not allowed
+	break;
+      case splashModeMono8:
+	for (i = 0; i < yStep; ++i) {
+	  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
+	  *destPtr++ = (Guchar)pix[0];
+	}
+	break;
       case splashModeRGB8:
+	for (i = 0; i < yStep; ++i) {
+	  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
+	  *destPtr++ = (Guchar)pix[0];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[2];
+	}
+	break;
+      case splashModeXBGR8:
+	for (i = 0; i < yStep; ++i) {
+	  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
+	  *destPtr++ = (Guchar)pix[2];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[0];
+	  *destPtr++ = (Guchar)255;	  
+	}
+	break;
       case splashModeBGR8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
-
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
-
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  p = colorBuf + xSrc * 3;
-	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      pixAcc1 += *p++;
-	      pixAcc2 += *p++;
-	    }
-	    p += 3 * (w - m);
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
+	for (i = 0; i < yStep; ++i) {
+	  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
+	  *destPtr++ = (Guchar)pix[2];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[0];
+	}
+	break;
+#if SPLASH_CMYK
+      case splashModeCMYK8:
+	for (i = 0; i < yStep; ++i) {
+	  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
+	  *destPtr++ = (Guchar)pix[0];
+	  *destPtr++ = (Guchar)pix[1];
+	  *destPtr++ = (Guchar)pix[2];
+	  *destPtr++ = (Guchar)pix[3];
+	}
+	break;
+#endif
+      }
 
-	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
-	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
-	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
+      // process alpha
+      if (srcAlpha) {
+	alpha = 0;
+	for (i = 0; i < xStep; ++i, ++xxa) {
+	  alpha += alphaLineBuf[xxa];
+	}
+	// alpha / xStep
+	alpha = (alpha * d) >> 23;
+	for (i = 0; i < yStep; ++i) {
+	  destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
+	  *destAlphaPtr = (Guchar)alpha;
+	}
+      }
+    }
 
-	  // set pixel
-	  if (vectorAntialias && clipRes != splashClipAllInside) {
-	    pipe.shape = (SplashCoord)255;
-	    drawAAPixel(&pipe, tx + x2, ty + y2);
-	  } else {
-	    drawPixel(&pipe, tx + x2, ty + y2,
-		      clipRes2 == splashClipAllInside);
-	  }
+    destPtr0 += yStep * scaledWidth * nComps;
+    if (srcAlpha) {
+      destAlphaPtr0 += yStep * scaledWidth;
+    }
+  }
 
-	  // x scale Bresenham
-	  xSrc += xStep;
+  gfree(alphaLineBuf);
+  gfree(lineBuf);
+}
 
-	  // x shear
-	  x1 += xSign;
+void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
+			    SplashColorMode srcMode, int nComps,
+			    GBool srcAlpha, int srcWidth, int srcHeight,
+			    int scaledWidth, int scaledHeight,
+			    SplashBitmap *dest) {
+  Guchar *lineBuf, *alphaLineBuf;
+  Guint pix[splashMaxColorComps];
+  Guint alpha;
+  Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
+  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
+  int i, j;
+
+  // Bresenham parameters for y scale
+  yp = scaledHeight / srcHeight;
+  yq = scaledHeight % srcHeight;
+
+  // Bresenham parameters for x scale
+  xp = scaledWidth / srcWidth;
+  xq = scaledWidth % srcWidth;
+
+  // allocate buffers
+  lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
+  if (srcAlpha) {
+    alphaLineBuf = (Guchar *)gmalloc(srcWidth);
+  } else {
+    alphaLineBuf = NULL;
+  }
 
-	  // y shear
-	  y1 += yShear1;
-	}
-	break;
+  // init y scale Bresenham
+  yt = 0;
 
-      case splashModeXBGR8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
-	  }
+  destPtr0 = dest->data;
+  destAlphaPtr0 = dest->alpha;
+  for (y = 0; y < srcHeight; ++y) {
 
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
-	  }
+    // y scale Bresenham
+    if ((yt += yq) >= srcHeight) {
+      yt -= srcHeight;
+      yStep = yp + 1;
+    } else {
+      yStep = yp;
+    }
 
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  p = colorBuf + xSrc * 4;
-	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      pixAcc1 += *p++;
-	      pixAcc2 += *p++;
-	      p++;
-	    }
-	    p += 4 * (w - m);
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
+    // read row from image
+    (*src)(srcData, lineBuf, alphaLineBuf);
 
-	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
-	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
-	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
-	  pix[3] = 255;
+    // init x scale Bresenham
+    xt = 0;
 
-	  // set pixel
-	  if (vectorAntialias && clipRes != splashClipAllInside) {
-	    pipe.shape = (SplashCoord)255;
-	    drawAAPixel(&pipe, tx + x2, ty + y2);
-	  } else {
-	    drawPixel(&pipe, tx + x2, ty + y2,
-		      clipRes2 == splashClipAllInside);
-	  }
+    xx = 0;
+    for (x = 0; x < srcWidth; ++x) {
 
-	  // x scale Bresenham
-	  xSrc += xStep;
+      // x scale Bresenham
+      if ((xt += xq) >= srcWidth) {
+	xt -= srcWidth;
+	xStep = xp + 1;
+      } else {
+	xStep = xp;
+      }
 
-	  // x shear
-	  x1 += xSign;
+      // compute the final pixel
+      for (i = 0; i < nComps; ++i) {
+	pix[i] = lineBuf[x * nComps + i];
+      }
 
-	  // y shear
-	  y1 += yShear1;
+      // store the pixel
+      switch (srcMode) {
+      case splashModeMono1: // mono1 is not allowed
+	break;
+      case splashModeMono8:
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
+	    *destPtr++ = (Guchar)pix[0];
+	  }
+	}
+	break;
+      case splashModeRGB8:
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
+	    *destPtr++ = (Guchar)pix[0];
+	    *destPtr++ = (Guchar)pix[1];
+	    *destPtr++ = (Guchar)pix[2];
+	  }
+	}
+	break;
+      case splashModeXBGR8:
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
+	    *destPtr++ = (Guchar)pix[2];
+	    *destPtr++ = (Guchar)pix[1];
+	    *destPtr++ = (Guchar)pix[0];
+	    *destPtr++ = (Guchar)255;	    
+	  }
+	}
+	break;
+      case splashModeBGR8:
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
+	    *destPtr++ = (Guchar)pix[2];
+	    *destPtr++ = (Guchar)pix[1];
+	    *destPtr++ = (Guchar)pix[0];
+	  }
 	}
 	break;
-
 #if SPLASH_CMYK
       case splashModeCMYK8:
-	for (x = 0; x < scaledWidth; ++x) {
-
-	  // x scale Bresenham
-	  xStep = xp;
-	  xt += xq;
-	  if (xt >= scaledWidth) {
-	    xt -= scaledWidth;
-	    ++xStep;
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
+	    *destPtr++ = (Guchar)pix[0];
+	    *destPtr++ = (Guchar)pix[1];
+	    *destPtr++ = (Guchar)pix[2];
+	    *destPtr++ = (Guchar)pix[3];
 	  }
+	}
+	break;
+#endif
+      }
 
-	  // rotation
-	  if (rot) {
-	    x2 = (int)y1;
-	    y2 = -x1;
-	  } else {
-	    x2 = x1;
-	    y2 = (int)y1;
+      // process alpha
+      if (srcAlpha) {
+	alpha = alphaLineBuf[x];
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j;
+	    *destAlphaPtr = (Guchar)alpha;
 	  }
+	}
+      }
 
-	  // compute the filtered pixel at (x,y) after the x and y scaling
-	  // operations
-	  m = xStep > 0 ? xStep : 1;
-	  p = colorBuf + xSrc * 4;
-	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
-	  for (i = 0; i < n; ++i) {
-	    for (j = 0; j < m; ++j) {
-	      pixAcc0 += *p++;
-	      pixAcc1 += *p++;
-	      pixAcc2 += *p++;
-	      pixAcc3 += *p++;
-	    }
-	    p += 4 * (w - m);
-	  }
-	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
+      xx += xStep;
+    }
+
+    destPtr0 += yStep * scaledWidth * nComps;
+    if (srcAlpha) {
+      destAlphaPtr0 += yStep * scaledWidth;
+    }
+  }
 
-	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
-	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
-	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
-	  pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
+  gfree(alphaLineBuf);
+  gfree(lineBuf);
+}
 
-	  // set pixel
-	  if (vectorAntialias && clipRes != splashClipAllInside) {
-	    pipe.shape = (SplashCoord)255;
-	    drawAAPixel(&pipe, tx + x2, ty + y2);
-	  } else {
-	    drawPixel(&pipe, tx + x2, ty + y2,
-		      clipRes2 == splashClipAllInside);
-	  }
+void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
+			   int nComps) {
+  Guchar *lineBuf;
+  Guchar *p0, *p1;
+  int w;
+
+  w = width * nComps;
+  lineBuf = (Guchar *)gmalloc(w);
+  for (p0 = img->data, p1 = img->data + (height - 1) * w;
+       p0 < p1;
+       p0 += w, p1 -= w) {
+    memcpy(lineBuf, p0, w);
+    memcpy(p0, p1, w);
+    memcpy(p1, lineBuf, w);
+  }
+  if (img->alpha) {
+    for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width;
+	 p0 < p1;
+	 p0 += width, p1 -= width) {
+      memcpy(lineBuf, p0, width);
+      memcpy(p0, p1, width);
+      memcpy(p1, lineBuf, width);
+    }
+  }
+  gfree(lineBuf);
+}
 
-	  // x scale Bresenham
-	  xSrc += xStep;
+void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
+		       SplashClipResult clipRes) {
+  SplashPipe pipe;
+  SplashColor pixel;
+  Guchar *ap;
+  int w, h, x0, y0, x1, y1, x, y;
 
-	  // x shear
-	  x1 += xSign;
+  // split the image into clipped and unclipped regions
+  w = src->getWidth();
+  h = src->getHeight();
+  if (clipRes == splashClipAllInside) {
+    x0 = 0;
+    y0 = 0;
+    x1 = w;
+    y1 = h;
+  } else {
+    if (state->clip->getNumPaths()) {
+      x0 = x1 = w;
+      y0 = y1 = h;
+    } else {
+      if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) {
+	x0 = 0;
+      }
+      if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) {
+	y0 = 0;
+      }
+      if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) {
+	x1 = w;
+      }
+      if (x1 < x0) {
+	x1 = x0;
+      }
+      if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
+	y1 = h;
+      }
+      if (y1 < y0) {
+	y1 = y0;
+      }
+    }
+  }
 
-	  // y shear
-	  y1 += yShear1;
+  // draw the unclipped region
+  if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
+    pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel,
+	     (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
+//  pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel, state->fillAlpha,
+//	   srcAlpha,
+//	   gFalse, opImagePattern);
+    if (srcAlpha) {
+      for (y = y0; y < y1; ++y) {
+	pipeSetXY(&pipe, xDest + x0, yDest + y);
+	ap = src->getAlphaPtr() + y * w + x0;
+	for (x = x0; x < x1; ++x) {
+	  src->getPixel(x, y, pixel);
+	  pipe.shape = *ap++;
+	  (this->*pipe.run)(&pipe);
+	}
+      }
+    } else {
+      for (y = y0; y < y1; ++y) {
+	pipeSetXY(&pipe, xDest + x0, yDest + y);
+	for (x = x0; x < x1; ++x) {
+	  src->getPixel(x, y, pixel);
+	  (this->*pipe.run)(&pipe);
 	}
-	break;
-#endif // SPLASH_CMYK
       }
     }
+    updateModX(xDest + x0);
+    updateModX(xDest + x1 - 1);
+    updateModY(yDest + y0);
+    updateModY(yDest + y1 - 1);
+  }
 
+  // draw the clipped regions
+  if (y0 > 0) {
+    blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0);
   }
+  if (y1 < h) {
+    blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1);
+  }
+  if (x0 > 0 && y0 < y1) {
+    blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0);
+  }
+  if (x1 < w && y0 < y1) {
+    blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0,
+		     w - x1, y1 - y0);
+  }
+}
 
-  gfree(colorBuf);
-  gfree(alphaBuf);
+void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
+			      int xSrc, int ySrc, int xDest, int yDest,
+			      int w, int h) {
+  SplashPipe pipe;
+  SplashColor pixel;
+  Guchar *ap;
+  int x, y;
 
-  return splashOk;
+  if (vectorAntialias) {
+    pipeInit(&pipe, xDest, yDest, NULL, pixel,
+	     (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
+//  pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
+//	   gTrue,
+//	   gFalse, opImagePattern);
+    drawAAPixelInit();
+    if (srcAlpha) {
+      for (y = 0; y < h; ++y) {
+	ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
+	for (x = 0; x < w; ++x) {
+	  src->getPixel(xSrc + x, ySrc + y, pixel);
+	  pipe.shape = *ap++;
+	  drawAAPixel(&pipe, xDest + x, yDest + y);
+	}
+      }
+    } else {
+      for (y = 0; y < h; ++y) {
+	for (x = 0; x < w; ++x) {
+	  src->getPixel(xSrc + x, ySrc + y, pixel);
+	  pipe.shape = 255;
+	  drawAAPixel(&pipe, xDest + x, yDest + y);
+	}
+      }
+    }
+  } else {
+    pipeInit(&pipe, xDest, yDest, NULL, pixel,
+	     (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
+//  pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
+//	   srcAlpha,
+//	   gFalse, opImagePattern);
+    if (srcAlpha) {
+      for (y = 0; y < h; ++y) {
+	ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
+	pipeSetXY(&pipe, xDest, yDest + y);
+	for (x = 0; x < w; ++x) {
+	  if (state->clip->test(xDest + x, yDest + y)) {
+	    src->getPixel(xSrc + x, ySrc + y, pixel);
+	    pipe.shape = *ap++;
+	    (this->*pipe.run)(&pipe);
+	    updateModX(xDest + x);
+	    updateModY(yDest + y);
+	  } else {
+	    pipeIncX(&pipe);
+	    ++ap;
+	  }
+	}
+      }
+    } else {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	for (x = 0; x < w; ++x) {
+	  if (state->clip->test(xDest + x, yDest + y)) {
+	    src->getPixel(xSrc + x, ySrc + y, pixel);
+	    (this->*pipe.run)(&pipe);
+	    updateModX(xDest + x);
+	    updateModY(yDest + y);
+	  } else {
+	    pipeIncX(&pipe);
+	  }
+	}
+      }
+    }
+  }
 }
 
 SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
@@ -3699,39 +4622,72 @@ SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
   }
 
   if (src->alpha) {
-    pipeInit(&pipe, xDest, yDest, NULL, pixel, (Guchar)splashRound(state->fillAlpha * 255),
-	     gTrue, nonIsolated);
-    for (y = 0; y < h; ++y) {
-      pipeSetXY(&pipe, xDest, yDest + y);
-      ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
-      for (x = 0; x < w; ++x) {
-	alpha = *ap++;
-	if (noClip || state->clip->test(xDest + x, yDest + y)) {
+    pipeInit(&pipe, xDest, yDest, NULL, pixel,
+	     (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated);
+    if (noClip) {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
+	for (x = 0; x < w; ++x) {
+	  src->getPixel(xSrc + x, ySrc + y, pixel);
+	  alpha = *ap++;
 	  // this uses shape instead of alpha, which isn't technically
 	  // correct, but works out the same
-	  src->getPixel(xSrc + x, ySrc + y, pixel);
 	  pipe.shape = alpha;
 	  (this->*pipe.run)(&pipe);
-	  updateModX(xDest + x);
-	  updateModY(yDest + y);
-	} else {
-	  pipeIncX(&pipe);
+	}
+      }
+      updateModX(xDest);
+      updateModX(xDest + w - 1);
+      updateModY(yDest);
+      updateModY(yDest + h - 1);
+    } else {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
+	for (x = 0; x < w; ++x) {
+	  src->getPixel(xSrc + x, ySrc + y, pixel);
+	  alpha = *ap++;
+	  if (state->clip->test(xDest + x, yDest + y)) {
+	    // this uses shape instead of alpha, which isn't technically
+	    // correct, but works out the same
+	    pipe.shape = alpha;
+	    (this->*pipe.run)(&pipe);
+	    updateModX(xDest + x);
+	    updateModY(yDest + y);
+	  } else {
+	    pipeIncX(&pipe);
+	  }
 	}
       }
     }
   } else {
-    pipeInit(&pipe, xDest, yDest, NULL, pixel, (Guchar)splashRound(state->fillAlpha * 255),
-	     gFalse, nonIsolated);
-    for (y = 0; y < h; ++y) {
-      pipeSetXY(&pipe, xDest, yDest + y);
-      for (x = 0; x < w; ++x) {
-	if (noClip || state->clip->test(xDest + x, yDest + y)) {
+    pipeInit(&pipe, xDest, yDest, NULL, pixel,
+	     (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated);
+    if (noClip) {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	for (x = 0; x < w; ++x) {
 	  src->getPixel(xSrc + x, ySrc + y, pixel);
 	  (this->*pipe.run)(&pipe);
-	  updateModX(xDest + x);
-	  updateModY(yDest + y);
-	} else {
-	  pipeIncX(&pipe);
+	}
+      }
+      updateModX(xDest);
+      updateModX(xDest + w - 1);
+      updateModY(yDest);
+      updateModY(yDest + h - 1);
+    } else {
+      for (y = 0; y < h; ++y) {
+	pipeSetXY(&pipe, xDest, yDest + y);
+	for (x = 0; x < w; ++x) {
+	  src->getPixel(xSrc + x, ySrc + y, pixel);
+	  if (state->clip->test(xDest + x, yDest + y)) {
+	    (this->*pipe.run)(&pipe);
+	    updateModX(xDest + x);
+	    updateModY(yDest + y);
+	  } else {
+	    pipeIncX(&pipe);
+	  }
 	}
       }
     }
@@ -3856,11 +4812,21 @@ void Splash::compositeBackground(SplashColorPtr color) {
       q = &bitmap->alpha[y * bitmap->width];
       for (x = 0; x < bitmap->width; ++x) {
 	alpha = *q++;
-	alpha1 = 255 - alpha;
-	p[0] = div255(alpha1 * color0 + alpha * p[0]);
-	p[1] = div255(alpha1 * color1 + alpha * p[1]);
-	p[2] = div255(alpha1 * color2 + alpha * p[2]);
-	p[3] = div255(alpha1 * color3 + alpha * p[3]);
+	if (alpha == 0)
+	{
+	  p[0] = color0;
+	  p[1] = color1;
+	  p[2] = color2;
+	  p[3] = color3;
+	}
+	else if (alpha != 255)
+	{
+	  alpha1 = 255 - alpha;
+	  p[0] = div255(alpha1 * color0 + alpha * p[0]);
+	  p[1] = div255(alpha1 * color1 + alpha * p[1]);
+	  p[2] = div255(alpha1 * color2 + alpha * p[2]);
+    p[3] = div255(alpha1 * color3 + alpha * p[3]);
+	}
 	p += 4;
       }
     }
diff --git a/splash/Splash.h b/splash/Splash.h
index e538808..ce59497 100644
--- a/splash/Splash.h
+++ b/splash/Splash.h
@@ -318,6 +318,66 @@ private:
 			      SplashPattern *pattern, SplashCoord alpha);
   GBool pathAllOutside(SplashPath *path);
   void fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noclip);
+  void arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
+			      int srcWidth, int srcHeight,
+			      SplashCoord *mat, GBool glyphMode);
+  SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData,
+			  int srcWidth, int srcHeight,
+			  int scaledWidth, int scaledHeight);
+  void scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void blitMask(SplashBitmap *src, int xDest, int yDest,
+		SplashClipResult clipRes);
+  SplashError arbitraryTransformImage(SplashImageSource src, void *srcData,
+			       SplashColorMode srcMode, int nComps,
+			       GBool srcAlpha,
+			       int srcWidth, int srcHeight,
+			       SplashCoord *mat);
+  SplashBitmap *scaleImage(SplashImageSource src, void *srcData,
+			   SplashColorMode srcMode, int nComps,
+			   GBool srcAlpha, int srcWidth, int srcHeight,
+			   int scaledWidth, int scaledHeight);
+  void scaleImageYdXd(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void scaleImageYdXu(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void scaleImageYuXd(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void scaleImageYuXu(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void vertFlipImage(SplashBitmap *img, int width, int height,
+		     int nComps);
+  void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
+		 SplashClipResult clipRes);
+  void blitImageClipped(SplashBitmap *src, GBool srcAlpha,
+			int xSrc, int ySrc, int xDest, int yDest,
+			int w, int h);
   void dumpPath(SplashPath *path);
   void dumpXPath(SplashXPath *path);
 


More information about the poppler mailing list