You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3336 lines
85 KiB
3336 lines
85 KiB
//========================================================================
|
|
//
|
|
// Splash.cc
|
|
//
|
|
//========================================================================
|
|
|
|
#include <aconf.h>
|
|
|
|
#ifdef USE_GCC_PRAGMAS
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "gmem.h"
|
|
#include "SplashErrorCodes.h"
|
|
#include "SplashMath.h"
|
|
#include "SplashBitmap.h"
|
|
#include "SplashState.h"
|
|
#include "SplashPath.h"
|
|
#include "SplashXPath.h"
|
|
#include "SplashXPathScanner.h"
|
|
#include "SplashPattern.h"
|
|
#include "SplashScreen.h"
|
|
#include "SplashFont.h"
|
|
#include "SplashGlyphBitmap.h"
|
|
#include "Splash.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
// distance of Bezier control point from center for circle approximation
|
|
// = (4 * (sqrt(2) - 1) / 3) * r
|
|
#define bezierCircle ((SplashCoord)0.55228475)
|
|
#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
|
|
|
|
// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
|
|
static inline Guchar div255(int x) {
|
|
return (Guchar)((x + (x >> 8) + 0x80) >> 8);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// SplashPipe
|
|
//------------------------------------------------------------------------
|
|
|
|
#define splashPipeMaxStages 9
|
|
|
|
struct SplashPipe {
|
|
// pixel coordinates
|
|
int x, y;
|
|
|
|
// source pattern
|
|
SplashPattern *pattern;
|
|
|
|
// source alpha and color
|
|
SplashCoord aInput;
|
|
GBool usesShape;
|
|
Guchar aSrc;
|
|
SplashColorPtr cSrc;
|
|
SplashColor cSrcVal;
|
|
|
|
// non-isolated group alpha0
|
|
Guchar *alpha0Ptr;
|
|
|
|
// soft mask
|
|
SplashColorPtr softMaskPtr;
|
|
|
|
// destination alpha and color
|
|
SplashColorPtr destColorPtr;
|
|
int destColorMask;
|
|
Guchar *destAlphaPtr;
|
|
|
|
// shape
|
|
SplashCoord shape;
|
|
|
|
// result alpha and color
|
|
GBool noTransparency;
|
|
SplashPipeResultColorCtrl resultColorCtrl;
|
|
|
|
// non-isolated group correction
|
|
int nonIsolatedGroup;
|
|
};
|
|
|
|
SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
|
|
splashPipeResultColorNoAlphaBlendMono,
|
|
splashPipeResultColorNoAlphaBlendMono,
|
|
splashPipeResultColorNoAlphaBlendRGB,
|
|
splashPipeResultColorNoAlphaBlendRGB
|
|
#if SPLASH_CMYK
|
|
,
|
|
splashPipeResultColorNoAlphaBlendCMYK
|
|
#endif
|
|
};
|
|
|
|
SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
|
|
splashPipeResultColorAlphaNoBlendMono,
|
|
splashPipeResultColorAlphaNoBlendMono,
|
|
splashPipeResultColorAlphaNoBlendRGB,
|
|
splashPipeResultColorAlphaNoBlendRGB
|
|
#if SPLASH_CMYK
|
|
,
|
|
splashPipeResultColorAlphaNoBlendCMYK
|
|
#endif
|
|
};
|
|
|
|
SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
|
|
splashPipeResultColorAlphaBlendMono,
|
|
splashPipeResultColorAlphaBlendMono,
|
|
splashPipeResultColorAlphaBlendRGB,
|
|
splashPipeResultColorAlphaBlendRGB
|
|
#if SPLASH_CMYK
|
|
,
|
|
splashPipeResultColorAlphaBlendCMYK
|
|
#endif
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static void blendXor(SplashColorPtr src, SplashColorPtr dest,
|
|
SplashColorPtr blend, SplashColorMode cm) {
|
|
int i;
|
|
|
|
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
|
|
blend[i] = src[i] ^ dest[i];
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// modified region
|
|
//------------------------------------------------------------------------
|
|
|
|
void Splash::clearModRegion() {
|
|
modXMin = bitmap->getWidth();
|
|
modYMin = bitmap->getHeight();
|
|
modXMax = -1;
|
|
modYMax = -1;
|
|
}
|
|
|
|
inline void Splash::updateModX(int x) {
|
|
if (x < modXMin) {
|
|
modXMin = x;
|
|
}
|
|
if (x > modXMax) {
|
|
modXMax = x;
|
|
}
|
|
}
|
|
|
|
inline void Splash::updateModY(int y) {
|
|
if (y < modYMin) {
|
|
modYMin = y;
|
|
}
|
|
if (y > modYMax) {
|
|
modYMax = y;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// pipeline
|
|
//------------------------------------------------------------------------
|
|
|
|
inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
|
|
SplashPattern *pattern, SplashColorPtr cSrc,
|
|
SplashCoord aInput, GBool usesShape,
|
|
GBool nonIsolatedGroup) {
|
|
pipeSetXY(pipe, x, y);
|
|
pipe->pattern = NULL;
|
|
|
|
// source color
|
|
if (pattern) {
|
|
if (pattern->isStatic()) {
|
|
pattern->getColor(x, y, pipe->cSrcVal);
|
|
} else {
|
|
pipe->pattern = pattern;
|
|
}
|
|
pipe->cSrc = pipe->cSrcVal;
|
|
} else {
|
|
pipe->cSrc = cSrc;
|
|
}
|
|
|
|
// source alpha
|
|
pipe->aInput = aInput;
|
|
if (!state->softMask) {
|
|
if (usesShape) {
|
|
pipe->aInput *= 255;
|
|
} else {
|
|
pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);
|
|
}
|
|
}
|
|
pipe->usesShape = usesShape;
|
|
|
|
// result alpha
|
|
if (aInput == 1 && !state->softMask && !usesShape &&
|
|
!state->inNonIsolatedGroup) {
|
|
pipe->noTransparency = gTrue;
|
|
} else {
|
|
pipe->noTransparency = gFalse;
|
|
}
|
|
|
|
// result color
|
|
if (pipe->noTransparency) {
|
|
// the !state->blendFunc case is handled separately in pipeRun
|
|
pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
|
|
} else if (!state->blendFunc) {
|
|
pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
|
|
} else {
|
|
pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
|
|
}
|
|
|
|
// non-isolated group correction
|
|
if (nonIsolatedGroup) {
|
|
pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];
|
|
} else {
|
|
pipe->nonIsolatedGroup = 0;
|
|
}
|
|
}
|
|
|
|
inline void Splash::pipeRun(SplashPipe *pipe) {
|
|
Guchar aSrc, aDest, alpha2, alpha0, aResult;
|
|
SplashColor cDest, cBlend;
|
|
Guchar cResult0, cResult1, cResult2, cResult3;
|
|
|
|
//----- source color
|
|
|
|
// static pattern: handled in pipeInit
|
|
// fixed color: handled in pipeInit
|
|
|
|
// dynamic pattern
|
|
if (pipe->pattern) {
|
|
pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
|
|
}
|
|
|
|
if (pipe->noTransparency && !state->blendFunc) {
|
|
|
|
//----- write destination pixel
|
|
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
cResult0 = pipe->cSrc[0];
|
|
if (state->screen->test(pipe->x, pipe->y, cResult0)) {
|
|
*pipe->destColorPtr |= pipe->destColorMask;
|
|
} else {
|
|
*pipe->destColorPtr &= ~pipe->destColorMask;
|
|
}
|
|
if (!(pipe->destColorMask >>= 1)) {
|
|
pipe->destColorMask = 0x80;
|
|
++pipe->destColorPtr;
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
*pipe->destColorPtr++ = pipe->cSrc[0];
|
|
break;
|
|
case splashModeRGB8:
|
|
*pipe->destColorPtr++ = pipe->cSrc[0];
|
|
*pipe->destColorPtr++ = pipe->cSrc[1];
|
|
*pipe->destColorPtr++ = pipe->cSrc[2];
|
|
break;
|
|
case splashModeBGR8:
|
|
*pipe->destColorPtr++ = pipe->cSrc[2];
|
|
*pipe->destColorPtr++ = pipe->cSrc[1];
|
|
*pipe->destColorPtr++ = pipe->cSrc[0];
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
*pipe->destColorPtr++ = pipe->cSrc[0];
|
|
*pipe->destColorPtr++ = pipe->cSrc[1];
|
|
*pipe->destColorPtr++ = pipe->cSrc[2];
|
|
*pipe->destColorPtr++ = pipe->cSrc[3];
|
|
break;
|
|
#endif
|
|
}
|
|
if (pipe->destAlphaPtr) {
|
|
*pipe->destAlphaPtr++ = 255;
|
|
}
|
|
|
|
} else {
|
|
|
|
//----- read destination pixel
|
|
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
|
|
break;
|
|
case splashModeMono8:
|
|
cDest[0] = *pipe->destColorPtr;
|
|
break;
|
|
case splashModeRGB8:
|
|
cDest[0] = pipe->destColorPtr[0];
|
|
cDest[1] = pipe->destColorPtr[1];
|
|
cDest[2] = pipe->destColorPtr[2];
|
|
break;
|
|
case splashModeBGR8:
|
|
cDest[0] = pipe->destColorPtr[2];
|
|
cDest[1] = pipe->destColorPtr[1];
|
|
cDest[2] = pipe->destColorPtr[0];
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
cDest[0] = pipe->destColorPtr[0];
|
|
cDest[1] = pipe->destColorPtr[1];
|
|
cDest[2] = pipe->destColorPtr[2];
|
|
cDest[3] = pipe->destColorPtr[3];
|
|
break;
|
|
#endif
|
|
}
|
|
if (pipe->destAlphaPtr) {
|
|
aDest = *pipe->destAlphaPtr;
|
|
} else {
|
|
aDest = 0xff;
|
|
}
|
|
|
|
//----- blend function
|
|
|
|
if (state->blendFunc) {
|
|
(*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);
|
|
}
|
|
|
|
//----- source alpha
|
|
|
|
if (state->softMask) {
|
|
if (pipe->usesShape) {
|
|
aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++
|
|
* pipe->shape);
|
|
} else {
|
|
aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);
|
|
}
|
|
} else if (pipe->usesShape) {
|
|
// pipe->aInput is premultiplied by 255 in pipeInit
|
|
aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);
|
|
} else {
|
|
// precomputed in pipeInit
|
|
aSrc = pipe->aSrc;
|
|
}
|
|
|
|
//----- result alpha and non-isolated group element correction
|
|
|
|
if (pipe->noTransparency) {
|
|
alpha2 = aResult = 255;
|
|
} else {
|
|
aResult = aSrc + aDest - div255(aSrc * aDest);
|
|
|
|
if (pipe->alpha0Ptr) {
|
|
alpha0 = *pipe->alpha0Ptr++;
|
|
alpha2 = aResult + alpha0 - div255(aResult * alpha0);
|
|
} else {
|
|
alpha2 = aResult;
|
|
}
|
|
}
|
|
|
|
//----- result color
|
|
|
|
cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
|
|
|
|
switch (pipe->resultColorCtrl) {
|
|
|
|
#if SPLASH_CMYK
|
|
case splashPipeResultColorNoAlphaBlendCMYK:
|
|
cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);
|
|
#endif
|
|
case splashPipeResultColorNoAlphaBlendRGB:
|
|
cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);
|
|
cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);
|
|
case splashPipeResultColorNoAlphaBlendMono:
|
|
cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);
|
|
break;
|
|
|
|
case splashPipeResultColorAlphaNoBlendMono:
|
|
if (alpha2 == 0) {
|
|
cResult0 = 0;
|
|
} else {
|
|
cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
|
|
aSrc * pipe->cSrc[0]) / alpha2);
|
|
}
|
|
break;
|
|
case splashPipeResultColorAlphaNoBlendRGB:
|
|
if (alpha2 == 0) {
|
|
cResult0 = 0;
|
|
cResult1 = 0;
|
|
cResult2 = 0;
|
|
} else {
|
|
cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
|
|
aSrc * pipe->cSrc[0]) / alpha2);
|
|
cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
|
|
aSrc * pipe->cSrc[1]) / alpha2);
|
|
cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
|
|
aSrc * pipe->cSrc[2]) / alpha2);
|
|
}
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashPipeResultColorAlphaNoBlendCMYK:
|
|
if (alpha2 == 0) {
|
|
cResult0 = 0;
|
|
cResult1 = 0;
|
|
cResult2 = 0;
|
|
cResult3 = 0;
|
|
} else {
|
|
cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
|
|
aSrc * pipe->cSrc[0]) / alpha2);
|
|
cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
|
|
aSrc * pipe->cSrc[1]) / alpha2);
|
|
cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
|
|
aSrc * pipe->cSrc[2]) / alpha2);
|
|
cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
|
|
aSrc * pipe->cSrc[3]) / alpha2);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case splashPipeResultColorAlphaBlendMono:
|
|
if (alpha2 == 0) {
|
|
cResult0 = 0;
|
|
} else {
|
|
cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[0] +
|
|
aDest * cBlend[0]) / 255) /
|
|
alpha2);
|
|
}
|
|
break;
|
|
case splashPipeResultColorAlphaBlendRGB:
|
|
if (alpha2 == 0) {
|
|
cResult0 = 0;
|
|
cResult1 = 0;
|
|
cResult2 = 0;
|
|
} else {
|
|
cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[0] +
|
|
aDest * cBlend[0]) / 255) /
|
|
alpha2);
|
|
cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[1] +
|
|
aDest * cBlend[1]) / 255) /
|
|
alpha2);
|
|
cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[2] +
|
|
aDest * cBlend[2]) / 255) /
|
|
alpha2);
|
|
}
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashPipeResultColorAlphaBlendCMYK:
|
|
if (alpha2 == 0) {
|
|
cResult0 = 0;
|
|
cResult1 = 0;
|
|
cResult2 = 0;
|
|
cResult3 = 0;
|
|
} else {
|
|
cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[0] +
|
|
aDest * cBlend[0]) / 255) /
|
|
alpha2);
|
|
cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[1] +
|
|
aDest * cBlend[1]) / 255) /
|
|
alpha2);
|
|
cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[2] +
|
|
aDest * cBlend[2]) / 255) /
|
|
alpha2);
|
|
cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
|
|
aSrc * ((255 - aDest) * pipe->cSrc[3] +
|
|
aDest * cBlend[3]) / 255) /
|
|
alpha2);
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
//----- non-isolated group correction
|
|
|
|
if (aResult != 0) {
|
|
switch (pipe->nonIsolatedGroup) {
|
|
#if SPLASH_CMYK
|
|
case 4:
|
|
cResult3 += (cResult3 - cDest[3]) * aDest *
|
|
(255 - aResult) / (255 * aResult);
|
|
#endif
|
|
case 3:
|
|
cResult2 += (cResult2 - cDest[2]) * aDest *
|
|
(255 - aResult) / (255 * aResult);
|
|
cResult1 += (cResult1 - cDest[1]) * aDest *
|
|
(255 - aResult) / (255 * aResult);
|
|
case 1:
|
|
cResult0 += (cResult0 - cDest[0]) * aDest *
|
|
(255 - aResult) / (255 * aResult);
|
|
case 0:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----- write destination pixel
|
|
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
if (state->screen->test(pipe->x, pipe->y, cResult0)) {
|
|
*pipe->destColorPtr |= pipe->destColorMask;
|
|
} else {
|
|
*pipe->destColorPtr &= ~pipe->destColorMask;
|
|
}
|
|
if (!(pipe->destColorMask >>= 1)) {
|
|
pipe->destColorMask = 0x80;
|
|
++pipe->destColorPtr;
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
*pipe->destColorPtr++ = cResult0;
|
|
break;
|
|
case splashModeRGB8:
|
|
*pipe->destColorPtr++ = cResult0;
|
|
*pipe->destColorPtr++ = cResult1;
|
|
*pipe->destColorPtr++ = cResult2;
|
|
break;
|
|
case splashModeBGR8:
|
|
*pipe->destColorPtr++ = cResult2;
|
|
*pipe->destColorPtr++ = cResult1;
|
|
*pipe->destColorPtr++ = cResult0;
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
*pipe->destColorPtr++ = cResult0;
|
|
*pipe->destColorPtr++ = cResult1;
|
|
*pipe->destColorPtr++ = cResult2;
|
|
*pipe->destColorPtr++ = cResult3;
|
|
break;
|
|
#endif
|
|
}
|
|
if (pipe->destAlphaPtr) {
|
|
*pipe->destAlphaPtr++ = aResult;
|
|
}
|
|
|
|
}
|
|
|
|
++pipe->x;
|
|
}
|
|
|
|
inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
|
|
pipe->x = x;
|
|
pipe->y = y;
|
|
if (state->softMask) {
|
|
pipe->softMaskPtr =
|
|
&state->softMask->data[y * state->softMask->rowSize + x];
|
|
}
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
|
|
pipe->destColorMask = 0x80 >> (x & 7);
|
|
break;
|
|
case splashModeMono8:
|
|
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
|
|
break;
|
|
case splashModeRGB8:
|
|
case splashModeBGR8:
|
|
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
|
|
break;
|
|
#endif
|
|
}
|
|
if (bitmap->alpha) {
|
|
pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
|
|
} else {
|
|
pipe->destAlphaPtr = NULL;
|
|
}
|
|
if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
|
|
pipe->alpha0Ptr =
|
|
&alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
|
|
(alpha0X + x)];
|
|
} else {
|
|
pipe->alpha0Ptr = NULL;
|
|
}
|
|
}
|
|
|
|
inline void Splash::pipeIncX(SplashPipe *pipe) {
|
|
++pipe->x;
|
|
if (state->softMask) {
|
|
++pipe->softMaskPtr;
|
|
}
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
if (!(pipe->destColorMask >>= 1)) {
|
|
pipe->destColorMask = 0x80;
|
|
++pipe->destColorPtr;
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
++pipe->destColorPtr;
|
|
break;
|
|
case splashModeRGB8:
|
|
case splashModeBGR8:
|
|
pipe->destColorPtr += 3;
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
pipe->destColorPtr += 4;
|
|
break;
|
|
#endif
|
|
}
|
|
if (pipe->destAlphaPtr) {
|
|
++pipe->destAlphaPtr;
|
|
}
|
|
if (pipe->alpha0Ptr) {
|
|
++pipe->alpha0Ptr;
|
|
}
|
|
}
|
|
|
|
inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
|
|
if (noClip || state->clip->test(x, y)) {
|
|
pipeSetXY(pipe, x, y);
|
|
pipeRun(pipe);
|
|
updateModX(x);
|
|
updateModY(y);
|
|
}
|
|
}
|
|
|
|
inline void Splash::drawAAPixelInit() {
|
|
aaBufY = -1;
|
|
}
|
|
|
|
inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
|
|
#if splashAASize == 4
|
|
static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
|
|
1, 2, 2, 3, 2, 3, 3, 4 };
|
|
int w;
|
|
#else
|
|
int xx, yy;
|
|
#endif
|
|
SplashColorPtr p;
|
|
int x0, x1, t;
|
|
|
|
if (x < 0 || x >= bitmap->width ||
|
|
y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
|
|
return;
|
|
}
|
|
|
|
// update aaBuf
|
|
if (y != aaBufY) {
|
|
memset(aaBuf->getDataPtr(), 0xff,
|
|
aaBuf->getRowSize() * aaBuf->getHeight());
|
|
x0 = 0;
|
|
x1 = bitmap->width - 1;
|
|
state->clip->clipAALine(aaBuf, &x0, &x1, y);
|
|
aaBufY = y;
|
|
}
|
|
|
|
// compute the shape value
|
|
#if splashAASize == 4
|
|
p = aaBuf->getDataPtr() + (x >> 1);
|
|
w = aaBuf->getRowSize();
|
|
if (x & 1) {
|
|
t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
|
|
bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
|
|
} else {
|
|
t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
|
|
bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
|
|
}
|
|
#else
|
|
t = 0;
|
|
for (yy = 0; yy < splashAASize; ++yy) {
|
|
for (xx = 0; xx < splashAASize; ++xx) {
|
|
p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
|
|
((x * splashAASize + xx) >> 3);
|
|
t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// draw the pixel
|
|
if (t != 0) {
|
|
pipeSetXY(pipe, x, y);
|
|
pipe->shape *= aaGamma[t];
|
|
pipeRun(pipe);
|
|
updateModX(x);
|
|
updateModY(y);
|
|
}
|
|
}
|
|
|
|
inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
|
|
GBool noClip) {
|
|
int x;
|
|
|
|
pipeSetXY(pipe, x0, y);
|
|
if (noClip) {
|
|
for (x = x0; x <= x1; ++x) {
|
|
pipeRun(pipe);
|
|
}
|
|
updateModX(x0);
|
|
updateModX(x1);
|
|
updateModY(y);
|
|
} else {
|
|
for (x = x0; x <= x1; ++x) {
|
|
if (state->clip->test(x, y)) {
|
|
pipeRun(pipe);
|
|
updateModX(x);
|
|
updateModY(y);
|
|
} else {
|
|
pipeIncX(pipe);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {
|
|
#if splashAASize == 4
|
|
static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
|
|
1, 2, 2, 3, 2, 3, 3, 4 };
|
|
SplashColorPtr p0, p1, p2, p3;
|
|
int t;
|
|
#else
|
|
SplashColorPtr p;
|
|
int xx, yy, t;
|
|
#endif
|
|
int x;
|
|
|
|
#if splashAASize == 4
|
|
p0 = aaBuf->getDataPtr() + (x0 >> 1);
|
|
p1 = p0 + aaBuf->getRowSize();
|
|
p2 = p1 + aaBuf->getRowSize();
|
|
p3 = p2 + aaBuf->getRowSize();
|
|
#endif
|
|
pipeSetXY(pipe, x0, y);
|
|
for (x = x0; x <= x1; ++x) {
|
|
|
|
// compute the shape value
|
|
#if splashAASize == 4
|
|
if (x & 1) {
|
|
t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
|
|
bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
|
|
++p0; ++p1; ++p2; ++p3;
|
|
} else {
|
|
t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
|
|
bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
|
|
}
|
|
#else
|
|
t = 0;
|
|
for (yy = 0; yy < splashAASize; ++yy) {
|
|
for (xx = 0; xx < splashAASize; ++xx) {
|
|
p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
|
|
((x * splashAASize + xx) >> 3);
|
|
t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (t != 0) {
|
|
pipe->shape = aaGamma[t];
|
|
pipeRun(pipe);
|
|
updateModX(x);
|
|
updateModY(y);
|
|
} else {
|
|
pipeIncX(pipe);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
// Transform a point from user space to device space.
|
|
inline void Splash::transform(SplashCoord *matrix,
|
|
SplashCoord xi, SplashCoord yi,
|
|
SplashCoord *xo, SplashCoord *yo) {
|
|
// [ m[0] m[1] 0 ]
|
|
// [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
|
|
// [ m[4] m[5] 1 ]
|
|
*xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
|
|
*yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Splash
|
|
//------------------------------------------------------------------------
|
|
|
|
Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
|
|
SplashScreenParams *screenParams) {
|
|
int i;
|
|
|
|
bitmap = bitmapA;
|
|
vectorAntialias = vectorAntialiasA;
|
|
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
|
|
screenParams);
|
|
if (vectorAntialias) {
|
|
aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
|
|
1, splashModeMono1, gFalse);
|
|
for (i = 0; i <= splashAASize * splashAASize; ++i) {
|
|
aaGamma[i] = splashPow((SplashCoord)i /
|
|
(SplashCoord)(splashAASize * splashAASize),
|
|
1.5);
|
|
}
|
|
} else {
|
|
aaBuf = NULL;
|
|
}
|
|
clearModRegion();
|
|
debugMode = gFalse;
|
|
}
|
|
|
|
Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
|
|
SplashScreen *screenA) {
|
|
int i;
|
|
|
|
bitmap = bitmapA;
|
|
vectorAntialias = vectorAntialiasA;
|
|
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
|
|
screenA);
|
|
if (vectorAntialias) {
|
|
aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
|
|
1, splashModeMono1, gFalse);
|
|
for (i = 0; i <= splashAASize * splashAASize; ++i) {
|
|
aaGamma[i] = splashPow((SplashCoord)i /
|
|
(SplashCoord)(splashAASize * splashAASize),
|
|
1.5);
|
|
}
|
|
} else {
|
|
aaBuf = NULL;
|
|
}
|
|
clearModRegion();
|
|
debugMode = gFalse;
|
|
}
|
|
|
|
Splash::~Splash() {
|
|
while (state->next) {
|
|
restoreState();
|
|
}
|
|
delete state;
|
|
if (vectorAntialias) {
|
|
delete aaBuf;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// state read
|
|
//------------------------------------------------------------------------
|
|
|
|
SplashCoord *Splash::getMatrix() {
|
|
return state->matrix;
|
|
}
|
|
|
|
SplashPattern *Splash::getStrokePattern() {
|
|
return state->strokePattern;
|
|
}
|
|
|
|
SplashPattern *Splash::getFillPattern() {
|
|
return state->fillPattern;
|
|
}
|
|
|
|
SplashScreen *Splash::getScreen() {
|
|
return state->screen;
|
|
}
|
|
|
|
SplashBlendFunc Splash::getBlendFunc() {
|
|
return state->blendFunc;
|
|
}
|
|
|
|
SplashCoord Splash::getStrokeAlpha() {
|
|
return state->strokeAlpha;
|
|
}
|
|
|
|
SplashCoord Splash::getFillAlpha() {
|
|
return state->fillAlpha;
|
|
}
|
|
|
|
SplashCoord Splash::getLineWidth() {
|
|
return state->lineWidth;
|
|
}
|
|
|
|
int Splash::getLineCap() {
|
|
return state->lineCap;
|
|
}
|
|
|
|
int Splash::getLineJoin() {
|
|
return state->lineJoin;
|
|
}
|
|
|
|
SplashCoord Splash::getMiterLimit() {
|
|
return state->miterLimit;
|
|
}
|
|
|
|
SplashCoord Splash::getFlatness() {
|
|
return state->flatness;
|
|
}
|
|
|
|
SplashCoord *Splash::getLineDash() {
|
|
return state->lineDash;
|
|
}
|
|
|
|
int Splash::getLineDashLength() {
|
|
return state->lineDashLength;
|
|
}
|
|
|
|
SplashCoord Splash::getLineDashPhase() {
|
|
return state->lineDashPhase;
|
|
}
|
|
|
|
SplashClip *Splash::getClip() {
|
|
return state->clip;
|
|
}
|
|
|
|
SplashBitmap *Splash::getSoftMask() {
|
|
return state->softMask;
|
|
}
|
|
|
|
GBool Splash::getInNonIsolatedGroup() {
|
|
return state->inNonIsolatedGroup;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// state write
|
|
//------------------------------------------------------------------------
|
|
|
|
void Splash::setMatrix(SplashCoord *matrix) {
|
|
memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
|
|
}
|
|
|
|
void Splash::setStrokePattern(SplashPattern *strokePattern) {
|
|
state->setStrokePattern(strokePattern);
|
|
}
|
|
|
|
void Splash::setFillPattern(SplashPattern *fillPattern) {
|
|
state->setFillPattern(fillPattern);
|
|
}
|
|
|
|
void Splash::setScreen(SplashScreen *screen) {
|
|
state->setScreen(screen);
|
|
}
|
|
|
|
void Splash::setBlendFunc(SplashBlendFunc func) {
|
|
state->blendFunc = func;
|
|
}
|
|
|
|
void Splash::setStrokeAlpha(SplashCoord alpha) {
|
|
state->strokeAlpha = alpha;
|
|
}
|
|
|
|
void Splash::setFillAlpha(SplashCoord alpha) {
|
|
state->fillAlpha = alpha;
|
|
}
|
|
|
|
void Splash::setLineWidth(SplashCoord lineWidth) {
|
|
state->lineWidth = lineWidth;
|
|
}
|
|
|
|
void Splash::setLineCap(int lineCap) {
|
|
state->lineCap = lineCap;
|
|
}
|
|
|
|
void Splash::setLineJoin(int lineJoin) {
|
|
state->lineJoin = lineJoin;
|
|
}
|
|
|
|
void Splash::setMiterLimit(SplashCoord miterLimit) {
|
|
state->miterLimit = miterLimit;
|
|
}
|
|
|
|
void Splash::setFlatness(SplashCoord flatness) {
|
|
if (flatness < 1) {
|
|
state->flatness = 1;
|
|
} else {
|
|
state->flatness = flatness;
|
|
}
|
|
}
|
|
|
|
void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
|
|
SplashCoord lineDashPhase) {
|
|
state->setLineDash(lineDash, lineDashLength, lineDashPhase);
|
|
}
|
|
|
|
void Splash::setStrokeAdjust(GBool strokeAdjust) {
|
|
state->strokeAdjust = strokeAdjust;
|
|
}
|
|
|
|
void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
|
|
SplashCoord x1, SplashCoord y1) {
|
|
state->clip->resetToRect(x0, y0, x1, y1);
|
|
}
|
|
|
|
SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
|
|
SplashCoord x1, SplashCoord y1) {
|
|
return state->clip->clipToRect(x0, y0, x1, y1);
|
|
}
|
|
|
|
SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
|
|
return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
|
|
}
|
|
|
|
void Splash::setSoftMask(SplashBitmap *softMask) {
|
|
state->setSoftMask(softMask);
|
|
}
|
|
|
|
void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
|
|
int alpha0XA, int alpha0YA) {
|
|
alpha0Bitmap = alpha0BitmapA;
|
|
alpha0X = alpha0XA;
|
|
alpha0Y = alpha0YA;
|
|
state->inNonIsolatedGroup = gTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// state save/restore
|
|
//------------------------------------------------------------------------
|
|
|
|
void Splash::saveState() {
|
|
SplashState *newState;
|
|
|
|
newState = state->copy();
|
|
newState->next = state;
|
|
state = newState;
|
|
}
|
|
|
|
SplashError Splash::restoreState() {
|
|
SplashState *oldState;
|
|
|
|
if (!state->next) {
|
|
return splashErrNoSave;
|
|
}
|
|
oldState = state;
|
|
state = state->next;
|
|
delete oldState;
|
|
return splashOk;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// drawing operations
|
|
//------------------------------------------------------------------------
|
|
|
|
void Splash::clear(SplashColorPtr color, Guchar alpha) {
|
|
SplashColorPtr row, p;
|
|
Guchar mono;
|
|
int x, y;
|
|
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
mono = (color[0] & 0x80) ? 0xff : 0x00;
|
|
if (bitmap->rowSize < 0) {
|
|
memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
|
|
mono, -bitmap->rowSize * bitmap->height);
|
|
} else {
|
|
memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
if (bitmap->rowSize < 0) {
|
|
memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
|
|
color[0], -bitmap->rowSize * bitmap->height);
|
|
} else {
|
|
memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
|
|
}
|
|
break;
|
|
case splashModeRGB8:
|
|
if (color[0] == color[1] && color[1] == color[2]) {
|
|
if (bitmap->rowSize < 0) {
|
|
memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
|
|
color[0], -bitmap->rowSize * bitmap->height);
|
|
} else {
|
|
memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
|
|
}
|
|
} else {
|
|
row = bitmap->data;
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = row;
|
|
for (x = 0; x < bitmap->width; ++x) {
|
|
*p++ = color[2];
|
|
*p++ = color[1];
|
|
*p++ = color[0];
|
|
}
|
|
row += bitmap->rowSize;
|
|
}
|
|
}
|
|
break;
|
|
case splashModeBGR8:
|
|
if (color[0] == color[1] && color[1] == color[2]) {
|
|
if (bitmap->rowSize < 0) {
|
|
memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
|
|
color[0], -bitmap->rowSize * bitmap->height);
|
|
} else {
|
|
memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
|
|
}
|
|
} else {
|
|
row = bitmap->data;
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = row;
|
|
for (x = 0; x < bitmap->width; ++x) {
|
|
*p++ = color[0];
|
|
*p++ = color[1];
|
|
*p++ = color[2];
|
|
}
|
|
row += bitmap->rowSize;
|
|
}
|
|
}
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
|
|
if (bitmap->rowSize < 0) {
|
|
memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
|
|
color[0], -bitmap->rowSize * bitmap->height);
|
|
} else {
|
|
memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
|
|
}
|
|
} else {
|
|
row = bitmap->data;
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = row;
|
|
for (x = 0; x < bitmap->width; ++x) {
|
|
*p++ = color[0];
|
|
*p++ = color[1];
|
|
*p++ = color[2];
|
|
*p++ = color[3];
|
|
}
|
|
row += bitmap->rowSize;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (bitmap->alpha) {
|
|
memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
|
|
}
|
|
|
|
updateModX(0);
|
|
updateModY(0);
|
|
updateModX(bitmap->width - 1);
|
|
updateModY(bitmap->height - 1);
|
|
}
|
|
|
|
SplashError Splash::stroke(SplashPath *path) {
|
|
SplashPath *path2, *dPath;
|
|
|
|
if (debugMode) {
|
|
printf("stroke [dash:%d] [width:%.2f]:\n",
|
|
state->lineDashLength, (double)state->lineWidth);
|
|
dumpPath(path);
|
|
}
|
|
opClipRes = splashClipAllOutside;
|
|
if (path->length == 0) {
|
|
return splashErrEmptyPath;
|
|
}
|
|
path2 = flattenPath(path, state->matrix, state->flatness);
|
|
if (state->lineDashLength > 0) {
|
|
dPath = makeDashedPath(path2);
|
|
delete path2;
|
|
path2 = dPath;
|
|
}
|
|
if (state->lineWidth == 0) {
|
|
strokeNarrow(path2);
|
|
} else {
|
|
strokeWide(path2);
|
|
}
|
|
delete path2;
|
|
return splashOk;
|
|
}
|
|
|
|
void Splash::strokeNarrow(SplashPath *path) {
|
|
SplashPipe pipe;
|
|
SplashXPath *xPath;
|
|
SplashXPathSeg *seg;
|
|
int x0, x1, x2, x3, y0, y1, x, y, t;
|
|
SplashCoord dx, dy, dxdy;
|
|
SplashClipResult clipRes;
|
|
int nClipRes[3];
|
|
int i;
|
|
|
|
nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
|
|
|
|
xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
|
|
|
|
pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,
|
|
gFalse, gFalse);
|
|
|
|
for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
|
|
|
|
x0 = splashFloor(seg->x0);
|
|
x1 = splashFloor(seg->x1);
|
|
y0 = splashFloor(seg->y0);
|
|
y1 = splashFloor(seg->y1);
|
|
|
|
// horizontal segment
|
|
if (y0 == y1) {
|
|
if (x0 > x1) {
|
|
t = x0; x0 = x1; x1 = t;
|
|
}
|
|
if ((clipRes = state->clip->testSpan(x0, x1, y0))
|
|
!= splashClipAllOutside) {
|
|
drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
|
|
}
|
|
|
|
// segment with |dx| > |dy|
|
|
} else if (splashAbs(seg->dxdy) > 1) {
|
|
dx = seg->x1 - seg->x0;
|
|
dy = seg->y1 - seg->y0;
|
|
dxdy = seg->dxdy;
|
|
if (y0 > y1) {
|
|
t = y0; y0 = y1; y1 = t;
|
|
t = x0; x0 = x1; x1 = t;
|
|
dx = -dx;
|
|
dy = -dy;
|
|
}
|
|
if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
|
|
x0 <= x1 ? x1 : x0, y1))
|
|
!= splashClipAllOutside) {
|
|
if (dx > 0) {
|
|
x2 = x0;
|
|
x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
|
|
drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,
|
|
clipRes == splashClipAllInside);
|
|
x2 = x3;
|
|
for (y = y0 + 1; y <= y1 - 1; ++y) {
|
|
x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
|
|
drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);
|
|
x2 = x3;
|
|
}
|
|
drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,
|
|
clipRes == splashClipAllInside);
|
|
} else {
|
|
x2 = x0;
|
|
x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
|
|
drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,
|
|
clipRes == splashClipAllInside);
|
|
x2 = x3;
|
|
for (y = y0 + 1; y <= y1 - 1; ++y) {
|
|
x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
|
|
drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);
|
|
x2 = x3;
|
|
}
|
|
drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,
|
|
clipRes == splashClipAllInside);
|
|
}
|
|
}
|
|
|
|
// segment with |dy| > |dx|
|
|
} else {
|
|
dxdy = seg->dxdy;
|
|
if (y0 > y1) {
|
|
t = x0; x0 = x1; x1 = t;
|
|
t = y0; y0 = y1; y1 = t;
|
|
}
|
|
if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
|
|
x0 <= x1 ? x1 : x0, y1))
|
|
!= splashClipAllOutside) {
|
|
drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);
|
|
for (y = y0 + 1; y <= y1 - 1; ++y) {
|
|
x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);
|
|
drawPixel(&pipe, x, y, clipRes == splashClipAllInside);
|
|
}
|
|
drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);
|
|
}
|
|
}
|
|
++nClipRes[clipRes];
|
|
}
|
|
if (nClipRes[splashClipPartial] ||
|
|
(nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
|
|
opClipRes = splashClipPartial;
|
|
} else if (nClipRes[splashClipAllInside]) {
|
|
opClipRes = splashClipAllInside;
|
|
} else {
|
|
opClipRes = splashClipAllOutside;
|
|
}
|
|
|
|
delete xPath;
|
|
}
|
|
|
|
void Splash::strokeWide(SplashPath *path) {
|
|
SplashPath *path2;
|
|
|
|
path2 = makeStrokePath(path, gFalse);
|
|
fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
|
|
delete path2;
|
|
}
|
|
|
|
SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
|
|
SplashCoord flatness) {
|
|
SplashPath *fPath;
|
|
SplashCoord flatness2;
|
|
Guchar flag;
|
|
int i;
|
|
|
|
fPath = new SplashPath();
|
|
flatness2 = flatness * flatness;
|
|
i = 0;
|
|
while (i < path->length) {
|
|
flag = path->flags[i];
|
|
if (flag & splashPathFirst) {
|
|
fPath->moveTo(path->pts[i].x, path->pts[i].y);
|
|
++i;
|
|
} else {
|
|
if (flag & splashPathCurve) {
|
|
flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
|
|
path->pts[i ].x, path->pts[i ].y,
|
|
path->pts[i+1].x, path->pts[i+1].y,
|
|
path->pts[i+2].x, path->pts[i+2].y,
|
|
matrix, flatness2, fPath);
|
|
i += 3;
|
|
} else {
|
|
fPath->lineTo(path->pts[i].x, path->pts[i].y);
|
|
++i;
|
|
}
|
|
if (path->flags[i-1] & splashPathClosed) {
|
|
fPath->close();
|
|
}
|
|
}
|
|
}
|
|
return fPath;
|
|
}
|
|
|
|
void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
|
|
SplashCoord x1, SplashCoord y1,
|
|
SplashCoord x2, SplashCoord y2,
|
|
SplashCoord x3, SplashCoord y3,
|
|
SplashCoord *matrix, SplashCoord flatness2,
|
|
SplashPath *fPath) {
|
|
SplashCoord cx[splashMaxCurveSplits + 1][3];
|
|
SplashCoord cy[splashMaxCurveSplits + 1][3];
|
|
int cNext[splashMaxCurveSplits + 1];
|
|
SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
|
|
SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
|
|
SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
|
|
int p1, p2, p3;
|
|
|
|
// initial segment
|
|
p1 = 0;
|
|
p2 = splashMaxCurveSplits;
|
|
cx[p1][0] = x0; cy[p1][0] = y0;
|
|
cx[p1][1] = x1; cy[p1][1] = y1;
|
|
cx[p1][2] = x2; cy[p1][2] = y2;
|
|
cx[p2][0] = x3; cy[p2][0] = y3;
|
|
cNext[p1] = p2;
|
|
|
|
while (p1 < splashMaxCurveSplits) {
|
|
|
|
// get the next segment
|
|
xl0 = cx[p1][0]; yl0 = cy[p1][0];
|
|
xx1 = cx[p1][1]; yy1 = cy[p1][1];
|
|
xx2 = cx[p1][2]; yy2 = cy[p1][2];
|
|
p2 = cNext[p1];
|
|
xr3 = cx[p2][0]; yr3 = cy[p2][0];
|
|
|
|
// compute the distances (in device space) from the control points
|
|
// to the midpoint of the straight line (this is a bit of a hack,
|
|
// but it's much faster than computing the actual distances to the
|
|
// line)
|
|
transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
|
|
transform(matrix, xx1, yy1, &tx, &ty);
|
|
dx = tx - mx;
|
|
dy = ty - my;
|
|
d1 = dx*dx + dy*dy;
|
|
transform(matrix, xx2, yy2, &tx, &ty);
|
|
dx = tx - mx;
|
|
dy = ty - my;
|
|
d2 = dx*dx + dy*dy;
|
|
|
|
// if the curve is flat enough, or no more subdivisions are
|
|
// allowed, add the straight line segment
|
|
if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
|
|
fPath->lineTo(xr3, yr3);
|
|
p1 = p2;
|
|
|
|
// otherwise, subdivide the curve
|
|
} else {
|
|
xl1 = (xl0 + xx1) * 0.5;
|
|
yl1 = (yl0 + yy1) * 0.5;
|
|
xh = (xx1 + xx2) * 0.5;
|
|
yh = (yy1 + yy2) * 0.5;
|
|
xl2 = (xl1 + xh) * 0.5;
|
|
yl2 = (yl1 + yh) * 0.5;
|
|
xr2 = (xx2 + xr3) * 0.5;
|
|
yr2 = (yy2 + yr3) * 0.5;
|
|
xr1 = (xh + xr2) * 0.5;
|
|
yr1 = (yh + yr2) * 0.5;
|
|
xr0 = (xl2 + xr1) * 0.5;
|
|
yr0 = (yl2 + yr1) * 0.5;
|
|
// add the new subdivision points
|
|
p3 = (p1 + p2) / 2;
|
|
cx[p1][1] = xl1; cy[p1][1] = yl1;
|
|
cx[p1][2] = xl2; cy[p1][2] = yl2;
|
|
cNext[p1] = p3;
|
|
cx[p3][0] = xr0; cy[p3][0] = yr0;
|
|
cx[p3][1] = xr1; cy[p3][1] = yr1;
|
|
cx[p3][2] = xr2; cy[p3][2] = yr2;
|
|
cNext[p3] = p2;
|
|
}
|
|
}
|
|
}
|
|
|
|
SplashPath *Splash::makeDashedPath(SplashPath *path) {
|
|
SplashPath *dPath;
|
|
SplashCoord lineDashTotal;
|
|
SplashCoord lineDashStartPhase, lineDashDist, segLen;
|
|
SplashCoord x0, y0, x1, y1, xa, ya;
|
|
GBool lineDashStartOn, lineDashOn, newPath;
|
|
int lineDashStartIdx, lineDashIdx;
|
|
int i, j, k;
|
|
|
|
lineDashTotal = 0;
|
|
for (i = 0; i < state->lineDashLength; ++i) {
|
|
lineDashTotal += state->lineDash[i];
|
|
}
|
|
lineDashStartPhase = state->lineDashPhase;
|
|
i = splashFloor(lineDashStartPhase / lineDashTotal);
|
|
lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
|
|
lineDashStartOn = gTrue;
|
|
lineDashStartIdx = 0;
|
|
while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
|
|
lineDashStartOn = !lineDashStartOn;
|
|
lineDashStartPhase -= state->lineDash[lineDashStartIdx];
|
|
++lineDashStartIdx;
|
|
}
|
|
|
|
dPath = new SplashPath();
|
|
|
|
// process each subpath
|
|
i = 0;
|
|
while (i < path->length) {
|
|
|
|
// find the end of the subpath
|
|
for (j = i;
|
|
j < path->length - 1 && !(path->flags[j] & splashPathLast);
|
|
++j) ;
|
|
|
|
// initialize the dash parameters
|
|
lineDashOn = lineDashStartOn;
|
|
lineDashIdx = lineDashStartIdx;
|
|
lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
|
|
|
|
// process each segment of the subpath
|
|
newPath = gTrue;
|
|
for (k = i; k < j; ++k) {
|
|
|
|
// grab the segment
|
|
x0 = path->pts[k].x;
|
|
y0 = path->pts[k].y;
|
|
x1 = path->pts[k+1].x;
|
|
y1 = path->pts[k+1].y;
|
|
segLen = splashDist(x0, y0, x1, y1);
|
|
|
|
// process the segment
|
|
while (segLen > 0) {
|
|
|
|
if (lineDashDist >= segLen) {
|
|
if (lineDashOn) {
|
|
if (newPath) {
|
|
dPath->moveTo(x0, y0);
|
|
newPath = gFalse;
|
|
}
|
|
dPath->lineTo(x1, y1);
|
|
}
|
|
lineDashDist -= segLen;
|
|
segLen = 0;
|
|
|
|
} else {
|
|
xa = x0 + (lineDashDist / segLen) * (x1 - x0);
|
|
ya = y0 + (lineDashDist / segLen) * (y1 - y0);
|
|
if (lineDashOn) {
|
|
if (newPath) {
|
|
dPath->moveTo(x0, y0);
|
|
newPath = gFalse;
|
|
}
|
|
dPath->lineTo(xa, ya);
|
|
}
|
|
x0 = xa;
|
|
y0 = ya;
|
|
segLen -= lineDashDist;
|
|
lineDashDist = 0;
|
|
}
|
|
|
|
// get the next entry in the dash array
|
|
if (lineDashDist <= 0) {
|
|
lineDashOn = !lineDashOn;
|
|
if (++lineDashIdx == state->lineDashLength) {
|
|
lineDashIdx = 0;
|
|
}
|
|
lineDashDist = state->lineDash[lineDashIdx];
|
|
newPath = gTrue;
|
|
}
|
|
}
|
|
}
|
|
i = j + 1;
|
|
}
|
|
|
|
return dPath;
|
|
}
|
|
|
|
SplashError Splash::fill(SplashPath *path, GBool eo) {
|
|
if (debugMode) {
|
|
printf("fill [eo:%d]:\n", eo);
|
|
dumpPath(path);
|
|
}
|
|
return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
|
|
}
|
|
|
|
SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
|
|
SplashPattern *pattern,
|
|
SplashCoord alpha) {
|
|
SplashPipe pipe;
|
|
SplashXPath *xPath;
|
|
SplashXPathScanner *scanner;
|
|
int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
|
|
SplashClipResult clipRes, clipRes2;
|
|
|
|
if (path->length == 0) {
|
|
return splashErrEmptyPath;
|
|
}
|
|
xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
|
|
if (vectorAntialias) {
|
|
xPath->aaScale();
|
|
}
|
|
xPath->sort();
|
|
scanner = new SplashXPathScanner(xPath, eo);
|
|
|
|
// get the min and max x and y values
|
|
if (vectorAntialias) {
|
|
scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
|
|
} else {
|
|
scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
|
|
}
|
|
|
|
// check clipping
|
|
if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
|
|
!= splashClipAllOutside) {
|
|
|
|
// limit the y range
|
|
if (yMinI < state->clip->getYMinI()) {
|
|
yMinI = state->clip->getYMinI();
|
|
}
|
|
if (yMaxI > state->clip->getYMaxI()) {
|
|
yMaxI = state->clip->getYMaxI();
|
|
}
|
|
|
|
pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);
|
|
|
|
// draw the spans
|
|
if (vectorAntialias) {
|
|
for (y = yMinI; y <= yMaxI; ++y) {
|
|
scanner->renderAALine(aaBuf, &x0, &x1, y);
|
|
if (clipRes != splashClipAllInside) {
|
|
state->clip->clipAALine(aaBuf, &x0, &x1, y);
|
|
}
|
|
drawAALine(&pipe, x0, x1, y);
|
|
}
|
|
} else {
|
|
for (y = yMinI; y <= yMaxI; ++y) {
|
|
while (scanner->getNextSpan(y, &x0, &x1)) {
|
|
if (clipRes == splashClipAllInside) {
|
|
drawSpan(&pipe, x0, x1, y, gTrue);
|
|
} else {
|
|
// limit the x range
|
|
if (x0 < state->clip->getXMinI()) {
|
|
x0 = state->clip->getXMinI();
|
|
}
|
|
if (x1 > state->clip->getXMaxI()) {
|
|
x1 = state->clip->getXMaxI();
|
|
}
|
|
clipRes2 = state->clip->testSpan(x0, x1, y);
|
|
drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
opClipRes = clipRes;
|
|
|
|
delete scanner;
|
|
delete xPath;
|
|
return splashOk;
|
|
}
|
|
|
|
SplashError Splash::xorFill(SplashPath *path, GBool eo) {
|
|
SplashPipe pipe;
|
|
SplashXPath *xPath;
|
|
SplashXPathScanner *scanner;
|
|
int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
|
|
SplashClipResult clipRes, clipRes2;
|
|
SplashBlendFunc origBlendFunc;
|
|
|
|
if (path->length == 0) {
|
|
return splashErrEmptyPath;
|
|
}
|
|
xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
|
|
xPath->sort();
|
|
scanner = new SplashXPathScanner(xPath, eo);
|
|
|
|
// get the min and max x and y values
|
|
scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
|
|
|
|
// check clipping
|
|
if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
|
|
!= splashClipAllOutside) {
|
|
|
|
// limit the y range
|
|
if (yMinI < state->clip->getYMinI()) {
|
|
yMinI = state->clip->getYMinI();
|
|
}
|
|
if (yMaxI > state->clip->getYMaxI()) {
|
|
yMaxI = state->clip->getYMaxI();
|
|
}
|
|
|
|
origBlendFunc = state->blendFunc;
|
|
state->blendFunc = &blendXor;
|
|
pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);
|
|
|
|
// draw the spans
|
|
for (y = yMinI; y <= yMaxI; ++y) {
|
|
while (scanner->getNextSpan(y, &x0, &x1)) {
|
|
if (clipRes == splashClipAllInside) {
|
|
drawSpan(&pipe, x0, x1, y, gTrue);
|
|
} else {
|
|
// limit the x range
|
|
if (x0 < state->clip->getXMinI()) {
|
|
x0 = state->clip->getXMinI();
|
|
}
|
|
if (x1 > state->clip->getXMaxI()) {
|
|
x1 = state->clip->getXMaxI();
|
|
}
|
|
clipRes2 = state->clip->testSpan(x0, x1, y);
|
|
drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
|
|
}
|
|
}
|
|
}
|
|
state->blendFunc = origBlendFunc;
|
|
}
|
|
opClipRes = clipRes;
|
|
|
|
delete scanner;
|
|
delete xPath;
|
|
return splashOk;
|
|
}
|
|
|
|
SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
|
|
int c, SplashFont *font) {
|
|
SplashGlyphBitmap glyph;
|
|
SplashCoord xt, yt;
|
|
int x0, y0, xFrac, yFrac;
|
|
SplashClipResult clipRes;
|
|
|
|
if (debugMode) {
|
|
printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
|
|
(double)x, (double)y, c, c, c);
|
|
}
|
|
transform(state->matrix, x, y, &xt, &yt);
|
|
x0 = splashFloor(xt);
|
|
xFrac = splashFloor((xt - x0) * splashFontFraction);
|
|
y0 = splashFloor(yt);
|
|
yFrac = splashFloor((yt - y0) * splashFontFraction);
|
|
if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
|
|
return splashErrNoGlyph;
|
|
}
|
|
if (clipRes != splashClipAllOutside) {
|
|
fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
|
|
}
|
|
opClipRes = clipRes;
|
|
if (glyph.freeData) {
|
|
gfree(glyph.data);
|
|
}
|
|
return splashOk;
|
|
}
|
|
|
|
void Splash::fillGlyph(SplashCoord x, SplashCoord y,
|
|
SplashGlyphBitmap *glyph) {
|
|
SplashCoord xt, yt;
|
|
int x0, y0;
|
|
|
|
transform(state->matrix, x, y, &xt, &yt);
|
|
x0 = splashFloor(xt);
|
|
y0 = splashFloor(yt);
|
|
SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x,
|
|
y0 - glyph->y,
|
|
x0 - glyph->x + glyph->w - 1,
|
|
y0 - glyph->y + glyph->h - 1);
|
|
if (clipRes != splashClipAllOutside) {
|
|
fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
|
|
}
|
|
opClipRes = clipRes;
|
|
}
|
|
|
|
void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
|
|
SplashPipe pipe;
|
|
int alpha0, alpha;
|
|
Guchar *p;
|
|
int x1, y1, xx, xx1, yy;
|
|
|
|
p = glyph->data;
|
|
int xStart = x0 - glyph->x;
|
|
int yStart = y0 - glyph->y;
|
|
int xxLimit = glyph->w;
|
|
int yyLimit = glyph->h;
|
|
|
|
if (yStart < 0)
|
|
{
|
|
p += glyph->w * -yStart; // move p to the beginning of the first painted row
|
|
yyLimit += yStart;
|
|
yStart = 0;
|
|
}
|
|
|
|
if (xStart < 0)
|
|
{
|
|
p += -xStart; // move p to the first painted pixel
|
|
xxLimit += xStart;
|
|
xStart = 0;
|
|
}
|
|
|
|
if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart;
|
|
if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart;
|
|
|
|
if (noClip) {
|
|
if (glyph->aa) {
|
|
pipeInit(&pipe, xStart, yStart,
|
|
state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
|
|
for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
|
|
pipeSetXY(&pipe, xStart, y1);
|
|
for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
|
|
alpha = p[xx];
|
|
if (alpha != 0) {
|
|
pipe.shape = (SplashCoord)(alpha / 255.0);
|
|
pipeRun(&pipe);
|
|
updateModX(x1);
|
|
updateModY(y1);
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
}
|
|
p += glyph->w;
|
|
}
|
|
} else {
|
|
const int widthEight = (int)ceil(glyph->w / 8.0);
|
|
|
|
pipeInit(&pipe, xStart, yStart,
|
|
state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
|
|
for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
|
|
pipeSetXY(&pipe, xStart, y1);
|
|
for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
|
|
alpha0 = p[xx / 8];
|
|
for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
|
|
if (alpha0 & 0x80) {
|
|
pipeRun(&pipe);
|
|
updateModX(x1);
|
|
updateModY(y1);
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
alpha0 <<= 1;
|
|
}
|
|
}
|
|
p += widthEight;
|
|
}
|
|
}
|
|
} else {
|
|
if (glyph->aa) {
|
|
pipeInit(&pipe, xStart, yStart,
|
|
state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
|
|
for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
|
|
pipeSetXY(&pipe, xStart, y1);
|
|
for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
|
|
if (state->clip->test(x1, y1)) {
|
|
alpha = p[xx];
|
|
if (alpha != 0) {
|
|
pipe.shape = (SplashCoord)(alpha / 255.0);
|
|
pipeRun(&pipe);
|
|
updateModX(x1);
|
|
updateModY(y1);
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
}
|
|
p += glyph->w;
|
|
}
|
|
} else {
|
|
const int widthEight = (int)ceil(glyph->w / 8.0);
|
|
|
|
pipeInit(&pipe, xStart, yStart,
|
|
state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
|
|
for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
|
|
pipeSetXY(&pipe, xStart, y1);
|
|
for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
|
|
alpha0 = p[xx / 8];
|
|
for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
|
|
if (state->clip->test(x1, y1)) {
|
|
if (alpha0 & 0x80) {
|
|
pipeRun(&pipe);
|
|
updateModX(x1);
|
|
updateModY(y1);
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
alpha0 <<= 1;
|
|
}
|
|
}
|
|
p += widthEight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (debugMode) {
|
|
printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
|
|
w, h, (double)mat[0], (double)mat[1], (double)mat[2],
|
|
(double)mat[3], (double)mat[4], (double)mat[5]);
|
|
}
|
|
|
|
if (w == 0 && h == 0) return splashErrZeroImage;
|
|
|
|
// check for singular matrix
|
|
if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 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);
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
scaledHeight = abs(ty2 - ty) + 1;
|
|
xSign = (xScale < 0) ? -1 : 1;
|
|
ySign = (yScale < 0) ? -1 : 1;
|
|
yShear1 = (SplashCoord)xSign * yShear;
|
|
|
|
// 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);
|
|
opClipRes = clipRes;
|
|
|
|
// compute Bresenham parameters for x and y scaling
|
|
yp = h / scaledHeight;
|
|
yq = h % scaledHeight;
|
|
xp = w / scaledWidth;
|
|
xq = w % scaledWidth;
|
|
|
|
// allocate pixel buffer
|
|
pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);
|
|
|
|
// initialize the pixel pipe
|
|
pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
|
|
gTrue, gFalse);
|
|
if (vectorAntialias) {
|
|
drawAAPixelInit();
|
|
}
|
|
|
|
// init y scale Bresenham
|
|
yt = 0;
|
|
lastYStep = 1;
|
|
|
|
for (y = 0; y < scaledHeight; ++y) {
|
|
|
|
// 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 = pixBuf;
|
|
for (i = 0; i < n; ++i) {
|
|
(*src)(srcData, p);
|
|
p += w;
|
|
}
|
|
}
|
|
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);
|
|
} 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;
|
|
}
|
|
} else {
|
|
clipRes2 = clipRes;
|
|
}
|
|
|
|
// init x scale Bresenham
|
|
xt = 0;
|
|
xSrc = 0;
|
|
|
|
// x shear
|
|
x1 = k1;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// loop-invariant constants
|
|
n = yStep > 0 ? yStep : 1;
|
|
|
|
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 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;
|
|
}
|
|
|
|
// blend fill color with background
|
|
if (pixAcc != 0) {
|
|
pipe.shape = (pixAcc == n * m)
|
|
? (SplashCoord)1
|
|
: (SplashCoord)pixAcc / (SplashCoord)(n * m);
|
|
if (vectorAntialias && clipRes2 != 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;
|
|
}
|
|
}
|
|
|
|
// free memory
|
|
gfree(pixBuf);
|
|
|
|
return splashOk;
|
|
}
|
|
|
|
SplashError Splash::drawImage(SplashImageSource src, void *srcData,
|
|
SplashColorMode srcMode, GBool srcAlpha,
|
|
int w, int h, SplashCoord *mat) {
|
|
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;
|
|
|
|
if (debugMode) {
|
|
printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
|
|
srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
|
|
(double)mat[3], (double)mat[4], (double)mat[5]);
|
|
}
|
|
|
|
// check color modes
|
|
ok = gFalse; // make gcc happy
|
|
nComps = 0; // make gcc happy
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
case splashModeMono8:
|
|
ok = srcMode == splashModeMono8;
|
|
nComps = 1;
|
|
break;
|
|
case splashModeRGB8:
|
|
ok = srcMode == splashModeRGB8;
|
|
nComps = 3;
|
|
break;
|
|
case splashModeBGR8:
|
|
ok = srcMode == splashModeBGR8;
|
|
nComps = 3;
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
ok = srcMode == splashModeCMYK8;
|
|
nComps = 4;
|
|
break;
|
|
#endif
|
|
}
|
|
if (!ok) {
|
|
return splashErrModeMismatch;
|
|
}
|
|
|
|
// check for singular matrix
|
|
if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 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);
|
|
} else {
|
|
ty = splashFloor(mat[5] + 0.01);
|
|
ty2 = splashFloor(mat[5] + yScale - 0.01);
|
|
}
|
|
scaledHeight = abs(ty2 - ty) + 1;
|
|
xSign = (xScale < 0) ? -1 : 1;
|
|
ySign = (yScale < 0) ? -1 : 1;
|
|
yShear1 = (SplashCoord)xSign * yShear;
|
|
|
|
// 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);
|
|
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;
|
|
|
|
// allocate pixel buffers
|
|
colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
|
|
if (srcAlpha) {
|
|
alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
|
|
} else {
|
|
alphaBuf = NULL;
|
|
}
|
|
|
|
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, state->fillAlpha,
|
|
srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
|
|
gFalse);
|
|
if (vectorAntialias) {
|
|
drawAAPixelInit();
|
|
}
|
|
|
|
if (srcAlpha) {
|
|
|
|
// init y scale Bresenham
|
|
yt = 0;
|
|
lastYStep = 1;
|
|
|
|
for (y = 0; y < scaledHeight; ++y) {
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
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);
|
|
} 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;
|
|
}
|
|
} else {
|
|
clipRes2 = clipRes;
|
|
}
|
|
|
|
// init x scale Bresenham
|
|
xt = 0;
|
|
xSrc = 0;
|
|
|
|
// x shear
|
|
x1 = k1;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// loop-invariant constants
|
|
n = yStep > 0 ? yStep : 1;
|
|
|
|
switch (srcMode) {
|
|
|
|
case splashModeMono1:
|
|
case splashModeMono8:
|
|
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;
|
|
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 * alphaMul;
|
|
|
|
if (alpha > 0) {
|
|
pix[0] = (int)((SplashCoord)pixAcc0 * 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;
|
|
}
|
|
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 * alphaMul;
|
|
|
|
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;
|
|
}
|
|
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 * alphaMul;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// x scale Bresenham
|
|
xSrc += xStep;
|
|
|
|
// x shear
|
|
x1 += xSign;
|
|
|
|
// y shear
|
|
y1 += yShear1;
|
|
}
|
|
break;
|
|
#endif // SPLASH_CMYK
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// init y scale Bresenham
|
|
yt = 0;
|
|
lastYStep = 1;
|
|
|
|
for (y = 0; y < scaledHeight; ++y) {
|
|
|
|
// 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;
|
|
for (i = 0; i < n; ++i) {
|
|
(*src)(srcData, p, NULL);
|
|
p += w * nComps;
|
|
}
|
|
}
|
|
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);
|
|
} 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;
|
|
}
|
|
} else {
|
|
clipRes2 = clipRes;
|
|
}
|
|
|
|
// init x scale Bresenham
|
|
xt = 0;
|
|
xSrc = 0;
|
|
|
|
// x shear
|
|
x1 = k1;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// loop-invariant constants
|
|
n = yStep > 0 ? yStep : 1;
|
|
|
|
switch (srcMode) {
|
|
|
|
case splashModeMono1:
|
|
case splashModeMono8:
|
|
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;
|
|
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);
|
|
|
|
pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
|
|
|
|
// set pixel
|
|
if (vectorAntialias && clipRes != splashClipAllInside) {
|
|
pipe.shape = (SplashCoord)1;
|
|
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;
|
|
}
|
|
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;
|
|
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);
|
|
|
|
pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
|
|
pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
|
|
pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
|
|
|
|
// set pixel
|
|
if (vectorAntialias && clipRes != splashClipAllInside) {
|
|
pipe.shape = (SplashCoord)1;
|
|
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;
|
|
}
|
|
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;
|
|
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);
|
|
|
|
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
|
|
if (vectorAntialias && clipRes != splashClipAllInside) {
|
|
pipe.shape = (SplashCoord)1;
|
|
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;
|
|
}
|
|
break;
|
|
#endif // SPLASH_CMYK
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
gfree(colorBuf);
|
|
gfree(alphaBuf);
|
|
|
|
return splashOk;
|
|
}
|
|
|
|
SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
|
|
int xDest, int yDest, int w, int h,
|
|
GBool noClip, GBool nonIsolated) {
|
|
SplashPipe pipe;
|
|
SplashColor pixel;
|
|
Guchar alpha;
|
|
Guchar *ap;
|
|
int x, y;
|
|
|
|
if (src->mode != bitmap->mode) {
|
|
return splashErrModeMismatch;
|
|
}
|
|
|
|
if (src->alpha) {
|
|
pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
|
|
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) {
|
|
src->getPixel(xSrc + x, ySrc + y, pixel);
|
|
alpha = *ap++;
|
|
if (noClip || 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 = (SplashCoord)(alpha / 255.0);
|
|
pipeRun(&pipe);
|
|
updateModX(xDest + x);
|
|
updateModY(yDest + y);
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
|
|
gFalse, nonIsolated);
|
|
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 (noClip || state->clip->test(xDest + x, yDest + y)) {
|
|
pipeRun(&pipe);
|
|
updateModX(xDest + x);
|
|
updateModY(yDest + y);
|
|
} else {
|
|
pipeIncX(&pipe);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return splashOk;
|
|
}
|
|
|
|
void Splash::compositeBackground(SplashColorPtr color) {
|
|
SplashColorPtr p;
|
|
Guchar *q;
|
|
Guchar alpha, alpha1, c, color0, color1, color2, color3;
|
|
int x, y, mask;
|
|
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
color0 = color[0];
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = &bitmap->data[y * bitmap->rowSize];
|
|
q = &bitmap->alpha[y * bitmap->width];
|
|
mask = 0x80;
|
|
for (x = 0; x < bitmap->width; ++x) {
|
|
alpha = *q++;
|
|
alpha1 = 255 - alpha;
|
|
c = (*p & mask) ? 0xff : 0x00;
|
|
c = div255(alpha1 * color0 + alpha * c);
|
|
if (c & 0x80) {
|
|
*p |= mask;
|
|
} else {
|
|
*p &= ~mask;
|
|
}
|
|
if (!(mask >>= 1)) {
|
|
mask = 0x80;
|
|
++p;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
color0 = color[0];
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = &bitmap->data[y * bitmap->rowSize];
|
|
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;
|
|
}
|
|
}
|
|
break;
|
|
case splashModeRGB8:
|
|
case splashModeBGR8:
|
|
color0 = color[0];
|
|
color1 = color[1];
|
|
color2 = color[2];
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = &bitmap->data[y * bitmap->rowSize];
|
|
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;
|
|
}
|
|
}
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
color0 = color[0];
|
|
color1 = color[1];
|
|
color2 = color[2];
|
|
color3 = color[3];
|
|
for (y = 0; y < bitmap->height; ++y) {
|
|
p = &bitmap->data[y * bitmap->rowSize];
|
|
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]);
|
|
p += 4;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
|
|
}
|
|
|
|
SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
|
|
int xDest, int yDest, int w, int h) {
|
|
SplashColor pixel;
|
|
SplashColorPtr p;
|
|
Guchar *q;
|
|
int x, y, mask;
|
|
|
|
if (src->mode != bitmap->mode) {
|
|
return splashErrModeMismatch;
|
|
}
|
|
|
|
switch (bitmap->mode) {
|
|
case splashModeMono1:
|
|
for (y = 0; y < h; ++y) {
|
|
p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
|
|
mask = 0x80 >> (xDest & 7);
|
|
for (x = 0; x < w; ++x) {
|
|
src->getPixel(xSrc + x, ySrc + y, pixel);
|
|
if (pixel[0]) {
|
|
*p |= mask;
|
|
} else {
|
|
*p &= ~mask;
|
|
}
|
|
if (!(mask >>= 1)) {
|
|
mask = 0x80;
|
|
++p;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
for (y = 0; y < h; ++y) {
|
|
p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
|
|
for (x = 0; x < w; ++x) {
|
|
src->getPixel(xSrc + x, ySrc + y, pixel);
|
|
*p++ = pixel[0];
|
|
}
|
|
}
|
|
break;
|
|
case splashModeRGB8:
|
|
case splashModeBGR8:
|
|
for (y = 0; y < h; ++y) {
|
|
p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
|
|
for (x = 0; x < w; ++x) {
|
|
src->getPixel(xSrc + x, ySrc + y, pixel);
|
|
*p++ = pixel[0];
|
|
*p++ = pixel[1];
|
|
*p++ = pixel[2];
|
|
}
|
|
}
|
|
break;
|
|
#if SPLASH_CMYK
|
|
case splashModeCMYK8:
|
|
for (y = 0; y < h; ++y) {
|
|
p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
|
|
for (x = 0; x < w; ++x) {
|
|
src->getPixel(xSrc + x, ySrc + y, pixel);
|
|
*p++ = pixel[0];
|
|
*p++ = pixel[1];
|
|
*p++ = pixel[2];
|
|
*p++ = pixel[3];
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (bitmap->alpha) {
|
|
for (y = 0; y < h; ++y) {
|
|
q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
|
|
for (x = 0; x < w; ++x) {
|
|
*q++ = 0x00;
|
|
}
|
|
}
|
|
}
|
|
|
|
return splashOk;
|
|
}
|
|
|
|
SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {
|
|
SplashPath *pathIn, *pathOut;
|
|
SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
|
|
SplashCoord crossprod, dotprod, miter, m;
|
|
GBool first, last, closed;
|
|
int subpathStart, next, i;
|
|
int left0, left1, left2, right0, right1, right2, join0, join1, join2;
|
|
int leftFirst, rightFirst, firstPt;
|
|
|
|
if (flatten) {
|
|
pathIn = flattenPath(path, state->matrix, state->flatness);
|
|
if (state->lineDashLength > 0) {
|
|
pathOut = makeDashedPath(pathIn);
|
|
delete pathIn;
|
|
pathIn = pathOut;
|
|
}
|
|
} else {
|
|
pathIn = path;
|
|
}
|
|
|
|
subpathStart = 0; // make gcc happy
|
|
closed = gFalse; // make gcc happy
|
|
left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
|
|
leftFirst = rightFirst = firstPt = 0; // make gcc happy
|
|
|
|
pathOut = new SplashPath();
|
|
w = state->lineWidth;
|
|
|
|
for (i = 0; i < pathIn->length - 1; ++i) {
|
|
if (pathIn->flags[i] & splashPathLast) {
|
|
continue;
|
|
}
|
|
if ((first = pathIn->flags[i] & splashPathFirst)) {
|
|
subpathStart = i;
|
|
closed = pathIn->flags[i] & splashPathClosed;
|
|
}
|
|
last = pathIn->flags[i+1] & splashPathLast;
|
|
|
|
// compute the deltas for segment (i, i+1)
|
|
d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,
|
|
pathIn->pts[i+1].x, pathIn->pts[i+1].y);
|
|
if (d == 0) {
|
|
// we need to draw end caps on zero-length lines
|
|
//~ not clear what the behavior should be for splashLineCapButt
|
|
//~ with d==0
|
|
dx = 0;
|
|
dy = 1;
|
|
} else {
|
|
d = (SplashCoord)1 / d;
|
|
dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);
|
|
dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);
|
|
}
|
|
wdx = (SplashCoord)0.5 * w * dx;
|
|
wdy = (SplashCoord)0.5 * w * dy;
|
|
|
|
// compute the deltas for segment (i+1, next)
|
|
next = last ? subpathStart + 1 : i + 2;
|
|
d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,
|
|
pathIn->pts[next].x, pathIn->pts[next].y);
|
|
if (d == 0) {
|
|
// we need to draw end caps on zero-length lines
|
|
//~ not clear what the behavior should be for splashLineCapButt
|
|
//~ with d==0
|
|
dxNext = 0;
|
|
dyNext = 1;
|
|
} else {
|
|
d = (SplashCoord)1 / d;
|
|
dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);
|
|
dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);
|
|
}
|
|
wdxNext = (SplashCoord)0.5 * w * dxNext;
|
|
wdyNext = (SplashCoord)0.5 * w * dyNext;
|
|
|
|
// draw the start cap
|
|
pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);
|
|
if (i == subpathStart) {
|
|
firstPt = pathOut->length - 1;
|
|
}
|
|
if (first && !closed) {
|
|
switch (state->lineCap) {
|
|
case splashLineCapButt:
|
|
pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
|
|
break;
|
|
case splashLineCapRound:
|
|
pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,
|
|
pathIn->pts[i].y + wdx - bezierCircle * wdy,
|
|
pathIn->pts[i].x - wdx - bezierCircle * wdy,
|
|
pathIn->pts[i].y - wdy + bezierCircle * wdx,
|
|
pathIn->pts[i].x - wdx,
|
|
pathIn->pts[i].y - wdy);
|
|
pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,
|
|
pathIn->pts[i].y - wdy - bezierCircle * wdx,
|
|
pathIn->pts[i].x + wdy - bezierCircle * wdx,
|
|
pathIn->pts[i].y - wdx - bezierCircle * wdy,
|
|
pathIn->pts[i].x + wdy,
|
|
pathIn->pts[i].y - wdx);
|
|
break;
|
|
case splashLineCapProjecting:
|
|
pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,
|
|
pathIn->pts[i].y + wdx - wdy);
|
|
pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,
|
|
pathIn->pts[i].y - wdx - wdy);
|
|
pathOut->lineTo(pathIn->pts[i].x + wdy,
|
|
pathIn->pts[i].y - wdx);
|
|
break;
|
|
}
|
|
} else {
|
|
pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
|
|
}
|
|
|
|
// draw the left side of the segment rectangle
|
|
left2 = pathOut->length - 1;
|
|
pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);
|
|
|
|
// draw the end cap
|
|
if (last && !closed) {
|
|
switch (state->lineCap) {
|
|
case splashLineCapButt:
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
|
|
break;
|
|
case splashLineCapRound:
|
|
pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,
|
|
pathIn->pts[i+1].y - wdx + bezierCircle * wdy,
|
|
pathIn->pts[i+1].x + wdx + bezierCircle * wdy,
|
|
pathIn->pts[i+1].y + wdy - bezierCircle * wdx,
|
|
pathIn->pts[i+1].x + wdx,
|
|
pathIn->pts[i+1].y + wdy);
|
|
pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,
|
|
pathIn->pts[i+1].y + wdy + bezierCircle * wdx,
|
|
pathIn->pts[i+1].x - wdy + bezierCircle * wdx,
|
|
pathIn->pts[i+1].y + wdx + bezierCircle * wdy,
|
|
pathIn->pts[i+1].x - wdy,
|
|
pathIn->pts[i+1].y + wdx);
|
|
break;
|
|
case splashLineCapProjecting:
|
|
pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,
|
|
pathIn->pts[i+1].y - wdx + wdy);
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,
|
|
pathIn->pts[i+1].y + wdx + wdy);
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy,
|
|
pathIn->pts[i+1].y + wdx);
|
|
break;
|
|
}
|
|
} else {
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
|
|
}
|
|
|
|
// draw the right side of the segment rectangle
|
|
right2 = pathOut->length - 1;
|
|
pathOut->close();
|
|
|
|
// draw the join
|
|
join2 = pathOut->length;
|
|
if (!last || closed) {
|
|
crossprod = dx * dyNext - dy * dxNext;
|
|
dotprod = -(dx * dxNext + dy * dyNext);
|
|
if (dotprod > 0.99999) {
|
|
// avoid a divide-by-zero -- set miter to something arbitrary
|
|
// such that sqrt(miter) will exceed miterLimit (and m is never
|
|
// used in that situation)
|
|
miter = (state->miterLimit + 1) * (state->miterLimit + 1);
|
|
m = 0;
|
|
} else {
|
|
miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
|
|
if (miter < 1) {
|
|
// this can happen because of floating point inaccuracies
|
|
miter = 1;
|
|
}
|
|
m = splashSqrt(miter - 1);
|
|
}
|
|
|
|
// round join
|
|
if (state->lineJoin == splashLineJoinRound) {
|
|
pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y);
|
|
pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y + bezierCircle2 * w,
|
|
pathIn->pts[i+1].x + bezierCircle2 * w,
|
|
pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].x,
|
|
pathIn->pts[i+1].y + (SplashCoord)0.5 * w);
|
|
pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,
|
|
pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y + bezierCircle2 * w,
|
|
pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y);
|
|
pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y - bezierCircle2 * w,
|
|
pathIn->pts[i+1].x - bezierCircle2 * w,
|
|
pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].x,
|
|
pathIn->pts[i+1].y - (SplashCoord)0.5 * w);
|
|
pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,
|
|
pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y - bezierCircle2 * w,
|
|
pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
|
|
pathIn->pts[i+1].y);
|
|
|
|
} else {
|
|
pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);
|
|
|
|
// angle < 180
|
|
if (crossprod < 0) {
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,
|
|
pathIn->pts[i+1].y + wdxNext);
|
|
// miter join inside limit
|
|
if (state->lineJoin == splashLineJoinMiter &&
|
|
splashSqrt(miter) <= state->miterLimit) {
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,
|
|
pathIn->pts[i+1].y + wdx + wdy * m);
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy,
|
|
pathIn->pts[i+1].y + wdx);
|
|
// bevel join or miter join outside limit
|
|
} else {
|
|
pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
|
|
}
|
|
|
|
// angle >= 180
|
|
} else {
|
|
pathOut->lineTo(pathIn->pts[i+1].x + wdy,
|
|
pathIn->pts[i+1].y - wdx);
|
|
// miter join inside limit
|
|
if (state->lineJoin == splashLineJoinMiter &&
|
|
splashSqrt(miter) <= state->miterLimit) {
|
|
pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,
|
|
pathIn->pts[i+1].y - wdx + wdy * m);
|
|
pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
|
|
pathIn->pts[i+1].y - wdxNext);
|
|
// bevel join or miter join outside limit
|
|
} else {
|
|
pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
|
|
pathIn->pts[i+1].y - wdxNext);
|
|
}
|
|
}
|
|
}
|
|
|
|
pathOut->close();
|
|
}
|
|
|
|
// add stroke adjustment hints
|
|
if (state->strokeAdjust) {
|
|
if (i >= subpathStart + 1) {
|
|
if (i >= subpathStart + 2) {
|
|
pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
|
|
pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
|
|
} else {
|
|
pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
|
|
}
|
|
pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
|
|
}
|
|
left0 = left1;
|
|
left1 = left2;
|
|
right0 = right1;
|
|
right1 = right2;
|
|
join0 = join1;
|
|
join1 = join2;
|
|
if (i == subpathStart) {
|
|
leftFirst = left2;
|
|
rightFirst = right2;
|
|
}
|
|
if (last) {
|
|
if (i >= subpathStart + 2) {
|
|
pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
|
|
pathOut->addStrokeAdjustHint(left1, right1,
|
|
join0, pathOut->length - 1);
|
|
} else {
|
|
pathOut->addStrokeAdjustHint(left1, right1,
|
|
firstPt, pathOut->length - 1);
|
|
}
|
|
if (closed) {
|
|
pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
|
|
pathOut->addStrokeAdjustHint(left1, right1,
|
|
rightFirst + 1, rightFirst + 1);
|
|
pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
|
|
left1 + 1, right1);
|
|
pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
|
|
join1, pathOut->length - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pathIn != path) {
|
|
delete pathIn;
|
|
}
|
|
|
|
return pathOut;
|
|
}
|
|
|
|
void Splash::dumpPath(SplashPath *path) {
|
|
int i;
|
|
|
|
for (i = 0; i < path->length; ++i) {
|
|
printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
|
|
i, (double)path->pts[i].x, (double)path->pts[i].y,
|
|
(path->flags[i] & splashPathFirst) ? " first" : "",
|
|
(path->flags[i] & splashPathLast) ? " last" : "",
|
|
(path->flags[i] & splashPathClosed) ? " closed" : "",
|
|
(path->flags[i] & splashPathCurve) ? " curve" : "");
|
|
}
|
|
}
|
|
|
|
void Splash::dumpXPath(SplashXPath *path) {
|
|
int i;
|
|
|
|
for (i = 0; i < path->length; ++i) {
|
|
printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n",
|
|
i, (double)path->segs[i].x0, (double)path->segs[i].y0,
|
|
(double)path->segs[i].x1, (double)path->segs[i].y1,
|
|
(path->segs[i].flags & splashXPathFirst) ? "F" : " ",
|
|
(path->segs[i].flags & splashXPathLast) ? "L" : " ",
|
|
(path->segs[i].flags & splashXPathEnd0) ? "0" : " ",
|
|
(path->segs[i].flags & splashXPathEnd1) ? "1" : " ",
|
|
(path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
|
|
(path->segs[i].flags & splashXPathVert) ? "V" : " ",
|
|
(path->segs[i].flags & splashXPathFlip) ? "P" : " ");
|
|
}
|
|
}
|