1328 lines
37 KiB
C++
1328 lines
37 KiB
C++
//========================================================================
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
|