Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

8211 lines
217 KiB
C++

//========================================================================
//
// Splash.cc
//
// Copyright 2003-2020 Glyph & Cog, LLC
//
//========================================================================
#include <aconf.h>
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include "gmem.h"
#include "gmempp.h"
#include "GString.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"
// the MSVC math.h doesn't define this
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
//------------------------------------------------------------------------
// 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);
}
// Clip x to lie in [0, 255].
static inline Guchar clip255(int x) {
return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x;
}
// Used by drawImage and fillImageMask to divide the target
// quadrilateral into sections.
struct ImageSection {
int y0, y1; // actual y range
int ia0, ia1; // vertex indices for edge A
int ib0, ib1; // vertex indices for edge B
SplashCoord xa0, ya0, xa1, ya1; // edge A
SplashCoord dxdya; // slope of edge A
SplashCoord xb0, yb0, xb1, yb1; // edge B
SplashCoord dxdyb; // slope of edge B
};
//------------------------------------------------------------------------
// SplashPipe
//------------------------------------------------------------------------
#define splashPipeMaxStages 9
struct SplashPipe {
// source pattern
SplashPattern *pattern;
// source alpha and color
Guchar aInput;
SplashColor cSrcVal;
// source overprint mask
//~ this is a kludge - this pointer should be passed as an arg to the
//~ pipeRun function, but that would require passing in a lot of
//~ null pointers, since it's rarely used
Guint *srcOverprintMaskPtr;
// special cases and result color
GBool noTransparency;
GBool shapeOnly;
SplashPipeResultColorCtrl resultColorCtrl;
// non-isolated group correction
// (this is only used when Splash::composite() is called to composite
// a non-isolated group onto the backdrop)
GBool nonIsolatedGroup;
// the "run" function
void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
};
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
};
//------------------------------------------------------------------------
// modified region
//------------------------------------------------------------------------
void Splash::clearModRegion() {
modXMin = bitmap->width;
modYMin = bitmap->height;
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, SplashPattern *pattern,
Guchar aInput, GBool usesShape,
GBool nonIsolatedGroup, GBool usesSrcOverprint) {
SplashColorMode mode;
mode = bitmap->mode;
pipe->pattern = NULL;
// source color
if (pattern && pattern->isStatic()) {
pattern->getColor(0, 0, pipe->cSrcVal);
pipe->pattern = NULL;
} else {
pipe->pattern = pattern;
}
// source alpha
pipe->aInput = aInput;
// source overprint mask
pipe->srcOverprintMaskPtr = NULL;
// special cases
pipe->noTransparency = aInput == 255 &&
!state->softMask &&
!usesShape &&
!state->inNonIsolatedGroup &&
!state->inKnockoutGroup &&
!nonIsolatedGroup &&
state->overprintMask == 0xffffffff;
pipe->shapeOnly = aInput == 255 &&
!state->softMask &&
usesShape &&
!state->inNonIsolatedGroup &&
!state->inKnockoutGroup &&
!nonIsolatedGroup &&
state->overprintMask == 0xffffffff;
// result color
if (pipe->noTransparency) {
// the !state->blendFunc case is handled separately in pipeRun
pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode];
} else if (!state->blendFunc) {
pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode];
} else {
pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode];
}
// non-isolated group correction
pipe->nonIsolatedGroup = nonIsolatedGroup;
// select the 'run' function
pipe->run = &Splash::pipeRun;
if (overprintMaskBitmap || usesSrcOverprint) {
// use Splash::pipeRun
} else if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
if (mode == splashModeMono1 && !bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleMono1;
} else if (mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleMono8;
} else if (mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleRGB8;
} else if (mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleBGR8;
#if SPLASH_CMYK
} else if (mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleCMYK8;
#endif
}
} else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) {
if (mode == splashModeMono1 && !bitmap->alpha) {
pipe->run = &Splash::pipeRunShapeMono1;
} else if (mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunShapeMono8;
} else if (mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunShapeRGB8;
} else if (mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunShapeBGR8;
#if SPLASH_CMYK
} else if (mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunShapeCMYK8;
#endif
} else if (mode == splashModeMono8 && !bitmap->alpha) {
// this is used when drawing soft-masked images
pipe->run = &Splash::pipeRunShapeNoAlphaMono8;
}
} else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
usesShape &&
!(state->inNonIsolatedGroup && groupBackBitmap->alpha) &&
!state->inKnockoutGroup &&
!state->blendFunc && !pipe->nonIsolatedGroup) {
if (mode == splashModeMono1 && !bitmap->alpha) {
pipe->run = &Splash::pipeRunAAMono1;
} else if (mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAAMono8;
} else if (mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAARGB8;
} else if (mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAABGR8;
#if SPLASH_CMYK
} else if (mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAACMYK8;
#endif
}
} else if (!pipe->pattern &&
aInput == 255 &&
state->softMask &&
usesShape &&
!state->inNonIsolatedGroup &&
!state->inKnockoutGroup &&
!nonIsolatedGroup &&
state->overprintMask == 0xffffffff &&
!state->blendFunc) {
if (mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSoftMaskMono8;
} else if (mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSoftMaskRGB8;
} else if (mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSoftMaskBGR8;
#if SPLASH_CMYK
} else if (mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSoftMaskCMYK8;
#endif
}
} else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
usesShape &&
state->inNonIsolatedGroup && groupBackBitmap->alpha &&
!state->inKnockoutGroup &&
!state->blendFunc && !pipe->nonIsolatedGroup) {
if (mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunNonIsoMono8;
} else if (mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunNonIsoRGB8;
} else if (mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunNonIsoBGR8;
#if SPLASH_CMYK
} else if (mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunNonIsoCMYK8;
#endif
}
}
}
// general case
void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar *shapePtr2;
Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
SplashColor cSrc, cDest, cBlend;
Guchar shapeVal, cResult0, cResult1, cResult2, cResult3;
int cSrcStride, shapeStride, x, lastX, t;
SplashColorPtr destColorPtr;
Guchar destColorMask;
Guchar *destAlphaPtr;
SplashColorPtr color0Ptr;
Guchar color0Mask;
Guchar *alpha0Ptr;
SplashColorPtr softMaskPtr;
Guint overprintMask;
Guint *overprintMaskPtr;
#if SPLASH_CMYK
Guchar aPrev;
SplashColor cSrc2, cDest2;
#endif
if (cSrcPtr && !pipe->pattern) {
cSrcStride = bitmapComps;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
if (shapePtr) {
shapePtr2 = shapePtr;
shapeStride = 1;
for (; x0 <= x1; ++x0) {
if (*shapePtr2) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr2;
if (pipe->srcOverprintMaskPtr) {
++pipe->srcOverprintMaskPtr;
}
}
} else {
shapeVal = 0xff;
shapePtr2 = &shapeVal;
shapeStride = 0;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
if (bitmap->mode == splashModeMono1) {
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
} else {
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps];
destColorMask = 0; // make gcc happy
}
if (bitmap->alpha) {
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
} else {
destAlphaPtr = NULL;
}
if (state->softMask) {
softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
} else {
softMaskPtr = NULL;
}
if (state->inKnockoutGroup) {
if (bitmap->mode == splashModeMono1) {
color0Ptr =
&groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
((groupBackX + x0) >> 3)];
color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7));
} else {
color0Ptr =
&groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
(groupBackX + x0) * bitmapComps];
color0Mask = 0; // make gcc happy
}
} else {
color0Ptr = NULL;
color0Mask = 0; // make gcc happy
}
if (state->inNonIsolatedGroup && groupBackBitmap->alpha) {
alpha0Ptr =
&groupBackBitmap->alpha[(groupBackY + y)
* groupBackBitmap->alphaRowSize +
(groupBackX + x0)];
} else {
alpha0Ptr = NULL;
}
if (overprintMaskBitmap) {
overprintMaskPtr = overprintMaskBitmap + y * bitmap->width + x0;
} else {
overprintMaskPtr = NULL;
}
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr2;
if (!shape) {
if (bitmap->mode == splashModeMono1) {
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
} else {
destColorPtr += bitmapComps;
}
if (destAlphaPtr) {
++destAlphaPtr;
}
if (softMaskPtr) {
++softMaskPtr;
}
if (color0Ptr) {
if (bitmap->mode == splashModeMono1) {
color0Ptr += color0Mask & 1;
color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
} else {
color0Ptr += bitmapComps;
}
}
if (alpha0Ptr) {
++alpha0Ptr;
}
cSrcPtr += cSrcStride;
shapePtr2 += shapeStride;
if (pipe->srcOverprintMaskPtr) {
++pipe->srcOverprintMaskPtr;
}
if (overprintMaskPtr) {
++overprintMaskPtr;
}
continue;
}
lastX = x;
//----- source color
// static pattern: handled in pipeInit
// fixed color: handled in pipeInit
// dynamic pattern
if (pipe->pattern) {
pipe->pattern->getColor(x, y, pipe->cSrcVal);
}
cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
if (pipe->noTransparency && !state->blendFunc) {
//----- result color
switch (bitmap->mode) {
case splashModeMono1:
case splashModeMono8:
cResult0 = state->grayTransfer[cSrcPtr[0]];
break;
case splashModeRGB8:
case splashModeBGR8:
cResult0 = state->rgbTransferR[cSrcPtr[0]];
cResult1 = state->rgbTransferG[cSrcPtr[1]];
cResult2 = state->rgbTransferB[cSrcPtr[2]];
break;
#if SPLASH_CMYK
case splashModeCMYK8:
cResult0 = state->cmykTransferC[cSrcPtr[0]];
cResult1 = state->cmykTransferM[cSrcPtr[1]];
cResult2 = state->cmykTransferY[cSrcPtr[2]];
cResult3 = state->cmykTransferK[cSrcPtr[3]];
break;
#endif
}
aResult = 255;
} else { // if (noTransparency && !blendFunc)
//----- read destination pixel
// (or backdrop color, for knockout groups)
if (color0Ptr) {
switch (bitmap->mode) {
case splashModeMono1:
cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00;
color0Ptr += color0Mask & 1;
color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
break;
case splashModeMono8:
cDest[0] = *color0Ptr++;
break;
case splashModeRGB8:
cDest[0] = color0Ptr[0];
cDest[1] = color0Ptr[1];
cDest[2] = color0Ptr[2];
color0Ptr += 3;
break;
case splashModeBGR8:
cDest[2] = color0Ptr[0];
cDest[1] = color0Ptr[1];
cDest[0] = color0Ptr[2];
color0Ptr += 3;
break;
#if SPLASH_CMYK
case splashModeCMYK8:
cDest[0] = color0Ptr[0];
cDest[1] = color0Ptr[1];
cDest[2] = color0Ptr[2];
cDest[3] = color0Ptr[3];
color0Ptr += 4;
break;
#endif
}
} else {
switch (bitmap->mode) {
case splashModeMono1:
cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00;
break;
case splashModeMono8:
cDest[0] = *destColorPtr;
break;
case splashModeRGB8:
cDest[0] = destColorPtr[0];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[2];
break;
case splashModeBGR8:
cDest[0] = destColorPtr[2];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[0];
break;
#if SPLASH_CMYK
case splashModeCMYK8:
cDest[0] = destColorPtr[0];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[2];
cDest[3] = destColorPtr[3];
break;
#endif
}
}
if (destAlphaPtr) {
aDest = *destAlphaPtr;
} else {
aDest = 0xff;
}
//----- read source color; handle overprint
if (pipe->srcOverprintMaskPtr) {
overprintMask = *pipe->srcOverprintMaskPtr++;
} else {
overprintMask = state->overprintMask;
}
if (overprintMaskPtr) {
*overprintMaskPtr++ |= overprintMask;
}
switch (bitmap->mode) {
case splashModeMono1:
case splashModeMono8:
cSrc[0] = state->grayTransfer[cSrcPtr[0]];
break;
case splashModeRGB8:
case splashModeBGR8:
cSrc[0] = state->rgbTransferR[cSrcPtr[0]];
cSrc[1] = state->rgbTransferG[cSrcPtr[1]];
cSrc[2] = state->rgbTransferB[cSrcPtr[2]];
break;
#if SPLASH_CMYK
case splashModeCMYK8:
if (alpha0Ptr) { // non-isolated group
if (color0Ptr) { // non-isolated, knockout group
aPrev = *alpha0Ptr;
} else { // non-isolated, non-knockout group
aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
}
} else { // isolated group
if (color0Ptr) { // isolated, knockout group
aPrev = 0;
} else { // isolated, non-knockout group
aPrev = aDest;
}
}
if (overprintMask & 0x01) {
cSrc[0] = state->cmykTransferC[cSrcPtr[0]];
} else {
cSrc[0] = div255(aPrev * cDest[0]);
}
if (overprintMask & 0x02) {
cSrc[1] = state->cmykTransferM[cSrcPtr[1]];
} else {
cSrc[1] = div255(aPrev * cDest[1]);
}
if (overprintMask & 0x04) {
cSrc[2] = state->cmykTransferY[cSrcPtr[2]];
} else {
cSrc[2] = div255(aPrev * cDest[2]);
}
if (overprintMask & 0x08) {
cSrc[3] = state->cmykTransferK[cSrcPtr[3]];
} else {
cSrc[3] = div255(aPrev * cDest[3]);
}
break;
#endif
}
//----- source alpha
if (softMaskPtr) {
if (shapePtr) {
aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape);
} else {
aSrc = div255(pipe->aInput * *softMaskPtr++);
}
} else if (shapePtr) {
aSrc = div255(pipe->aInput * shape);
} else {
aSrc = pipe->aInput;
}
//----- non-isolated group correction
if (pipe->nonIsolatedGroup) {
// This path is only used when Splash::composite() is called to
// composite a non-isolated group onto the backdrop. In this
// case, shape is the source (group) alpha.
t = (aDest * 255) / shape - aDest;
switch (bitmap->mode) {
#if SPLASH_CMYK
case splashModeCMYK8:
cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255);
#endif
case splashModeRGB8:
case splashModeBGR8:
cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255);
cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255);
case splashModeMono1:
case splashModeMono8:
cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255);
break;
}
}
//----- blend function
if (state->blendFunc) {
#if SPLASH_CMYK
if (bitmap->mode == splashModeCMYK8) {
// convert colors to additive
cSrc2[0] = (Guchar)(0xff - cSrc[0]);
cSrc2[1] = (Guchar)(0xff - cSrc[1]);
cSrc2[2] = (Guchar)(0xff - cSrc[2]);
cSrc2[3] = (Guchar)(0xff - cSrc[3]);
cDest2[0] = (Guchar)(0xff - cDest[0]);
cDest2[1] = (Guchar)(0xff - cDest[1]);
cDest2[2] = (Guchar)(0xff - cDest[2]);
cDest2[3] = (Guchar)(0xff - cDest[3]);
(*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode);
// convert result back to subtractive
cBlend[0] = (Guchar)(0xff - cBlend[0]);
cBlend[1] = (Guchar)(0xff - cBlend[1]);
cBlend[2] = (Guchar)(0xff - cBlend[2]);
cBlend[3] = (Guchar)(0xff - cBlend[3]);
} else
#endif
(*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
}
//----- result alpha and non-isolated group element correction
// alphaI = alpha_i
// alphaIm1 = alpha_(i-1)
if (pipe->noTransparency) {
alphaI = alphaIm1 = aResult = 255;
} else if (alpha0Ptr) {
if (color0Ptr) {
// non-isolated, knockout
aResult = aSrc;
alpha0 = *alpha0Ptr++;
alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0));
alphaIm1 = alpha0;
} else {
// non-isolated, non-knockout
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alpha0 = *alpha0Ptr++;
alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest));
}
} else {
if (color0Ptr) {
// isolated, knockout
aResult = aSrc;
alphaI = aSrc;
alphaIm1 = 0;
} else {
// isolated, non-knockout
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
alphaIm1 = aDest;
}
}
//----- result color
switch (pipe->resultColorCtrl) {
case splashPipeResultColorNoAlphaBlendMono:
cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
break;
case splashPipeResultColorNoAlphaBlendRGB:
cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
break;
#if SPLASH_CMYK
case splashPipeResultColorNoAlphaBlendCMYK:
cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]);
break;
#endif
case splashPipeResultColorAlphaNoBlendMono:
if (alphaI == 0) {
cResult0 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
/ alphaI);
}
break;
case splashPipeResultColorAlphaNoBlendRGB:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
/ alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1])
/ alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2])
/ alphaI);
}
break;
#if SPLASH_CMYK
case splashPipeResultColorAlphaNoBlendCMYK:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
/ alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1])
/ alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2])
/ alphaI);
cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3])
/ alphaI);
}
break;
#endif
case splashPipeResultColorAlphaBlendMono:
if (alphaI == 0) {
cResult0 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
aSrc * ((255 - alphaIm1) * cSrc[0] +
alphaIm1 * cBlend[0]) / 255)
/ alphaI);
}
break;
case splashPipeResultColorAlphaBlendRGB:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
aSrc * ((255 - alphaIm1) * cSrc[0] +
alphaIm1 * cBlend[0]) / 255)
/ alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] +
aSrc * ((255 - alphaIm1) * cSrc[1] +
alphaIm1 * cBlend[1]) / 255)
/ alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] +
aSrc * ((255 - alphaIm1) * cSrc[2] +
alphaIm1 * cBlend[2]) / 255)
/ alphaI);
}
break;
#if SPLASH_CMYK
case splashPipeResultColorAlphaBlendCMYK:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
aSrc * ((255 - alphaIm1) * cSrc[0] +
alphaIm1 * cBlend[0]) / 255)
/ alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] +
aSrc * ((255 - alphaIm1) * cSrc[1] +
alphaIm1 * cBlend[1]) / 255)
/ alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] +
aSrc * ((255 - alphaIm1) * cSrc[2] +
alphaIm1 * cBlend[2]) / 255)
/ alphaI);
cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] +
aSrc * ((255 - alphaIm1) * cSrc[3] +
alphaIm1 * cBlend[3]) / 255)
/ alphaI);
}
break;
#endif
}
} // if (noTransparency && !blendFunc)
//----- write destination pixel
switch (bitmap->mode) {
case splashModeMono1:
if (state->screen->test(x, y, cResult0)) {
*destColorPtr |= destColorMask;
} else {
*destColorPtr &= (Guchar)~destColorMask;
}
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
break;
case splashModeMono8:
*destColorPtr++ = cResult0;
break;
case splashModeRGB8:
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr += 3;
break;
case splashModeBGR8:
destColorPtr[0] = cResult2;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult0;
destColorPtr += 3;
break;
#if SPLASH_CMYK
case splashModeCMYK8:
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr[3] = cResult3;
destColorPtr += 4;
break;
#endif
}
if (destAlphaPtr) {
*destAlphaPtr++ = aResult;
}
cSrcPtr += cSrcStride;
shapePtr2 += shapeStride;
} // for (x ...)
updateModX(lastX);
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeMono1 && !bitmap->alpha) {
void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar cResult0;
SplashColorPtr destColorPtr;
Guchar destColorMask;
SplashScreenCursor screenCursor;
int cSrcStride, x;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModX(x1);
updateModY(y);
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
screenCursor = state->screen->getTestCursor(y);
for (x = x0; x <= x1; ++x) {
//----- write destination pixel
cResult0 = state->grayTransfer[cSrcPtr[0]];
if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
*destColorPtr |= destColorMask;
} else {
*destColorPtr &= (Guchar)~destColorMask;
}
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
cSrcPtr += cSrcStride;
}
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeMono8 && bitmap->alpha) {
void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModX(x1);
updateModY(y);
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- write destination pixel
*destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
*destAlphaPtr++ = 255;
cSrcPtr += cSrcStride;
}
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeRGB8 && bitmap->alpha) {
void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModX(x1);
updateModY(y);
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- write destination pixel
destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]];
destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]];
destColorPtr += 3;
*destAlphaPtr++ = 255;
cSrcPtr += cSrcStride;
}
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeBGR8 && bitmap->alpha) {
void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModX(x1);
updateModY(y);
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- write destination pixel
destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]];
destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]];
destColorPtr += 3;
*destAlphaPtr++ = 255;
cSrcPtr += cSrcStride;
}
}
#if SPLASH_CMYK
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x;
if (cSrcPtr) {
cSrcStride = 4;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModX(x1);
updateModY(y);
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- write destination pixel
destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]];
destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]];
destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]];
destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]];
destColorPtr += 4;
*destAlphaPtr++ = 255;
cSrcPtr += cSrcStride;
}
}
#endif
// special case:
// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
// bitmap->mode == splashModeMono1 && !bitmap->alpha
void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
Guchar destColorMask;
SplashScreenCursor screenCursor;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
screenCursor = state->screen->getTestCursor(y);
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = shape;
//----- special case for aSrc = 255
if (aSrc == 255) {
cResult0 = cSrc0;
} else {
//----- read destination pixel
cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
//----- result color
// note: aDest = alphaI = aResult = 0xff
cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
}
//----- write destination pixel
if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
*destColorPtr |= destColorMask;
} else {
*destColorPtr &= (Guchar)~destColorMask;
}
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
// bitmap->mode == splashModeMono8 && bitmap->alpha
void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
++destColorPtr;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = shape;
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
} else {
//----- read destination alpha
aDest = *destAlphaPtr;
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
} else {
//----- read destination pixel
cDest0 = *destColorPtr;
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
}
}
//----- write destination pixel
*destColorPtr++ = cResult0;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
// bitmap->mode == splashModeRGB8 && bitmap->alpha
void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = shape;
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination alpha
aDest = *destAlphaPtr;
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
// bitmap->mode == splashModeBGR8 && bitmap->alpha
void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = shape;
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination alpha
aDest = *destAlphaPtr;
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination pixel
cDest0 = destColorPtr[2];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[0];
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
}
//----- write destination pixel
destColorPtr[0] = cResult2;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult0;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
#if SPLASH_CMYK
// special case:
// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
// bitmap->mode == splashModeCMYK8 && bitmap->alpha
void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2, cSrc3;
Guchar cDest0, cDest1, cDest2, cDest3;
Guchar cResult0, cResult1, cResult2, cResult3;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 4;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 4;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
cDest3 = destColorPtr[3];
aDest = *destAlphaPtr;
//----- overprint
cSrc0 = state->cmykTransferC[cSrcPtr[0]];
cSrc1 = state->cmykTransferM[cSrcPtr[1]];
cSrc2 = state->cmykTransferY[cSrcPtr[2]];
cSrc3 = state->cmykTransferK[cSrcPtr[3]];
//----- source alpha
aSrc = shape;
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
cResult3 = cSrc3;
} else {
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
cResult3 = cSrc3;
} else {
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
}
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr[3] = cResult3;
destColorPtr += 4;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
#endif
// special case:
// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
// bitmap->mode == splashModeMono8 && !bitmap->alpha
void Splash::pipeRunShapeNoAlphaMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr,
SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
++destColorPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = shape;
//----- special case for aSrc = 255
if (aSrc == 255) {
cResult0 = cSrc0;
} else {
//----- read destination pixel
cDest0 = *destColorPtr;
//----- result color
cResult0 = div255((255 - aSrc) * cDest0 + aSrc * cSrc0);
}
//----- write destination pixel
*destColorPtr++ = cResult0;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeMono1 && !bitmap->alpha
void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
Guchar destColorMask;
SplashScreenCursor screenCursor;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
screenCursor = state->screen->getTestCursor(y);
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
//----- source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result color
// note: aDest = alphaI = aResult = 0xff
cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
//----- write destination pixel
if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
*destColorPtr |= destColorMask;
} else {
*destColorPtr &= (Guchar)~destColorMask;
}
destColorPtr += destColorMask & 1;
destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeMono8 && bitmap->alpha
void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
++destColorPtr;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = *destColorPtr;
aDest = *destAlphaPtr;
//----- source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
if (alphaI == 0) {
cResult0 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
}
//----- write destination pixel
*destColorPtr++ = cResult0;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeRGB8 && bitmap->alpha
void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
aDest = *destAlphaPtr;
//----- source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeBGR8 && bitmap->alpha
void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[2];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[0];
aDest = *destAlphaPtr;
//----- source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
//----- write destination pixel
destColorPtr[0] = cResult2;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult0;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
#if SPLASH_CMYK
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeCMYK8 && bitmap->alpha
void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2, cSrc3;
Guchar cDest0, cDest1, cDest2, cDest3;
Guchar cResult0, cResult1, cResult2, cResult3;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 4;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 4;
++destAlphaPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
cDest3 = destColorPtr[3];
aDest = *destAlphaPtr;
//----- overprint
if (state->overprintMask & 1) {
cSrc0 = state->cmykTransferC[cSrcPtr[0]];
} else {
cSrc0 = div255(aDest * cDest0);
}
if (state->overprintMask & 2) {
cSrc1 = state->cmykTransferM[cSrcPtr[1]];
} else {
cSrc1 = div255(aDest * cDest1);
}
if (state->overprintMask & 4) {
cSrc2 = state->cmykTransferY[cSrcPtr[2]];
} else {
cSrc2 = div255(aDest * cDest2);
}
if (state->overprintMask & 8) {
cSrc3 = state->cmykTransferK[cSrcPtr[3]];
} else {
cSrc3 = div255(aDest * cDest3);
}
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr[3] = cResult3;
destColorPtr += 4;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
#endif
// special case:
// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
// !state->blendFunc &&
// bitmap->mode == splashModeMono8 && bitmap->alpha
void Splash::pipeRunSoftMaskMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
SplashColorPtr softMaskPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
++destColorPtr;
++destAlphaPtr;
++softMaskPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = div255(*softMaskPtr++ * shape);
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
} else {
//----- read destination alpha
aDest = *destAlphaPtr;
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
} else {
//----- read destination pixel
cDest0 = destColorPtr[0];
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
}
}
//----- write destination pixel
destColorPtr[0] = cResult0;
++destColorPtr;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
// !state->blendFunc &&
// bitmap->mode == splashModeRGB8 && bitmap->alpha
void Splash::pipeRunSoftMaskRGB8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
SplashColorPtr softMaskPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
++softMaskPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = div255(*softMaskPtr++ * shape);
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination alpha
aDest = *destAlphaPtr;
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
// special case:
// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
// !state->blendFunc &&
// bitmap->mode == splashModeBGR8 && bitmap->alpha
void Splash::pipeRunSoftMaskBGR8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
SplashColorPtr softMaskPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
++softMaskPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = div255(*softMaskPtr++ * shape);
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination alpha
aDest = *destAlphaPtr;
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
} else {
//----- read destination pixel
cDest0 = destColorPtr[2];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[0];
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
}
//----- write destination pixel
destColorPtr[2] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[0] = cResult2;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
#if SPLASH_CMYK
// special case:
// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
// !state->blendFunc &&
// bitmap->mode == splashModeCMYK8 && bitmap->alpha
void Splash::pipeRunSoftMaskCMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, aResult;
Guchar cSrc0, cSrc1, cSrc2, cSrc3;
Guchar cDest0, cDest1, cDest2, cDest3;
Guchar cResult0, cResult1, cResult2, cResult3;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
SplashColorPtr softMaskPtr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 4;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 4;
++destAlphaPtr;
++softMaskPtr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
cDest3 = destColorPtr[3];
//----- read destination alpha
aDest = *destAlphaPtr;
//----- overprint
cSrc0 = state->cmykTransferC[cSrcPtr[0]];
cSrc1 = state->cmykTransferM[cSrcPtr[1]];
cSrc2 = state->cmykTransferY[cSrcPtr[2]];
cSrc3 = state->cmykTransferK[cSrcPtr[3]];
//----- source alpha
aSrc = div255(*softMaskPtr++ * shape);
//----- special case for aSrc = 255
if (aSrc == 255) {
aResult = 255;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
cResult3 = cSrc3;
} else {
//----- special case for aDest = 0
if (aDest == 0) {
aResult = aSrc;
cResult0 = cSrc0;
cResult1 = cSrc1;
cResult2 = cSrc2;
cResult3 = cSrc3;
} else {
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alphaI = aResult;
//----- result color
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
}
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr[3] = cResult3;
destColorPtr += 4;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
}
updateModX(lastX);
}
#endif
void Splash::pipeRunNonIsoMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
Guchar cSrc0, cDest0, cResult0;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
Guchar *alpha0Ptr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 1;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
* groupBackBitmap->alphaRowSize +
(groupBackX + x0)];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 1;
++destAlphaPtr;
++alpha0Ptr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
aDest = *destAlphaPtr;
//----- source color
cSrc0 = state->grayTransfer[cSrcPtr[0]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alpha0 = *alpha0Ptr++;
alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
//----- result color
if (alphaI == 0) {
cResult0 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
}
//----- write destination pixel
*destColorPtr++ = cResult0;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
} // for (x ...)
updateModX(lastX);
}
void Splash::pipeRunNonIsoRGB8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
Guchar *alpha0Ptr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
* groupBackBitmap->alphaRowSize +
(groupBackX + x0)];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
++alpha0Ptr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
aDest = *destAlphaPtr;
//----- source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alpha0 = *alpha0Ptr++;
alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
//----- result color
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
} // for (x ...)
updateModX(lastX);
}
void Splash::pipeRunNonIsoBGR8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
Guchar cSrc0, cSrc1, cSrc2;
Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
Guchar *alpha0Ptr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 3;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
* groupBackBitmap->alphaRowSize +
(groupBackX + x0)];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 3;
++destAlphaPtr;
++alpha0Ptr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[2];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[0];
aDest = *destAlphaPtr;
//----- source color
cSrc0 = state->rgbTransferR[cSrcPtr[0]];
cSrc1 = state->rgbTransferG[cSrcPtr[1]];
cSrc2 = state->rgbTransferB[cSrcPtr[2]];
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alpha0 = *alpha0Ptr++;
alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
//----- result color
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
}
//----- write destination pixel
destColorPtr[0] = cResult2;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult0;
destColorPtr += 3;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
} // for (x ...)
updateModX(lastX);
}
#if SPLASH_CMYK
void Splash::pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, alpha0, aResult, aPrev;
Guchar cSrc0, cSrc1, cSrc2, cSrc3;
Guchar cDest0, cDest1, cDest2, cDest3;
Guchar cResult0, cResult1, cResult2, cResult3;
SplashColorPtr destColorPtr;
Guchar *destAlphaPtr;
Guchar *alpha0Ptr;
int cSrcStride, x, lastX;
if (cSrcPtr) {
cSrcStride = 4;
} else {
cSrcPtr = pipe->cSrcVal;
cSrcStride = 0;
}
for (; x0 <= x1; ++x0) {
if (*shapePtr) {
break;
}
cSrcPtr += cSrcStride;
++shapePtr;
}
if (x0 > x1) {
return;
}
updateModX(x0);
updateModY(y);
lastX = x0;
useDestRow(y);
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
* groupBackBitmap->alphaRowSize +
(groupBackX + x0)];
for (x = x0; x <= x1; ++x) {
//----- shape
shape = *shapePtr;
if (!shape) {
destColorPtr += 4;
++destAlphaPtr;
++alpha0Ptr;
cSrcPtr += cSrcStride;
++shapePtr;
continue;
}
lastX = x;
//----- read destination pixel
cDest0 = destColorPtr[0];
cDest1 = destColorPtr[1];
cDest2 = destColorPtr[2];
cDest3 = destColorPtr[3];
aDest = *destAlphaPtr;
//----- overprint
aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
if (state->overprintMask & 0x01) {
cSrc0 = state->cmykTransferC[cSrcPtr[0]];
} else {
cSrc0 = div255(aPrev * cDest0);
}
if (state->overprintMask & 0x02) {
cSrc1 = state->cmykTransferM[cSrcPtr[1]];
} else {
cSrc1 = div255(aPrev * cDest1);
}
if (state->overprintMask & 0x04) {
cSrc2 = state->cmykTransferY[cSrcPtr[2]];
} else {
cSrc2 = div255(aPrev * cDest2);
}
if (state->overprintMask & 0x08) {
cSrc3 = state->cmykTransferK[cSrcPtr[3]];
} else {
cSrc3 = div255(aPrev * cDest3);
}
//----- source alpha
aSrc = div255(pipe->aInput * shape);
//----- result alpha and non-isolated group element correction
aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
alpha0 = *alpha0Ptr++;
alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
//----- result color
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
}
//----- write destination pixel
destColorPtr[0] = cResult0;
destColorPtr[1] = cResult1;
destColorPtr[2] = cResult2;
destColorPtr[3] = cResult3;
destColorPtr += 4;
*destAlphaPtr++ = aResult;
cSrcPtr += cSrcStride;
++shapePtr;
} // for (x ...)
updateModX(lastX);
}
#endif
void Splash::useDestRow(int y) {
int y0, y1, yy;
if (groupDestInitMode == splashGroupDestPreInit) {
return;
}
if (groupDestInitYMin > groupDestInitYMax) {
y0 = y1 = y;
groupDestInitYMin = groupDestInitYMax = y;
} else if (y < groupDestInitYMin) {
y0 = y;
y1 = groupDestInitYMin - 1;
groupDestInitYMin = y;
} else if (y > groupDestInitYMax) {
y0 = groupDestInitYMax + 1;
y1 = y;
groupDestInitYMax = y;
} else {
return;
}
for (yy = y0; yy <= y1; ++yy) {
if (groupDestInitMode == splashGroupDestInitZero) {
// same as clear(color=0, alpha=0)
memset(bitmap->data + bitmap->rowSize * yy, 0,
bitmap->rowSize < 0 ? -bitmap->rowSize : bitmap->rowSize);
if (bitmap->alpha) {
memset(bitmap->alpha + bitmap->alphaRowSize * yy, 0,
bitmap->alphaRowSize);
}
} else { // (groupDestInitMode == splashGroupDestInitCopy)
// same as blitTransparent
copyGroupBackdropRow(yy);
}
}
}
void Splash::copyGroupBackdropRow(int y) {
SplashColorPtr p, q;
Guchar mask, srcMask;
int x;
if (groupBackBitmap->mode != bitmap->mode) {
return;
}
if (bitmap->mode == splashModeMono1) {
p = &bitmap->data[y * bitmap->rowSize];
mask = (Guchar)0x80;
q = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize
+ (groupBackX >> 3)];
srcMask = (Guchar)(0x80 >> (groupBackX & 7));
for (x = 0; x < bitmap->width; ++x) {
if (*q & srcMask) {
*p |= mask;
} else {
*p &= (Guchar)~mask;
}
if (!(mask = (Guchar)(mask >> 1))) {
mask = 0x80;
++p;
}
if (!(srcMask = (Guchar)(srcMask >> 1))) {
srcMask = 0x80;
++q;
}
}
} else {
p = &bitmap->data[y * bitmap->rowSize];
q = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize
+ bitmapComps * groupBackX];
memcpy(p, q, bitmapComps * bitmap->width);
}
if (bitmap->alpha) {
memset(&bitmap->alpha[y * bitmap->alphaRowSize], 0, bitmap->width);
}
}
//------------------------------------------------------------------------
// 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];
}
//------------------------------------------------------------------------
// SplashImageCache
//------------------------------------------------------------------------
SplashImageCache::SplashImageCache() {
tag = NULL;
width = 0;
height = 0;
mode = splashModeRGB8;
alpha = gFalse;
interpolate = gFalse;
colorData = NULL;
alphaData = NULL;
refCount = 1;
}
SplashImageCache::~SplashImageCache() {
if (tag) {
delete tag;
}
gfree(colorData);
gfree(alphaData);
}
GBool SplashImageCache::match(GString *aTag, int aWidth, int aHeight,
SplashColorMode aMode, GBool aAlpha,
GBool aInterpolate) {
return aTag && tag && !aTag->cmp(tag) &&
aWidth == width && aHeight == height &&
aMode == mode && aAlpha == alpha &&
aInterpolate == interpolate;
}
void SplashImageCache::reset(GString *aTag, int aWidth, int aHeight,
SplashColorMode aMode, GBool aAlpha,
GBool aInterpolate) {
if (tag) {
delete tag;
}
if (aTag) {
tag = aTag->copy();
} else {
tag = NULL;
}
width = aWidth;
height = aHeight;
mode = aMode;
alpha = aAlpha;
interpolate = aInterpolate;
gfree(colorData);
colorData = NULL;
gfree(alphaData);
alphaData = NULL;
}
void SplashImageCache::incRefCount() {
++refCount;
}
void SplashImageCache::decRefCount() {
--refCount;
if (refCount == 0) {
delete this;
}
}
//------------------------------------------------------------------------
// ImageScaler
//------------------------------------------------------------------------
// Abstract base class.
class ImageScaler {
public:
ImageScaler() {}
virtual ~ImageScaler() {}
// Compute the next line of the scaled image. This can be called up
// to [scaledHeight] times.
virtual void nextLine() = 0;
// Retrieve the color and alpha data generated by the most recent
// call to nextLine().
virtual Guchar *colorData() = 0;
virtual Guchar *alphaData() = 0;
};
//------------------------------------------------------------------------
// BasicImageScaler
//------------------------------------------------------------------------
// The actual image scaler.
class BasicImageScaler: public ImageScaler {
public:
BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
int aScaledWidth, int aScaledHeight, GBool aInterpolate);
virtual ~BasicImageScaler();
virtual void nextLine();
virtual Guchar *colorData() { return colorLine; }
virtual Guchar *alphaData() { return alphaLine; }
protected:
void vertDownscaleHorizDownscale();
void vertDownscaleHorizUpscaleNoInterp();
void vertDownscaleHorizUpscaleInterp();
void vertUpscaleHorizDownscaleNoInterp();
void vertUpscaleHorizDownscaleInterp();
void vertUpscaleHorizUpscaleNoInterp();
void vertUpscaleHorizUpscaleInterp();
// source image data function
SplashImageSource src;
void *srcData;
// source image size
int srcWidth;
int srcHeight;
// scaled image size
int scaledWidth;
int scaledHeight;
// number of color and alpha components
int nComps;
GBool hasAlpha;
// params/state for vertical scaling
int yp, yq;
int yt, yn;
int ySrcCur, yScaledCur;
SplashCoord yInvScale;
// params for horizontal scaling
int xp, xq;
SplashCoord xInvScale;
// scaling function
void (BasicImageScaler::*scalingFunc)();
// temporary buffers for vertical scaling
Guchar *colorTmpBuf0;
Guchar *colorTmpBuf1;
Guchar *colorTmpBuf2;
Guchar *alphaTmpBuf0;
Guchar *alphaTmpBuf1;
Guchar *alphaTmpBuf2;
Guint *colorAccBuf;
Guint *alphaAccBuf;
// output of horizontal scaling
Guchar *colorLine;
Guchar *alphaLine;
};
BasicImageScaler::BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
int aSrcWidth, int aSrcHeight,
int aNComps, GBool aHasAlpha,
int aScaledWidth, int aScaledHeight,
GBool aInterpolate) {
colorTmpBuf0 = NULL;
colorTmpBuf1 = NULL;
colorTmpBuf2 = NULL;
alphaTmpBuf0 = NULL;
alphaTmpBuf1 = NULL;
alphaTmpBuf2 = NULL;
colorAccBuf = NULL;
alphaAccBuf = NULL;
colorLine = NULL;
alphaLine = NULL;
src = aSrc;
srcData = aSrcData;
srcWidth = aSrcWidth;
srcHeight = aSrcHeight;
scaledWidth = aScaledWidth;
scaledHeight = aScaledHeight;
nComps = aNComps;
hasAlpha = aHasAlpha;
// select scaling function; allocate buffers
if (scaledHeight <= srcHeight) {
// vertical downscaling
yp = srcHeight / scaledHeight;
yq = srcHeight % scaledHeight;
yt = 0;
colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
colorAccBuf = (Guint *)gmallocn(srcWidth, nComps * (int)sizeof(Guint));
if (hasAlpha) {
alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
alphaAccBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
}
if (scaledWidth <= srcWidth) {
scalingFunc = &BasicImageScaler::vertDownscaleHorizDownscale;
} else {
if (aInterpolate) {
scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleInterp;
} else {
scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleNoInterp;
}
}
} else {
// vertical upscaling
yp = scaledHeight / srcHeight;
yq = scaledHeight % srcHeight;
yt = 0;
yn = 0;
if (aInterpolate) {
yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
colorTmpBuf1 = (Guchar *)gmallocn(srcWidth, nComps);
if (hasAlpha) {
alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
alphaTmpBuf1 = (Guchar *)gmalloc(srcWidth);
}
ySrcCur = 0;
yScaledCur = 0;
if (scaledWidth <= srcWidth) {
scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleInterp;
} else {
colorTmpBuf2 = (Guchar *)gmallocn(srcWidth, nComps);
if (hasAlpha) {
alphaTmpBuf2 = (Guchar *)gmalloc(srcWidth);
}
scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleInterp;
}
} else {
colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
if (hasAlpha) {
alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
}
if (scaledWidth <= srcWidth) {
scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleNoInterp;
} else {
scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleNoInterp;
}
}
}
if (scaledWidth <= srcWidth) {
xp = srcWidth / scaledWidth;
xq = srcWidth % scaledWidth;
} else {
xp = scaledWidth / srcWidth;
xq = scaledWidth % srcWidth;
if (aInterpolate) {
xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
}
}
colorLine = (Guchar *)gmallocn(scaledWidth, nComps);
if (hasAlpha) {
alphaLine = (Guchar *)gmalloc(scaledWidth);
}
}
BasicImageScaler::~BasicImageScaler() {
gfree(colorTmpBuf0);
gfree(colorTmpBuf1);
gfree(colorTmpBuf2);
gfree(alphaTmpBuf0);
gfree(alphaTmpBuf1);
gfree(alphaTmpBuf2);
gfree(colorAccBuf);
gfree(alphaAccBuf);
gfree(colorLine);
gfree(alphaLine);
}
void BasicImageScaler::nextLine() {
(this->*scalingFunc)();
}
void BasicImageScaler::vertDownscaleHorizDownscale() {
//--- vert downscale
int yStep = yp;
yt += yq;
if (yt >= scaledHeight) {
yt -= scaledHeight;
++yStep;
}
memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
if (hasAlpha) {
memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
}
int nRowComps = srcWidth * nComps;
for (int i = 0; i < yStep; ++i) {
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
for (int j = 0; j < nRowComps; ++j) {
colorAccBuf[j] += colorTmpBuf0[j];
}
if (hasAlpha) {
for (int j = 0; j < srcWidth; ++j) {
alphaAccBuf[j] += alphaTmpBuf0[j];
}
}
}
//--- horiz downscale
int colorAcc[splashMaxColorComps];
int xt = 0;
int unscaledColorIdx = 0;
int unscaledAlphaIdx = 0;
int scaledColorIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
int xStep = xp;
xt += xq;
if (xt >= scaledWidth) {
xt -= scaledWidth;
++xStep;
}
for (int j = 0; j < nComps; ++j) {
colorAcc[j] = 0;
}
for (int i = 0; i < xStep; ++i) {
for (int j = 0; j < nComps; ++j) {
colorAcc[j] += colorAccBuf[unscaledColorIdx + j];
}
unscaledColorIdx += nComps;
}
int nPixels = yStep * xStep;
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / nPixels);
}
scaledColorIdx += nComps;
if (hasAlpha) {
int alphaAcc = 0;
for (int i = 0; i < xStep; ++i) {
alphaAcc += alphaAccBuf[unscaledAlphaIdx];
++unscaledAlphaIdx;
}
alphaLine[scaledIdx] = (Guchar)(alphaAcc / nPixels);
}
}
}
void BasicImageScaler::vertDownscaleHorizUpscaleNoInterp() {
//--- vert downscale
int yStep = yp;
yt += yq;
if (yt >= scaledHeight) {
yt -= scaledHeight;
++yStep;
}
memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
if (hasAlpha) {
memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
}
int nRowComps = srcWidth * nComps;
for (int i = 0; i < yStep; ++i) {
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
for (int j = 0; j < nRowComps; ++j) {
colorAccBuf[j] += colorTmpBuf0[j];
}
if (hasAlpha) {
for (int j = 0; j < srcWidth; ++j) {
alphaAccBuf[j] += alphaTmpBuf0[j];
}
}
}
//--- horiz upscale
Guchar colorBuf[splashMaxColorComps];
int xt = 0;
int scaledColorIdx = 0;
int srcColorIdx = 0;
int scaledAlphaIdx = 0;
for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
int xStep = xp;
xt += xq;
if (xt >= srcWidth) {
xt -= srcWidth;
++xStep;
}
for (int j = 0; j < nComps; ++j) {
colorBuf[j] = (Guchar)(colorAccBuf[srcColorIdx + j] / yStep);
}
srcColorIdx += nComps;
for (int i = 0; i < xStep; ++i) {
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] = colorBuf[j];
}
scaledColorIdx += nComps;
}
if (hasAlpha) {
Guchar alphaBuf = (Guchar)(alphaAccBuf[srcIdx] / yStep);
for (int i = 0; i < xStep; ++i) {
alphaLine[scaledAlphaIdx] = alphaBuf;
++scaledAlphaIdx;
}
}
}
}
void BasicImageScaler::vertDownscaleHorizUpscaleInterp() {
//--- vert downscale
int yStep = yp;
yt += yq;
if (yt >= scaledHeight) {
yt -= scaledHeight;
++yStep;
}
memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
if (hasAlpha) {
memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
}
int nRowComps = srcWidth * nComps;
for (int i = 0; i < yStep; ++i) {
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
for (int j = 0; j < nRowComps; ++j) {
colorAccBuf[j] += colorTmpBuf0[j];
}
if (hasAlpha) {
for (int j = 0; j < srcWidth; ++j) {
alphaAccBuf[j] += alphaTmpBuf0[j];
}
}
}
for (int j = 0; j < srcWidth * nComps; ++j) {
colorAccBuf[j] /= yStep;
}
if (hasAlpha) {
for (int j = 0; j < srcWidth; ++j) {
alphaAccBuf[j] /= yStep;
}
}
//--- horiz upscale
int scaledColorIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
int x0 = splashFloor(xs - 0.5);
int x1 = x0 + 1;
SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
SplashCoord s1 = (SplashCoord)1 - s0;
if (x0 < 0) {
x0 = 0;
}
if (x1 >= srcWidth) {
x1 = srcWidth - 1;
}
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] =
(Guchar)(int)(s0 * colorAccBuf[x0 * nComps + j] +
s1 * colorAccBuf[x1 * nComps + j]);
}
scaledColorIdx += nComps;
if (hasAlpha) {
alphaLine[scaledIdx] = (Guchar)(int)(s0 * alphaAccBuf[x0] +
s1 * alphaAccBuf[x1]);
}
}
}
void BasicImageScaler::vertUpscaleHorizDownscaleNoInterp() {
//--- vert upscale
if (yn == 0) {
yn = yp;
yt += yq;
if (yt >= srcHeight) {
yt -= srcHeight;
++yn;
}
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
}
--yn;
//--- horiz downscale
int colorAcc[splashMaxColorComps];
int xt = 0;
int unscaledColorIdx = 0;
int unscaledAlphaIdx = 0;
int scaledColorIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
int xStep = xp;
xt += xq;
if (xt >= scaledWidth) {
xt -= scaledWidth;
++xStep;
}
for (int j = 0; j < nComps; ++j) {
colorAcc[j] = 0;
}
for (int i = 0; i < xStep; ++i) {
for (int j = 0; j < nComps; ++j) {
colorAcc[j] += colorTmpBuf0[unscaledColorIdx + j];
}
unscaledColorIdx += nComps;
}
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
}
scaledColorIdx += nComps;
if (hasAlpha) {
int alphaAcc = 0;
for (int i = 0; i < xStep; ++i) {
alphaAcc += alphaTmpBuf0[unscaledAlphaIdx];
++unscaledAlphaIdx;
}
alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
}
}
}
void BasicImageScaler::vertUpscaleHorizDownscaleInterp() {
//--- vert upscale
if (ySrcCur == 0) {
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
(*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
ySrcCur = 1;
}
SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
int y0 = splashFloor(ys - 0.5);
int y1 = y0 + 1;
SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
SplashCoord vs1 = (SplashCoord)1 - vs0;
if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
Guchar *t = colorTmpBuf0;
colorTmpBuf0 = colorTmpBuf1;
colorTmpBuf1 = t;
if (hasAlpha) {
t = alphaTmpBuf0;
alphaTmpBuf0 = alphaTmpBuf1;
alphaTmpBuf1 = t;
}
(*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
++ySrcCur;
}
Guchar *color0 = colorTmpBuf0;
Guchar *color1 = colorTmpBuf1;
Guchar *alpha0 = alphaTmpBuf0;
Guchar *alpha1 = alphaTmpBuf1;
if (y0 < 0) {
y0 = 0;
color1 = color0;
alpha1 = alpha0;
}
if (y1 >= srcHeight) {
y1 = srcHeight - 1;
color0 = color1;
alpha0 = alpha1;
}
++yScaledCur;
//--- horiz downscale
int colorAcc[splashMaxColorComps];
int xt = 0;
int unscaledColorIdx = 0;
int unscaledAlphaIdx = 0;
int scaledColorIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
int xStep = xp;
xt += xq;
if (xt >= scaledWidth) {
xt -= scaledWidth;
++xStep;
}
for (int j = 0; j < nComps; ++j) {
colorAcc[j] = 0;
}
for (int i = 0; i < xStep; ++i) {
for (int j = 0; j < nComps; ++j) {
colorAcc[j] += (int)(vs0 * (int)color0[unscaledColorIdx + j] +
vs1 * (int)color1[unscaledColorIdx + j]);
}
unscaledColorIdx += nComps;
}
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
}
scaledColorIdx += nComps;
if (hasAlpha) {
int alphaAcc = 0;
for (int i = 0; i < xStep; ++i) {
alphaAcc += (int)(vs0 * (int)alpha0[unscaledAlphaIdx] +
vs1 * (int)alpha1[unscaledAlphaIdx]);
++unscaledAlphaIdx;
}
alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
}
}
}
void BasicImageScaler::vertUpscaleHorizUpscaleNoInterp() {
//--- vert upscale
if (yn == 0) {
yn = yp;
yt += yq;
if (yt >= srcHeight) {
yt -= srcHeight;
++yn;
}
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
}
--yn;
//--- horiz upscale
int xt = 0;
int scaledColorIdx = 0;
int srcColorIdx = 0;
int scaledAlphaIdx = 0;
for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
int xStep = xp;
xt += xq;
if (xt >= srcWidth) {
xt -= srcWidth;
++xStep;
}
for (int i = 0; i < xStep; ++i) {
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] = colorTmpBuf0[srcColorIdx + j];
}
scaledColorIdx += nComps;
}
srcColorIdx += nComps;
if (hasAlpha) {
Guchar alphaBuf = alphaTmpBuf0[srcIdx];
for (int i = 0; i < xStep; ++i) {
alphaLine[scaledAlphaIdx] = alphaBuf;
++scaledAlphaIdx;
}
}
}
}
void BasicImageScaler::vertUpscaleHorizUpscaleInterp() {
//--- vert upscale
if (ySrcCur == 0) {
(*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
(*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
ySrcCur = 1;
}
SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
int y0 = splashFloor(ys - 0.5);
int y1 = y0 + 1;
SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
SplashCoord vs1 = (SplashCoord)1 - vs0;
if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
Guchar *t = colorTmpBuf0;
colorTmpBuf0 = colorTmpBuf1;
colorTmpBuf1 = t;
if (hasAlpha) {
t = alphaTmpBuf0;
alphaTmpBuf0 = alphaTmpBuf1;
alphaTmpBuf1 = t;
}
(*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
++ySrcCur;
}
Guchar *color0 = colorTmpBuf0;
Guchar *color1 = colorTmpBuf1;
Guchar *alpha0 = alphaTmpBuf0;
Guchar *alpha1 = alphaTmpBuf1;
if (y0 < 0) {
y0 = 0;
color1 = color0;
alpha1 = alpha0;
}
if (y1 >= srcHeight) {
y1 = srcHeight - 1;
color0 = color1;
alpha0 = alpha1;
}
++yScaledCur;
for (int srcIdx = 0; srcIdx < srcWidth * nComps; ++srcIdx) {
colorTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)color0[srcIdx] +
vs1 * (int)color1[srcIdx]);
}
if (hasAlpha) {
for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
alphaTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)alpha0[srcIdx] +
vs1 * (int)alpha1[srcIdx]);
}
}
//--- horiz upscale
int scaledColorIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
int x0 = splashFloor(xs - 0.5);
int x1 = x0 + 1;
SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
SplashCoord hs1 = (SplashCoord)1 - hs0;
if (x0 < 0) {
x0 = 0;
}
if (x1 >= srcWidth) {
x1 = srcWidth - 1;
}
for (int j = 0; j < nComps; ++j) {
colorLine[scaledColorIdx + j] =
(Guchar)(int)(hs0 * (int)colorTmpBuf2[x0 * nComps + j] +
hs1 * (int)colorTmpBuf2[x1 * nComps + j]);
}
scaledColorIdx += nComps;
if (hasAlpha) {
alphaLine[scaledIdx] = (Guchar)(int)(hs0 * (int)alphaTmpBuf2[x0] +
hs1 * (int)alphaTmpBuf2[x1]);
}
}
}
//------------------------------------------------------------------------
// SavingImageScaler
//------------------------------------------------------------------------
// Wrapper around BasicImageScaler that saves the scaled image for use
// by ReplayImageScaler.
class SavingImageScaler: public BasicImageScaler {
public:
SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
int aScaledWidth, int aScaledHeight, GBool aInterpolate,
Guchar *aColorCache, Guchar *aAlphaCache);
virtual void nextLine();
private:
Guchar *colorPtr;
Guchar *alphaPtr;
};
SavingImageScaler::SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
int aSrcWidth, int aSrcHeight,
int aNComps, GBool aHasAlpha,
int aScaledWidth, int aScaledHeight,
GBool aInterpolate,
Guchar *aColorCache, Guchar *aAlphaCache):
BasicImageScaler(aSrc, aSrcData, aSrcWidth, aSrcHeight, aNComps, aHasAlpha,
aScaledWidth, aScaledHeight, aInterpolate)
{
colorPtr = aColorCache;
alphaPtr = aAlphaCache;
}
void SavingImageScaler::nextLine() {
BasicImageScaler::nextLine();
memcpy(colorPtr, colorData(), scaledWidth * nComps);
colorPtr += scaledWidth * nComps;
if (hasAlpha) {
memcpy(alphaPtr, alphaData(), scaledWidth);
alphaPtr += scaledWidth;
}
}
//------------------------------------------------------------------------
// ReplayImageScaler
//------------------------------------------------------------------------
// "Replay" a scaled image saved by SavingImageScaler.
class ReplayImageScaler: public ImageScaler {
public:
ReplayImageScaler(int aNComps, GBool aHasAlpha,
int aScaledWidth,
Guchar *aColorCache, Guchar *aAlphaCache);
virtual void nextLine();
virtual Guchar *colorData() { return colorLine; }
virtual Guchar *alphaData() { return alphaLine; }
private:
int nComps;
GBool hasAlpha;
int scaledWidth;
Guchar *colorPtr;
Guchar *alphaPtr;
Guchar *colorLine;
Guchar *alphaLine;
};
ReplayImageScaler::ReplayImageScaler(int aNComps, GBool aHasAlpha,
int aScaledWidth,
Guchar *aColorCache, Guchar *aAlphaCache) {
nComps = aNComps;
hasAlpha = aHasAlpha;
scaledWidth = aScaledWidth;
colorPtr = aColorCache;
alphaPtr = aAlphaCache;
colorLine = NULL;
alphaLine = NULL;
}
void ReplayImageScaler::nextLine() {
colorLine = colorPtr;
alphaLine = alphaPtr;
colorPtr += scaledWidth * nComps;
if (hasAlpha) {
alphaPtr += scaledWidth;
}
}
//------------------------------------------------------------------------
// ImageMaskScaler
//------------------------------------------------------------------------
class ImageMaskScaler {
public:
// Set up a MaskScaler to scale from [srcWidth]x[srcHeight] to
// [scaledWidth]x[scaledHeight].
ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
int aSrcWidth, int aSrcHeight,
int aScaledWidth, int aScaledHeight, GBool aInterpolate);
~ImageMaskScaler();
// Compute the next line of the scaled image mask. This can be
// called up to [scaledHeight] times.
void nextLine();
// Retrieve the data generated by the most recent call to
// nextLine().
Guchar *data() { return line; }
private:
void vertDownscaleHorizDownscale();
void vertDownscaleHorizUpscaleNoInterp();
void vertDownscaleHorizUpscaleInterp();
void vertUpscaleHorizDownscaleNoInterp();
void vertUpscaleHorizDownscaleInterp();
void vertUpscaleHorizUpscaleNoInterp();
void vertUpscaleHorizUpscaleInterp();
// source image data function
SplashImageMaskSource src;
void *srcData;
// source image size
int srcWidth;
int srcHeight;
// scaled image size
int scaledWidth;
int scaledHeight;
// params/state for vertical scaling
int yp, yq;
int yt, yn;
int ySrcCur, yScaledCur;
SplashCoord yInvScale;
// params for horizontal scaling
int xp, xq;
SplashCoord xInvScale;
// vertical and horizontal scaling functions
void (ImageMaskScaler::*scalingFunc)();
// temporary buffers for vertical scaling
Guchar *tmpBuf0;
Guchar *tmpBuf1;
Guchar *tmpBuf2;
Guint *accBuf;
// output of horizontal scaling
Guchar *line;
};
ImageMaskScaler::ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
int aSrcWidth, int aSrcHeight,
int aScaledWidth, int aScaledHeight,
GBool aInterpolate) {
tmpBuf0 = NULL;
tmpBuf1 = NULL;
tmpBuf2 = NULL;
accBuf = NULL;
line = NULL;
src = aSrc;
srcData = aSrcData;
srcWidth = aSrcWidth;
srcHeight = aSrcHeight;
scaledWidth = aScaledWidth;
scaledHeight = aScaledHeight;
// select scaling function; allocate buffers
if (scaledHeight <= srcHeight) {
// vertical downscaling
yp = srcHeight / scaledHeight;
yq = srcHeight % scaledHeight;
yt = 0;
tmpBuf0 = (Guchar *)gmalloc(srcWidth);
accBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
if (scaledWidth <= srcWidth) {
scalingFunc = &ImageMaskScaler::vertDownscaleHorizDownscale;
} else {
if (aInterpolate) {
scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleInterp;
} else {
scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp;
}
}
} else {
// vertical upscaling
yp = scaledHeight / srcHeight;
yq = scaledHeight % srcHeight;
yt = 0;
yn = 0;
if (aInterpolate) {
yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
tmpBuf0 = (Guchar *)gmalloc(srcWidth);
tmpBuf1 = (Guchar *)gmalloc(srcWidth);
ySrcCur = 0;
yScaledCur = 0;
if (scaledWidth <= srcWidth) {
scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleInterp;
} else {
tmpBuf2 = (Guchar *)gmalloc(srcWidth);
scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleInterp;
}
} else {
tmpBuf0 = (Guchar *)gmalloc(srcWidth);
if (scaledWidth <= srcWidth) {
scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp;
} else {
scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp;
}
}
}
if (scaledWidth <= srcWidth) {
xp = srcWidth / scaledWidth;
xq = srcWidth % scaledWidth;
} else {
xp = scaledWidth / srcWidth;
xq = scaledWidth % srcWidth;
if (aInterpolate) {
xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
}
}
line = (Guchar *)gmalloc(scaledWidth);
}
ImageMaskScaler::~ImageMaskScaler() {
gfree(tmpBuf0);
gfree(tmpBuf1);
gfree(tmpBuf2);
gfree(accBuf);
gfree(line);
}
void ImageMaskScaler::nextLine() {
(this->*scalingFunc)();
}
void ImageMaskScaler::vertDownscaleHorizDownscale() {
//--- vert downscale
int yStep = yp;
yt += yq;
if (yt >= scaledHeight) {
yt -= scaledHeight;
++yStep;
}
memset(accBuf, 0, srcWidth * sizeof(Guint));
for (int i = 0; i < yStep; ++i) {
(*src)(srcData, tmpBuf0);
for (int j = 0; j < srcWidth; ++j) {
accBuf[j] += tmpBuf0[j];
}
}
//--- horiz downscale
int acc;
int xt = 0;
int unscaledIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
int xStep = xp;
xt += xq;
if (xt >= scaledWidth) {
xt -= scaledWidth;
++xStep;
}
acc = 0;
for (int i = 0; i < xStep; ++i) {
acc += accBuf[unscaledIdx];
++unscaledIdx;
}
line[scaledIdx] = (Guchar)((255 * acc) / (xStep * yStep));
}
}
void ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp() {
//--- vert downscale
int yStep = yp;
yt += yq;
if (yt >= scaledHeight) {
yt -= scaledHeight;
++yStep;
}
memset(accBuf, 0, srcWidth * sizeof(Guint));
for (int i = 0; i < yStep; ++i) {
(*src)(srcData, tmpBuf0);
for (int j = 0; j < srcWidth; ++j) {
accBuf[j] += tmpBuf0[j];
}
}
//--- horiz upscale
int xt = 0;
int scaledIdx = 0;
for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
int xStep = xp;
xt += xq;
if (xt >= srcWidth) {
xt -= srcWidth;
++xStep;
}
Guchar buf = (Guchar)((255 * accBuf[srcIdx]) / yStep);
for (int i = 0; i < xStep; ++i) {
line[scaledIdx] = buf;
++scaledIdx;
}
}
}
void ImageMaskScaler::vertDownscaleHorizUpscaleInterp() {
//--- vert downscale
int yStep = yp;
yt += yq;
if (yt >= scaledHeight) {
yt -= scaledHeight;
++yStep;
}
memset(accBuf, 0, srcWidth * sizeof(Guint));
for (int i = 0; i < yStep; ++i) {
(*src)(srcData, tmpBuf0);
for (int j = 0; j < srcWidth; ++j) {
accBuf[j] += tmpBuf0[j];
}
}
for (int j = 0; j < srcWidth; ++j) {
accBuf[j] = (255 * accBuf[j]) / yStep;
}
//--- horiz upscale
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
int x0 = splashFloor(xs - 0.5);
int x1 = x0 + 1;
SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
SplashCoord s1 = (SplashCoord)1 - s0;
if (x0 < 0) {
x0 = 0;
}
if (x1 >= srcWidth) {
x1 = srcWidth - 1;
}
line[scaledIdx] = (Guchar)(int)(s0 * accBuf[x0] + s1 * accBuf[x1]);
}
}
void ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp() {
//--- vert upscale
if (yn == 0) {
yn = yp;
yt += yq;
if (yt >= srcHeight) {
yt -= srcHeight;
++yn;
}
(*src)(srcData, tmpBuf0);
}
--yn;
//--- horiz downscale
int acc;
int xt = 0;
int unscaledIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
int xStep = xp;
xt += xq;
if (xt >= scaledWidth) {
xt -= scaledWidth;
++xStep;
}
acc = 0;
for (int i = 0; i < xStep; ++i) {
acc += tmpBuf0[unscaledIdx];
++unscaledIdx;
}
line[scaledIdx] = (Guchar)((255 * acc) / xStep);
}
}
void ImageMaskScaler::vertUpscaleHorizDownscaleInterp() {
//--- vert upscale
if (ySrcCur == 0) {
(*src)(srcData, tmpBuf0);
(*src)(srcData, tmpBuf1);
ySrcCur = 1;
}
SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
int y0 = splashFloor(ys - 0.5);
int y1 = y0 + 1;
SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
SplashCoord vs1 = (SplashCoord)1 - vs0;
if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
Guchar *t = tmpBuf0;
tmpBuf0 = tmpBuf1;
tmpBuf1 = t;
(*src)(srcData, tmpBuf1);
++ySrcCur;
}
Guchar *mask0 = tmpBuf0;
Guchar *mask1 = tmpBuf1;
if (y0 < 0) {
y0 = 0;
mask1 = mask0;
}
if (y1 >= srcHeight) {
y1 = srcHeight - 1;
mask0 = mask1;
}
++yScaledCur;
//--- horiz downscale
int acc;
int xt = 0;
int unscaledIdx = 0;
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
int xStep = xp;
xt += xq;
if (xt >= scaledWidth) {
xt -= scaledWidth;
++xStep;
}
acc = 0;
for (int i = 0; i < xStep; ++i) {
acc += (int)(vs0 * (int)mask0[unscaledIdx] +
vs1 * (int)mask1[unscaledIdx]);
++unscaledIdx;
}
line[scaledIdx] = (Guchar)((255 * acc) / xStep);
}
}
void ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp() {
//--- vert upscale
if (yn == 0) {
yn = yp;
yt += yq;
if (yt >= srcHeight) {
yt -= srcHeight;
++yn;
}
(*src)(srcData, tmpBuf0);
}
--yn;
//--- horiz upscale
int xt = 0;
int scaledIdx = 0;
for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
int xStep = xp;
xt += xq;
if (xt >= srcWidth) {
xt -= srcWidth;
++xStep;
}
Guchar buf = (Guchar)(255 * tmpBuf0[srcIdx]);
for (int i = 0; i < xStep; ++i) {
line[scaledIdx] = buf;
++scaledIdx;
}
}
}
void ImageMaskScaler::vertUpscaleHorizUpscaleInterp() {
//--- vert upscale
if (ySrcCur == 0) {
(*src)(srcData, tmpBuf0);
(*src)(srcData, tmpBuf1);
ySrcCur = 1;
}
SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
int y0 = splashFloor(ys - 0.5);
int y1 = y0 + 1;
SplashCoord vs0 = (SplashCoord)255 * ((SplashCoord)y1 + 0.5 - ys);
SplashCoord vs1 = (SplashCoord)255 - vs0;
if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
Guchar *t = tmpBuf0;
tmpBuf0 = tmpBuf1;
tmpBuf1 = t;
(*src)(srcData, tmpBuf1);
++ySrcCur;
}
Guchar *mask0 = tmpBuf0;
Guchar *mask1 = tmpBuf1;
if (y0 < 0) {
y0 = 0;
mask1 = mask0;
}
if (y1 >= srcHeight) {
y1 = srcHeight - 1;
mask0 = mask1;
}
++yScaledCur;
for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
tmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)mask0[srcIdx] +
vs1 * (int)mask1[srcIdx]);
}
//--- horiz upscale
for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
int x0 = splashFloor(xs - 0.5);
int x1 = x0 + 1;
SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
SplashCoord hs1 = (SplashCoord)1 - hs0;
if (x0 < 0) {
x0 = 0;
}
if (x1 >= srcWidth) {
x1 = srcWidth - 1;
}
line[scaledIdx] = (Guchar)(int)(hs0 * (int)tmpBuf2[x0] +
hs1 * (int)tmpBuf2[x1]);
}
}
//------------------------------------------------------------------------
// Splash
//------------------------------------------------------------------------
Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
SplashImageCache *imageCacheA,
SplashScreenParams *screenParams) {
bitmap = bitmapA;
bitmapComps = splashColorModeNComps[bitmap->mode];
vectorAntialias = vectorAntialiasA;
inShading = gFalse;
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
screenParams);
scanBuf = (Guchar *)gmalloc(bitmap->width);
if (bitmap->mode == splashModeMono1) {
scanBuf2 = (Guchar *)gmalloc(bitmap->width);
} else {
scanBuf2 = NULL;
}
groupBackBitmap = NULL;
groupDestInitMode = splashGroupDestPreInit;
overprintMaskBitmap = NULL;
minLineWidth = 0;
clearModRegion();
debugMode = gFalse;
if (imageCacheA) {
imageCache = imageCacheA;
imageCache->incRefCount();
} else {
imageCache = new SplashImageCache();
}
}
Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
SplashImageCache *imageCacheA, SplashScreen *screenA) {
bitmap = bitmapA;
bitmapComps = splashColorModeNComps[bitmap->mode];
vectorAntialias = vectorAntialiasA;
inShading = gFalse;
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
screenA);
scanBuf = (Guchar *)gmalloc(bitmap->width);
if (bitmap->mode == splashModeMono1) {
scanBuf2 = (Guchar *)gmalloc(bitmap->width);
} else {
scanBuf2 = NULL;
}
groupBackBitmap = NULL;
groupDestInitMode = splashGroupDestPreInit;
overprintMaskBitmap = NULL;
minLineWidth = 0;
clearModRegion();
debugMode = gFalse;
if (imageCacheA) {
imageCache = imageCacheA;
imageCache->incRefCount();
} else {
imageCache = new SplashImageCache();
}
}
Splash::~Splash() {
imageCache->decRefCount();
while (state->next) {
restoreState();
}
delete state;
gfree(scanBuf);
gfree(scanBuf2);
}
//------------------------------------------------------------------------
// 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;
}
SplashStrokeAdjustMode Splash::getStrokeAdjust() {
return state->strokeAdjust;
}
SplashClip *Splash::getClip() {
return state->clip;
}
SplashBitmap *Splash::getSoftMask() {
return state->softMask;
}
GBool Splash::getInNonIsolatedGroup() {
return state->inNonIsolatedGroup;
}
GBool Splash::getInKnockoutGroup() {
return state->inKnockoutGroup;
}
//------------------------------------------------------------------------
// 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) {
if (lineCap >= 0 && lineCap <= 2) {
state->lineCap = lineCap;
} else {
state->lineCap = 0;
}
}
void Splash::setLineJoin(int lineJoin) {
if (lineJoin >= 0 && lineJoin <= 2) {
state->lineJoin = lineJoin;
} else {
state->lineJoin = 0;
}
}
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(SplashStrokeAdjustMode strokeAdjust) {
state->strokeAdjust = strokeAdjust;
}
void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1) {
state->clipResetToRect(x0, y0, x1, y1);
}
SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1) {
return state->clipToRect(x0, y0, x1, y1);
}
SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
return state->clipToPath(path, eo);
}
void Splash::setSoftMask(SplashBitmap *softMask) {
state->setSoftMask(softMask);
}
void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
int groupBackXA, int groupBackYA,
SplashGroupDestInitMode groupDestInitModeA,
GBool nonIsolated, GBool knockout) {
groupBackBitmap = groupBackBitmapA;
groupBackX = groupBackXA;
groupBackY = groupBackYA;
groupDestInitMode = groupDestInitModeA;
groupDestInitYMin = 1;
groupDestInitYMax = 0;
state->inNonIsolatedGroup = nonIsolated;
state->inKnockoutGroup = knockout;
}
void Splash::forceDeferredInit(int y, int h) {
useDestRow(y);
useDestRow(y + h - 1);
}
void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
Guchar *gray) {
state->setTransfer(red, green, blue, gray);
}
void Splash::setOverprintMask(Guint overprintMask) {
state->overprintMask = overprintMask;
}
void Splash::setEnablePathSimplification(GBool en) {
state->enablePathSimplification = en;
}
//------------------------------------------------------------------------
// 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[0];
*p++ = color[1];
*p++ = color[2];
}
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[2];
*p++ = color[1];
*p++ = color[0];
}
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->alphaRowSize * bitmap->height);
}
updateModX(0);
updateModY(0);
updateModX(bitmap->width - 1);
updateModY(bitmap->height - 1);
}
SplashError Splash::stroke(SplashPath *path) {
SplashPath *path2, *dPath;
SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal;
int lineCap, lineJoin, i;
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);
// Compute an approximation of the transformed line width.
// Given a CTM of [m0 m1],
// [m2 m3]
// if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else
// use min{|m1|,|m2|}.
// This handles the common cases -- [s 0 ] and [0 s] --
// [0 +/-s] [+/-s 0]
// well, and still does something reasonable for the uncommon
// case transforms.
t0 = splashAbs(state->matrix[0]);
t1 = splashAbs(state->matrix[1]);
t2 = splashAbs(state->matrix[2]);
t3 = splashAbs(state->matrix[3]);
if (t0 * t3 >= t1 * t2) {
w = (t0 < t3) ? t0 : t3;
} else {
w = (t1 < t2) ? t1 : t2;
}
w2 = w * state->lineWidth;
// construct the dashed path
if (state->lineDashLength > 0) {
// check the maximum transformed dash element length (using the
// same approximation as for line width) -- if it's less than 0.1
// pixel, don't apply the dash pattern; this avoids a huge
// performance/memory hit with PDF files that use absurd dash
// patterns like [0.0007 0.0003]
lineDashTotal = 0;
lineDashMax = 0;
for (i = 0; i < state->lineDashLength; ++i) {
lineDashTotal += state->lineDash[i];
if (state->lineDash[i] > lineDashMax) {
lineDashMax = state->lineDash[i];
}
}
// Acrobat simply draws nothing if the dash array is [0]
if (lineDashTotal == 0) {
delete path2;
return splashOk;
}
if (w * lineDashMax > 0.1) {
dPath = makeDashedPath(path2);
delete path2;
path2 = dPath;
if (path2->length == 0) {
delete path2;
return splashErrEmptyPath;
}
}
}
// round caps on narrow lines look bad, and can't be
// stroke-adjusted, so use projecting caps instead (but we can't do
// this if there are zero-length dashes or segments, because those
// turn into round dots)
lineCap = state->lineCap;
lineJoin = state->lineJoin;
if (state->strokeAdjust == splashStrokeAdjustCAD &&
w2 < 3.5) {
if (lineCap == splashLineCapRound &&
!state->lineDashContainsZeroLengthDashes() &&
!path->containsZeroLengthSubpaths()) {
lineCap = splashLineCapProjecting;
}
if (lineJoin == splashLineJoinRound) {
lineJoin = splashLineJoinBevel;
}
}
// if there is a min line width set, and the transformed line width
// is smaller, use the min line width
if (w > 0 && w2 < minLineWidth) {
strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel);
} else if (bitmap->mode == splashModeMono1 || !vectorAntialias) {
// in monochrome mode or if antialiasing is disabled, use 0-width
// lines for any transformed line width <= 1 -- lines less than 1
// pixel wide look too fat without antialiasing
if (w2 < 1.001) {
strokeNarrow(path2);
} else {
strokeWide(path2, state->lineWidth, lineCap, lineJoin);
}
} else {
// in gray and color modes, only use 0-width lines if the line
// width is explicitly set to 0
if (state->lineWidth == 0) {
strokeNarrow(path2);
} else {
strokeWide(path2, state->lineWidth, lineCap, lineJoin);
}
}
delete path2;
return splashOk;
}
void Splash::strokeNarrow(SplashPath *path) {
SplashPipe pipe;
SplashXPath *xPath;
SplashXPathSeg *seg;
int x0, x1, y0, y1, xa, xb, y;
SplashCoord dxdy;
SplashClipResult clipRes;
int nClipRes[3];
int i;
nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse,
state->enablePathSimplification,
state->strokeAdjust);
pipeInit(&pipe, state->strokePattern,
(Guchar)splashRound(state->strokeAlpha * 255),
gTrue, gFalse);
for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
if (seg->y0 <= seg->y1) {
y0 = splashFloor(seg->y0);
y1 = splashFloor(seg->y1);
x0 = splashFloor(seg->x0);
x1 = splashFloor(seg->x1);
} else {
y0 = splashFloor(seg->y1);
y1 = splashFloor(seg->y0);
x0 = splashFloor(seg->x1);
x1 = splashFloor(seg->x0);
}
if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
x0 <= x1 ? x1 : x0, y1,
state->strokeAdjust))
!= splashClipAllOutside) {
if (y0 == y1) {
if (x0 <= x1) {
drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
} else {
drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
}
} else {
dxdy = seg->dxdy;
y = state->clip->getYMinI(state->strokeAdjust);
if (y0 < y) {
y0 = y;
x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
}
y = state->clip->getYMaxI(state->strokeAdjust);
if (y1 > y) {
y1 = y;
x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
}
if (x0 <= x1) {
xa = x0;
for (y = y0; y <= y1; ++y) {
if (y < y1) {
xb = splashFloor(seg->x0 +
((SplashCoord)y + 1 - seg->y0) * dxdy);
} else {
xb = x1 + 1;
}
if (xa == xb) {
drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
} else {
drawStrokeSpan(&pipe, xa, xb - 1, y,
clipRes == splashClipAllInside);
}
xa = xb;
}
} else {
xa = x0;
for (y = y0; y <= y1; ++y) {
if (y < y1) {
xb = splashFloor(seg->x0 +
((SplashCoord)y + 1 - seg->y0) * dxdy);
} else {
xb = x1 - 1;
}
if (xa == xb) {
drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
} else {
drawStrokeSpan(&pipe, xb + 1, xa, y,
clipRes == splashClipAllInside);
}
xa = xb;
}
}
}
}
++nClipRes[clipRes];
}
if (nClipRes[splashClipPartial] ||
(nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
opClipRes = splashClipPartial;
} else if (nClipRes[splashClipAllInside]) {
opClipRes = splashClipAllInside;
} else {
opClipRes = splashClipAllOutside;
}
delete xPath;
}
void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y,
GBool noClip) {
int x;
x = state->clip->getXMinI(state->strokeAdjust);
if (x > x0) {
x0 = x;
}
x = state->clip->getXMaxI(state->strokeAdjust);
if (x < x1) {
x1 = x;
}
if (x0 > x1) {
return;
}
for (x = x0; x <= x1; ++x) {
scanBuf[x] = 0xff;
}
if (!noClip) {
if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) {
return;
}
}
(this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL);
}
void Splash::strokeWide(SplashPath *path, SplashCoord w,
int lineCap, int lineJoin) {
SplashPath *path2;
path2 = makeStrokePath(path, w, lineCap, lineJoin, 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();
#if USE_FIXEDPOINT
flatness2 = flatness;
#else
flatness2 = flatness * flatness;
#endif
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);
#if USE_FIXEDPOINT
d1 = splashDist(tx, ty, mx, my);
#else
dx = tx - mx;
dy = ty - my;
d1 = dx*dx + dy*dy;
#endif
transform(matrix, xx2, yy2, &tx, &ty);
#if USE_FIXEDPOINT
d2 = splashDist(tx, ty, mx, my);
#else
dx = tx - mx;
dy = ty - my;
d2 = dx*dx + dy*dy;
#endif
// 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 = splashAvg(xl0, xx1);
yl1 = splashAvg(yl0, yy1);
xh = splashAvg(xx1, xx2);
yh = splashAvg(yy1, yy2);
xl2 = splashAvg(xl1, xh);
yl2 = splashAvg(yl1, yh);
xr2 = splashAvg(xx2, xr3);
yr2 = splashAvg(yy2, yr3);
xr1 = splashAvg(xh, xr2);
yr1 = splashAvg(yh, yr2);
xr0 = splashAvg(xl2, xr1);
yr0 = splashAvg(yl2, yr1);
// 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, lineDashEndOn, lineDashOn, newPath;
int lineDashStartIdx, lineDashIdx, subpathStart, nDashes;
int i, j, k;
lineDashTotal = 0;
for (i = 0; i < state->lineDashLength; ++i) {
lineDashTotal += state->lineDash[i];
}
// Acrobat simply draws nothing if the dash array is [0]
if (lineDashTotal == 0) {
return new SplashPath();
}
lineDashStartPhase = state->lineDashPhase;
if (lineDashStartPhase > lineDashTotal * 2) {
i = splashFloor(lineDashStartPhase / (lineDashTotal * 2));
lineDashStartPhase -= lineDashTotal * i * 2;
} else if (lineDashStartPhase < 0) {
i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2));
lineDashStartPhase += lineDashTotal * i * 2;
}
i = splashFloor(lineDashStartPhase / lineDashTotal);
lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
lineDashStartOn = gTrue;
lineDashStartIdx = 0;
if (lineDashStartPhase > 0) {
while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
lineDashStartOn = !lineDashStartOn;
lineDashStartPhase -= state->lineDash[lineDashStartIdx];
if (++lineDashStartIdx == state->lineDashLength) {
lineDashStartIdx = 0;
}
}
}
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;
lineDashEndOn = lineDashStartOn;
lineDashIdx = lineDashStartIdx;
lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
subpathStart = dPath->length;
nDashes = 0;
// 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) {
// Special case for zero-length dash segments: draw a very
// short -- but not zero-length -- segment. This ensures that
// we get the correct behavior with butt and projecting line
// caps. The PS/PDF specs imply that zero-length segments are
// not drawn unless the line cap is round, but Acrobat and
// Ghostscript both draw very short segments (for butt caps)
// and squares (for projecting caps).
if (lineDashDist == 0) {
if (lineDashOn) {
if (newPath) {
dPath->moveTo(x0, y0);
newPath = gFalse;
++nDashes;
}
xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0);
ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0);
dPath->lineTo(xa, ya);
}
} else if (lineDashDist >= segLen) {
if (lineDashOn) {
if (newPath) {
dPath->moveTo(x0, y0);
newPath = gFalse;
++nDashes;
}
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;
++nDashes;
}
dPath->lineTo(xa, ya);
}
x0 = xa;
y0 = ya;
segLen -= lineDashDist;
lineDashDist = 0;
}
lineDashEndOn = lineDashOn;
// 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;
}
}
}
// in a closed subpath, where the dash pattern is "on" at both the
// start and end of the subpath, we need to merge the start and
// end to get a proper line join
if ((path->flags[j] & splashPathClosed) &&
lineDashStartOn &&
lineDashEndOn) {
if (nDashes == 1) {
dPath->close();
} else if (nDashes > 1) {
k = subpathStart;
do {
++k;
dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y);
} while (!(dPath->flags[k] & splashPathLast));
++k;
memmove(&dPath->pts[subpathStart], &dPath->pts[k],
(dPath->length - k) * sizeof(SplashPathPoint));
memmove(&dPath->flags[subpathStart], &dPath->flags[k],
(dPath->length - k) * sizeof(Guchar));
dPath->length -= k - subpathStart;
dPath->curSubpath -= k - subpathStart;
}
}
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;
SplashPath *path2;
SplashXPath *xPath;
SplashXPathScanner *scanner;
int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t;
SplashClipResult clipRes;
if (path->length == 0) {
return splashErrEmptyPath;
}
if (pathAllOutside(path)) {
opClipRes = splashClipAllOutside;
return splashOk;
}
path2 = tweakFillPath(path);
xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue,
state->enablePathSimplification,
state->strokeAdjust);
if (path2 != path) {
delete path2;
}
xMin = xPath->getXMin();
yMin = xPath->getYMin();
xMax = xPath->getXMax();
yMax = xPath->getYMax();
if (xMin > xMax || yMin > yMax) {
delete xPath;
return splashOk;
}
scanner = new SplashXPathScanner(xPath, eo, yMin, yMax);
// check clipping
if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
state->strokeAdjust))
!= splashClipAllOutside) {
if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
xMin = t;
}
if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
xMax = t;
}
if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
yMin = t;
}
if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
yMax = t;
}
if (xMin > xMax || yMin > yMax) {
delete scanner;
delete xPath;
return splashOk;
}
pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255),
gTrue, gFalse);
// draw the spans
if (vectorAntialias && !inShading) {
for (y = yMin; y <= yMax; ++y) {
scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
if (xMin2 <= xMax2) {
if (clipRes != splashClipAllInside) {
state->clip->clipSpan(scanBuf, y, xMin2, xMax2,
state->strokeAdjust);
}
(this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL);
}
}
} else {
for (y = yMin; y <= yMax; ++y) {
scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
if (xMin2 <= xMax2) {
if (clipRes != splashClipAllInside) {
state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2,
state->strokeAdjust);
}
(this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL);
}
}
}
}
opClipRes = clipRes;
delete scanner;
delete xPath;
return splashOk;
}
// Applies various tweaks to a fill path:
// (1) add stroke adjust hints to a filled rectangle
// (2) applies a minimum width to a zero-width filled rectangle (so
// stroke adjustment works correctly
// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto
// lineto closepath fill') to a minimum-width filled rectangle
//
// These tweaks only apply to paths with a single subpath.
//
// Returns either the unchanged input path or a new path (in which
// case the returned path must be deleted by the caller).
SplashPath *Splash::tweakFillPath(SplashPath *path) {
SplashPath *path2;
SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w;
int n;
if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) {
return path;
}
n = path->getLength();
if (!((n == 2) ||
(n == 3 &&
path->flags[1] == 0) ||
(n == 4 &&
path->flags[1] == 0 &&
path->flags[2] == 0) ||
(n == 5 &&
path->flags[1] == 0 &&
path->flags[2] == 0 &&
path->flags[3] == 0))) {
return path;
}
path2 = path;
// degenerate fill (2 or 3 points) or rectangle of (nearly) zero
// width --> replace with a min-width rectangle and hint
if (n == 2 ||
(n == 3 && (path->flags[0] & splashPathClosed)) ||
(n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 &&
splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) ||
((n == 4 ||
(n == 5 && (path->flags[0] & splashPathClosed))) &&
((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 &&
splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 &&
splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 &&
splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) ||
(splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 &&
splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 &&
splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 &&
splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) {
wx = state->matrix[0] + state->matrix[2];
wy = state->matrix[1] + state->matrix[3];
w = splashSqrt(wx*wx + wy*wy);
if (w < 0.001) {
w = 0;
} else {
// min width is 0.1 -- this constant is minWidth * sqrt(2)
w = (SplashCoord)0.1414 / w;
}
xx0 = path->pts[0].x;
yy0 = path->pts[0].y;
if (n <= 3) {
xx1 = path->pts[1].x;
yy1 = path->pts[1].y;
} else {
xx1 = path->pts[2].x;
yy1 = path->pts[2].y;
}
dx = xx1 - xx0;
dy = yy1 - yy0;
d = splashSqrt(dx * dx + dy * dy);
if (d < 0.001) {
d = 0;
} else {
d = w / d;
}
dx *= d;
dy *= d;
path2 = new SplashPath();
path2->moveTo(xx0 + dy, yy0 - dx);
path2->lineTo(xx1 + dy, yy1 - dx);
path2->lineTo(xx1 - dy, yy1 + dx);
path2->lineTo(xx0 - dy, yy0 + dx);
path2->close(gTrue);
path2->addStrokeAdjustHint(0, 2, 0, 4);
path2->addStrokeAdjustHint(1, 3, 0, 4);
// unclosed rectangle --> close and hint
} else if (n == 4 && !(path->flags[0] & splashPathClosed)) {
path2->close(gTrue);
path2->addStrokeAdjustHint(0, 2, 0, 4);
path2->addStrokeAdjustHint(1, 3, 0, 4);
// closed rectangle --> hint
} else if (n == 5 && (path->flags[0] & splashPathClosed)) {
path2->addStrokeAdjustHint(0, 2, 0, 4);
path2->addStrokeAdjustHint(1, 3, 0, 4);
}
return path2;
}
GBool Splash::pathAllOutside(SplashPath *path) {
SplashCoord xMin1, yMin1, xMax1, yMax1;
SplashCoord xMin2, yMin2, xMax2, yMax2;
SplashCoord x, y;
int xMinI, yMinI, xMaxI, yMaxI;
int i;
xMin1 = xMax1 = path->pts[0].x;
yMin1 = yMax1 = path->pts[0].y;
for (i = 1; i < path->length; ++i) {
if (path->pts[i].x < xMin1) {
xMin1 = path->pts[i].x;
} else if (path->pts[i].x > xMax1) {
xMax1 = path->pts[i].x;
}
if (path->pts[i].y < yMin1) {
yMin1 = path->pts[i].y;
} else if (path->pts[i].y > yMax1) {
yMax1 = path->pts[i].y;
}
}
transform(state->matrix, xMin1, yMin1, &x, &y);
xMin2 = xMax2 = x;
yMin2 = yMax2 = y;
transform(state->matrix, xMin1, yMax1, &x, &y);
if (x < xMin2) {
xMin2 = x;
} else if (x > xMax2) {
xMax2 = x;
}
if (y < yMin2) {
yMin2 = y;
} else if (y > yMax2) {
yMax2 = y;
}
transform(state->matrix, xMax1, yMin1, &x, &y);
if (x < xMin2) {
xMin2 = x;
} else if (x > xMax2) {
xMax2 = x;
}
if (y < yMin2) {
yMin2 = y;
} else if (y > yMax2) {
yMax2 = y;
}
transform(state->matrix, xMax1, yMax1, &x, &y);
if (x < xMin2) {
xMin2 = x;
} else if (x > xMax2) {
xMax2 = x;
}
if (y < yMin2) {
yMin2 = y;
} else if (y > yMax2) {
yMax2 = y;
}
// sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are
// 32-bit integers, so coords need to be < 2^31
SplashXPath::clampCoords(&xMin2, &yMin2);
SplashXPath::clampCoords(&xMax2, &yMax2);
xMinI = splashFloor(xMin2);
yMinI = splashFloor(yMin2);
xMaxI = splashFloor(xMax2);
yMaxI = splashFloor(yMax2);
return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI,
state->strokeAdjust) ==
splashClipAllOutside;
}
SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
int c, SplashFont *font) {
SplashGlyphBitmap glyph;
SplashCoord xt, yt;
int x0, y0, xFrac, yFrac;
SplashError err;
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)) {
return splashErrNoGlyph;
}
err = fillGlyph2(x0, y0, &glyph);
if (glyph.freeData) {
gfree(glyph.data);
}
return err;
}
SplashError 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);
return fillGlyph2(x0, y0, glyph);
}
SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
SplashPipe pipe;
SplashClipResult clipRes;
Guchar alpha;
Guchar *p;
int xMin, yMin, xMax, yMax;
int x, y, xg, yg, xx, t;
xg = x0 - glyph->x;
yg = y0 - glyph->y;
xMin = xg;
xMax = xg + glyph->w - 1;
yMin = yg;
yMax = yg + glyph->h - 1;
if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
state->strokeAdjust))
!= splashClipAllOutside) {
pipeInit(&pipe, state->fillPattern,
(Guchar)splashRound(state->fillAlpha * 255),
gTrue, gFalse);
if (clipRes == splashClipAllInside) {
if (glyph->aa) {
p = glyph->data;
for (y = yMin; y <= yMax; ++y) {
(this->*pipe.run)(&pipe, xMin, xMax, y,
glyph->data + (y - yMin) * glyph->w, NULL);
}
} else {
p = glyph->data;
for (y = yMin; y <= yMax; ++y) {
for (x = xMin; x <= xMax; x += 8) {
alpha = *p++;
for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00;
alpha = (Guchar)(alpha << 1);
}
}
(this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
}
} else {
if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
xMin = t;
}
if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
xMax = t;
}
if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
yMin = t;
}
if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
yMax = t;
}
if (xMin <= xMax && yMin <= yMax) {
if (glyph->aa) {
for (y = yMin; y <= yMax; ++y) {
p = glyph->data + (y - yg) * glyph->w + (xMin - xg);
memcpy(scanBuf + xMin, p, xMax - xMin + 1);
state->clip->clipSpan(scanBuf, y, xMin, xMax,
state->strokeAdjust);
(this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
} else {
for (y = yMin; y <= yMax; ++y) {
p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3)
+ ((xMin - xg) >> 3);
alpha = *p++;
xx = (xMin - xg) & 7;
alpha = (Guchar)(alpha << xx);
for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) {
scanBuf[x] = (alpha & 0x80) ? 255 : 0;
alpha = (Guchar)(alpha << 1);
}
for (; x <= xMax; x += 8) {
alpha = *p++;
for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0;
alpha = (Guchar)(alpha << 1);
}
}
state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
state->strokeAdjust);
(this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
}
}
}
}
opClipRes = clipRes;
return splashOk;
}
void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
int *xyMinI, int *xyMaxI) {
if (state->strokeAdjust == splashStrokeAdjustOff) {
// make sure the coords fit in 32-bit ints
#if USE_FIXEDPOINT
if (xyMin < -32767) {
xyMin = -32767;
} else if (xyMin > 32767) {
xyMin = 32767;
}
if (xyMax < -32767) {
xyMax = -32767;
} else if (xyMax > 32767) {
xyMax = 32767;
}
#else
if (xyMin < -1e9) {
xyMin = -1e9;
} else if (xyMin > 1e9) {
xyMin = 1e9;
}
if (xyMax < -1e9) {
xyMax = -1e9;
} else if (xyMax > 1e9) {
xyMax = 1e9;
}
#endif
*xyMinI = splashFloor(xyMin);
*xyMaxI = splashFloor(xyMax);
if (*xyMaxI <= *xyMinI) {
*xyMaxI = *xyMinI + 1;
}
} else {
splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust);
}
}
struct SplashDrawImageMaskRowData {
SplashPipe pipe;
};
// The glyphMode flag is not currently used, but may be useful if the
// stroke adjustment behavior is changed.
SplashError Splash::fillImageMask(GString *imageTag,
SplashImageMaskSource src, void *srcData,
int w, int h, SplashCoord *mat,
GBool glyphMode, GBool interpolate) {
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]);
}
//--- check for singular matrix
if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
return splashErrSingularMatrix;
}
//--- compute image bbox, check clipping
GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
GBool horizFlip = gFalse;
GBool vertFlip = gFalse;
int xMin, yMin, xMax, yMax;
if (flipsOnly) {
horizFlip = mat[0] < 0;
vertFlip = mat[3] < 0;
if (vertFlip) {
if (horizFlip) { // bottom-up, mirrored
getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
} else { // bottom-up
getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
}
} else {
if (horizFlip) { // top-down, mirrored
getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
} else { // top-down
getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
}
}
} else {
int xx = splashRound(mat[4]); // (0,0)
int yy = splashRound(mat[5]);
xMin = xMax = xx;
yMin = yMax = yy;
xx = splashRound(mat[0] + mat[4]); // (1,0)
yy = splashRound(mat[1] + mat[5]);
if (xx < xMin) {
xMin = xx;
} else if (xx > xMax) {
xMax = xx;
}
if (yy < yMin) {
yMin = yy;
} else if (yy > yMax) {
yMax = yy;
}
xx = splashRound(mat[2] + mat[4]); // (0,1)
yy = splashRound(mat[3] + mat[5]);
if (xx < xMin) {
xMin = xx;
} else if (xx > xMax) {
xMax = xx;
}
if (yy < yMin) {
yMin = yy;
} else if (yy > yMax) {
yMax = yy;
}
xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
yy = splashRound(mat[1] + mat[3] + mat[5]);
if (xx < xMin) {
xMin = xx;
} else if (xx > xMax) {
xMax = xx;
}
if (yy < yMin) {
yMin = yy;
} else if (yy > yMax) {
yMax = yy;
}
if (xMax <= xMin) {
xMax = xMin + 1;
}
if (yMax <= yMin) {
yMax = yMin + 1;
}
}
SplashClipResult clipRes =
state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
state->strokeAdjust);
// If the scaled mask is much wider and/or taller than the clip
// region, we use the "arbitrary" scaling path, to avoid a
// potentially very slow loop in the flips-only path (which scans
// the full width and height of the scaled mask, regardless of the
// clip region).
int clipW = state->clip->getXMaxI(state->strokeAdjust)
- state->clip->getXMinI(state->strokeAdjust);
int clipH = state->clip->getYMaxI(state->strokeAdjust)
- state->clip->getYMinI(state->strokeAdjust);
GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
//--- set up the SplashDrawImageMaskRowData object and the pipes
SplashDrawImageMaskRowData dd;
pipeInit(&dd.pipe, state->fillPattern,
(Guchar)splashRound(state->fillAlpha * 255),
gTrue, gFalse);
//--- choose the drawRow function
SplashDrawImageMaskRowFunc drawRowFunc;
if (clipRes == splashClipAllInside) {
drawRowFunc = &Splash::drawImageMaskRowNoClip;
} else {
if (vectorAntialias) {
drawRowFunc = &Splash::drawImageMaskRowClipAA;
} else {
drawRowFunc = &Splash::drawImageMaskRowClipNoAA;
}
}
//--- horizontal/vertical flips only
if (flipsOnly && !veryLarge) {
if (clipRes != splashClipAllOutside) {
int scaledWidth = xMax - xMin;
int scaledHeight = yMax - yMin;
ImageMaskScaler scaler(src, srcData, w, h,
scaledWidth, scaledHeight, interpolate);
Guchar *tmpLine = NULL;
if (horizFlip) {
tmpLine = (Guchar *)gmalloc(scaledWidth);
}
if (vertFlip) {
if (horizFlip) { // bottom-up, mirrored
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
(this->*drawRowFunc)(&dd, tmpLine,
xMin, yMax - 1 - y, scaledWidth);
}
} else { // bottom-up
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
(this->*drawRowFunc)(&dd, scaler.data(),
xMin, yMax - 1 - y, scaledWidth);
}
}
} else {
if (horizFlip) { // top-down, mirrored
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
(this->*drawRowFunc)(&dd, tmpLine,
xMin, yMin + y, scaledWidth);
}
} else { // top-down
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
(this->*drawRowFunc)(&dd, scaler.data(),
xMin, yMin + y, scaledWidth);
}
}
}
gfree(tmpLine);
}
//--- arbitrary transform
} else {
// estimate of size of scaled image
int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ mat[1] * mat[1]));
int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ mat[3] * mat[3]));
if (scaledWidth < 1) {
scaledWidth = 1;
}
if (scaledHeight < 1) {
scaledHeight = 1;
}
GBool downscaling = gTrue;
if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
downscaling = gFalse;
scaledWidth = w;
scaledHeight = h;
}
// compute mapping from device space to scaled image space
SplashCoord mat1[6];
mat1[0] = mat[0] / scaledWidth;
mat1[1] = mat[1] / scaledWidth;
mat1[2] = mat[2] / scaledHeight;
mat1[3] = mat[3] / scaledHeight;
mat1[4] = mat[4];
mat1[5] = mat[5];
SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
if (splashAbs(det) < 1e-6) {
// this should be caught by the singular matrix check in drawImage
return splashErrSingularMatrix;
}
SplashCoord invMat[6];
invMat[0] = mat1[3] / det;
invMat[1] = -mat1[1] / det;
invMat[2] = -mat1[2] / det;
invMat[3] = mat1[0] / det;
// the extra "+ 0.5 * (...)" terms are here because the
// drawImageArbitrary(No)Interp functions multiply by pixel
// centers, (x + 0.5, y + 0.5)
invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ (invMat[0] + invMat[2]) * 0.5;
invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ (invMat[1] + invMat[3]) * 0.5;
// if downscaling: store the downscaled image mask
// if upscaling: store the unscaled image mask
Guchar *scaledMask = (Guchar *)gmallocn(scaledHeight, scaledWidth);
if (downscaling) {
ImageMaskScaler scaler(src, srcData, w, h,
scaledWidth, scaledHeight, interpolate);
Guchar *ptr = scaledMask;
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
memcpy(ptr, scaler.data(), scaledWidth);
ptr += scaledWidth;
}
} else {
Guchar *ptr = scaledMask;
for (int y = 0; y < scaledHeight; ++y) {
(*src)(srcData, ptr);
for (int x = 0; x < scaledWidth; ++x) {
*ptr = (Guchar)(*ptr * 255);
++ptr;
}
}
}
// draw it
if (interpolate) {
drawImageMaskArbitraryInterp(scaledMask,
&dd, drawRowFunc, invMat,
scaledWidth, scaledHeight,
xMin, yMin, xMax, yMax);
} else {
drawImageMaskArbitraryNoInterp(scaledMask,
&dd, drawRowFunc, invMat,
scaledWidth, scaledHeight,
xMin, yMin, xMax, yMax);
}
// free the downscaled/unscaled image
gfree(scaledMask);
}
return splashOk;
}
void Splash::drawImageMaskArbitraryNoInterp(
Guchar *scaledMask,
SplashDrawImageMaskRowData *dd,
SplashDrawImageMaskRowFunc drawRowFunc,
SplashCoord *invMat,
int scaledWidth, int scaledHeight,
int xMin, int yMin, int xMax, int yMax) {
int tt = state->clip->getXMinI(state->strokeAdjust);
if (tt > xMin) {
xMin = tt;
}
tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
if (tt < xMax) {
xMax = tt;
}
tt = state->clip->getYMinI(state->strokeAdjust);
if (tt > yMin) {
yMin = tt;
}
tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
if (tt < yMax) {
yMax = tt;
}
if (xMax <= xMin || yMax <= yMin) {
return;
}
Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
for (int y = yMin; y < yMax; ++y) {
int rowMin = xMax;
int rowMax = 0;
for (int x = xMin; x < xMax; ++x) {
// note: invMat includes a "+0.5" factor so that this is really
// a multiply by (x+0.5, y+0.5)
int xx = splashFloor((SplashCoord)x * invMat[0]
+ (SplashCoord)y * invMat[2] + invMat[4]);
int yy = splashFloor((SplashCoord)x * invMat[1]
+ (SplashCoord)y * invMat[3] + invMat[5]);
if (xx >= 0 && xx < scaledWidth &&
yy >= 0 && yy < scaledHeight) {
Guchar *p = scaledMask + (yy * scaledWidth + xx);
Guchar *q = buf + (x - xMin);
*q = *p;
if (x < rowMin) {
rowMin = x;
}
rowMax = x + 1;
}
}
if (rowMin < rowMax) {
(this->*drawRowFunc)(dd, buf + (rowMin - xMin),
rowMin, y, rowMax - rowMin);
}
}
gfree(buf);
}
void Splash::drawImageMaskArbitraryInterp(
Guchar *scaledMask,
SplashDrawImageMaskRowData *dd,
SplashDrawImageMaskRowFunc drawRowFunc,
SplashCoord *invMat,
int scaledWidth, int scaledHeight,
int xMin, int yMin, int xMax, int yMax) {
int tt = state->clip->getXMinI(state->strokeAdjust);
if (tt > xMin) {
xMin = tt;
}
tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
if (tt < xMax) {
xMax = tt;
}
tt = state->clip->getYMinI(state->strokeAdjust);
if (tt > yMin) {
yMin = tt;
}
tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
if (tt < yMax) {
yMax = tt;
}
if (xMax <= xMin || yMax <= yMin) {
return;
}
Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
for (int y = yMin; y < yMax; ++y) {
int rowMin = xMax;
int rowMax = 0;
for (int x = xMin; x < xMax; ++x) {
// note: invMat includes a "+0.5" factor so that this is really
// a multiply by (x+0.5, y+0.5)
SplashCoord xs = (SplashCoord)x * invMat[0]
+ (SplashCoord)y * invMat[2] + invMat[4];
SplashCoord ys = (SplashCoord)x * invMat[1]
+ (SplashCoord)y * invMat[3] + invMat[5];
int x0 = splashFloor(xs - 0.5);
int x1 = x0 + 1;
int y0 = splashFloor(ys - 0.5);
int y1 = y0 + 1;
if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
SplashCoord sx1 = (SplashCoord)1 - sx0;
SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
SplashCoord sy1 = (SplashCoord)1 - sy0;
if (x0 < 0) {
x0 = 0;
}
if (x1 >= scaledWidth) {
x1 = scaledWidth - 1;
}
if (y0 < 0) {
y0 = 0;
}
if (y1 >= scaledHeight) {
y1 = scaledHeight - 1;
}
Guchar *p00 = scaledMask + (y0 * scaledWidth + x0);
Guchar *p10 = scaledMask + (y0 * scaledWidth + x1);
Guchar *p01 = scaledMask + (y1 * scaledWidth + x0);
Guchar *p11 = scaledMask + (y1 * scaledWidth + x1);
Guchar *q = buf + (x - xMin);
*q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
if (x < rowMin) {
rowMin = x;
}
rowMax = x + 1;
}
}
if (rowMin < rowMax) {
(this->*drawRowFunc)(dd, buf + (rowMin - xMin),
rowMin, y, rowMax - rowMin);
}
}
gfree(buf);
}
void Splash::mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width) {
Guchar *p, *q;
p = maskIn;
q = maskOut + (width - 1);
for (int i = 0; i < width; ++i) {
*q = *p;
++p;
--q;
}
}
void Splash::drawImageMaskRowNoClip(SplashDrawImageMaskRowData *data,
Guchar *maskData,
int x, int y, int width) {
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, maskData, NULL);
}
void Splash::drawImageMaskRowClipNoAA(SplashDrawImageMaskRowData *data,
Guchar *maskData,
int x, int y, int width) {
if (y < 0 || y >= bitmap->height) {
return;
}
if (x < 0) {
maskData -= x;
width += x;
x = 0;
}
if (x + width > bitmap->width) {
width = bitmap->width - x;
}
if (width <= 0) {
return;
}
memcpy(scanBuf + x, maskData, width);
state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
state->strokeAdjust);
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
scanBuf + x, NULL);
}
void Splash::drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
Guchar *maskData,
int x, int y, int width) {
if (y < 0 || y >= bitmap->height) {
return;
}
if (x < 0) {
maskData -= x;
width += x;
x = 0;
}
if (x + width > bitmap->width) {
width = bitmap->width - x;
}
if (width <= 0) {
return;
}
memcpy(scanBuf + x, maskData, width);
state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
scanBuf + x, NULL);
}
struct SplashDrawImageRowData {
int nComps;
GBool srcAlpha;
SplashPipe pipe;
};
SplashError Splash::drawImage(GString *imageTag,
SplashImageSource src, void *srcData,
SplashColorMode srcMode, GBool srcAlpha,
int w, int h, SplashCoord *mat,
GBool interpolate) {
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
GBool ok = gFalse;
int nComps = 0;
switch (bitmap->mode) {
case splashModeMono1:
case splashModeMono8:
ok = srcMode == splashModeMono8;
nComps = 1;
break;
case splashModeRGB8:
case splashModeBGR8:
ok = srcMode == splashModeRGB8;
nComps = 3;
break;
#if SPLASH_CMYK
case splashModeCMYK8:
ok = srcMode == splashModeCMYK8;
nComps = 4;
break;
#endif
default:
ok = gFalse;
break;
}
if (!ok) {
return splashErrModeMismatch;
}
//--- check for singular matrix
if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
return splashErrSingularMatrix;
}
//--- compute image bbox, check clipping
GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
GBool horizFlip = gFalse;
GBool vertFlip = gFalse;
int xMin, yMin, xMax, yMax;
if (flipsOnly) {
horizFlip = mat[0] < 0;
vertFlip = mat[3] < 0;
if (vertFlip) {
if (horizFlip) { // bottom-up, mirrored
getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
} else { // bottom-up
getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
}
} else {
if (horizFlip) { // top-down, mirrored
getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
} else { // top-down
getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
}
}
} else {
int xx = splashRound(mat[4]); // (0,0)
int yy = splashRound(mat[5]);
xMin = xMax = xx;
yMin = yMax = yy;
xx = splashRound(mat[0] + mat[4]); // (1,0)
yy = splashRound(mat[1] + mat[5]);
if (xx < xMin) {
xMin = xx;
} else if (xx > xMax) {
xMax = xx;
}
if (yy < yMin) {
yMin = yy;
} else if (yy > yMax) {
yMax = yy;
}
xx = splashRound(mat[2] + mat[4]); // (0,1)
yy = splashRound(mat[3] + mat[5]);
if (xx < xMin) {
xMin = xx;
} else if (xx > xMax) {
xMax = xx;
}
if (yy < yMin) {
yMin = yy;
} else if (yy > yMax) {
yMax = yy;
}
xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
yy = splashRound(mat[1] + mat[3] + mat[5]);
if (xx < xMin) {
xMin = xx;
} else if (xx > xMax) {
xMax = xx;
}
if (yy < yMin) {
yMin = yy;
} else if (yy > yMax) {
yMax = yy;
}
if (xMax <= xMin) {
xMax = xMin + 1;
}
if (yMax <= yMin) {
yMax = yMin + 1;
}
}
SplashClipResult clipRes =
state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
state->strokeAdjust);
// If the scaled image is much wider and/or taller than the clip
// region, we use the arbitrary transform path, to avoid a
// potentially very slow loop in the flips-only path (which scans
// the full width and height of the scaled image, regardless of the
// clip region).
int clipW = state->clip->getXMaxI(state->strokeAdjust)
- state->clip->getXMinI(state->strokeAdjust);
int clipH = state->clip->getYMaxI(state->strokeAdjust)
- state->clip->getYMinI(state->strokeAdjust);
GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
//--- set up the SplashDrawImageRowData object and the pipes
SplashDrawImageRowData dd;
dd.nComps = nComps;
dd.srcAlpha = srcAlpha;
pipeInit(&dd.pipe, NULL,
(Guchar)splashRound(state->fillAlpha * 255),
clipRes != splashClipAllInside || srcAlpha,
gFalse);
//--- choose the drawRow function
SplashDrawImageRowFunc drawRowFunc;
if (clipRes == splashClipAllInside) {
if (srcAlpha) {
drawRowFunc = &Splash::drawImageRowNoClipAlpha;
} else {
drawRowFunc = &Splash::drawImageRowNoClipNoAlpha;
}
} else {
if (srcAlpha) {
if (vectorAntialias) {
drawRowFunc = &Splash::drawImageRowClipAlphaAA;
} else {
drawRowFunc = &Splash::drawImageRowClipAlphaNoAA;
}
} else {
if (vectorAntialias) {
drawRowFunc = &Splash::drawImageRowClipNoAlphaAA;
} else {
drawRowFunc = &Splash::drawImageRowClipNoAlphaNoAA;
}
}
}
//--- horizontal/vertical flips only
if (flipsOnly && !veryLarge) {
if (clipRes != splashClipAllOutside) {
int scaledWidth = xMax - xMin;
int scaledHeight = yMax - yMin;
ImageScaler *scaler = getImageScaler(imageTag, src, srcData,
w, h, nComps,
scaledWidth, scaledHeight,
srcMode, srcAlpha, interpolate);
Guchar *tmpLine = NULL;
Guchar *tmpAlphaLine = NULL;
if (horizFlip) {
tmpLine = (Guchar *)gmallocn(scaledWidth, nComps);
if (srcAlpha) {
tmpAlphaLine = (Guchar *)gmalloc(scaledWidth);
}
}
if (vertFlip) {
if (horizFlip) { // bottom-up, mirrored
for (int y = 0; y < scaledHeight; ++y) {
scaler->nextLine();
mirrorImageRow(scaler->colorData(), scaler->alphaData(),
tmpLine, tmpAlphaLine,
scaledWidth, nComps, srcAlpha);
(this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
xMin, yMax - 1 - y, scaledWidth);
}
} else { // bottom-up
for (int y = 0; y < scaledHeight; ++y) {
scaler->nextLine();
(this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
xMin, yMax - 1 - y, scaledWidth);
}
}
} else {
if (horizFlip) { // top-down, mirrored
for (int y = 0; y < scaledHeight; ++y) {
scaler->nextLine();
mirrorImageRow(scaler->colorData(), scaler->alphaData(),
tmpLine, tmpAlphaLine,
scaledWidth, nComps, srcAlpha);
(this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
xMin, yMin + y, scaledWidth);
}
} else { // top-down
for (int y = 0; y < scaledHeight; ++y) {
scaler->nextLine();
(this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
xMin, yMin + y, scaledWidth);
}
}
}
gfree(tmpLine);
gfree(tmpAlphaLine);
delete scaler;
}
//--- arbitrary transform
} else {
// estimate of size of scaled image
int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ mat[1] * mat[1]));
int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ mat[3] * mat[3]));
if (scaledWidth < 1) {
scaledWidth = 1;
}
if (scaledHeight < 1) {
scaledHeight = 1;
}
if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
scaledWidth = w;
scaledHeight = h;
}
// compute mapping from device space to scaled image space
SplashCoord mat1[6];
mat1[0] = mat[0] / scaledWidth;
mat1[1] = mat[1] / scaledWidth;
mat1[2] = mat[2] / scaledHeight;
mat1[3] = mat[3] / scaledHeight;
mat1[4] = mat[4];
mat1[5] = mat[5];
SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
if (splashAbs(det) < 1e-6) {
// this should be caught by the singular matrix check in drawImage
return splashErrSingularMatrix;
}
SplashCoord invMat[6];
invMat[0] = mat1[3] / det;
invMat[1] = -mat1[1] / det;
invMat[2] = -mat1[2] / det;
invMat[3] = mat1[0] / det;
// the extra "+ 0.5 * (...)" terms are here because the
// drawImageArbitrary(No)Interp functions multiply by pixel
// centers, (x + 0.5, y + 0.5)
invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ (invMat[0] + invMat[2]) * 0.5;
invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ (invMat[1] + invMat[3]) * 0.5;
Guchar *scaledColor, *scaledAlpha;
GBool freeScaledImage;
getScaledImage(imageTag, src, srcData, w, h, nComps,
scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
&scaledColor, &scaledAlpha, &freeScaledImage);
// draw it
if (interpolate) {
drawImageArbitraryInterp(scaledColor, scaledAlpha,
&dd, drawRowFunc, invMat,
scaledWidth, scaledHeight,
xMin, yMin, xMax, yMax,
nComps, srcAlpha);
} else {
drawImageArbitraryNoInterp(scaledColor, scaledAlpha,
&dd, drawRowFunc, invMat,
scaledWidth, scaledHeight,
xMin, yMin, xMax, yMax,
nComps, srcAlpha);
}
// free the downscaled/unscaled image
if (freeScaledImage) {
gfree(scaledColor);
gfree(scaledAlpha);
}
}
return splashOk;
}
ImageScaler *Splash::getImageScaler(GString *imageTag,
SplashImageSource src, void *srcData,
int w, int h, int nComps,
int scaledWidth, int scaledHeight,
SplashColorMode srcMode,
GBool srcAlpha, GBool interpolate) {
// Notes:
//
// * If the scaled image width or height is greater than 2000, we
// don't cache it.
//
// * Caching is done on the third consecutive use (second
// consecutive reuse) of an image; this avoids overhead on the
// common case of single-use images.
if (scaledWidth < 2000 && scaledHeight < 2000 &&
imageCache->match(imageTag, scaledWidth, scaledHeight,
srcMode, srcAlpha, interpolate)) {
if (imageCache->colorData) {
return new ReplayImageScaler(nComps, srcAlpha, scaledWidth,
imageCache->colorData,
imageCache->alphaData);
} else {
int lineSize;
if (scaledWidth < INT_MAX / nComps) {
lineSize = scaledWidth * nComps;
} else {
lineSize = -1;
}
imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
if (srcAlpha) {
imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
}
return new SavingImageScaler(src, srcData,
w, h, nComps, srcAlpha,
scaledWidth, scaledHeight,
interpolate,
imageCache->colorData,
imageCache->alphaData);
}
} else {
imageCache->reset(imageTag, scaledWidth, scaledHeight,
srcMode, srcAlpha, interpolate);
return new BasicImageScaler(src, srcData,
w, h, nComps, srcAlpha,
scaledWidth, scaledHeight,
interpolate);
}
}
void Splash::getScaledImage(GString *imageTag,
SplashImageSource src, void *srcData,
int w, int h, int nComps,
int scaledWidth, int scaledHeight,
SplashColorMode srcMode,
GBool srcAlpha, GBool interpolate,
Guchar **scaledColor, Guchar **scaledAlpha,
GBool *freeScaledImage) {
// Notes:
//
// * If the scaled image width or height is greater than 2000, we
// don't cache it.
//
// * This buffers the whole image anyway, so there's no reason to
// skip caching on the first reuse.
if (scaledWidth >= 2000 || scaledHeight >= 2000) {
int lineSize;
if (scaledWidth < INT_MAX / nComps) {
lineSize = scaledWidth * nComps;
} else {
lineSize = -1;
}
*scaledColor = (Guchar *)gmallocn(scaledHeight, lineSize);
if (srcAlpha) {
*scaledAlpha = (Guchar *)gmallocn(scaledHeight, scaledWidth);
} else {
*scaledAlpha = NULL;
}
*freeScaledImage = gTrue;
if (scaledWidth == w && scaledHeight == h) {
Guchar *colorPtr = *scaledColor;
Guchar *alphaPtr = *scaledAlpha;
for (int y = 0; y < scaledHeight; ++y) {
(*src)(srcData, colorPtr, alphaPtr);
colorPtr += scaledWidth * nComps;
if (srcAlpha) {
alphaPtr += scaledWidth;
}
}
} else {
BasicImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
scaledWidth, scaledHeight, interpolate);
Guchar *colorPtr = *scaledColor;
Guchar *alphaPtr = *scaledAlpha;
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
colorPtr += scaledWidth * nComps;
if (srcAlpha) {
memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
alphaPtr += scaledWidth;
}
}
}
} else {
if (!imageCache->match(imageTag, scaledWidth, scaledHeight,
srcMode, srcAlpha, interpolate) ||
!imageCache->colorData) {
imageCache->reset(imageTag, scaledWidth, scaledHeight,
srcMode, srcAlpha, interpolate);
int lineSize;
if (scaledWidth < INT_MAX / nComps) {
lineSize = scaledWidth * nComps;
} else {
lineSize = -1;
}
imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
if (srcAlpha) {
imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
}
if (scaledWidth == w && scaledHeight == h) {
Guchar *colorPtr = imageCache->colorData;
Guchar *alphaPtr = imageCache->alphaData;
for (int y = 0; y < scaledHeight; ++y) {
(*src)(srcData, colorPtr, alphaPtr);
colorPtr += scaledWidth * nComps;
if (srcAlpha) {
alphaPtr += scaledWidth;
}
}
} else {
SavingImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
scaledWidth, scaledHeight, interpolate,
imageCache->colorData, imageCache->alphaData);
Guchar *colorPtr = imageCache->colorData;
Guchar *alphaPtr = imageCache->alphaData;
for (int y = 0; y < scaledHeight; ++y) {
scaler.nextLine();
memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
colorPtr += scaledWidth * nComps;
if (srcAlpha) {
memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
alphaPtr += scaledWidth;
}
}
}
}
*scaledColor = imageCache->colorData;
*scaledAlpha = imageCache->alphaData;
*freeScaledImage = gFalse;
}
}
void Splash::drawImageArbitraryNoInterp(Guchar *scaledColor,
Guchar *scaledAlpha,
SplashDrawImageRowData *dd,
SplashDrawImageRowFunc drawRowFunc,
SplashCoord *invMat,
int scaledWidth, int scaledHeight,
int xMin, int yMin, int xMax, int yMax,
int nComps, GBool srcAlpha) {
int tt = state->clip->getXMinI(state->strokeAdjust);
if (tt > xMin) {
xMin = tt;
}
tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
if (tt < xMax) {
xMax = tt;
}
tt = state->clip->getYMinI(state->strokeAdjust);
if (tt > yMin) {
yMin = tt;
}
tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
if (tt < yMax) {
yMax = tt;
}
if (xMax <= xMin || yMax <= yMin) {
return;
}
Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
Guchar *alphaBuf = NULL;
if (srcAlpha) {
alphaBuf = (Guchar *)gmalloc(xMax - xMin);
}
for (int y = yMin; y < yMax; ++y) {
int rowMin = xMax;
int rowMax = 0;
for (int x = xMin; x < xMax; ++x) {
// note: invMat includes a "+0.5" factor so that this is really
// a multiply by (x+0.5, y+0.5)
int xx = splashFloor((SplashCoord)x * invMat[0]
+ (SplashCoord)y * invMat[2] + invMat[4]);
int yy = splashFloor((SplashCoord)x * invMat[1]
+ (SplashCoord)y * invMat[3] + invMat[5]);
if (xx >= 0 && xx < scaledWidth &&
yy >= 0 && yy < scaledHeight) {
Guchar *p = scaledColor + (yy * scaledWidth + xx) * nComps;
Guchar *q = colorBuf + (x - xMin) * nComps;
for (int i = 0; i < nComps; ++i) {
*q++ = *p++;
}
if (srcAlpha) {
alphaBuf[x - xMin] = scaledAlpha[yy * scaledWidth + xx];
}
if (x < rowMin) {
rowMin = x;
}
rowMax = x + 1;
}
}
if (rowMin < rowMax) {
(this->*drawRowFunc)(dd,
colorBuf + (rowMin - xMin) * nComps,
alphaBuf + (rowMin - xMin),
rowMin, y, rowMax - rowMin);
}
}
gfree(colorBuf);
gfree(alphaBuf);
}
void Splash::drawImageArbitraryInterp(Guchar *scaledColor, Guchar *scaledAlpha,
SplashDrawImageRowData *dd,
SplashDrawImageRowFunc drawRowFunc,
SplashCoord *invMat,
int scaledWidth, int scaledHeight,
int xMin, int yMin, int xMax, int yMax,
int nComps, GBool srcAlpha) {
int tt = state->clip->getXMinI(state->strokeAdjust);
if (tt > xMin) {
xMin = tt;
}
tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
if (tt < xMax) {
xMax = tt;
}
tt = state->clip->getYMinI(state->strokeAdjust);
if (tt > yMin) {
yMin = tt;
}
tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
if (tt < yMax) {
yMax = tt;
}
if (xMax <= xMin || yMax <= yMin) {
return;
}
Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
Guchar *alphaBuf = NULL;
if (srcAlpha) {
alphaBuf = (Guchar *)gmalloc(xMax - xMin);
}
for (int y = yMin; y < yMax; ++y) {
int rowMin = xMax;
int rowMax = 0;
for (int x = xMin; x < xMax; ++x) {
// note: invMat includes a "+0.5" factor so that this is really
// a multiply by (x+0.5, y+0.5)
SplashCoord xs = (SplashCoord)x * invMat[0]
+ (SplashCoord)y * invMat[2] + invMat[4];
SplashCoord ys = (SplashCoord)x * invMat[1]
+ (SplashCoord)y * invMat[3] + invMat[5];
int x0 = splashFloor(xs - 0.5);
int x1 = x0 + 1;
int y0 = splashFloor(ys - 0.5);
int y1 = y0 + 1;
if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
SplashCoord sx1 = (SplashCoord)1 - sx0;
SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
SplashCoord sy1 = (SplashCoord)1 - sy0;
if (x0 < 0) {
x0 = 0;
}
if (x1 >= scaledWidth) {
x1 = scaledWidth - 1;
}
if (y0 < 0) {
y0 = 0;
}
if (y1 >= scaledHeight) {
y1 = scaledHeight - 1;
}
Guchar *p00 = scaledColor + (y0 * scaledWidth + x0) * nComps;
Guchar *p10 = scaledColor + (y0 * scaledWidth + x1) * nComps;
Guchar *p01 = scaledColor + (y1 * scaledWidth + x0) * nComps;
Guchar *p11 = scaledColor + (y1 * scaledWidth + x1) * nComps;
Guchar *q = colorBuf + (x - xMin) * nComps;
for (int i = 0; i < nComps; ++i) {
*q++ = (Guchar)(int)(sx0 * (sy0 * (int)*p00++ + sy1 * (int)*p01++) +
sx1 * (sy0 * (int)*p10++ + sy1 * (int)*p11++));
}
if (srcAlpha) {
p00 = scaledAlpha + (y0 * scaledWidth + x0);
p10 = scaledAlpha + (y0 * scaledWidth + x1);
p01 = scaledAlpha + (y1 * scaledWidth + x0);
p11 = scaledAlpha + (y1 * scaledWidth + x1);
q = alphaBuf + (x - xMin);
*q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
}
if (x < rowMin) {
rowMin = x;
}
rowMax = x + 1;
}
}
if (rowMin < rowMax) {
(this->*drawRowFunc)(dd,
colorBuf + (rowMin - xMin) * nComps,
alphaBuf + (rowMin - xMin),
rowMin, y, rowMax - rowMin);
}
}
gfree(colorBuf);
gfree(alphaBuf);
}
void Splash::mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
Guchar *colorOut, Guchar *alphaOut,
int width, int nComps, GBool srcAlpha) {
Guchar *p, *q;
p = colorIn;
q = colorOut + (width - 1) * nComps;
for (int i = 0; i < width; ++i) {
for (int j = 0; j < nComps; ++j) {
q[j] = p[j];
}
p += nComps;
q -= nComps;
}
if (srcAlpha) {
p = alphaIn;
q = alphaOut + (width - 1);
for (int i = 0; i < width; ++i) {
*q = *p;
++p;
--q;
}
}
}
void Splash::drawImageRowNoClipNoAlpha(SplashDrawImageRowData *data,
Guchar *colorData, Guchar *alphaData,
int x, int y, int width) {
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, NULL, colorData);
}
void Splash::drawImageRowNoClipAlpha(SplashDrawImageRowData *data,
Guchar *colorData, Guchar *alphaData,
int x, int y, int width) {
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
alphaData, colorData);
}
void Splash::drawImageRowClipNoAlphaNoAA(SplashDrawImageRowData *data,
Guchar *colorData,
Guchar *alphaData,
int x, int y, int width) {
if (y < 0 || y >= bitmap->height) {
return;
}
if (x < 0) {
colorData -= x * data->nComps;
width += x;
x = 0;
}
if (x + width > bitmap->width) {
width = bitmap->width - x;
}
if (width <= 0) {
return;
}
memset(scanBuf + x, 0xff, width);
state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
state->strokeAdjust);
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
scanBuf + x, colorData);
}
void Splash::drawImageRowClipNoAlphaAA(SplashDrawImageRowData *data,
Guchar *colorData,
Guchar *alphaData,
int x, int y, int width) {
if (y < 0 || y >= bitmap->height) {
return;
}
if (x < 0) {
colorData -= x * data->nComps;
width += x;
x = 0;
}
if (x + width > bitmap->width) {
width = bitmap->width - x;
}
if (width <= 0) {
return;
}
memset(scanBuf + x, 0xff, width);
state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
scanBuf + x, colorData);
}
void Splash::drawImageRowClipAlphaNoAA(SplashDrawImageRowData *data,
Guchar *colorData,
Guchar *alphaData,
int x, int y, int width) {
if (y < 0 || y >= bitmap->height) {
return;
}
if (x < 0) {
colorData -= x * data->nComps;
alphaData -= x;
width += x;
x = 0;
}
if (x + width > bitmap->width) {
width = bitmap->width - x;
}
if (width <= 0) {
return;
}
memcpy(scanBuf + x, alphaData, width);
state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
state->strokeAdjust);
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
scanBuf + x, colorData);
}
void Splash::drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
Guchar *colorData,
Guchar *alphaData,
int x, int y, int width) {
if (y < 0 || y >= bitmap->height) {
return;
}
if (x < 0) {
colorData -= x * data->nComps;
alphaData -= x;
width += x;
x = 0;
}
if (x + width > bitmap->width) {
width = bitmap->width - x;
}
if (width <= 0) {
return;
}
memcpy(scanBuf + x, alphaData, width);
state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
(this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
scanBuf + x, colorData);
}
SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
int xDest, int yDest, int w, int h,
GBool noClip, GBool nonIsolated) {
SplashPipe pipe;
Guchar *mono1Ptr, *lineBuf, *linePtr;
Guchar mono1Mask, b;
int x0, x1, x, y0, y1, y, t;
if (!(src->mode == bitmap->mode ||
(src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
(src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
return splashErrModeMismatch;
}
pipeInit(&pipe, NULL,
(Guchar)splashRound(state->fillAlpha * 255),
!noClip || src->alpha != NULL, nonIsolated);
if (src->mode == splashModeMono1) {
// in mono1 mode, pipeRun expects the source to be in mono8
// format, so we need to extract the source color values into
// scanBuf, expanding them from mono1 to mono8
if (noClip) {
if (src->alpha) {
for (y = 0; y < h; ++y) {
mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
for (x = 0; x < w; ++x) {
scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
mono1Ptr += mono1Mask & 1;
mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
}
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
src->alpha +
(ySrc + y) * src->alphaRowSize + xSrc,
scanBuf);
}
} else {
for (y = 0; y < h; ++y) {
mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
for (x = 0; x < w; ++x) {
scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
mono1Ptr += mono1Mask & 1;
mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
}
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
NULL,
scanBuf);
}
}
} else {
x0 = xDest;
if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
x0 = t;
}
x1 = xDest + w;
if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
x1 = t;
}
y0 = yDest;
if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
y0 = t;
}
y1 = yDest + h;
if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
y1 = t;
}
if (x0 < x1 && y0 < y1) {
if (src->alpha) {
for (y = y0; y < y1; ++y) {
mono1Ptr = src->data
+ (ySrc + y - yDest) * src->rowSize
+ ((xSrc + x0 - xDest) >> 3);
mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
for (x = x0; x < x1; ++x) {
scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
mono1Ptr += mono1Mask & 1;
mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
}
memcpy(scanBuf2 + x0,
src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
(xSrc + x0 - xDest),
x1 - x0);
if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
state->strokeAdjust)) {
continue;
}
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, x0, x1 - 1, y,
scanBuf2 + x0,
scanBuf + x0);
}
} else {
for (y = y0; y < y1; ++y) {
mono1Ptr = src->data
+ (ySrc + y - yDest) * src->rowSize
+ ((xSrc + x0 - xDest) >> 3);
mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
for (x = x0; x < x1; ++x) {
scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
mono1Ptr += mono1Mask & 1;
mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
}
memset(scanBuf2 + x0, 0xff, x1 - x0);
if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
state->strokeAdjust)) {
continue;
}
(this->*pipe.run)(&pipe, x0, x1 - 1, y,
scanBuf2 + x0,
scanBuf + x0);
}
}
}
}
} else if (src->mode == splashModeBGR8) {
// in BGR8 mode, pipeRun expects the source to be in RGB8 format,
// so we need to swap bytes
lineBuf = (Guchar *)gmallocn(w, 3);
if (noClip) {
if (src->alpha) {
for (y = 0; y < h; ++y) {
memcpy(lineBuf,
src->data + (ySrc + y) * src->rowSize + xSrc * 3,
w * 3);
for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
b = linePtr[0];
linePtr[0] = linePtr[2];
linePtr[2] = b;
}
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
src->alpha +
(ySrc + y) * src->alphaRowSize + xSrc,
lineBuf);
}
} else {
for (y = 0; y < h; ++y) {
memcpy(lineBuf,
src->data + (ySrc + y) * src->rowSize + xSrc * 3,
w * 3);
for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
b = linePtr[0];
linePtr[0] = linePtr[2];
linePtr[2] = b;
}
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
NULL, lineBuf);
}
}
} else {
x0 = xDest;
if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
x0 = t;
}
x1 = xDest + w;
if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
x1 = t;
}
y0 = yDest;
if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
y0 = t;
}
y1 = yDest + h;
if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
y1 = t;
}
if (x0 < x1 && y0 < y1) {
if (src->alpha) {
for (y = y0; y < y1; ++y) {
memcpy(scanBuf + x0,
src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
(xSrc + x0 - xDest),
x1 - x0);
state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
memcpy(lineBuf,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * 3,
(x1 - x0) * 3);
for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
b = linePtr[0];
linePtr[0] = linePtr[2];
linePtr[2] = b;
}
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, x0, x1 - 1, y,
scanBuf + x0, lineBuf);
}
} else {
for (y = y0; y < y1; ++y) {
memset(scanBuf + x0, 0xff, x1 - x0);
state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
memcpy(lineBuf,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * 3,
(x1 - x0) * 3);
for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
b = linePtr[0];
linePtr[0] = linePtr[2];
linePtr[2] = b;
}
(this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
scanBuf + x0,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * bitmapComps);
}
}
}
}
gfree(lineBuf);
} else { // src->mode not mono1 or BGR8
if (noClip) {
if (src->alpha) {
for (y = 0; y < h; ++y) {
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
src->alpha +
(ySrc + y) * src->alphaRowSize + xSrc,
src->data + (ySrc + y) * src->rowSize +
xSrc * bitmapComps);
}
} else {
for (y = 0; y < h; ++y) {
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
NULL,
src->data + (ySrc + y) * src->rowSize +
xSrc * bitmapComps);
}
}
} else {
x0 = xDest;
if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
x0 = t;
}
x1 = xDest + w;
if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
x1 = t;
}
y0 = yDest;
if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
y0 = t;
}
y1 = yDest + h;
if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
y1 = t;
}
if (x0 < x1 && y0 < y1) {
if (src->alpha) {
for (y = y0; y < y1; ++y) {
memcpy(scanBuf + x0,
src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
(xSrc + x0 - xDest),
x1 - x0);
state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, x0, x1 - 1, y,
scanBuf + x0,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * bitmapComps);
}
} else {
for (y = y0; y < y1; ++y) {
memset(scanBuf + x0, 0xff, x1 - x0);
state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
(this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
scanBuf + x0,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * bitmapComps);
}
}
}
}
}
return splashOk;
}
SplashError Splash::compositeWithOverprint(SplashBitmap *src,
Guint *srcOverprintMaskBitmap,
int xSrc, int ySrc,
int xDest, int yDest, int w, int h,
GBool noClip, GBool nonIsolated) {
SplashPipe pipe;
int x0, x1, y0, y1, y, t;
if (!(src->mode == bitmap->mode ||
(src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
(src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
return splashErrModeMismatch;
}
pipeInit(&pipe, NULL,
(Guchar)splashRound(state->fillAlpha * 255),
!noClip || src->alpha != NULL, nonIsolated, gTrue);
if (noClip) {
if (src->alpha) {
for (y = 0; y < h; ++y) {
pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
src->alpha +
(ySrc + y) * src->alphaRowSize + xSrc,
src->data + (ySrc + y) * src->rowSize +
xSrc * bitmapComps);
}
} else {
for (y = 0; y < h; ++y) {
pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
(this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
NULL,
src->data + (ySrc + y) * src->rowSize +
xSrc * bitmapComps);
}
}
} else {
x0 = xDest;
if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
x0 = t;
}
x1 = xDest + w;
if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
x1 = t;
}
y0 = yDest;
if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
y0 = t;
}
y1 = yDest + h;
if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
y1 = t;
}
if (x0 < x1 && y0 < y1) {
if (src->alpha) {
for (y = y0; y < y1; ++y) {
memcpy(scanBuf + x0,
src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
(xSrc + x0 - xDest),
x1 - x0);
state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
+ (ySrc + y - yDest) * w
+ (xSrc + x0 - xDest);
// this uses shape instead of alpha, which isn't technically
// correct, but works out the same
(this->*pipe.run)(&pipe, x0, x1 - 1, y,
scanBuf + x0,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * bitmapComps);
}
} else {
for (y = y0; y < y1; ++y) {
memset(scanBuf + x0, 0xff, x1 - x0);
state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
+ (ySrc + y - yDest) * w
+ (xSrc + x0 - xDest);
(this->*pipe.run)(&pipe, x0, x1 - 1, y,
scanBuf + x0,
src->data +
(ySrc + y - yDest) * src->rowSize +
(xSrc + x0 - xDest) * bitmapComps);
}
}
}
}
return splashOk;
}
void Splash::compositeBackground(SplashColorPtr color) {
SplashColorPtr p;
Guchar *q;
Guchar alpha, alpha1, c, color0, color1, color2, mask;
#if SPLASH_CMYK
Guchar color3;
#endif
int x, y;
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->alphaRowSize];
mask = 0x80;
for (x = 0; x < bitmap->width; ++x) {
alpha = *q++;
if (alpha == 0) {
if (color0 & 0x80) {
*p |= mask;
} else {
*p &= (Guchar)~mask;
}
} else if (alpha != 255) {
alpha1 = (Guchar)(255 - alpha);
c = (*p & mask) ? 0xff : 0x00;
c = div255(alpha1 * color0 + alpha * c);
if (c & 0x80) {
*p |= mask;
} else {
*p &= (Guchar)~mask;
}
}
if (!(mask = (Guchar)(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->alphaRowSize];
for (x = 0; x < bitmap->width; ++x) {
alpha = *q++;
if (alpha == 0) {
p[0] = color0;
} else if (alpha != 255) {
alpha1 = (Guchar)(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->alphaRowSize];
for (x = 0; x < bitmap->width; ++x) {
alpha = *q++;
if (alpha == 0) {
p[0] = color0;
p[1] = color1;
p[2] = color2;
} else if (alpha != 255) {
alpha1 = (Guchar)(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->alphaRowSize];
for (x = 0; x < bitmap->width; ++x) {
alpha = *q++;
if (alpha == 0) {
p[0] = color0;
p[1] = color1;
p[2] = color2;
p[3] = color3;
} else if (alpha != 255) {
alpha1 = (Guchar)(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->alphaRowSize * bitmap->height);
}
SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
int xDest, int yDest, int w, int h) {
SplashColorPtr p, q;
Guchar mask, srcMask;
int x, y;
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 = (Guchar)(0x80 >> (xDest & 7));
q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
srcMask = (Guchar)(0x80 >> (xSrc & 7));
for (x = 0; x < w; ++x) {
if (*q & srcMask) {
*p |= mask;
} else {
*p &= (Guchar)~mask;
}
if (!(mask = (Guchar)(mask >> 1))) {
mask = 0x80;
++p;
}
if (!(srcMask = (Guchar)(srcMask >> 1))) {
srcMask = 0x80;
++q;
}
}
}
break;
case splashModeMono8:
for (y = 0; y < h; ++y) {
p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
q = &src->data[(ySrc + y) * src->rowSize + xSrc];
memcpy(p, q, w);
}
break;
case splashModeRGB8:
case splashModeBGR8:
for (y = 0; y < h; ++y) {
p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
memcpy(p, q, 3 * w);
}
break;
#if SPLASH_CMYK
case splashModeCMYK8:
for (y = 0; y < h; ++y) {
p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
memcpy(p, q, 4 * w);
}
break;
#endif
}
if (bitmap->alpha) {
for (y = 0; y < h; ++y) {
q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest];
memset(q, 0, w);
}
}
return splashOk;
}
SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc,
int xDest, int yDest, int w, int h) {
SplashColorPtr p, q;
Guchar *alpha0Ptr;
Guchar alpha0, aSrc, mask, srcMask;
int x, y;
if (bitmap->mode != dest->mode ||
!bitmap->alpha ||
!dest->alpha ||
!groupBackBitmap) {
return splashErrModeMismatch;
}
switch (bitmap->mode) {
case splashModeMono1:
for (y = 0; y < h; ++y) {
p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)];
mask = (Guchar)(0x80 >> (xDest & 7));
q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)];
srcMask = (Guchar)(0x80 >> (xSrc & 7));
for (x = 0; x < w; ++x) {
if (*q & srcMask) {
*p |= mask;
} else {
*p &= (Guchar)~mask;
}
if (!(mask = (Guchar)(mask >> 1))) {
mask = 0x80;
++p;
}
if (!(srcMask = (Guchar)(srcMask >> 1))) {
srcMask = 0x80;
++q;
}
}
}
break;
case splashModeMono8:
for (y = 0; y < h; ++y) {
p = &dest->data[(yDest + y) * dest->rowSize + xDest];
q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc];
memcpy(p, q, w);
}
break;
case splashModeRGB8:
case splashModeBGR8:
for (y = 0; y < h; ++y) {
p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest];
q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc];
memcpy(p, q, 3 * w);
}
break;
#if SPLASH_CMYK
case splashModeCMYK8:
for (y = 0; y < h; ++y) {
p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest];
q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc];
memcpy(p, q, 4 * w);
}
break;
#endif
}
for (y = 0; y < h; ++y) {
p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest];
q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y)
* groupBackBitmap->alphaRowSize +
(groupBackX + xSrc)];
for (x = 0; x < w; ++x) {
alpha0 = *alpha0Ptr++;
aSrc = *q++;
*p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc));
}
}
return splashOk;
}
SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
int lineCap, int lineJoin,
GBool flatten) {
SplashPath *pathIn, *dashPath, *pathOut;
SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
SplashCoord crossprod, dotprod, miter, m;
SplashCoord angle, angleNext, dAngle, xc, yc;
SplashCoord dxJoin, dyJoin, dJoin, kappa;
SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
GBool first, last, closed;
int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
int left0, left1, left2, right0, right1, right2, join0, join1, join2;
int leftFirst, rightFirst, firstPt;
pathOut = new SplashPath();
if (path->length == 0) {
return pathOut;
}
if (flatten) {
pathIn = flattenPath(path, state->matrix, state->flatness);
if (state->lineDashLength > 0) {
dashPath = makeDashedPath(pathIn);
delete pathIn;
pathIn = dashPath;
if (pathIn->length == 0) {
delete pathIn;
return pathOut;
}
}
} else {
pathIn = path;
}
subpathStart0 = subpathStart1 = 0; // make gcc happy
seg = 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
i0 = 0;
for (i1 = i0;
!(pathIn->flags[i1] & splashPathLast) &&
i1 + 1 < pathIn->length &&
pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
pathIn->pts[i1+1].y == pathIn->pts[i1].y;
++i1) ;
while (i1 < pathIn->length) {
if ((first = pathIn->flags[i0] & splashPathFirst)) {
subpathStart0 = i0;
subpathStart1 = i1;
seg = 0;
closed = pathIn->flags[i0] & splashPathClosed;
}
j0 = i1 + 1;
if (j0 < pathIn->length) {
for (j1 = j0;
!(pathIn->flags[j1] & splashPathLast) &&
j1 + 1 < pathIn->length &&
pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
pathIn->pts[j1+1].y == pathIn->pts[j1].y;
++j1) ;
} else {
j1 = j0;
}
if (pathIn->flags[i1] & splashPathLast) {
if (first && lineCap == splashLineCapRound) {
// special case: zero-length subpath with round line caps -->
// draw a circle
pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
pathIn->pts[i0].y);
pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
pathIn->pts[i0].y + bezierCircle2 * w,
pathIn->pts[i0].x + bezierCircle2 * w,
pathIn->pts[i0].y + (SplashCoord)0.5 * w,
pathIn->pts[i0].x,
pathIn->pts[i0].y + (SplashCoord)0.5 * w);
pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w,
pathIn->pts[i0].y + (SplashCoord)0.5 * w,
pathIn->pts[i0].x - (SplashCoord)0.5 * w,
pathIn->pts[i0].y + bezierCircle2 * w,
pathIn->pts[i0].x - (SplashCoord)0.5 * w,
pathIn->pts[i0].y);
pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
pathIn->pts[i0].y - bezierCircle2 * w,
pathIn->pts[i0].x - bezierCircle2 * w,
pathIn->pts[i0].y - (SplashCoord)0.5 * w,
pathIn->pts[i0].x,
pathIn->pts[i0].y - (SplashCoord)0.5 * w);
pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
pathIn->pts[i0].y - (SplashCoord)0.5 * w,
pathIn->pts[i0].x + (SplashCoord)0.5 * w,
pathIn->pts[i0].y - bezierCircle2 * w,
pathIn->pts[i0].x + (SplashCoord)0.5 * w,
pathIn->pts[i0].y);
pathOut->close();
}
i0 = j0;
i1 = j1;
continue;
}
last = pathIn->flags[j1] & splashPathLast;
if (last) {
k0 = subpathStart1 + 1;
} else {
k0 = j1 + 1;
}
for (k1 = k0;
!(pathIn->flags[k1] & splashPathLast) &&
k1 + 1 < pathIn->length &&
pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
pathIn->pts[k1+1].y == pathIn->pts[k1].y;
++k1) ;
// compute the deltas for segment (i1, j0)
#if USE_FIXEDPOINT
// the 1/d value can be small, which introduces significant
// inaccuracies in fixed point mode
d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
pathIn->pts[j0].x, pathIn->pts[j0].y);
dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
#else
d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
pathIn->pts[j0].x, pathIn->pts[j0].y);
dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
#endif
wdx = (SplashCoord)0.5 * w * dx;
wdy = (SplashCoord)0.5 * w * dy;
// draw the start cap
if (i0 == subpathStart0) {
firstPt = pathOut->length;
}
if (first && !closed) {
switch (lineCap) {
case splashLineCapButt:
pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
break;
case splashLineCapRound:
pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
pathIn->pts[i0].y + wdx - bezierCircle * wdy,
pathIn->pts[i0].x - wdx - bezierCircle * wdy,
pathIn->pts[i0].y - wdy + bezierCircle * wdx,
pathIn->pts[i0].x - wdx,
pathIn->pts[i0].y - wdy);
pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
pathIn->pts[i0].y - wdy - bezierCircle * wdx,
pathIn->pts[i0].x + wdy - bezierCircle * wdx,
pathIn->pts[i0].y - wdx - bezierCircle * wdy,
pathIn->pts[i0].x + wdy,
pathIn->pts[i0].y - wdx);
break;
case splashLineCapProjecting:
pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy,
pathIn->pts[i0].y + wdx - wdy);
pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
pathIn->pts[i0].y - wdx - wdy);
break;
}
} else {
pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
}
// draw the left side of the segment rectangle and the end cap
left2 = pathOut->length - 1;
if (last && !closed) {
switch (lineCap) {
case splashLineCapButt:
pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
break;
case splashLineCapRound:
pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
pathIn->pts[j0].y - wdx + bezierCircle * wdy,
pathIn->pts[j0].x + wdx + bezierCircle * wdy,
pathIn->pts[j0].y + wdy - bezierCircle * wdx,
pathIn->pts[j0].x + wdx,
pathIn->pts[j0].y + wdy);
pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
pathIn->pts[j0].y + wdy + bezierCircle * wdx,
pathIn->pts[j0].x - wdy + bezierCircle * wdx,
pathIn->pts[j0].y + wdx + bezierCircle * wdy,
pathIn->pts[j0].x - wdy,
pathIn->pts[j0].y + wdx);
break;
case splashLineCapProjecting:
pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
pathIn->pts[j0].y - wdx + wdy);
pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
pathIn->pts[j0].y + wdx + wdy);
break;
}
} else {
pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
}
// draw the right side of the segment rectangle
// (NB: if stroke adjustment is enabled, the closepath operation MUST
// add a segment because this segment is used for a hint)
right2 = pathOut->length - 1;
pathOut->close(state->strokeAdjust != splashStrokeAdjustOff);
// draw the join
join2 = pathOut->length;
if (!last || closed) {
// compute the deltas for segment (j1, k0)
#if USE_FIXEDPOINT
// the 1/d value can be small, which introduces significant
// inaccuracies in fixed point mode
d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
pathIn->pts[k0].x, pathIn->pts[k0].y);
dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
#else
d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
pathIn->pts[k0].x, pathIn->pts[k0].y);
dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
#endif
wdxNext = (SplashCoord)0.5 * w * dxNext;
wdyNext = (SplashCoord)0.5 * w * dyNext;
// compute the join parameters
crossprod = dx * dyNext - dy * dxNext;
dotprod = -(dx * dxNext + dy * dyNext);
if (dotprod > 0.9999) {
// 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)
// (note: the comparison value (0.9999) has to be less than
// 1-epsilon, where epsilon is the smallest value
// representable in the fixed point format)
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 (lineJoin == splashLineJoinRound) {
// join angle < 180
if (crossprod < 0) {
angle = atan2((double)dx, (double)-dy);
angleNext = atan2((double)dxNext, (double)-dyNext);
if (angle < angleNext) {
angle += 2 * M_PI;
}
dAngle = (angle - angleNext) / M_PI;
if (dAngle < 0.501) {
// span angle is <= 90 degrees -> draw a single arc
kappa = dAngle * bezierCircle * w;
cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
pathIn->pts[j0].y + wdxNext);
pathOut->curveTo(cx2, cy2, cx1, cy1,
pathIn->pts[j0].x - wdy,
pathIn->pts[j0].y + wdx);
} else {
// span angle is > 90 degrees -> split into two arcs
dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext);
if (dJoin > 0) {
dxJoin = (-wdyNext + wdy) / dJoin;
dyJoin = (wdxNext - wdx) / dJoin;
xc = pathIn->pts[j0].x
+ (SplashCoord)0.5 * w
* cos((double)((SplashCoord)0.5 * (angle + angleNext)));
yc = pathIn->pts[j0].y
+ (SplashCoord)0.5 * w
* sin((double)((SplashCoord)0.5 * (angle + angleNext)));
kappa = dAngle * bezierCircle2 * w;
cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
cx2 = xc - kappa * dxJoin;
cy2 = yc - kappa * dyJoin;
cx3 = xc + kappa * dxJoin;
cy3 = yc + kappa * dyJoin;
cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
pathIn->pts[j0].y + wdxNext);
pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc);
pathOut->curveTo(cx2, cy2, cx1, cy1,
pathIn->pts[j0].x - wdy,
pathIn->pts[j0].y + wdx);
}
}
// join angle >= 180
} else {
angle = atan2((double)-dx, (double)dy);
angleNext = atan2((double)-dxNext, (double)dyNext);
if (angleNext < angle) {
angleNext += 2 * M_PI;
}
dAngle = (angleNext - angle) / M_PI;
if (dAngle < 0.501) {
// span angle is <= 90 degrees -> draw a single arc
kappa = dAngle * bezierCircle * w;
cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
pathOut->lineTo(pathIn->pts[j0].x + wdy,
pathIn->pts[j0].y - wdx);
pathOut->curveTo(cx1, cy1, cx2, cy2,
pathIn->pts[j0].x + wdyNext,
pathIn->pts[j0].y - wdxNext);
} else {
// span angle is > 90 degrees -> split into two arcs
dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext);
if (dJoin > 0) {
dxJoin = (wdyNext - wdy) / dJoin;
dyJoin = (-wdxNext + wdx) / dJoin;
xc = pathIn->pts[j0].x
+ (SplashCoord)0.5 * w
* cos((double)((SplashCoord)0.5 * (angle + angleNext)));
yc = pathIn->pts[j0].y
+ (SplashCoord)0.5 * w
* sin((double)((SplashCoord)0.5 * (angle + angleNext)));
kappa = dAngle * bezierCircle2 * w;
cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
cx2 = xc - kappa * dxJoin;
cy2 = yc - kappa * dyJoin;
cx3 = xc + kappa * dxJoin;
cy3 = yc + kappa * dyJoin;
cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
pathOut->lineTo(pathIn->pts[j0].x + wdy,
pathIn->pts[j0].y - wdx);
pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc);
pathOut->curveTo(cx3, cy3, cx4, cy4,
pathIn->pts[j0].x + wdyNext,
pathIn->pts[j0].y - wdxNext);
}
}
}
} else {
pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
// join angle < 180
if (crossprod < 0) {
pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
pathIn->pts[j0].y + wdxNext);
// miter join inside limit
if (lineJoin == splashLineJoinMiter &&
splashSqrt(miter) <= state->miterLimit) {
pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
pathIn->pts[j0].y + wdx + wdy * m);
pathOut->lineTo(pathIn->pts[j0].x - wdy,
pathIn->pts[j0].y + wdx);
// bevel join or miter join outside limit
} else {
pathOut->lineTo(pathIn->pts[j0].x - wdy,
pathIn->pts[j0].y + wdx);
}
// join angle >= 180
} else {
pathOut->lineTo(pathIn->pts[j0].x + wdy,
pathIn->pts[j0].y - wdx);
// miter join inside limit
if (lineJoin == splashLineJoinMiter &&
splashSqrt(miter) <= state->miterLimit) {
pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
pathIn->pts[j0].y - wdx + wdy * m);
pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
pathIn->pts[j0].y - wdxNext);
// bevel join or miter join outside limit
} else {
pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
pathIn->pts[j0].y - wdxNext);
}
}
}
pathOut->close();
}
// add stroke adjustment hints
if (state->strokeAdjust != splashStrokeAdjustOff) {
// subpath with one segment
if (seg == 0 && last) {
switch (lineCap) {
case splashLineCapButt:
pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
firstPt, pathOut->length - 1);
break;
case splashLineCapProjecting:
pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
firstPt, pathOut->length - 1, gTrue);
break;
case splashLineCapRound:
break;
}
pathOut->addStrokeAdjustHint(left2, right2,
firstPt, pathOut->length - 1);
} else {
// start of subpath
if (seg == 1) {
// start cap
if (!closed) {
switch (lineCap) {
case splashLineCapButt:
pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
firstPt, firstPt + 1);
pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
right1 + 1, right1 + 1);
break;
case splashLineCapProjecting:
pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
firstPt, firstPt + 1, gTrue);
pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
right1 + 1, right1 + 1, gTrue);
break;
case splashLineCapRound:
break;
}
}
// first segment
pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
}
// middle of subpath
if (seg > 1) {
pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
}
// end of subpath
if (last) {
if (closed) {
// first segment
pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
left2 + 1, right2);
pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
join2, pathOut->length - 1);
// last segment
pathOut->addStrokeAdjustHint(left2, right2,
left1 + 1, right1);
pathOut->addStrokeAdjustHint(left2, right2,
join1, pathOut->length - 1);
pathOut->addStrokeAdjustHint(left2, right2,
leftFirst - 1, leftFirst);
pathOut->addStrokeAdjustHint(left2, right2,
rightFirst + 1, rightFirst + 1);
} else {
// last segment
pathOut->addStrokeAdjustHint(left2, right2,
left1 + 1, right1);
pathOut->addStrokeAdjustHint(left2, right2,
join1, pathOut->length - 1);
// end cap
switch (lineCap) {
case splashLineCapButt:
pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
left2 + 1, left2 + 2);
break;
case splashLineCapProjecting:
pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
left2 + 1, left2 + 2, gTrue);
break;
case splashLineCapRound:
break;
}
}
}
}
left0 = left1;
left1 = left2;
right0 = right1;
right1 = right2;
join0 = join1;
join1 = join2;
if (seg == 0) {
leftFirst = left2;
rightFirst = right2;
}
}
i0 = j0;
i1 = j1;
++seg;
}
if (pathIn != path) {
delete pathIn;
}
return pathOut;
}
SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin,
int *xMax, int *yMax) {
int t;
if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) {
*xMin = t;
}
if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) {
*xMax = t;
}
if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) {
*yMin = t;
}
if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) {
*yMax = t;
}
if (*xMin >= *xMax || *yMin >= *yMax) {
return splashClipAllOutside;
}
return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1,
state->strokeAdjust);
}
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" : "");
}
if (path->hintsLength == 0) {
printf(" no hints\n");
} else {
for (i = 0; i < path->hintsLength; ++i) {
printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n",
i, path->hints[i].ctrl0, path->hints[i].ctrl1,
path->hints[i].firstPt, path->hints[i].lastPt);
}
}
}
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 count=%d\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].count);
}
}