diff options
| author | Calvin Morrison <calvin@pobox.com> | 2023-04-05 14:13:39 -0400 | 
|---|---|---|
| committer | Calvin Morrison <calvin@pobox.com> | 2023-04-05 14:13:39 -0400 | 
| commit | 835e373b3eeaabcd0621ed6798ab500f37982fae (patch) | |
| tree | dfa16b0e2e1b4956b38f693220eac4e607802133 /xpdf/ShadingImage.cc | |
Diffstat (limited to 'xpdf/ShadingImage.cc')
| -rw-r--r-- | xpdf/ShadingImage.cc | 1327 | 
1 files changed, 1327 insertions, 0 deletions
diff --git a/xpdf/ShadingImage.cc b/xpdf/ShadingImage.cc new file mode 100644 index 0000000..d8dc24e --- /dev/null +++ b/xpdf/ShadingImage.cc @@ -0,0 +1,1327 @@ +//======================================================================== +// +// ShadingImage.cc +// +// Copyright 2020 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <math.h> +#include "Trace.h" +#include "GfxState.h" +#include "SplashBitmap.h" +#include "SplashPattern.h" +#include "SplashPath.h" +#include "Splash.h" +#include "ShadingImage.h" + +// Max recursive depth for a patch mesh shading fill. +#define patchMaxDepth 10 + +// Max delta allowed in any color component for a patch mesh shading +// fill. +#define patchColorDelta (dblToCol(1 / 256.0)) + +SplashBitmap *ShadingImage::generateBitmap(GfxState *state, +					   GfxShading *shading, +					   SplashColorMode mode, +					   GBool reverseVideo, +					   Splash *parentSplash, +					   SplashBitmap *parentBitmap, +					   int *xOut, int *yOut) { +  switch (shading->getType()) { +  case 1: +    return generateFunctionBitmap(state, (GfxFunctionShading *)shading, +				  mode, reverseVideo, +				  parentSplash, parentBitmap, xOut, yOut); +    break; +  case 2: +    return generateAxialBitmap(state, (GfxAxialShading *)shading, +			       mode, reverseVideo, +			       parentSplash, parentBitmap, xOut, yOut); +    break; +  case 3: +    return generateRadialBitmap(state, (GfxRadialShading *)shading, +				mode, reverseVideo, +				parentSplash, parentBitmap, xOut, yOut); +    break; +  case 4: +  case 5: +    return generateGouraudTriangleBitmap(state, +					 (GfxGouraudTriangleShading *)shading, +					 mode, reverseVideo, +					 parentSplash, parentBitmap, +					 xOut, yOut); +    break; +  case 6: +  case 7: +    return generatePatchMeshBitmap(state, (GfxPatchMeshShading *)shading, +				   mode, reverseVideo, +				   parentSplash, parentBitmap, xOut, yOut); +    break; +  default: +    return NULL; +  } +} + +SplashBitmap *ShadingImage::generateFunctionBitmap(GfxState *state, +						   GfxFunctionShading *shading, +						   SplashColorMode mode, +						   GBool reverseVideo, +						   Splash *parentSplash, +						   SplashBitmap *parentBitmap, +						   int *xOut, int *yOut) { +  // get the shading parameters +  double x0, y0, x1, y1; +  shading->getDomain(&x0, &y0, &x1, &y1); +  double *patternMat = shading->getMatrix(); + +  // get the clip bbox +  double fxMin, fyMin, fxMax, fyMax; +  state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // convert to integer coords +  int xMin = (int)floor(fxMin); +  int yMin = (int)floor(fyMin); +  int xMax = (int)floor(fxMax) + 1; +  int yMax = (int)floor(fyMax) + 1; +  int bitmapWidth = xMax - xMin; +  int bitmapHeight = yMax - yMin; + +  // allocate the bitmap +  traceMessage("function shading fill bitmap"); +  SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, +					  gTrue, gTrue, parentBitmap); +  int nComps = splashColorModeNComps[mode]; + +  // compute the domain -> device space transform = mat * CTM +  double *ctm = state->getCTM(); +  double mat[6]; +  mat[0] = patternMat[0] * ctm[0] + patternMat[1] * ctm[2]; +  mat[1] = patternMat[0] * ctm[1] + patternMat[1] * ctm[3]; +  mat[2] = patternMat[2] * ctm[0] + patternMat[3] * ctm[2]; +  mat[3] = patternMat[2] * ctm[1] + patternMat[3] * ctm[3]; +  mat[4] = patternMat[4] * ctm[0] + patternMat[5] * ctm[2] + ctm[4]; +  mat[5] = patternMat[4] * ctm[1] + patternMat[5] * ctm[3] + ctm[5]; + +  // compute the device space -> domain transform +  double det = mat[0] * mat[3] - mat[1] * mat[2]; +  if (fabs(det) < 0.000001) { +    return NULL; +  } +  det = 1 / det; +  double iMat[6]; +  iMat[0] = mat[3] * det; +  iMat[1] = -mat[1] * det; +  iMat[2] = -mat[2] * det; +  iMat[3] = mat[0] * det; +  iMat[4] = (mat[2] * mat[5] - mat[3] * mat[4]) * det; +  iMat[5] = (mat[1] * mat[4] - mat[0] * mat[5]) * det; + +  // fill the bitmap +  SplashColorPtr dataPtr = bitmap->getDataPtr(); +  Guchar *alphaPtr = bitmap->getAlphaPtr(); +  for (int y = 0; y < bitmapHeight; ++y) { +    for (int x = 0; x < bitmapWidth; ++x) { + +      // convert coords to the pattern domain +      double tx = xMin + x + 0.5; +      double ty = yMin + y + 0.5; +      double xx = tx * iMat[0] + ty * iMat[2] + iMat[4]; +      double yy = tx * iMat[1] + ty * iMat[3] + iMat[5]; + +      // get the color +      if (xx >= x0 && xx <= x1 && yy >= y0 && yy <= y1) { +	GfxColor color; +	shading->getColor(xx, yy, &color); +	SplashColor sColor; +	computeShadingColor(state, mode, reverseVideo, &color, sColor); +	for (int i = 0; i < nComps; ++i) { +	  *dataPtr++ = sColor[i]; +	} +	*alphaPtr++ = 0xff; +      } else { +	dataPtr += nComps; +	*alphaPtr++ = 0; +      } +    } +  } + +  *xOut = xMin; +  *yOut = yMin; +  return bitmap; +} + +SplashBitmap *ShadingImage::generateAxialBitmap(GfxState *state, +						GfxAxialShading *shading, +						SplashColorMode mode, +						GBool reverseVideo, +						Splash *parentSplash, +						SplashBitmap *parentBitmap, +						int *xOut, int *yOut) { +  // get the shading parameters +  double x0, y0, x1, y1; +  shading->getCoords(&x0, &y0, &x1, &y1); +  double t0 = shading->getDomain0(); +  double t1 = shading->getDomain1(); +  GBool ext0 = shading->getExtend0(); +  GBool ext1 = shading->getExtend1(); +  double dx = x1 - x0; +  double dy = y1 - y0; +  double d = dx * dx + dy * dy; +  GBool dZero = fabs(d) < 0.0001; +  if (!dZero) { +    d = 1 / d; +  } +  if (dZero && !ext0 && !ext1) { +    return NULL; +  } + +  // get the clip bbox +  double fxMin, fyMin, fxMax, fyMax; +  state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // convert to integer coords +  int xMin = (int)floor(fxMin); +  int yMin = (int)floor(fyMin); +  int xMax = (int)floor(fxMax) + 1; +  int yMax = (int)floor(fyMax) + 1; +  int bitmapWidth = xMax - xMin; +  int bitmapHeight = yMax - yMin; + +  // compute the inverse CTM +  double *ctm = state->getCTM(); +  double det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; +  if (fabs(det) < 0.000001) { +    return NULL; +  } +  det = 1 / det; +  double ictm[6]; +  ictm[0] = ctm[3] * det; +  ictm[1] = -ctm[1] * det; +  ictm[2] = -ctm[2] * det; +  ictm[3] = ctm[0] * det; +  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; +  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; + +  // convert axis endpoints to device space +  double xx0, yy0, xx1, yy1; +  state->transform(x0, y0, &xx0, &yy0); +  state->transform(x1, y1, &xx1, &yy1); + +  // allocate the bitmap +  traceMessage("axial shading fill bitmap"); +  SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, +					  gTrue, gTrue, parentBitmap); +  int nComps = splashColorModeNComps[mode]; + +  // special case: zero-length axis +  if (dZero) { +    GfxColor color; +    if (ext0) { +      shading->getColor(t0, &color); +    } else { +      shading->getColor(t1, &color); +    } +    SplashColor sColor; +    computeShadingColor(state, mode, reverseVideo, &color, sColor); +    SplashColorPtr dataPtr = bitmap->getDataPtr(); +    for (int y = 0; y < bitmapHeight; ++y) { +      for (int x = 0; x < bitmapWidth; ++x) { +	for (int i = 0; i < nComps; ++i) { +	  *dataPtr++ = sColor[i]; +	} +      } +    } +    memset(bitmap->getAlphaPtr(), 0xff, (size_t)bitmapWidth * bitmapHeight); + +  // special case: horizontal axis (in device space) +  } else if (fabs(yy0 - yy1) < 0.01) { +    for (int x = 0; x < bitmapWidth; ++x) { +      SplashColorPtr dataPtr = bitmap->getDataPtr() + x * nComps; +      Guchar *alphaPtr = bitmap->getAlphaPtr() + x; +      double tx = xMin + x + 0.5; +      double ty = yMin + 0.5; +      double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; +      double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; +      double s = ((xx - x0) * dx + (yy - y0) * dy) * d; +      GBool go = gFalse; +      if (s < 0) { +	go = ext0; +      } else if (s > 1) { +	go = ext1; +      } else { +	go = gTrue; +      } +      if (go) { +	GfxColor color; +	if (s <= 0) { +	  shading->getColor(t0, &color); +	} else if (s >= 1) { +	  shading->getColor(t1, &color); +	} else { +	  double t = t0 + s * (t1 - t0); +	  shading->getColor(t, &color); +	} +	SplashColor sColor; +	computeShadingColor(state, mode, reverseVideo, &color, sColor); +	for (int y = 0; y < bitmapHeight; ++y) { +	  for (int i = 0; i < nComps; ++i) { +	    dataPtr[i] = sColor[i]; +	  } +	  dataPtr += bitmap->getRowSize(); +	  *alphaPtr = 0xff; +	  alphaPtr += bitmapWidth; +	} +      } else { +	for (int y = 0; y < bitmapHeight; ++y) { +	  *alphaPtr = 0; +	  alphaPtr += bitmapWidth; +	} +      } +    } + +  // special case: vertical axis (in device space) +  } else if (fabs(xx0 - xx1) < 0.01) { +    for (int y = 0; y < bitmapHeight; ++y) { +      SplashColorPtr dataPtr = bitmap->getDataPtr() + y * bitmap->getRowSize(); +      Guchar *alphaPtr = bitmap->getAlphaPtr() + y * bitmapWidth; +      double tx = xMin + 0.5; +      double ty = yMin + y + 0.5; +      double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; +      double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; +      double s = ((xx - x0) * dx + (yy - y0) * dy) * d; +      GBool go = gFalse; +      if (s < 0) { +	go = ext0; +      } else if (s > 1) { +	go = ext1; +      } else { +	go = gTrue; +      } +      if (go) { +	GfxColor color; +	if (s <= 0) { +	  shading->getColor(t0, &color); +	} else if (s >= 1) { +	  shading->getColor(t1, &color); +	} else { +	  double t = t0 + s * (t1 - t0); +	  shading->getColor(t, &color); +	} +	SplashColor sColor; +	computeShadingColor(state, mode, reverseVideo, &color, sColor); +	for (int x = 0; x < bitmapWidth; ++x) { +	  for (int i = 0; i < nComps; ++i) { +	    dataPtr[i] = sColor[i]; +	  } +	  dataPtr += nComps; +	} +	memset(alphaPtr, 0xff, bitmapWidth); +      } else { +	memset(alphaPtr, 0, bitmapWidth); +      } +    } + +  // general case +  } else { +    // pre-compute colors along the axis +    int nColors = (int)(1.5 * sqrt((xx1 - xx0) * (xx1 - xx0) +				   + (yy1 - yy0) * (yy1 - yy0))); +    if (nColors < 16) { +      nColors = 16; +    } else if (nColors > 1024) { +      nColors = 1024; +    } +    SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps); +    SplashColorPtr sColor = sColors; +    for (int i = 0; i < nColors; ++i) { +      double s = (double)i / (double)(nColors - 1); +      double t = t0 + s * (t1 - t0); +      GfxColor color; +      shading->getColor(t, &color); +      computeShadingColor(state, mode, reverseVideo, &color, sColor); +      sColor += nComps; +    } + +    SplashColorPtr dataPtr = bitmap->getDataPtr(); +    Guchar *alphaPtr = bitmap->getAlphaPtr(); +    for (int y = 0; y < bitmapHeight; ++y) { +      for (int x = 0; x < bitmapWidth; ++x) { + +	// convert coords to user space +	double tx = xMin + x + 0.5; +	double ty = yMin + y + 0.5; +	double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; +	double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; + +	// compute the position along the axis +	double s = ((xx - x0) * dx + (yy - y0) * dy) * d; +	GBool go = gFalse; +	if (s < 0) { +	  go = ext0; +	} else if (s > 1) { +	  go = ext1; +	} else { +	  go = gTrue; +	} +	if (go) { +	  if (s <= 0) { +	    sColor = sColors; +	  } else if (s >= 1) { +	    sColor = sColors + (nColors - 1) * nComps; +	  } else { +	    int i = (int)((nColors - 1) * s + 0.5); +	    sColor = sColors + i * nComps; +	  } +	  for (int i = 0; i < nComps; ++i) { +	    *dataPtr++ = sColor[i]; +	  } +	  *alphaPtr++ = 0xff; +	} else { +	  dataPtr += nComps; +	  *alphaPtr++ = 0; +	} +      } +    } +    gfree(sColors); +  } + +  *xOut = xMin; +  *yOut = yMin; +  return bitmap; +} + +SplashBitmap *ShadingImage::generateRadialBitmap(GfxState *state, +						 GfxRadialShading *shading, +						 SplashColorMode mode, +						 GBool reverseVideo, +						 Splash *parentSplash, +						 SplashBitmap *parentBitmap, +						 int *xOut, int *yOut) { +  // get the shading parameters +  double x0, y0, r0, x1, y1, r1; +  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); +  double t0 = shading->getDomain0(); +  double t1 = shading->getDomain1(); +  GBool ext0 = shading->getExtend0(); +  GBool ext1 = shading->getExtend1(); +  double h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); +  GBool enclosed = fabs(r1 - r0) >= h; + +  // get the clip bbox +  double fxMin, fyMin, fxMax, fyMax; +  state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // intersect with shading region (in user space): if the extend +  // flags are false (or just the larger extend flag is false, in the +  // "enclosed" case), we can use the bbox for the two circles +  if ((!ext0 && !ext1) || +      (enclosed && !(r0 > r1 ? ext0 : ext1))) { +    double uxMin = (x0 - r0) < (x1 - r1) ? (x0 - r0) : (x1 - r1); +    double uxMax = (x0 + r0) > (x1 + r1) ? (x0 + r0) : (x1 + r1); +    double uyMin = (y0 - r0) < (y1 - r1) ? (y0 - r0) : (y1 - r1); +    double uyMax = (y0 + r0) > (y1 + r1) ? (y0 + r0) : (y1 + r1); +    double dxMin, dyMin, dxMax, dyMax; +    transformBBox(state, uxMin, uyMin, uxMax, uyMax, +		  &dxMin, &dyMin, &dxMax, &dyMax); +    if (dxMin > fxMin) { +      fxMin = dxMin; +    } +    if (dxMax < dxMax) { +      fxMax = dxMax; +    } +    if (dyMin > fyMin) { +      fyMin = dyMin; +    } +    if (dyMax < fyMax) { +      fyMax = dyMax; +    } +    if (fxMin > fxMax || fyMin > fyMax) { +      return NULL; +    } +  } + +  // convert to integer coords +  int xMin = (int)floor(fxMin); +  int yMin = (int)floor(fyMin); +  int xMax = (int)floor(fxMax) + 1; +  int yMax = (int)floor(fyMax) + 1; +  int bitmapWidth = xMax - xMin; +  int bitmapHeight = yMax - yMin; + +  // compute the inverse CTM +  double *ctm = state->getCTM(); +  double det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; +  if (fabs(det) < 0.000001) { +    return NULL; +  } +  det = 1 / det; +  double ictm[6]; +  ictm[0] = ctm[3] * det; +  ictm[1] = -ctm[1] * det; +  ictm[2] = -ctm[2] * det; +  ictm[3] = ctm[0] * det; +  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; +  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; + +  // allocate the bitmap +  traceMessage("radial shading fill bitmap"); +  SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, +					  gTrue, gTrue, parentBitmap); +  int nComps = splashColorModeNComps[mode]; + +  // pre-compute colors along the axis +  int nColors = (int)sqrt((double)(bitmapWidth * bitmapWidth +				   + bitmapHeight * bitmapHeight)); +  if (nColors < 16) { +    nColors = 16; +  } else if (nColors > 1024) { +    nColors = 1024; +  } +  SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps); +  SplashColorPtr sColor = sColors; +  for (int i = 0; i < nColors; ++i) { +    double s = (double)i / (double)(nColors - 1); +    double t = t0 + s * (t1 - t0); +    GfxColor color; +    shading->getColor(t, &color); +    computeShadingColor(state, mode, reverseVideo, &color, sColor); +    sColor += nComps; +  } + +  // special case: in the "enclosed" + extended case, we can fill the +  // bitmap with the outer color and just render inside the larger +  // circle +  int bxMin, byMin, bxMax, byMax; +  if (enclosed && +      ((r0 > r1 && ext0) || (r1 > r0 && ext1))) { +    double uxMin, uyMin, uxMax, uyMax; +    if (r0 > r1) { +      sColor = sColors; +      uxMin = x0 - r0; +      uxMax = x0 + r0; +      uyMin = y0 - r0; +      uyMax = y0 + r0; +    } else { +      sColor = sColors + (nColors - 1) * nComps; +      uxMin = x1 - r1; +      uxMax = x1 + r1; +      uyMin = y1 - r1; +      uyMax = y1 + r1; +    } + +    // convert bbox of larger circle to device space +    double dxMin, dyMin, dxMax, dyMax; +    transformBBox(state, uxMin, uyMin, uxMax, uyMax, +		  &dxMin, &dyMin, &dxMax, &dyMax); +    bxMin = (int)floor(dxMin - xMin); +    if (bxMin < 0) { +      bxMin = 0; +    } +    byMin = (int)floor(dyMin - yMin); +    if (byMin < 0) { +      byMin = 0; +    } +    bxMax = (int)floor(dxMax - xMin) + 1; +    if (bxMax > bitmapWidth) { +      bxMax = bitmapWidth; +    } +    byMax = (int)floor(dyMax - yMin) + 1; +    if (byMax > bitmapHeight) { +      byMax = bitmapHeight; +    } + +    // fill bitmap (except for the rectangle containing the larger circle) +    SplashColorPtr dataPtr = bitmap->getDataPtr(); +    Guchar *alphaPtr = bitmap->getAlphaPtr(); +    for (int y = 0; y < bitmapHeight; ++y) { +      for (int x = 0; x < bitmapWidth; ++x) { +	if (y >= byMin && y < byMax && x >= bxMin && x < bxMax) { +	  dataPtr += nComps; +	  ++alphaPtr; +	} else { +	  for (int i = 0; i < nComps; ++i) { +	    *dataPtr++ = sColor[i]; +	  } +	  *alphaPtr++ = 0xff; +	} +      } +    } + +  } else { +    bxMin = 0; +    byMin = 0; +    bxMax = bitmapWidth; +    byMax = bitmapHeight; +  } + +  // render the shading into the bitmap +  double dx = x1 - x0; +  double dy = y1 - y0; +  double dr = r1 - r0; +  double r0dr = r0 * dr; +  double r02 = r0 * r0; +  double a = dx * dx + dy * dy - dr * dr; +  GBool aIsZero; +  double a2; +  if (fabs(a) < 0.00001) { +    aIsZero = gTrue; +    a2 = 0; +  } else { +    aIsZero = gFalse; +    a2 = 1 / (2 * a); +  } +  for (int y = byMin; y < byMax; ++y) { +    SplashColorPtr dataPtr = bitmap->getDataPtr() +                             + y * bitmap->getRowSize() + bxMin * nComps; +    Guchar *alphaPtr = bitmap->getAlphaPtr() +                       + y * bitmap->getAlphaRowSize() + bxMin; +    for (int x = bxMin; x < bxMax; ++x) { + +      // convert coords to user space +      double tx = xMin + x + 0.5; +      double ty = yMin + y + 0.5; +      double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; +      double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; + +      // compute the radius of the circle at x,y +      double b = 2 * ((xx - x0) * dx + (yy - y0) * dy + r0dr); +      double c = (xx - x0) * (xx - x0) + (yy - y0) * (yy - y0) - r02; +      double s = 0; +      GBool go = gFalse; +      if (aIsZero) { +	if (fabs(b) < 0.000001) { +	  if (c <= 0) { +	    if (ext0) { +	      s = 0; +	      go = gTrue; +	    } +	  } else { +	    if (ext1) { +	      s = 1; +	      go = gTrue; +	    } +	  } +	} else { +	  double s0 = c / b; +	  double rs0 = r0 + s0 * (r1 - r0); +	  if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {  +	    s = s0; +	    go = gTrue; +	  } +	} +      } else { +	double e = b*b - 4*a*c; +	if (e >= 0) { +	  double es = sqrt(e); +	  double s0 = (b + es) * a2; +	  double s1 = (b - es) * a2; +	  double rs0 = r0 + s0 * (r1 - r0); +	  double rs1 = r0 + s1 * (r1 - r0); +	  if (s0 > s1) { +	    if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { +	      s = s0; +	      go = gTrue; +	    } else if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) { +	      s = s1; +	      go = gTrue; +	    } +	  } else { +	    if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) { +	      s = s1; +	      go = gTrue; +	    } else if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { +	      s = s0; +	      go = gTrue; +	    } +	  } +	} +      } +      if (!go) { +	dataPtr += nComps; +	*alphaPtr++ = 0x00; +	continue; +      } +      if (s <= 0) { +	sColor = sColors; +      } else if (s >= 1) { +	sColor = sColors + (nColors - 1) * nComps; +      } else { +	int i = (int)((nColors - 1) * s + 0.5); +	sColor = sColors + i * nComps; +      } +      for (int i = 0; i < nComps; ++i) { +	*dataPtr++ = sColor[i]; +      } +      *alphaPtr++ = 0xff; +    } +  } + +  gfree(sColors); + +  *xOut = xMin; +  *yOut = yMin; +  return bitmap; +} + +SplashBitmap *ShadingImage::generateGouraudTriangleBitmap( +					GfxState *state, +					GfxGouraudTriangleShading *shading, +					SplashColorMode mode, +					GBool reverseVideo, +					Splash *parentSplash, +					SplashBitmap *parentBitmap, +					int *xOut, int *yOut) { +  // get the clip bbox +  double fxMin, fyMin, fxMax, fyMax; +  state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // get the shading bbox +  double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax; +  shading->getBBox(&tx0, &ty0, &tx1, &ty1); +  state->transform(tx0, ty0, &dx, &dy); +  txMin = txMax = dx; +  tyMin = tyMax = dy; +  state->transform(tx0, ty1, &dx, &dy); +  if (dx < txMin) { +    txMin = dx; +  } else if (dx > txMax) { +    txMax = dx; +  } +  if (dy < tyMin) { +    tyMin = dy; +  } else if (dy > tyMax) { +    tyMax = dy; +  } +  state->transform(tx1, ty0, &dx, &dy); +  if (dx < txMin) { +    txMin = dx; +  } else if (dx > txMax) { +    txMax = dx; +  } +  if (dy < tyMin) { +    tyMin = dy; +  } else if (dy > tyMax) { +    tyMax = dy; +  } +  state->transform(tx1, ty1, &dx, &dy); +  if (dx < txMin) { +    txMin = dx; +  } else if (dx > txMax) { +    txMax = dx; +  } +  if (dy < tyMin) { +    tyMin = dy; +  } else if (dy > tyMax) { +    tyMax = dy; +  } +  if (txMin > fxMin) { +    fxMin = txMin; +  } +  if (txMax < fxMax) { +    fxMax = txMax; +  } +  if (tyMin > fyMin) { +    fyMin = tyMin; +  } +  if (tyMax < fyMax) { +    fyMax = tyMax; +  } +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // convert to integer coords +  int xMin = (int)floor(fxMin); +  int yMin = (int)floor(fyMin); +  int xMax = (int)floor(fxMax) + 1; +  int yMax = (int)floor(fyMax) + 1; +  int bitmapWidth = xMax - xMin; +  int bitmapHeight = yMax - yMin; + +  // allocate the bitmap +  traceMessage("Gouraud triangle shading fill bitmap"); +  SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, +					  gTrue, gTrue, parentBitmap); + +  // clear the bitmap +  memset(bitmap->getDataPtr(), 0, bitmap->getHeight() * bitmap->getRowSize()); +  memset(bitmap->getAlphaPtr(), 0, bitmap->getHeight() * bitmap->getWidth()); + +  // draw the triangles +  for (int i = 0; i < shading->getNTriangles(); ++i) { +    double x0, y0, x1, y1, x2, y2; +    double color0[gfxColorMaxComps]; +    double color1[gfxColorMaxComps]; +    double color2[gfxColorMaxComps]; +    shading->getTriangle(i, &x0, &y0, color0, +			 &x1, &y1, color1, +			 &x2, &y2, color2); +    gouraudFillTriangle(state, bitmap, mode, reverseVideo, +			xMin, yMin, xMax, yMax, +			x0, y0, color0, x1, y1, color1, x2, y2, color2, +			shading); +  } + +  *xOut = xMin; +  *yOut = yMin; +  return bitmap; +} + +void ShadingImage::gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap, +				       SplashColorMode mode, +				       GBool reverseVideo, +				       int xMin, int yMin, int xMax, int yMax, +				       double x0, double y0, double *color0, +				       double x1, double y1, double *color1, +				       double x2, double y2, double *color2, +				       GfxGouraudTriangleShading *shading) { +  int nShadingComps = shading->getNComps(); +  int nBitmapComps = splashColorModeNComps[mode]; + +  //--- transform the vertices to device space, sort by y +  double dx0, dy0, dx1, dy1, dx2, dy2; +  state->transform(x0, y0, &dx0, &dy0); +  state->transform(x1, y1, &dx1, &dy1); +  state->transform(x2, y2, &dx2, &dy2); +  if (dy0 > dy1) { +    double t = dx0;  dx0 = dx1;  dx1 = t; +    t = dy0;  dy0 = dy1;  dy1 = t; +    double *tc = color0;  color0 = color1;  color1 = tc; +  } +  if (dy1 > dy2) { +    double t = dx1;  dx1 = dx2;  dx2 = t; +    t = dy1;  dy1 = dy2;  dy2 = t; +    double *tc = color1;  color1 = color2;  color2 = tc; +  } +  if (dy0 > dy1) { +    double t = dx0;  dx0 = dx1;  dx1 = t; +    t = dy0;  dy0 = dy1;  dy1 = t; +    double *tc = color0;  color0 = color1;  color1 = tc; +  } + +  //--- y loop +  int syMin = (int)floor(dy0); +  if (syMin < yMin) { +    syMin = yMin; +  } +  int syMax = (int)floor(dy2) + 1; +  if (syMax > yMax) { +    syMax = yMax; +  } +  for (int sy = syMin; sy < syMax; ++sy) { + +    //--- vertical interpolation +    double xx0, xx1; +    double cc0[gfxColorMaxComps], cc1[gfxColorMaxComps]; +    if (sy <= dy0) { +      xx0 = xx1 = dx0; +      for (int i = 0; i < nShadingComps; ++i) { +	cc0[i] = cc1[i] = color0[i]; +      } +    } else if (sy >= dy2) { +      xx0 = xx1 = dx2; +      for (int i = 0; i < nShadingComps; ++i) { +	cc0[i] = cc1[i] = color2[i]; +      } +    } else { +      if (sy <= dy1) { +	double interp = (sy - dy0) / (dy1 - dy0); +	xx0 = dx0 + interp * (dx1 - dx0); +	for (int i = 0; i < nShadingComps; ++i) { +	  cc0[i] = color0[i] + interp * (color1[i] - color0[i]); +	} +      } else { +	double interp = (sy - dy1) / (dy2 - dy1); +	xx0 = dx1 + interp * (dx2 - dx1); +	for (int i = 0; i < nShadingComps; ++i) { +	  cc0[i] = color1[i] + interp * (color2[i] - color1[i]); +	} +      } +      double interp = (sy - dy0) / (dy2 - dy0); +      xx1 = dx0 + interp * (dx2 - dx0); +      for (int i = 0; i < nShadingComps; ++i) { +	cc1[i] = color0[i] + interp * (color2[i] - color0[i]); +      } +    } + +    //--- x loop +    if (xx0 > xx1) { +      double t = xx0;  xx0 = xx1;  xx1 = t; +      for (int i = 0; i < nShadingComps; ++i) { +	t = cc0[i];  cc0[i] = cc1[i];  cc1[i] = t; +      } +    } +    int sxMin = (int)floor(xx0); +    if (sxMin < xMin) { +      sxMin = xMin; +    } +    int sxMax = (int)floor(xx1) + 1; +    if (sxMax > xMax) { +      sxMax = xMax; +    } +    SplashColorPtr dataPtr = bitmap->getDataPtr() +                             + (sy - yMin) * bitmap->getRowSize() +                             + (sxMin - xMin) * nBitmapComps; +    if (sxMin < sxMax) { +      Guchar *alphaPtr = bitmap->getAlphaPtr() +	                 + (sy - yMin) * bitmap->getWidth() +	                 + (sxMin - xMin); +      memset(alphaPtr, 0xff, sxMax - sxMin); +    } +    for (int sx = sxMin; sx < sxMax; ++sx) { + +      //--- horizontal interpolation +      double cc[gfxColorMaxComps]; +      if (sx <= xx0) { +	for (int i = 0; i < nShadingComps; ++i) { +	  cc[i] = cc0[i]; +	} +      } else if (sx >= xx1) { +	for (int i = 0; i < nShadingComps; ++i) { +	  cc[i] = cc1[i]; +	} +      } else { +	for (int i = 0; i < nShadingComps; ++i) { +	  double interp = (sx - xx0) / (xx1 - xx0); +	  cc[i] = cc0[i] + interp * (cc1[i] - cc0[i]); +	} +      } + +      //--- compute color and set pixel +      GfxColor gColor; +      shading->getColor(cc, &gColor); +      SplashColor sColor; +      computeShadingColor(state, mode, reverseVideo, &gColor, sColor); +      for (int i = 0; i < nBitmapComps; ++i) { +	dataPtr[i] = sColor[i]; +      } +      dataPtr += nBitmapComps; +    } +  } +} + +SplashBitmap *ShadingImage::generatePatchMeshBitmap( +					GfxState *state, +					GfxPatchMeshShading *shading, +					SplashColorMode mode, +					GBool reverseVideo, +					Splash *parentSplash, +					SplashBitmap *parentBitmap, +					int *xOut, int *yOut) { +  // get the clip bbox +  double fxMin, fyMin, fxMax, fyMax; +  state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // get the shading bbox +  double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax; +  shading->getBBox(&tx0, &ty0, &tx1, &ty1); +  state->transform(tx0, ty0, &dx, &dy); +  txMin = txMax = dx; +  tyMin = tyMax = dy; +  state->transform(tx0, ty1, &dx, &dy); +  if (dx < txMin) { +    txMin = dx; +  } else if (dx > txMax) { +    txMax = dx; +  } +  if (dy < tyMin) { +    tyMin = dy; +  } else if (dy > tyMax) { +    tyMax = dy; +  } +  state->transform(tx1, ty0, &dx, &dy); +  if (dx < txMin) { +    txMin = dx; +  } else if (dx > txMax) { +    txMax = dx; +  } +  if (dy < tyMin) { +    tyMin = dy; +  } else if (dy > tyMax) { +    tyMax = dy; +  } +  state->transform(tx1, ty1, &dx, &dy); +  if (dx < txMin) { +    txMin = dx; +  } else if (dx > txMax) { +    txMax = dx; +  } +  if (dy < tyMin) { +    tyMin = dy; +  } else if (dy > tyMax) { +    tyMax = dy; +  } +  if (txMin > fxMin) { +    fxMin = txMin; +  } +  if (txMax < fxMax) { +    fxMax = txMax; +  } +  if (tyMin > fyMin) { +    fyMin = tyMin; +  } +  if (tyMax < fyMax) { +    fyMax = tyMax; +  } +  if (fxMin > fxMax || fyMin > fyMax) { +    return NULL; +  } + +  // convert to integer coords +  int xMin = (int)floor(fxMin); +  int yMin = (int)floor(fyMin); +  int xMax = (int)floor(fxMax) + 1; +  int yMax = (int)floor(fyMax) + 1; +  int bitmapWidth = xMax - xMin; +  int bitmapHeight = yMax - yMin; + +  // allocate the bitmap +  traceMessage("Gouraud triangle shading fill bitmap"); +  SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, +					  gTrue, gTrue, parentBitmap); + +  // allocate a Splash object +  // vector antialiasing is disabled to avoid artifacts along triangle edges +  Splash *splash = new Splash(bitmap, gFalse, +			      parentSplash->getImageCache(), +			      parentSplash->getScreen()); +  SplashColor zero; +  for (int i = 0; i < splashColorModeNComps[mode]; ++i) { +    zero[i] = 0; +  } +  splash->clear(zero, 0x00); + +  // draw the patches +  int start; +  if (shading->getNPatches() > 128) { +    start = 3; +  } else if (shading->getNPatches() > 64) { +    start = 2; +  } else if (shading->getNPatches() > 16) { +    start = 1; +  } else { +    start = 0; +  } +  for (int i = 0; i < shading->getNPatches(); ++i) { +    fillPatch(state, splash, mode, reverseVideo, +	      xMin, yMin, shading->getPatch(i), shading, start); +  } + +  delete splash; + +  *xOut = xMin; +  *yOut = yMin; +  return bitmap; +} + +void ShadingImage::fillPatch(GfxState *state, Splash *splash, +			     SplashColorMode mode, GBool reverseVideo, +			     int xMin, int yMin, +			     GfxPatch *patch, +			     GfxPatchMeshShading *shading, +			     int depth) { +  GfxColor c00; +  shading->getColor(patch->color[0][0], &c00); +  GBool stop = gFalse; + +  // stop subdivision at max depth +  if (depth == patchMaxDepth) { +    stop = gTrue; +  } + +  // stop subdivision if colors are close enough +  if (!stop) { +    int nComps = shading->getColorSpace()->getNComps(); +    GfxColor c01, c10, c11; +    shading->getColor(patch->color[0][1], &c01); +    shading->getColor(patch->color[1][0], &c10); +    shading->getColor(patch->color[1][1], &c11); +    int i; +    for (i = 0; i < nComps; ++i) { +      if (abs(c00.c[i] - c01.c[i]) > patchColorDelta || +	  abs(c01.c[i] - c11.c[i]) > patchColorDelta || +	  abs(c11.c[i] - c10.c[i]) > patchColorDelta || +	  abs(c10.c[i] - c00.c[i]) > patchColorDelta) { +	break; +      } +    } +    if (i == nComps) { +      stop = gTrue; +    } +  } + +  // stop subdivision if patch is small enough +  if (!stop) { +    double xxMin = 0; +    double yyMin = 0; +    double xxMax = 0; +    double yyMax = 0; +    for (int j = 0; j < 4; ++j) { +      for (int i = 0; i < 4; ++i) { +	double xx, yy; +	state->transformDelta(patch->x[i][j], patch->y[i][j], &xx, &yy); +	if (i == 0 && j == 0) { +	  xxMin = xxMax = xx; +	  yyMin = yyMax = yy; +	} else { +	  if (xx < xxMin) { +	    xxMin = xx; +	  } else if (xx > xxMax) { +	    xxMax = xx; +	  } +	  if (yy < yyMin) { +	    yyMin = yy; +	  } else if (yy > yyMax) { +	    yyMax = yy; +	  } +	} +      } +    } +    if (xxMax - xxMin < 1 && yyMax - yyMin < 1) { +      stop = gTrue; +    } +  } + +  // draw the patch +  if (stop) { +    SplashColor sColor; +    computeShadingColor(state, mode, reverseVideo, &c00, sColor); +    splash->setFillPattern(new SplashSolidColor(sColor)); +    SplashPath *path = new SplashPath(); +    double xx0, yy0, xx1, yy1, xx2, yy2, xx3, yy3; +    state->transform(patch->x[0][0], patch->y[0][0], &xx0, &yy0); +    path->moveTo(xx0 - xMin, yy0 - yMin); +    state->transform(patch->x[0][1], patch->y[0][1], &xx1, &yy1); +    state->transform(patch->x[0][2], patch->y[0][2], &xx2, &yy2); +    state->transform(patch->x[0][3], patch->y[0][3], &xx3, &yy3); +    path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, +		  xx3 - xMin, yy3 - yMin); +    state->transform(patch->x[1][3], patch->y[1][3], &xx1, &yy1); +    state->transform(patch->x[2][3], patch->y[2][3], &xx2, &yy2); +    state->transform(patch->x[3][3], patch->y[3][3], &xx3, &yy3); +    path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, +		  xx3 - xMin, yy3 - yMin); +    state->transform(patch->x[3][2], patch->y[3][2], &xx1, &yy1); +    state->transform(patch->x[3][1], patch->y[3][1], &xx2, &yy2); +    state->transform(patch->x[3][0], patch->y[3][0], &xx3, &yy3); +    path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, +		  xx3 - xMin, yy3 - yMin); +    state->transform(patch->x[2][0], patch->y[2][0], &xx1, &yy1); +    state->transform(patch->x[1][0], patch->y[1][0], &xx2, &yy2); +    path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, +		  xx0 - xMin, yy0 - yMin); +    path->close(); +    splash->fill(path, gFalse); +    delete path; + +  // subdivide the patch +  } else { +    double xx[4][8], yy[4][8]; +    for (int i = 0; i < 4; ++i) { +      xx[i][0] = patch->x[i][0]; +      yy[i][0] = patch->y[i][0]; +      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]); +      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]); +      double xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]); +      double yym = 0.5 * (patch->y[i][1] + patch->y[i][2]); +      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]); +      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]); +      xx[i][2] = 0.5 * (xx[i][1] + xxm); +      yy[i][2] = 0.5 * (yy[i][1] + yym); +      xx[i][5] = 0.5 * (xxm + xx[i][6]); +      yy[i][5] = 0.5 * (yym + yy[i][6]); +      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]); +      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]); +      xx[i][7] = patch->x[i][3]; +      yy[i][7] = patch->y[i][3]; +    } +    GfxPatch patch00, patch01, patch10, patch11; +    for (int i = 0; i < 4; ++i) { +      patch00.x[0][i] = xx[0][i]; +      patch00.y[0][i] = yy[0][i]; +      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]); +      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]); +      double xxm = 0.5 * (xx[1][i] + xx[2][i]); +      double yym = 0.5 * (yy[1][i] + yy[2][i]); +      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]); +      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]); +      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm); +      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym); +      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]); +      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]); +      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]); +      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]); +      patch10.x[0][i] = patch00.x[3][i]; +      patch10.y[0][i] = patch00.y[3][i]; +      patch10.x[3][i] = xx[3][i]; +      patch10.y[3][i] = yy[3][i]; +    } +    for (int i = 4; i < 8; ++i) { +      patch01.x[0][i-4] = xx[0][i]; +      patch01.y[0][i-4] = yy[0][i]; +      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]); +      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]); +      double xxm = 0.5 * (xx[1][i] + xx[2][i]); +      double yym = 0.5 * (yy[1][i] + yy[2][i]); +      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]); +      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]); +      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm); +      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym); +      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]); +      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]); +      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]); +      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]); +      patch11.x[0][i-4] = patch01.x[3][i-4]; +      patch11.y[0][i-4] = patch01.y[3][i-4]; +      patch11.x[3][i-4] = xx[3][i]; +      patch11.y[3][i-4] = yy[3][i]; +    } +    for (int i = 0; i < shading->getNComps(); ++i) { +      patch00.color[0][0][i] = patch->color[0][0][i]; +      patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] + +				      patch->color[0][1][i]); +      patch01.color[0][0][i] = patch00.color[0][1][i]; +      patch01.color[0][1][i] = patch->color[0][1][i]; +      patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] + +				      patch->color[1][1][i]); +      patch11.color[0][1][i] = patch01.color[1][1][i]; +      patch11.color[1][1][i] = patch->color[1][1][i]; +      patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] + +				      patch->color[1][0][i]); +      patch10.color[1][1][i] = patch11.color[1][0][i]; +      patch10.color[1][0][i] = patch->color[1][0][i]; +      patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] + +				      patch->color[0][0][i]); +      patch00.color[1][0][i] = patch10.color[0][0][i]; +      patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] + +				      patch01.color[1][1][i]); +      patch01.color[1][0][i] = patch00.color[1][1][i]; +      patch11.color[0][0][i] = patch00.color[1][1][i]; +      patch10.color[0][1][i] = patch00.color[1][1][i]; +    } +    fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch00, +	      shading, depth + 1); +    fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch10, +	      shading, depth + 1); +    fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch01, +	      shading, depth + 1); +    fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch11, +	      shading, depth + 1); +  } +} + +void ShadingImage::computeShadingColor(GfxState *state, +				       SplashColorMode mode, +				       GBool reverseVideo, +				       GfxColor *color, +				       SplashColorPtr sColor) { +  GfxGray gray; +  GfxRGB rgb; +#if SPLASH_CMYK +  GfxCMYK cmyk; +#endif + +  state->setFillColor(color); +  switch (mode) { +  case splashModeMono8: +    state->getFillGray(&gray); +    if (reverseVideo) { +      gray = gfxColorComp1 - gray; +    } +    sColor[0] = colToByte(gray); +    break; +  case splashModeRGB8: +    state->getFillRGB(&rgb); +    if (reverseVideo) { +      rgb.r = gfxColorComp1 - rgb.r; +      rgb.g = gfxColorComp1 - rgb.g; +      rgb.b = gfxColorComp1 - rgb.b; +    } +    sColor[0] = colToByte(rgb.r); +    sColor[1] = colToByte(rgb.g); +    sColor[2] = colToByte(rgb.b); +    break; +#if SPLASH_CMYK +  case splashModeCMYK8: +    state->getFillCMYK(&cmyk); +    sColor[0] = colToByte(cmyk.c); +    sColor[1] = colToByte(cmyk.m); +    sColor[2] = colToByte(cmyk.y); +    sColor[3] = colToByte(cmyk.k); +    break; +#endif +  case splashModeMono1: +  case splashModeBGR8: +    // mode cannot be Mono1 or BGR8 +    break; +  } +} + +// Transform a user space bbox to a device space bbox. +void ShadingImage::transformBBox(GfxState *state, +				 double uxMin, double uyMin, +				 double uxMax, double uyMax, +				 double *dxMin, double *dyMin, +				 double *dxMax, double *dyMax) { +  double tx, ty; +  state->transform(uxMin, uyMin, &tx, &ty); +  *dxMin = *dxMax = tx; +  *dyMin = *dyMax = ty; +  state->transform(uxMin, uyMax, &tx, &ty); +  if (tx < *dxMin) { +    *dxMin = tx; +  } else if (tx > *dxMax) { +    *dxMax = tx; +  } +  if (ty < *dyMin) { +    *dyMin = ty; +  } else if (ty > *dyMax) { +    *dyMax = ty; +  } +  state->transform(uxMax, uyMin, &tx, &ty); +  if (tx < *dxMin) { +    *dxMin = tx; +  } else if (tx > *dxMax) { +    *dxMax = tx; +  } +  if (ty < *dyMin) { +    *dyMin = ty; +  } else if (ty > *dyMax) { +    *dyMax = ty; +  } +  state->transform(uxMax, uyMax, &tx, &ty); +  if (tx < *dxMin) { +    *dxMin = tx; +  } else if (tx > *dxMax) { +    *dxMax = tx; +  } +  if (ty < *dyMin) { +    *dyMin = ty; +  } else if (ty > *dyMax) { +    *dyMax = ty; +  } +} +  | 
