diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 8d4758a..d1e37b4 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -19,6 +19,7 @@ // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2006 Scott Turner // Copyright (C) 2007 Koji Otani +// Copyright (C) 2009 Thomas Freitag // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -69,6 +70,107 @@ static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } +#if SPLASH_CMYK + +//------------------------------------------------------------------------- +// helper for Blend functions (convert CMYK to RGB, do blend, convert back) +//------------------------------------------------------------------------- + +// based in GfxState.cc + + +static GfxColorComp clip01(GfxColorComp x) { + return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x; +} + +static double clip01(double x) { + return (x < 0) ? 0 : (x > 1) ? 1 : x; +} + +static void cmykToRGB(SplashColorPtr cmyk, SplashColor rgb) { + double c, m, y, k, c1, m1, y1, k1, r, g, b, x; + + c = colToDbl(byteToCol(cmyk[0])); + m = colToDbl(byteToCol(cmyk[1])); + y = colToDbl(byteToCol(cmyk[2])); + k = colToDbl(byteToCol(cmyk[3])); + c1 = 1 - c; + m1 = 1 - m; + y1 = 1 - y; + k1 = 1 - k; + // this is a matrix multiplication, unrolled for performance + // C M Y K + x = c1 * m1 * y1 * k1; // 0 0 0 0 + r = g = b = x; + x = c1 * m1 * y1 * k; // 0 0 0 1 + r += 0.1373 * x; + g += 0.1216 * x; + b += 0.1255 * x; + x = c1 * m1 * y * k1; // 0 0 1 0 + r += x; + g += 0.9490 * x; + x = c1 * m1 * y * k; // 0 0 1 1 + r += 0.1098 * x; + g += 0.1020 * x; + x = c1 * m * y1 * k1; // 0 1 0 0 + r += 0.9255 * x; + b += 0.5490 * x; + x = c1 * m * y1 * k; // 0 1 0 1 + r += 0.1412 * x; + x = c1 * m * y * k1; // 0 1 1 0 + r += 0.9294 * x; + g += 0.1098 * x; + b += 0.1412 * x; + x = c1 * m * y * k; // 0 1 1 1 + r += 0.1333 * x; + x = c * m1 * y1 * k1; // 1 0 0 0 + g += 0.6784 * x; + b += 0.9373 * x; + x = c * m1 * y1 * k; // 1 0 0 1 + g += 0.0588 * x; + b += 0.1412 * x; + x = c * m1 * y * k1; // 1 0 1 0 + g += 0.6510 * x; + b += 0.3137 * x; + x = c * m1 * y * k; // 1 0 1 1 + g += 0.0745 * x; + x = c * m * y1 * k1; // 1 1 0 0 + r += 0.1804 * x; + g += 0.1922 * x; + b += 0.5725 * x; + x = c * m * y1 * k; // 1 1 0 1 + b += 0.0078 * x; + x = c * m * y * k1; // 1 1 1 0 + r += 0.2118 * x; + g += 0.2119 * x; + b += 0.2235 * x; + rgb[0] = colToByte(clip01(dblToCol(r))); + rgb[1] = colToByte(clip01(dblToCol(g))); + rgb[2] = colToByte(clip01(dblToCol(b))); +} + +static void rgbToCMYK(SplashColor rgb, SplashColorPtr cmyk) { + GfxColorComp c, m, y, k; + + c = clip01(gfxColorComp1 - byteToCol(rgb[0])); + m = clip01(gfxColorComp1 - byteToCol(rgb[1])); + y = clip01(gfxColorComp1 - byteToCol(rgb[2])); + k = c; + if (m < k) { + k = m; + } + if (y < k) { + k = y; + } + cmyk[0] = colToByte(c - k); + cmyk[1] = colToByte(m - k); + cmyk[2] = colToByte(y - k); + cmyk[3] = colToByte(k); +} + + +#endif + //------------------------------------------------------------------------ // Blend functions //------------------------------------------------------------------------ @@ -77,8 +179,23 @@ static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = (dest[i] * src[i]) / 255; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = (rgbDest[i] * rgbSrc[i]) / 255; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = (dest[i] * src[i]) / 255; + } } } @@ -86,8 +203,23 @@ static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = rgbDest[i] + rgbSrc[i] - (rgbDest[i] * rgbSrc[i]) / 255; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; + } } } @@ -95,10 +227,27 @@ static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < 0x80 - ? (src[i] * 2 * dest[i]) / 255 - : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = rgbDest[i] < 0x80 + ? (rgbSrc[i] * 2 * rgbDest[i]) / 255 + : 255 - 2 * ((255 - rgbSrc[i]) * (255 - rgbDest[i])) / 255; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] < 0x80 + ? (src[i] * 2 * dest[i]) / 255 + : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255; + } } } @@ -106,8 +255,23 @@ static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < src[i] ? dest[i] : src[i]; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = rgbDest[i] < rgbSrc[i] ? rgbDest[i] : rgbSrc[i]; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] < src[i] ? dest[i] : src[i]; + } } } @@ -115,8 +279,23 @@ static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] > src[i] ? dest[i] : src[i]; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = rgbDest[i] > rgbSrc[i] ? rgbDest[i] : rgbSrc[i]; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] > src[i] ? dest[i] : src[i]; + } } } @@ -125,13 +304,33 @@ static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, SplashColorMode cm) { int i, x; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] == 255) { - blend[i] = 255; - } else { - x = (dest[i] * 255) / (255 - src[i]); - blend[i] = x <= 255 ? x : 255; - } +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + if (rgbSrc[i] == 255) { + rgbBlend[i] = 255; + } else { + x = (rgbDest[i] * 255) / (255 - rgbSrc[i]); + rgbBlend[i] = x <= 255 ? x : 255; + } + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + if (src[i] == 255) { + blend[i] = 255; + } else { + x = (dest[i] * 255) / (255 - src[i]); + blend[i] = x <= 255 ? x : 255; + } + } } } @@ -139,13 +338,33 @@ static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] == 0) { - blend[i] = 0; - } else { - x = ((255 - dest[i]) * 255) / src[i]; - blend[i] = x <= 255 ? 255 - x : 0; - } +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + if (rgbSrc[i] == 0) { + rgbBlend[i] = 0; + } else { + x = ((255 - rgbDest[i]) * 255) / rgbSrc[i]; + rgbBlend[i] = x <= 255 ? 255 - x : 0; + } + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + if (src[i] == 0) { + blend[i] = 0; + } else { + x = ((255 - dest[i]) * 255) / src[i]; + blend[i] = x <= 255 ? 255 - x : 0; + } + } } } @@ -153,10 +372,27 @@ static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = src[i] < 0x80 - ? (dest[i] * 2 * src[i]) / 255 - : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = rgbSrc[i] < 0x80 + ? (rgbDest[i] * 2 * rgbSrc[i]) / 255 + : 255 - 2 * ((255 - rgbDest[i]) * (255 - rgbSrc[i])) / 255; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = src[i] < 0x80 + ? (dest[i] * 2 * src[i]) / 255 + : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255; + } } } @@ -164,19 +400,45 @@ static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] < 0x80) { - blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / - (255 * 255); - } else { - if (dest[i] < 0x40) { - x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) - + 4 * 255) * dest[i]) / 255; - } else { - x = (int)sqrt(255.0 * dest[i]); - } - blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255; - } +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + if (rgbSrc[i] < 0x80) { + rgbBlend[i] = rgbDest[i] - (255 - 2 * rgbSrc[i]) * rgbDest[i] * (255 - rgbDest[i]) / + (255 * 255); + } else { + if (rgbDest[i] < 0x40) { + x = (((((16 * rgbDest[i] - 12 * 255) * rgbDest[i]) / 255) + + 4 * 255) * rgbDest[i]) / 255; + } else { + x = (int)sqrt(255.0 * rgbDest[i]); + } + rgbBlend[i] = rgbDest[i] + (2 * rgbSrc[i] - 255) * (x - rgbDest[i]) / 255; + } + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + if (src[i] < 0x80) { + blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / + (255 * 255); + } else { + if (dest[i] < 0x40) { + x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + + 4 * 255) * dest[i]) / 255; + } else { + x = (int)sqrt(255.0 * dest[i]); + } + blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255; + } + } } } @@ -194,8 +456,23 @@ static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255; +#ifdef SPLASH_CMYK + if (cm == splashModeCMYK8) { + SplashColor rgbSrc; + SplashColor rgbDest; + SplashColor rgbBlend; + cmykToRGB(src, rgbSrc); + cmykToRGB(dest, rgbDest); + for (i = 0; i < 3; ++i) { + rgbBlend[i] = rgbDest[i] + rgbSrc[i] - (2 * rgbDest[i] * rgbSrc[i]) / 255; + } + rgbToCMYK(rgbBlend, blend); + } else +#endif + { + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255; + } } } @@ -606,7 +883,11 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, colorMode != splashModeMono1; setupScreenParams(72.0, 72.0); reverseVideo = reverseVideoA; - splashColorCopy(paperColor, paperColorA); + if (paperColorA != NULL) + splashColorCopy(paperColor, paperColorA); + else + splashClearColor(paperColor); + keepAlphaChannel = paperColorA == NULL; xref = NULL; @@ -623,7 +904,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, font = NULL; needFontUpdate = gFalse; textClipPath = NULL; - + haveCSPattern = gFalse; transpGroupStack = NULL; } @@ -789,7 +1070,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { } void SplashOutputDev::endPage() { - if (colorMode != splashModeMono1) { + if (colorMode != splashModeMono1 && !keepAlphaChannel) { splash->compositeBackground(paperColor); } } @@ -1672,7 +1953,29 @@ void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font, splash->fillGlyph(0, 0, &glyph); } +void SplashOutputDev::beginTextObject(GfxState *state) { + if (state->getFillColorSpace()->getMode() == csPattern) { + haveCSPattern = gTrue; + saveState(state); + savedRender = state->getRender(); + state->setRender(7); + } +} + void SplashOutputDev::endTextObject(GfxState *state) { + if (haveCSPattern) { + state->setRender(savedRender); + haveCSPattern = gFalse; + if (state->getFillColorSpace()->getMode() != csPattern) { + if (textClipPath) { + splash->fill(textClipPath, gTrue); + delete textClipPath; + textClipPath = NULL; + } + restoreState(state); + updateFillColor(state); + } + } if (textClipPath) { splash->clipToPath(textClipPath, gFalse); delete textClipPath; @@ -1704,6 +2007,10 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { return gTrue; } +void SplashOutputDev::endMaskClip(GfxState * state) { + splash->setSoftMask(NULL); +} + void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) { @@ -1714,7 +2021,6 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, if (state->getFillColorSpace()->isNonMarking()) { return; } - ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; @@ -1730,13 +2036,31 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, imgMaskData.height = height; imgMaskData.y = 0; - splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, - t3GlyphStack != NULL); - if (inlineImg) { - while (imgMaskData.y < height) { - imgMaskData.imgStr->getLine(); - ++imgMaskData.y; - } + if (state->getFillColorSpace()->getMode() == csPattern) { + SplashBitmap *maskBitmap; + Splash *maskSplash; + SplashColor maskColor; + + maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), + 1, splashModeMono8, gFalse); + maskSplash = new Splash(maskBitmap, vectorAntialias); + maskColor[0] = 0; + maskSplash->clear(maskColor); + maskColor[0] = 0xff; + maskSplash->setFillPattern(new SplashSolidColor(maskColor)); + maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, + t3GlyphStack != NULL); + delete maskSplash; + splash->setSoftMask(maskBitmap); + } else { + splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, + t3GlyphStack != NULL); + if (inlineImg) { + while (imgMaskData.y < height) { + imgMaskData.imgStr->getLine(); + ++imgMaskData.y; + } + } } delete imgMaskData.imgStr; @@ -2560,7 +2884,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, // create the temporary bitmap bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, - bitmapTopDown); + bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, transpGroup->origSplash->getScreen()); if (isolated) { @@ -2722,7 +3046,7 @@ void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/, break; #if SPLASH_CMYK case splashModeCMYK8: - lum = (1 - color[4] / 255.0) + lum = (1 - color[3] / 255.0) - (0.3 / 255.0) * color[0] - (0.59 / 255.0) * color[1] - (0.11 / 255.0) * color[2]; diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 02c7f9a..9994361 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -14,6 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Takashi Iwai +// Copyright (C) 2009 Thomas Freitag // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -30,8 +31,8 @@ #include "goo/gtypes.h" #include "splash/SplashTypes.h" #include "poppler-config.h" -#include "OutputDev.h" -#include "GfxState.h" +#include "poppler/OutputDev.h" +#include "poppler/GfxState.h" class Gfx8BitFont; class SplashBitmap; @@ -79,6 +80,10 @@ public: // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gTrue; } + // This device now supports text in pattern colorspace! + virtual GBool supportTextCSPattern(GfxState *state) + { return state->getFillColorSpace()->getMode() == csPattern; } + //----- initialization and control // Start a page. @@ -130,6 +135,7 @@ public: double dx, double dy, CharCode code, Unicode *u, int uLen); virtual void endType3Char(GfxState *state); + virtual void beginTextObject(GfxState *state); virtual void endTextObject(GfxState *state); //----- image drawing @@ -150,6 +156,12 @@ public: Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap); + // If current colorspace ist pattern, + // need this device special handling for masks in pattern colorspace? + // Default is false + virtual GBool fillMaskCSPattern(GfxState * state) + { return state->getFillColorSpace()->getMode() == csPattern; } + virtual void endMaskClip(GfxState * /*state*/); //----- Type 3 font operators virtual void type3D0(GfxState *state, double wx, double wy); @@ -171,7 +183,7 @@ public: // Called to indicate that a new PDF document has been loaded. void startDoc(XRef *xrefA); - + void setPaperColor(SplashColorPtr paperColorA); GBool isReverseVideo() { return reverseVideo; } @@ -225,6 +237,11 @@ private: static GBool maskedImageSrc(void *data, SplashColorPtr line, Guchar *alphaLine); + GBool haveCSPattern; // set if text has been drawn with a + // clipping render mode because of pattern colorspace + int savedRender; // use if pattern colorspace + GBool keepAlphaChannel; // don't fill with paper color, keep alpha channel + SplashColorMode colorMode; int bitmapRowPad; GBool bitmapTopDown;