diff options
Diffstat (limited to 'Build/source/libs/poppler/poppler-src/poppler/SplashOutputDev.cc')
-rw-r--r-- | Build/source/libs/poppler/poppler-src/poppler/SplashOutputDev.cc | 4932 |
1 files changed, 0 insertions, 4932 deletions
diff --git a/Build/source/libs/poppler/poppler-src/poppler/SplashOutputDev.cc b/Build/source/libs/poppler/poppler-src/poppler/SplashOutputDev.cc deleted file mode 100644 index 3e883970e3e..00000000000 --- a/Build/source/libs/poppler/poppler-src/poppler/SplashOutputDev.cc +++ /dev/null @@ -1,4932 +0,0 @@ -//======================================================================== -// -// SplashOutputDev.cc -// -// Copyright 2003 Glyph & Cog, LLC -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005 Takashi Iwai <tiwai@suse.de> -// Copyright (C) 2006 Stefan Schweizer <genstef@gentoo.org> -// Copyright (C) 2006-2018 Albert Astals Cid <aacid@kde.org> -// Copyright (C) 2006 Krzysztof Kowalczyk <kkowalczyk@gmail.com> -// Copyright (C) 2006 Scott Turner <scotty1024@mac.com> -// Copyright (C) 2007 Koji Otani <sho@bbr.jp> -// Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com> -// Copyright (C) 2009-2016 Thomas Freitag <Thomas.Freitag@alfa.de> -// Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org> -// Copyright (C) 2009, 2014-2016 William Bader <williambader@hotmail.com> -// Copyright (C) 2010 Patrick Spendrin <ps_ml@gmx.de> -// Copyright (C) 2010 Brian Cameron <brian.cameron@oracle.com> -// Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com> -// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com> -// Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com> -// Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com> -// Copyright (C) 2011, 2012, 2017 Adrian Johnson <ajohnson@redneon.com> -// Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com> -// Copyright (C) 2013 Li Junling <lijunling@sina.com> -// Copyright (C) 2014 Ed Porras <ed@moto-research.com> -// Copyright (C) 2014 Richard PALO <richard@netbsd.org> -// Copyright (C) 2015 Tamas Szekeres <szekerest@gmail.com> -// Copyright (C) 2015 Kenji Uno <ku@digitaldolphins.jp> -// Copyright (C) 2016 Takahiro Hashimoto <kenya888.en@gmail.com> -// Copyright (C) 2017 Even Rouault <even.rouault@spatialys.com> -// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich -// Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de> -// -// 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 -// -//======================================================================== - -#include <config.h> - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include <string.h> -#include <cmath> -#include "goo/gfile.h" -#include "GlobalParams.h" -#include "Error.h" -#include "Object.h" -#include "Gfx.h" -#include "GfxFont.h" -#include "Page.h" -#include "PDFDoc.h" -#include "Link.h" -#include "FontEncodingTables.h" -#include "fofi/FoFiTrueType.h" -#include "splash/SplashBitmap.h" -#include "splash/SplashGlyphBitmap.h" -#include "splash/SplashPattern.h" -#include "splash/SplashScreen.h" -#include "splash/SplashPath.h" -#include "splash/SplashState.h" -#include "splash/SplashErrorCodes.h" -#include "splash/SplashFontEngine.h" -#include "splash/SplashFont.h" -#include "splash/SplashFontFile.h" -#include "splash/SplashFontFileID.h" -#include "splash/Splash.h" -#include "SplashOutputDev.h" -#include <algorithm> - -#ifdef VMS -#if (__VMS_VER < 70000000) -extern "C" int unlink(char *filename); -#endif -#endif - -static const double s_minLineWidth = 0.0; - -static inline void convertGfxColor(SplashColorPtr dest, - SplashColorMode colorMode, - GfxColorSpace *colorSpace, - GfxColor *src) { - SplashColor color; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - // make gcc happy - color[0] = color[1] = color[2] = 0; -#ifdef SPLASH_CMYK - color[3] = 0; -#endif - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - colorSpace->getGray(src, &gray); - color[0] = colToByte(gray); - break; - case splashModeXBGR8: - color[3] = 255; - // fallthrough - case splashModeBGR8: - case splashModeRGB8: - colorSpace->getRGB(src, &rgb); - color[0] = colToByte(rgb.r); - color[1] = colToByte(rgb.g); - color[2] = colToByte(rgb.b); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - colorSpace->getCMYK(src, &cmyk); - color[0] = colToByte(cmyk.c); - color[1] = colToByte(cmyk.m); - color[2] = colToByte(cmyk.y); - color[3] = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - colorSpace->getDeviceN(src, &deviceN); - for (int i = 0; i < SPOT_NCOMPS + 4; i++) - color[i] = colToByte(deviceN.c[i]); - break; -#endif - } - splashColorCopy(dest, color); -} - -// Copy a color according to the color mode. -// Use convertGfxShortColor() below when the destination is a bitmap -// to avoid overwriting cells. -// Calling this in SplashGouraudPattern::getParameterizedColor() fixes bug 90570. -// Use convertGfxColor() above when the destination is an array of SPOT_NCOMPS+4 bytes, -// to ensure that everything is initialized. - -static inline void convertGfxShortColor(SplashColorPtr dest, - SplashColorMode colorMode, - GfxColorSpace *colorSpace, - GfxColor *src) { - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - { - GfxGray gray; - colorSpace->getGray(src, &gray); - dest[0] = colToByte(gray); - } - break; - case splashModeXBGR8: - dest[3] = 255; - // fallthrough - case splashModeBGR8: - case splashModeRGB8: - { - GfxRGB rgb; - colorSpace->getRGB(src, &rgb); - dest[0] = colToByte(rgb.r); - dest[1] = colToByte(rgb.g); - dest[2] = colToByte(rgb.b); - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - { - GfxCMYK cmyk; - colorSpace->getCMYK(src, &cmyk); - dest[0] = colToByte(cmyk.c); - dest[1] = colToByte(cmyk.m); - dest[2] = colToByte(cmyk.y); - dest[3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - { - GfxColor deviceN; - colorSpace->getDeviceN(src, &deviceN); - for (int i = 0; i < SPOT_NCOMPS + 4; i++) - dest[i] = colToByte(deviceN.c[i]); - } - break; -#endif - } -} - -//------------------------------------------------------------------------ -// SplashGouraudPattern -//------------------------------------------------------------------------ -SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA, - GfxState *stateA, GfxGouraudTriangleShading *shadingA) { - state = stateA; - shading = shadingA; - bDirectColorTranslation = bDirectColorTranslationA; - gfxMode = shadingA->getColorSpace()->getMode(); -} - -SplashGouraudPattern::~SplashGouraudPattern() { -} - -void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) { - GfxColor src; - GfxColorSpace* srcColorSpace = shading->getColorSpace(); - int colorComps = 3; -#ifdef SPLASH_CMYK - if (mode == splashModeCMYK8) - colorComps=4; - else if (mode == splashModeDeviceN8) - colorComps=4 + SPOT_NCOMPS; -#endif - - shading->getParameterizedColor(colorinterp, &src); - - if (bDirectColorTranslation) { - for (int m = 0; m < colorComps; ++m) - dest[m] = colToByte(src.c[m]); - } else { - convertGfxShortColor(dest, mode, srcColorSpace, &src); - } -} - -//------------------------------------------------------------------------ -// SplashFunctionPattern -//------------------------------------------------------------------------ - -SplashFunctionPattern::SplashFunctionPattern(SplashColorMode colorModeA, GfxState *stateA, GfxFunctionShading *shadingA) -{ - Matrix ctm; - SplashColor defaultColor; - GfxColor srcColor; - double *matrix = shadingA->getMatrix(); - - shading = shadingA; - state = stateA; - colorMode = colorModeA; - - state->getCTM(&ctm); - - double a1 = ctm.m[0]; - double b1 = ctm.m[1]; - double c1 = ctm.m[2]; - double d1 = ctm.m[3]; - - ctm.m[0] = matrix[0] * a1 + matrix[1] * c1; - ctm.m[1] = matrix[0] * b1 + matrix[1] * d1; - ctm.m[2] = matrix[2] * a1 + matrix[3] * c1; - ctm.m[3] = matrix[2] * b1 + matrix[3] * d1; - ctm.m[4] = matrix[4] * a1 + matrix[5] * c1 + ctm.m[4]; - ctm.m[5] = matrix[4] * b1 + matrix[5] * d1 + ctm.m[5]; - ctm.invertTo(&ictm); - - gfxMode = shadingA->getColorSpace()->getMode(); - shadingA->getColorSpace()->getDefaultColor(&srcColor); - shadingA->getDomain(&xMin, &yMin, &xMax, &yMax); - convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); -} - -SplashFunctionPattern::~SplashFunctionPattern() { -} - -GBool SplashFunctionPattern::getColor(int x, int y, SplashColorPtr c) { - GfxColor gfxColor; - double xc, yc; - - ictm.transform(x, y, &xc, &yc); - if (xc < xMin || xc > xMax || yc < yMin || yc > yMax) return gFalse; - shading->getColor(xc, yc, &gfxColor); - convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); - return gTrue; -} - -//------------------------------------------------------------------------ -// SplashUnivariatePattern -//------------------------------------------------------------------------ - -SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) { - Matrix ctm; - double xMin, yMin, xMax, yMax; - - shading = shadingA; - state = stateA; - colorMode = colorModeA; - - state->getCTM(&ctm); - ctm.invertTo(&ictm); - - // get the function domain - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - dt = t1 - t0; - - stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax); - gfxMode = shadingA->getColorSpace()->getMode(); -} - -SplashUnivariatePattern::~SplashUnivariatePattern() { -} - -GBool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) { - GfxColor gfxColor; - double xc, yc, t; - - ictm.transform(x, y, &xc, &yc); - if (! getParameter (xc, yc, &t)) - return gFalse; - - const int filled = shading->getColor(t, &gfxColor); - if (unlikely(filled < shading->getColorSpace()->getNComps())) { - for (int i = filled; i < shading->getColorSpace()->getNComps(); ++i) - gfxColor.c[i] = 0; - } - convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); - return gTrue; -} - -GBool SplashUnivariatePattern::testPosition(int x, int y) { - double xc, yc, t; - - ictm.transform(x, y, &xc, &yc); - if (! getParameter (xc, yc, &t)) - return gFalse; - return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0); -} - - -//------------------------------------------------------------------------ -// SplashRadialPattern -//------------------------------------------------------------------------ -#define RADIAL_EPSILON (1. / 1024 / 1024) - -SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA): - SplashUnivariatePattern(colorModeA, stateA, shadingA) -{ - SplashColor defaultColor; - GfxColor srcColor; - - shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr); - dx -= x0; - dy -= y0; - dr -= r0; - a = dx*dx + dy*dy - dr*dr; - if (fabs(a) > RADIAL_EPSILON) - inva = 1.0 / a; - shadingA->getColorSpace()->getDefaultColor(&srcColor); - convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); -} - -SplashRadialPattern::~SplashRadialPattern() { -} - -GBool SplashRadialPattern::getParameter(double xs, double ys, double *t) { - double b, c, s0, s1; - - // We want to solve this system of equations: - // - // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2 - // 2. xc(s) = x0 + s * (x1 - xo) - // 3. yc(s) = y0 + s * (y1 - yo) - // 4. rc(s) = r0 + s * (r1 - ro) - // - // To simplify the system a little, we translate - // our coordinates to have the origin in (x0,y0) - - xs -= x0; - ys -= y0; - - // Then we have to solve the equation: - // A*s^2 - 2*B*s + C = 0 - // where - // A = dx^2 + dy^2 - dr^2 - // B = xs*dx + ys*dy + r0*dr - // C = xs^2 + ys^2 - r0^2 - - b = xs*dx + ys*dy + r0*dr; - c = xs*xs + ys*ys - r0*r0; - - if (fabs(a) <= RADIAL_EPSILON) { - // A is 0, thus the equation simplifies to: - // -2*B*s + C = 0 - // If B is 0, we can either have no solution or an indeterminate - // equation, thus we behave as if we had an invalid solution - if (fabs(b) <= RADIAL_EPSILON) - return gFalse; - - s0 = s1 = 0.5 * c / b; - } else { - double d; - - d = b*b - a*c; - if (d < 0) - return gFalse; - - d = sqrt (d); - s0 = b + d; - s1 = b - d; - - // If A < 0, one of the two solutions will have negative radius, - // thus it will be ignored. Otherwise we know that s1 <= s0 - // (because d >=0 implies b - d <= b + d), so if both are valid it - // will be the true solution. - s0 *= inva; - s1 *= inva; - } - - if (r0 + s0 * dr >= 0) { - if (0 <= s0 && s0 <= 1) { - *t = t0 + dt * s0; - return gTrue; - } else if (s0 < 0 && shading->getExtend0()) { - *t = t0; - return gTrue; - } else if (s0 > 1 && shading->getExtend1()) { - *t = t1; - return gTrue; - } - } - - if (r0 + s1 * dr >= 0) { - if (0 <= s1 && s1 <= 1) { - *t = t0 + dt * s1; - return gTrue; - } else if (s1 < 0 && shading->getExtend0()) { - *t = t0; - return gTrue; - } else if (s1 > 1 && shading->getExtend1()) { - *t = t1; - return gTrue; - } - } - - return gFalse; -} - -#undef RADIAL_EPSILON - -//------------------------------------------------------------------------ -// SplashAxialPattern -//------------------------------------------------------------------------ - -SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA): - SplashUnivariatePattern(colorModeA, stateA, shadingA) -{ - SplashColor defaultColor; - GfxColor srcColor; - - shadingA->getCoords(&x0, &y0, &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; - const double mul_denominator = (dx * dx + dy * dy); - if (unlikely(mul_denominator == 0)) { - mul = 0; - } else { - mul = 1 / mul_denominator; - } - shadingA->getColorSpace()->getDefaultColor(&srcColor); - convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); -} - -SplashAxialPattern::~SplashAxialPattern() { -} - -GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) { - double s; - - xc -= x0; - yc -= y0; - - s = (xc * dx + yc * dy) * mul; - if (0 <= s && s <= 1) { - *t = t0 + dt * s; - } else if (s < 0 && shading->getExtend0()) { - *t = t0; - } else if (s > 1 && shading->getExtend1()) { - *t = t1; - } else { - return gFalse; - } - - return gTrue; -} - -//------------------------------------------------------------------------ -// Type 3 font cache size parameters -#define type3FontCacheAssoc 8 -#define type3FontCacheMaxSets 8 -#define type3FontCacheSize (128*1024) - -//------------------------------------------------------------------------ -// 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); -} - -#ifdef SPLASH_CMYK - -#include "GfxState_helpers.h" - -//------------------------------------------------------------------------- -// helper for Blend functions (convert CMYK to RGB, do blend, convert back) -//------------------------------------------------------------------------- - -// based in GfxState.cc - -static void cmykToRGB(SplashColorPtr cmyk, SplashColor rgb) { - double c, m, y, k, c1, m1, y1, k1, r, g, b; - - 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; - cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); - 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 -//------------------------------------------------------------------------ - -static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = (dest[i] * src[i]) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#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; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < src[i] ? dest[i] : src[i]; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] > src[i] ? dest[i] : src[i]; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { - int i, x; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#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; - } - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i, x; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#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; - } - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#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; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i, x; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#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; - } - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i]; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } - if (cm == splashModeDeviceN8) { - for (i = 4; i < splashColorModeNComps[cm]; ++i) { - if (dest[i] == 0 && src[i] == 0) - blend[i] = 0; - } - } -#endif -} - -static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - 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 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } - if (cm == splashModeDeviceN8) { - for (i = 4; i < splashColorModeNComps[cm]; ++i) { - if (dest[i] == 0 && src[i] == 0) - blend[i] = 0; - } - } -#endif -} - -static int getLum(int r, int g, int b) { - return (int)(0.3 * r + 0.59 * g + 0.11 * b); -} - -static int getSat(int r, int g, int b) { - int rgbMin, rgbMax; - - rgbMin = rgbMax = r; - if (g < rgbMin) { - rgbMin = g; - } else if (g > rgbMax) { - rgbMax = g; - } - if (b < rgbMin) { - rgbMin = b; - } else if (b > rgbMax) { - rgbMax = b; - } - return rgbMax - rgbMin; -} - -static void clipColor(int rIn, int gIn, int bIn, - Guchar *rOut, Guchar *gOut, Guchar *bOut) { - int lum, rgbMin, rgbMax; - - lum = getLum(rIn, gIn, bIn); - rgbMin = rgbMax = rIn; - if (gIn < rgbMin) { - rgbMin = gIn; - } else if (gIn > rgbMax) { - rgbMax = gIn; - } - if (bIn < rgbMin) { - rgbMin = bIn; - } else if (bIn > rgbMax) { - rgbMax = bIn; - } - if (rgbMin < 0) { - *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); - *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); - *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); - } else if (rgbMax > 255) { - *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); - *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); - *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); - } else { - *rOut = rIn; - *gOut = gIn; - *bOut = bIn; - } -} - -static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum, - Guchar *rOut, Guchar *gOut, Guchar *bOut) { - int d; - - d = lum - getLum(rIn, gIn, bIn); - clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); -} - -static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat, - Guchar *rOut, Guchar *gOut, Guchar *bOut) { - int rgbMin, rgbMid, rgbMax; - Guchar *minOut, *midOut, *maxOut; - - if (rIn < gIn) { - rgbMin = rIn; minOut = rOut; - rgbMid = gIn; midOut = gOut; - } else { - rgbMin = gIn; minOut = gOut; - rgbMid = rIn; midOut = rOut; - } - if (bIn > rgbMid) { - rgbMax = bIn; maxOut = bOut; - } else if (bIn > rgbMin) { - rgbMax = rgbMid; maxOut = midOut; - rgbMid = bIn; midOut = bOut; - } else { - rgbMax = rgbMid; maxOut = midOut; - rgbMid = rgbMin; midOut = minOut; - rgbMin = bIn; minOut = bOut; - } - if (rgbMax > rgbMin) { - *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); - *maxOut = (Guchar)sat; - } else { - *midOut = *maxOut = 0; - } - *minOut = 0; -} - -static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - Guchar r0, g0, b0; -#ifdef SPLASH_CMYK - Guchar r1, g1, b1; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - // NB: inputs have already been converted to additive mode - setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), - &r1, &g1, &b1); - blend[0] = r1; - blend[1] = g1; - blend[2] = b1; - blend[3] = dest2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { - Guchar r0, g0, b0; -#ifdef SPLASH_CMYK - Guchar r1, g1, b1; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - setSat(dest2[0], dest2[1], dest2[2], getSat(src2[0], src2[1], src2[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), - &r1, &g1, &b1); - blend[0] = r1; - blend[1] = g1; - blend[2] = b1; - blend[3] = dest2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { -#ifdef SPLASH_CMYK - Guchar r, g, b; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - setLum(src2[0], src2[1], src2[2], getLum(dest2[0], dest2[1], dest2[2]), - &r, &g, &b); - blend[0] = r; - blend[1] = g; - blend[2] = b; - blend[3] = dest2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { -#ifdef SPLASH_CMYK - Guchar r, g, b; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]), - &r, &g, &b); - blend[0] = r; - blend[1] = g; - blend[2] = b; - blend[3] = src2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -// NB: This must match the GfxBlendMode enum defined in GfxState.h. -static const SplashBlendFunc splashOutBlendFuncs[] = { - nullptr, - &splashOutBlendMultiply, - &splashOutBlendScreen, - &splashOutBlendOverlay, - &splashOutBlendDarken, - &splashOutBlendLighten, - &splashOutBlendColorDodge, - &splashOutBlendColorBurn, - &splashOutBlendHardLight, - &splashOutBlendSoftLight, - &splashOutBlendDifference, - &splashOutBlendExclusion, - &splashOutBlendHue, - &splashOutBlendSaturation, - &splashOutBlendColor, - &splashOutBlendLuminosity -}; - -//------------------------------------------------------------------------ -// SplashOutFontFileID -//------------------------------------------------------------------------ - -class SplashOutFontFileID: public SplashFontFileID { -public: - - SplashOutFontFileID(const Ref *rA) { r = *rA; } - - ~SplashOutFontFileID() {} - - GBool matches(SplashFontFileID *id) override { - return ((SplashOutFontFileID *)id)->r.num == r.num && - ((SplashOutFontFileID *)id)->r.gen == r.gen; - } - -private: - - Ref r; -}; - -//------------------------------------------------------------------------ -// T3FontCache -//------------------------------------------------------------------------ - -struct T3FontCacheTag { - Gushort code; - Gushort mru; // valid bit (0x8000) and MRU index -}; - -class T3FontCache { -public: - - T3FontCache(const Ref *fontID, double m11A, double m12A, - double m21A, double m22A, - int glyphXA, int glyphYA, int glyphWA, int glyphHA, - GBool aa, GBool validBBoxA); - ~T3FontCache(); - T3FontCache(const T3FontCache &) = delete; - T3FontCache& operator=(const T3FontCache &) = delete; - GBool matches(const Ref *idA, double m11A, double m12A, - double m21A, double m22A) - { return fontID.num == idA->num && fontID.gen == idA->gen && - m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } - - Ref fontID; // PDF font ID - double m11, m12, m21, m22; // transform matrix - int glyphX, glyphY; // pixel offset of glyph bitmaps - int glyphW, glyphH; // size of glyph bitmaps, in pixels - GBool validBBox; // false if the bbox was [0 0 0 0] - int glyphSize; // size of glyph bitmaps, in bytes - int cacheSets; // number of sets in cache - int cacheAssoc; // cache associativity (glyphs per set) - Guchar *cacheData; // glyph pixmap cache - T3FontCacheTag *cacheTags; // cache tags, i.e., char codes -}; - -T3FontCache::T3FontCache(const Ref *fontIDA, double m11A, double m12A, - double m21A, double m22A, - int glyphXA, int glyphYA, int glyphWA, int glyphHA, - GBool validBBoxA, GBool aa) { - - fontID = *fontIDA; - m11 = m11A; - m12 = m12A; - m21 = m21A; - m22 = m22A; - glyphX = glyphXA; - glyphY = glyphYA; - glyphW = glyphWA; - glyphH = glyphHA; - validBBox = validBBoxA; - // sanity check for excessively large glyphs (which most likely - // indicate an incorrect BBox) - if (glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0 || glyphW * glyphH > 100000) { - glyphW = glyphH = 100; - validBBox = gFalse; - } - if (aa) { - glyphSize = glyphW * glyphH; - } else { - glyphSize = ((glyphW + 7) >> 3) * glyphH; - } - cacheAssoc = type3FontCacheAssoc; - for (cacheSets = type3FontCacheMaxSets; - cacheSets > 1 && - cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; - cacheSets >>= 1) ; - if (glyphSize < 10485760 / cacheAssoc / cacheSets) { - cacheData = (Guchar *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize); - } else { - error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n" - " This could teoretically result in wrong rendering,\n" - " but most probably the document is bogus.\n" - " Please report a bug if you think the rendering may be wrong because of this."); - cacheData = nullptr; - } - if (cacheData != nullptr) - { - cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, - sizeof(T3FontCacheTag)); - for (int i = 0; i < cacheSets * cacheAssoc; ++i) { - cacheTags[i].mru = i & (cacheAssoc - 1); - } - } - else - { - cacheTags = nullptr; - } -} - -T3FontCache::~T3FontCache() { - gfree(cacheData); - gfree(cacheTags); -} - -struct T3GlyphStack { - Gushort code; // character code - - GBool haveDx; // set after seeing a d0/d1 operator - GBool doNotCache; // set if we see a gsave/grestore before - // the d0/d1 - - //----- cache info - T3FontCache *cache; // font cache for the current font - T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph - Guchar *cacheData; // pointer to cache data for the glyph - - //----- saved state - SplashBitmap *origBitmap; - Splash *origSplash; - double origCTM4, origCTM5; - - T3GlyphStack *next; // next object on stack -}; - -//------------------------------------------------------------------------ -// SplashTransparencyGroup -//------------------------------------------------------------------------ - -struct SplashTransparencyGroup { - int tx, ty; // translation coordinates - SplashBitmap *tBitmap; // bitmap for transparency group - SplashBitmap *softmask; // bitmap for softmasks - GfxColorSpace *blendingColorSpace; - GBool isolated; - - //----- for knockout - SplashBitmap *shape; - GBool knockout; - SplashCoord knockoutOpacity; - GBool fontAA; - - //----- saved state - SplashBitmap *origBitmap; - Splash *origSplash; - - SplashTransparencyGroup *next; -}; - -//------------------------------------------------------------------------ -// SplashOutputDev -//------------------------------------------------------------------------ - -SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, - int bitmapRowPadA, - GBool reverseVideoA, - SplashColorPtr paperColorA, - GBool bitmapTopDownA, - SplashThinLineMode thinLineMode, - GBool overprintPreviewA) { - colorMode = colorModeA; - bitmapRowPad = bitmapRowPadA; - bitmapTopDown = bitmapTopDownA; - bitmapUpsideDown = gFalse; - fontAntialias = gTrue; - vectorAntialias = gTrue; - overprintPreview = overprintPreviewA; - enableFreeTypeHinting = gFalse; - enableSlightHinting = gFalse; - setupScreenParams(72.0, 72.0); - reverseVideo = reverseVideoA; - if (paperColorA != nullptr) { - splashColorCopy(paperColor, paperColorA); - } else { - splashClearColor(paperColor); - } - skipHorizText = gFalse; - skipRotatedText = gFalse; - keepAlphaChannel = paperColorA == nullptr; - - doc = nullptr; - - bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - splash = new Splash(bitmap, vectorAntialias, &screenParams); - splash->setMinLineWidth(s_minLineWidth); - splash->setThinLineMode(thinLineMode); - splash->clear(paperColor, 0); - - fontEngine = nullptr; - - nT3Fonts = 0; - t3GlyphStack = nullptr; - - font = nullptr; - needFontUpdate = gFalse; - textClipPath = nullptr; - transpGroupStack = nullptr; - nestCount = 0; - xref = nullptr; -} - -void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { - screenParams.size = -1; - screenParams.dotRadius = -1; - screenParams.gamma = (SplashCoord)1.0; - screenParams.blackThreshold = (SplashCoord)0.0; - screenParams.whiteThreshold = (SplashCoord)1.0; - - // use clustered dithering for resolution >= 300 dpi - // (compare to 299.9 to avoid floating point issues) - if (hDPI > 299.9 && vDPI > 299.9) { - screenParams.type = splashScreenStochasticClustered; - if (screenParams.size < 0) { - screenParams.size = 64; - } - if (screenParams.dotRadius < 0) { - screenParams.dotRadius = 2; - } - } else { - screenParams.type = splashScreenDispersed; - if (screenParams.size < 0) { - screenParams.size = 4; - } - } -} - -SplashOutputDev::~SplashOutputDev() { - int i; - - for (i = 0; i < nT3Fonts; ++i) { - delete t3FontCache[i]; - } - if (fontEngine) { - delete fontEngine; - } - if (splash) { - delete splash; - } - if (bitmap) { - delete bitmap; - } - delete textClipPath; -} - -void SplashOutputDev::startDoc(PDFDoc *docA) { - int i; - - doc = docA; - if (fontEngine) { - delete fontEngine; - } - fontEngine = new SplashFontEngine( - globalParams->getEnableFreeType(), - enableFreeTypeHinting, - enableSlightHinting, - getFontAntialias() && - colorMode != splashModeMono1); - for (i = 0; i < nT3Fonts; ++i) { - delete t3FontCache[i]; - } - nT3Fonts = 0; -} - -void SplashOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { - int w, h; - double *ctm; - SplashCoord mat[6]; - SplashColor color; - - xref = xrefA; - if (state) { - setupScreenParams(state->getHDPI(), state->getVDPI()); - w = (int)(state->getPageWidth() + 0.5); - if (w <= 0) { - w = 1; - } - h = (int)(state->getPageHeight() + 0.5); - if (h <= 0) { - h = 1; - } - } else { - w = h = 1; - } - SplashThinLineMode thinLineMode = splashThinLineDefault; - if (splash) { - thinLineMode = splash->getThinLineMode(); - delete splash; - splash = nullptr; - } - if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { - if (bitmap) { - delete bitmap; - bitmap = nullptr; - } - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - if (!bitmap->getDataPtr()) { - delete bitmap; - w = h = 1; - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - } - } - splash = new Splash(bitmap, vectorAntialias, &screenParams); - splash->setThinLineMode(thinLineMode); - splash->setMinLineWidth(s_minLineWidth); - if (state) { - ctm = state->getCTM(); - mat[0] = (SplashCoord)ctm[0]; - mat[1] = (SplashCoord)ctm[1]; - mat[2] = (SplashCoord)ctm[2]; - mat[3] = (SplashCoord)ctm[3]; - mat[4] = (SplashCoord)ctm[4]; - mat[5] = (SplashCoord)ctm[5]; - splash->setMatrix(mat); - } - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - color[0] = 0; - break; - case splashModeXBGR8: - color[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - color[0] = color[1] = color[2] = 0; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - color[0] = color[1] = color[2] = color[3] = 0; - break; - case splashModeDeviceN8: - for (int i = 0; i < 4 + SPOT_NCOMPS; i++) - color[i] = 0; - break; -#endif - } - splash->setStrokePattern(new SplashSolidColor(color)); - splash->setFillPattern(new SplashSolidColor(color)); - splash->setLineCap(splashLineCapButt); - splash->setLineJoin(splashLineJoinMiter); - splash->setLineDash(nullptr, 0, 0); - splash->setMiterLimit(10); - splash->setFlatness(1); - // the SA parameter supposedly defaults to false, but Acrobat - // apparently hardwires it to true - splash->setStrokeAdjust(gTrue); - splash->clear(paperColor, 0); -} - -void SplashOutputDev::endPage() { - if (colorMode != splashModeMono1 && !keepAlphaChannel) { - splash->compositeBackground(paperColor); - } -} - -void SplashOutputDev::saveState(GfxState *state) { - splash->saveState(); - if (t3GlyphStack && !t3GlyphStack->haveDx) { - t3GlyphStack->doNotCache = gTrue; - error(errSyntaxWarning, -1, - "Save (q) operator before d0/d1 in Type 3 glyph"); - } -} - -void SplashOutputDev::restoreState(GfxState *state) { - splash->restoreState(); - needFontUpdate = gTrue; - if (t3GlyphStack && !t3GlyphStack->haveDx) { - t3GlyphStack->doNotCache = gTrue; - error(errSyntaxWarning, -1, - "Restore (Q) operator before d0/d1 in Type 3 glyph"); - } -} - -void SplashOutputDev::updateAll(GfxState *state) { - updateLineDash(state); - updateLineJoin(state); - updateLineCap(state); - updateLineWidth(state); - updateFlatness(state); - updateMiterLimit(state); - updateStrokeAdjust(state); - updateFillColorSpace(state); - updateFillColor(state); - updateStrokeColorSpace(state); - updateStrokeColor(state); - needFontUpdate = gTrue; -} - -void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, - double m31, double m32) { - double *ctm; - SplashCoord mat[6]; - - ctm = state->getCTM(); - mat[0] = (SplashCoord)ctm[0]; - mat[1] = (SplashCoord)ctm[1]; - mat[2] = (SplashCoord)ctm[2]; - mat[3] = (SplashCoord)ctm[3]; - mat[4] = (SplashCoord)ctm[4]; - mat[5] = (SplashCoord)ctm[5]; - splash->setMatrix(mat); -} - -void SplashOutputDev::updateLineDash(GfxState *state) { - double *dashPattern; - int dashLength; - double dashStart; - SplashCoord dash[20]; - int i; - - state->getLineDash(&dashPattern, &dashLength, &dashStart); - if (dashLength > 20) { - dashLength = 20; - } - for (i = 0; i < dashLength; ++i) { - dash[i] = (SplashCoord)dashPattern[i]; - if (dash[i] < 0) { - dash[i] = 0; - } - } - splash->setLineDash(dash, dashLength, (SplashCoord)dashStart); -} - -void SplashOutputDev::updateFlatness(GfxState *state) { -#if 0 // Acrobat ignores the flatness setting, and always renders curves - // with a fairly small flatness value - splash->setFlatness(state->getFlatness()); -#endif -} - -void SplashOutputDev::updateLineJoin(GfxState *state) { - splash->setLineJoin(state->getLineJoin()); -} - -void SplashOutputDev::updateLineCap(GfxState *state) { - splash->setLineCap(state->getLineCap()); -} - -void SplashOutputDev::updateMiterLimit(GfxState *state) { - splash->setMiterLimit(state->getMiterLimit()); -} - -void SplashOutputDev::updateLineWidth(GfxState *state) { - splash->setLineWidth(state->getLineWidth()); -} - -void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) { -#if 0 // the SA parameter supposedly defaults to false, but Acrobat - // apparently hardwires it to true - splash->setStrokeAdjust(state->getStrokeAdjust()); -#endif -} - -void SplashOutputDev::updateFillColorSpace(GfxState *state) { -#ifdef SPLASH_CMYK - if (colorMode == splashModeDeviceN8) - state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif -} - -void SplashOutputDev::updateStrokeColorSpace(GfxState *state) { -#ifdef SPLASH_CMYK - if (colorMode == splashModeDeviceN8) - state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif -} - -void SplashOutputDev::updateFillColor(GfxState *state) { - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - state->getFillGray(&gray); - splash->setFillPattern(getColor(gray)); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - state->getFillRGB(&rgb); - splash->setFillPattern(getColor(&rgb)); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - state->getFillCMYK(&cmyk); - splash->setFillPattern(getColor(&cmyk)); - break; - case splashModeDeviceN8: - state->getFillDeviceN(&deviceN); - splash->setFillPattern(getColor(&deviceN)); - break; -#endif - } -} - -void SplashOutputDev::updateStrokeColor(GfxState *state) { - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - state->getStrokeGray(&gray); - splash->setStrokePattern(getColor(gray)); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - state->getStrokeRGB(&rgb); - splash->setStrokePattern(getColor(&rgb)); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - state->getStrokeCMYK(&cmyk); - splash->setStrokePattern(getColor(&cmyk)); - break; - case splashModeDeviceN8: - state->getStrokeDeviceN(&deviceN); - splash->setStrokePattern(getColor(&deviceN)); - break; -#endif - } -} - -SplashPattern *SplashOutputDev::getColor(GfxGray gray) { - SplashColor color; - - if (reverseVideo) { - gray = gfxColorComp1 - gray; - } - color[0] = colToByte(gray); - return new SplashSolidColor(color); -} - -SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { - GfxColorComp r, g, b; - SplashColor color; - - if (reverseVideo) { - r = gfxColorComp1 - rgb->r; - g = gfxColorComp1 - rgb->g; - b = gfxColorComp1 - rgb->b; - } else { - r = rgb->r; - g = rgb->g; - b = rgb->b; - } - color[0] = colToByte(r); - color[1] = colToByte(g); - color[2] = colToByte(b); - if (colorMode == splashModeXBGR8) color[3] = 255; - return new SplashSolidColor(color); -} - -#ifdef SPLASH_CMYK -SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { - SplashColor color; - - color[0] = colToByte(cmyk->c); - color[1] = colToByte(cmyk->m); - color[2] = colToByte(cmyk->y); - color[3] = colToByte(cmyk->k); - return new SplashSolidColor(color); -} - -SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) { - SplashColor color; - - for (int i = 0; i < 4 + SPOT_NCOMPS; i++) - color[i] = colToByte(deviceN->c[i]); - return new SplashSolidColor(color); -} -#endif - -void SplashOutputDev::getMatteColor(SplashColorMode colorMode, GfxImageColorMap *colorMap, GfxColor *matteColorIn, SplashColor matteColor) { - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - colorMap->getColorSpace()->getGray(matteColorIn, &gray); - matteColor[0] = colToByte(gray); - break; - case splashModeRGB8: - case splashModeBGR8: - colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); - matteColor[0] = colToByte(rgb.r); - matteColor[1] = colToByte(rgb.g); - matteColor[2] = colToByte(rgb.b); - break; - case splashModeXBGR8: - colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); - matteColor[0] = colToByte(rgb.r); - matteColor[1] = colToByte(rgb.g); - matteColor[2] = colToByte(rgb.b); - matteColor[3] = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - colorMap->getColorSpace()->getCMYK(matteColorIn, &cmyk); - matteColor[0] = colToByte(cmyk.c); - matteColor[1] = colToByte(cmyk.m); - matteColor[2] = colToByte(cmyk.y); - matteColor[3] = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - colorMap->getColorSpace()->getDeviceN(matteColorIn, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - matteColor[cp] = colToByte(deviceN.c[cp]); - break; -#endif - } -} - -void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, - GBool overprintFlag, - int overprintMode, - GfxColor *singleColor, - GBool grayIndexed) { -#ifdef SPLASH_CMYK - Guint mask; - GfxCMYK cmyk; - GBool additive = gFalse; - int i; - - if (colorSpace->getMode() == csIndexed) { - setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(), - overprintFlag, - overprintMode, - singleColor, - grayIndexed); - return; - } - if (overprintFlag && overprintPreview) { - mask = colorSpace->getOverprintMask(); - if (singleColor && overprintMode && - colorSpace->getMode() == csDeviceCMYK) { - colorSpace->getCMYK(singleColor, &cmyk); - if (cmyk.c == 0) { - mask &= ~1; - } - if (cmyk.m == 0) { - mask &= ~2; - } - if (cmyk.y == 0) { - mask &= ~4; - } - if (cmyk.k == 0) { - mask &= ~8; - } - } - if (grayIndexed) { - mask &= ~7; - } else if (colorSpace->getMode() == csSeparation) { - GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace; - additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking(); - } else if (colorSpace->getMode() == csDeviceN) { - GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace; - additive = mask == 0x0f && !deviceNCS->isNonMarking(); - for (i = 0; i < deviceNCS->getNComps() && additive; i++) { - if (deviceNCS->getColorantName(i)->cmp("Cyan") == 0) { - additive = gFalse; - } else if (deviceNCS->getColorantName(i)->cmp("Magenta") == 0) { - additive = gFalse; - } else if (deviceNCS->getColorantName(i)->cmp("Yellow") == 0) { - additive = gFalse; - } else if (deviceNCS->getColorantName(i)->cmp("Black") == 0) { - additive = gFalse; - } - } - } - } else { - mask = 0xffffffff; - } - splash->setOverprintMask(mask, additive); -#endif -} - -void SplashOutputDev::updateBlendMode(GfxState *state) { - splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]); -} - -void SplashOutputDev::updateFillOpacity(GfxState *state) { - splash->setFillAlpha((SplashCoord)state->getFillOpacity()); - if (transpGroupStack != nullptr && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) { - transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity(); - } -} - -void SplashOutputDev::updateStrokeOpacity(GfxState *state) { - splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); - if (transpGroupStack != nullptr && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) { - transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity(); - } -} - -void SplashOutputDev::updatePatternOpacity(GfxState *state) { - splash->setPatternAlpha((SplashCoord)state->getStrokeOpacity(), (SplashCoord)state->getFillOpacity()); -} - -void SplashOutputDev::clearPatternOpacity(GfxState *state) { - splash->clearPatternAlpha(); -} - -void SplashOutputDev::updateFillOverprint(GfxState *state) { - splash->setFillOverprint(state->getFillOverprint()); -} - -void SplashOutputDev::updateStrokeOverprint(GfxState *state) { - splash->setStrokeOverprint(state->getStrokeOverprint()); -} - -void SplashOutputDev::updateOverprintMode(GfxState *state) { - splash->setOverprintMode(state->getOverprintMode()); -} - -void SplashOutputDev::updateTransfer(GfxState *state) { - Function **transfer; - Guchar red[256], green[256], blue[256], gray[256]; - double x, y; - int i; - - transfer = state->getTransfer(); - if (transfer[0] && - transfer[0]->getInputSize() == 1 && - transfer[0]->getOutputSize() == 1) { - if (transfer[1] && - transfer[1]->getInputSize() == 1 && - transfer[1]->getOutputSize() == 1 && - transfer[2] && - transfer[2]->getInputSize() == 1 && - transfer[2]->getOutputSize() == 1 && - transfer[3] && - transfer[3]->getInputSize() == 1 && - transfer[3]->getOutputSize() == 1) { - for (i = 0; i < 256; ++i) { - x = i / 255.0; - transfer[0]->transform(&x, &y); - red[i] = (Guchar)(y * 255.0 + 0.5); - transfer[1]->transform(&x, &y); - green[i] = (Guchar)(y * 255.0 + 0.5); - transfer[2]->transform(&x, &y); - blue[i] = (Guchar)(y * 255.0 + 0.5); - transfer[3]->transform(&x, &y); - gray[i] = (Guchar)(y * 255.0 + 0.5); - } - } else { - for (i = 0; i < 256; ++i) { - x = i / 255.0; - transfer[0]->transform(&x, &y); - red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5); - } - } - } else { - for (i = 0; i < 256; ++i) { - red[i] = green[i] = blue[i] = gray[i] = (Guchar)i; - } - } - splash->setTransfer(red, green, blue, gray); -} - -void SplashOutputDev::updateFont(GfxState * /*state*/) { - needFontUpdate = gTrue; -} - -void SplashOutputDev::doUpdateFont(GfxState *state) { - GfxFont *gfxFont; - GfxFontLoc *fontLoc; - GfxFontType fontType; - SplashOutFontFileID *id = nullptr; - SplashFontFile *fontFile; - SplashFontSrc *fontsrc = nullptr; - FoFiTrueType *ff; - GooString *fileName; - char *tmpBuf; - int tmpBufLen; - int *codeToGID; - double *textMat; - double m11, m12, m21, m22, fontSize; - int faceIndex = 0; - SplashCoord mat[4]; - int n, i; - GBool recreateFont = gFalse; - GBool doAdjustFontMatrix = gFalse; - - needFontUpdate = gFalse; - font = nullptr; - fileName = nullptr; - tmpBuf = nullptr; - fontLoc = nullptr; - - if (!(gfxFont = state->getFont())) { - goto err1; - } - fontType = gfxFont->getType(); - if (fontType == fontType3) { - goto err1; - } - - // sanity-check the font size - skip anything larger than 10 inches - // (this avoids problems allocating memory for the font cache) - if (state->getTransformedFontSize() - > 10 * (state->getHDPI() + state->getVDPI())) { - goto err1; - } - - // check the font file cache -reload: - delete id; - delete fontLoc; - fontLoc = nullptr; - if (fontsrc && !fontsrc->isFile) { - fontsrc->unref(); - fontsrc = nullptr; - } - - id = new SplashOutFontFileID(gfxFont->getID()); - if ((fontFile = fontEngine->getFontFile(id))) { - delete id; - - } else { - - if (!(fontLoc = gfxFont->locateFont((xref) ? xref : doc->getXRef(), nullptr))) { - error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - goto err2; - } - - // embedded font - if (fontLoc->locType == gfxFontLocEmbedded) { - // if there is an embedded font, read it to memory - tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef(), &tmpBufLen); - if (! tmpBuf) - goto err2; - - // external font - } else { // gfxFontLocExternal - fileName = fontLoc->path; - fontType = fontLoc->fontType; - doAdjustFontMatrix = gTrue; - } - - fontsrc = new SplashFontSrc; - if (fileName) - fontsrc->setFile(fileName, gFalse); - else - fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue); - - // load the font file - switch (fontType) { - case fontType1: - if (!(fontFile = fontEngine->loadType1Font( - id, - fontsrc, - (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontType1C: - if (!(fontFile = fontEngine->loadType1CFont( - id, - fontsrc, - (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontType1COT: - if (!(fontFile = fontEngine->loadOpenTypeT1CFont( - id, - fontsrc, - (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontTrueType: - case fontTrueTypeOT: - if (fileName) - ff = FoFiTrueType::load(fileName->getCString()); - else - ff = FoFiTrueType::make(tmpBuf, tmpBufLen); - if (ff) { - codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); - n = 256; - delete ff; - // if we're substituting for a non-TrueType font, we need to mark - // all notdef codes as "do not draw" (rather than drawing TrueType - // notdef glyphs) - if (gfxFont->getType() != fontTrueType && - gfxFont->getType() != fontTrueTypeOT) { - for (i = 0; i < 256; ++i) { - if (codeToGID[i] == 0) { - codeToGID[i] = -1; - } - } - } - } else { - codeToGID = nullptr; - n = 0; - } - if (!(fontFile = fontEngine->loadTrueTypeFont( - id, - fontsrc, - codeToGID, n))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontCIDType0: - case fontCIDType0C: - if (!(fontFile = fontEngine->loadCIDFont( - id, - fontsrc))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontCIDType0COT: - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), - n * sizeof(int)); - } else { - codeToGID = nullptr; - n = 0; - } - if (!(fontFile = fontEngine->loadOpenTypeCFFFont( - id, - fontsrc, - codeToGID, n))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontCIDType2: - case fontCIDType2OT: - codeToGID = nullptr; - n = 0; - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - if (n) { - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), - n * sizeof(int)); - } - } else { - if (fileName) - ff = FoFiTrueType::load(fileName->getCString()); - else - ff = FoFiTrueType::make(tmpBuf, tmpBufLen); - if (! ff) - { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - goto err2; - } - codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n); - delete ff; - } - if (!(fontFile = fontEngine->loadTrueTypeFont( - id, - fontsrc, - codeToGID, n, faceIndex))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - default: - // this shouldn't happen - goto err2; - } - fontFile->doAdjustMatrix = doAdjustFontMatrix; - } - - // get the font matrix - textMat = state->getTextMat(); - fontSize = state->getFontSize(); - m11 = textMat[0] * fontSize * state->getHorizScaling(); - m12 = textMat[1] * fontSize * state->getHorizScaling(); - m21 = textMat[2] * fontSize; - m22 = textMat[3] * fontSize; - - // create the scaled font - mat[0] = m11; mat[1] = m12; - mat[2] = m21; mat[3] = m22; - font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); - - // for substituted fonts: adjust the font matrix -- compare the - // width of 'm' in the original font and the substituted font - if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) { - double w1, w2, w3; - CharCode code; - char *name; - for (code = 0; code < 256; ++code) { - if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && - name[0] == 'm' && name[1] == '\0') { - break; - } - } - if (code < 256) { - w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); - w2 = font->getGlyphAdvance(code); - w3 = ((Gfx8BitFont *)gfxFont)->getWidth(0); - if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) { - // if real font is substantially narrower than substituted - // font, reduce the font size accordingly - if (w1 > 0.01 && w1 < 0.9 * w2) { - w1 /= w2; - m11 *= w1; - m21 *= w1; - recreateFont = gTrue; - } - } - } - } - - if (recreateFont) - { - mat[0] = m11; mat[1] = m12; - mat[2] = m21; mat[3] = m22; - font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); - } - - delete fontLoc; - if (fontsrc && !fontsrc->isFile) - fontsrc->unref(); - return; - - err2: - delete id; - delete fontLoc; - err1: - if (fontsrc && !fontsrc->isFile) - fontsrc->unref(); - return; -} - -void SplashOutputDev::stroke(GfxState *state) { - SplashPath *path; - - if (state->getStrokeColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), - state->getOverprintMode(), state->getStrokeColor()); - path = convertPath(state, state->getPath(), gFalse); - splash->stroke(path); - delete path; -} - -void SplashOutputDev::fill(GfxState *state) { - SplashPath *path; - - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - path = convertPath(state, state->getPath(), gTrue); - splash->fill(path, gFalse); - delete path; -} - -void SplashOutputDev::eoFill(GfxState *state) { - SplashPath *path; - - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - path = convertPath(state, state->getPath(), gTrue); - splash->fill(path, gTrue); - delete path; -} - -void SplashOutputDev::clip(GfxState *state) { - SplashPath *path; - - path = convertPath(state, state->getPath(), gTrue); - splash->clipToPath(path, gFalse); - delete path; -} - -void SplashOutputDev::eoClip(GfxState *state) { - SplashPath *path; - - path = convertPath(state, state->getPath(), gTrue); - splash->clipToPath(path, gTrue); - delete path; -} - -void SplashOutputDev::clipToStrokePath(GfxState *state) { - SplashPath *path, *path2; - - path = convertPath(state, state->getPath(), gFalse); - path2 = splash->makeStrokePath(path, state->getLineWidth()); - delete path; - splash->clipToPath(path2, gFalse); - delete path2; -} - -SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path, - GBool dropEmptySubpaths) { - SplashPath *sPath; - GfxSubpath *subpath; - int n, i, j; - - n = dropEmptySubpaths ? 1 : 0; - sPath = new SplashPath(); - for (i = 0; i < path->getNumSubpaths(); ++i) { - subpath = path->getSubpath(i); - if (subpath->getNumPoints() > n) { - sPath->reserve(subpath->getNumPoints() + 1); - sPath->moveTo((SplashCoord)subpath->getX(0), - (SplashCoord)subpath->getY(0)); - j = 1; - while (j < subpath->getNumPoints()) { - if (subpath->getCurve(j)) { - sPath->curveTo((SplashCoord)subpath->getX(j), - (SplashCoord)subpath->getY(j), - (SplashCoord)subpath->getX(j+1), - (SplashCoord)subpath->getY(j+1), - (SplashCoord)subpath->getX(j+2), - (SplashCoord)subpath->getY(j+2)); - j += 3; - } else { - sPath->lineTo((SplashCoord)subpath->getX(j), - (SplashCoord)subpath->getY(j)); - ++j; - } - } - if (subpath->isClosed()) { - sPath->close(); - } - } - } - return sPath; -} - -void SplashOutputDev::drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, - Unicode *u, int uLen) { - SplashPath *path; - int render; - GBool doFill, doStroke, doClip, strokeAdjust; - double m[4]; - GBool horiz; - - if (skipHorizText || skipRotatedText) { - state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); - horiz = m[0] > 0 && fabs(m[1]) < 0.001 && - fabs(m[2]) < 0.001 && m[3] < 0; - if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { - return; - } - } - - // check for invisible text -- this is used by Acrobat Capture - render = state->getRender(); - if (render == 3) { - return; - } - - if (needFontUpdate) { - doUpdateFont(state); - } - if (!font) { - return; - } - - x -= originX; - y -= originY; - - doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); - doStroke = ((render & 3) == 1 || (render & 3) == 2) && - !state->getStrokeColorSpace()->isNonMarking(); - doClip = render & 4; - - path = nullptr; - SplashCoord lineWidth = splash->getLineWidth(); - if (doStroke && lineWidth == 0.0) - splash->setLineWidth(1 / state->getVDPI()); - if (doStroke || doClip) { - if ((path = font->getGlyphPath(code))) { - path->offset((SplashCoord)x, (SplashCoord)y); - } - } - - // don't use stroke adjustment when stroking text -- the results - // tend to be ugly (because characters with horizontal upper or - // lower edges get misaligned relative to the other characters) - strokeAdjust = gFalse; // make gcc happy - if (doStroke) { - strokeAdjust = splash->getStrokeAdjust(); - splash->setStrokeAdjust(gFalse); - } - - // fill and stroke - if (doFill && doStroke) { - if (path) { - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - splash->fill(path, gFalse); - setOverprintMask(state->getStrokeColorSpace(), - state->getStrokeOverprint(), - state->getOverprintMode(), - state->getStrokeColor()); - splash->stroke(path); - } - - // fill - } else if (doFill) { - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); - - // stroke - } else if (doStroke) { - if (path) { - setOverprintMask(state->getStrokeColorSpace(), - state->getStrokeOverprint(), - state->getOverprintMode(), - state->getStrokeColor()); - splash->stroke(path); - } - } - splash->setLineWidth(lineWidth); - - // clip - if (doClip) { - if (path) { - if (textClipPath) { - textClipPath->append(path); - } else { - textClipPath = path; - path = nullptr; - } - } - } - - if (doStroke) { - splash->setStrokeAdjust(strokeAdjust); - } - - if (path) { - delete path; - } -} - -GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, - double dx, double dy, - CharCode code, Unicode *u, int uLen) { - GfxFont *gfxFont; - const Ref *fontID; - double *ctm, *bbox; - T3FontCache *t3Font; - T3GlyphStack *t3gs; - GBool validBBox; - double m[4]; - GBool horiz; - double x1, y1, xMin, yMin, xMax, yMax, xt, yt; - int i, j; - - // check for invisible text -- this is used by Acrobat Capture - if (state->getRender() == 3) { - // this is a bit of cheating, we say yes, font is already on cache - // so we actually skip the rendering of it - return gTrue; - } - - if (skipHorizText || skipRotatedText) { - state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); - horiz = m[0] > 0 && fabs(m[1]) < 0.001 && - fabs(m[2]) < 0.001 && m[3] < 0; - if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { - return gTrue; - } - } - - if (!(gfxFont = state->getFont())) { - return gFalse; - } - fontID = gfxFont->getID(); - ctm = state->getCTM(); - state->transform(0, 0, &xt, &yt); - - // is it the first (MRU) font in the cache? - if (!(nT3Fonts > 0 && - t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { - - // is the font elsewhere in the cache? - for (i = 1; i < nT3Fonts; ++i) { - if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { - t3Font = t3FontCache[i]; - for (j = i; j > 0; --j) { - t3FontCache[j] = t3FontCache[j - 1]; - } - t3FontCache[0] = t3Font; - break; - } - } - if (i >= nT3Fonts) { - - // create new entry in the font cache - if (nT3Fonts == splashOutT3FontCacheSize) { - t3gs = t3GlyphStack; - while (t3gs != nullptr) { - if (t3gs->cache == t3FontCache[nT3Fonts - 1]) { - error(errSyntaxWarning, -1, "t3FontCache reaches limit but font still on stack in SplashOutputDev::beginType3Char"); - return gTrue; - } - t3gs = t3gs->next; - } - delete t3FontCache[nT3Fonts - 1]; - --nT3Fonts; - } - for (j = nT3Fonts; j > 0; --j) { - t3FontCache[j] = t3FontCache[j - 1]; - } - ++nT3Fonts; - bbox = gfxFont->getFontBBox(); - if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { - // unspecified bounding box -- just take a guess - xMin = xt - 5; - xMax = xMin + 30; - yMax = yt + 15; - yMin = yMax - 45; - validBBox = gFalse; - } else { - state->transform(bbox[0], bbox[1], &x1, &y1); - xMin = xMax = x1; - yMin = yMax = y1; - state->transform(bbox[0], bbox[3], &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(bbox[2], bbox[1], &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(bbox[2], bbox[3], &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - validBBox = gTrue; - } - t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], - (int)floor(xMin - xt) - 2, - (int)floor(yMin - yt) - 2, - (int)ceil(xMax) - (int)floor(xMin) + 4, - (int)ceil(yMax) - (int)floor(yMin) + 4, - validBBox, - colorMode != splashModeMono1); - } - } - t3Font = t3FontCache[0]; - - // is the glyph in the cache? - i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; - for (j = 0; j < t3Font->cacheAssoc; ++j) { - if (t3Font->cacheTags != nullptr) { - if ((t3Font->cacheTags[i+j].mru & 0x8000) && - t3Font->cacheTags[i+j].code == code) { - drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j], - t3Font->cacheData + (i+j) * t3Font->glyphSize); - return gTrue; - } - } - } - - // push a new Type 3 glyph record - t3gs = new T3GlyphStack(); - t3gs->next = t3GlyphStack; - t3GlyphStack = t3gs; - t3GlyphStack->code = code; - t3GlyphStack->cache = t3Font; - t3GlyphStack->cacheTag = nullptr; - t3GlyphStack->cacheData = nullptr; - t3GlyphStack->haveDx = gFalse; - t3GlyphStack->doNotCache = gFalse; - - return gFalse; -} - -void SplashOutputDev::endType3Char(GfxState *state) { - T3GlyphStack *t3gs; - double *ctm; - - if (t3GlyphStack->cacheTag) { - --nestCount; - memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), - t3GlyphStack->cache->glyphSize); - delete bitmap; - delete splash; - bitmap = t3GlyphStack->origBitmap; - splash = t3GlyphStack->origSplash; - ctm = state->getCTM(); - state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], - t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); - updateCTM(state, 0, 0, 0, 0, 0, 0); - drawType3Glyph(state, t3GlyphStack->cache, - t3GlyphStack->cacheTag, t3GlyphStack->cacheData); - } - t3gs = t3GlyphStack; - t3GlyphStack = t3gs->next; - delete t3gs; -} - -void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { - if (likely(t3GlyphStack != nullptr)) { - t3GlyphStack->haveDx = gTrue; - } else { - error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D0"); - } -} - -void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury) { - double *ctm; - T3FontCache *t3Font; - SplashColor color; - double xt, yt, xMin, xMax, yMin, yMax, x1, y1; - int i, j; - - // ignore multiple d0/d1 operators - if (!t3GlyphStack || t3GlyphStack->haveDx) { - return; - } - t3GlyphStack->haveDx = gTrue; - // don't cache if we got a gsave/grestore before the d1 - if (t3GlyphStack->doNotCache) { - return; - } - - if (unlikely(t3GlyphStack == nullptr)) { - error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1"); - return; - } - - if (unlikely(t3GlyphStack->origBitmap != nullptr)) { - error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1"); - return; - } - - if (unlikely(t3GlyphStack->origSplash != nullptr)) { - error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1"); - return; - } - - t3Font = t3GlyphStack->cache; - - // check for a valid bbox - state->transform(0, 0, &xt, &yt); - state->transform(llx, lly, &x1, &y1); - xMin = xMax = x1; - yMin = yMax = y1; - state->transform(llx, ury, &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(urx, lly, &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(urx, ury, &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - if (xMin - xt < t3Font->glyphX || - yMin - yt < t3Font->glyphY || - xMax - xt > t3Font->glyphX + t3Font->glyphW || - yMax - yt > t3Font->glyphY + t3Font->glyphH) { - if (t3Font->validBBox) { - error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph"); - } - return; - } - - if (t3Font->cacheTags == nullptr) - return; - - // allocate a cache entry - i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; - for (j = 0; j < t3Font->cacheAssoc; ++j) { - if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { - t3Font->cacheTags[i+j].mru = 0x8000; - t3Font->cacheTags[i+j].code = t3GlyphStack->code; - t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; - t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; - } else { - ++t3Font->cacheTags[i+j].mru; - } - } - - // save state - t3GlyphStack->origBitmap = bitmap; - t3GlyphStack->origSplash = splash; - ctm = state->getCTM(); - t3GlyphStack->origCTM4 = ctm[4]; - t3GlyphStack->origCTM5 = ctm[5]; - - // create the temporary bitmap - if (colorMode == splashModeMono1) { - bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono1, gFalse); - splash = new Splash(bitmap, gFalse, - t3GlyphStack->origSplash->getScreen()); - color[0] = 0; - splash->clear(color); - color[0] = 0xff; - } else { - bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono8, gFalse); - splash = new Splash(bitmap, vectorAntialias, - t3GlyphStack->origSplash->getScreen()); - color[0] = 0x00; - splash->clear(color); - color[0] = 0xff; - } - splash->setMinLineWidth(s_minLineWidth); - splash->setThinLineMode(splashThinLineDefault); - splash->setFillPattern(new SplashSolidColor(color)); - splash->setStrokePattern(new SplashSolidColor(color)); - //~ this should copy other state from t3GlyphStack->origSplash? - state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], - -t3Font->glyphX, -t3Font->glyphY); - updateCTM(state, 0, 0, 0, 0, 0, 0); - ++nestCount; -} - -void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, - T3FontCacheTag * /*tag*/, Guchar *data) { - SplashGlyphBitmap glyph; - - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - glyph.x = -t3Font->glyphX; - glyph.y = -t3Font->glyphY; - glyph.w = t3Font->glyphW; - glyph.h = t3Font->glyphH; - glyph.aa = colorMode != splashModeMono1; - glyph.data = data; - glyph.freeData = gFalse; - splash->fillGlyph(0, 0, &glyph); -} - -void SplashOutputDev::beginTextObject(GfxState *state) { -} - -void SplashOutputDev::endTextObject(GfxState *state) { - if (textClipPath) { - splash->clipToPath(textClipPath, gFalse); - delete textClipPath; - textClipPath = nullptr; - } -} - -struct SplashOutImageMaskData { - ImageStream *imgStr; - GBool invert; - int width, height, y; -}; - -GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { - SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; - Guchar *p; - SplashColorPtr q; - int x; - - if (imgMaskData->y == imgMaskData->height) { - return gFalse; - } - if (!(p = imgMaskData->imgStr->getLine())) { - return gFalse; - } - for (x = 0, q = line; x < imgMaskData->width; ++x) { - *q++ = *p++ ^ imgMaskData->invert; - } - ++imgMaskData->y; - return gTrue; -} - -void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool interpolate, GBool inlineImg) { - double *ctm; - SplashCoord mat[6]; - SplashOutImageMaskData imgMaskData; - - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - - ctm = state->getCTM(); - for (int i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - imgMaskData.imgStr = new ImageStream(str, width, 1, 1); - imgMaskData.imgStr->reset(); - imgMaskData.invert = invert ? 0 : 1; - imgMaskData.width = width; - imgMaskData.height = height; - imgMaskData.y = 0; - - splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); - if (inlineImg) { - while (imgMaskData.y < height) { - imgMaskData.imgStr->getLine(); - ++imgMaskData.y; - } - } - - delete imgMaskData.imgStr; - str->close(); -} - -void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, - Object *ref, Stream *str, - int width, int height, - GBool invert, - GBool inlineImg, double *baseMatrix) { - double *ctm; - SplashCoord mat[6]; - SplashOutImageMaskData imgMaskData; - Splash *maskSplash; - SplashColor maskColor; - double bbox[4] = {0, 0, 1, 1}; // default; - - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - - ctm = state->getCTM(); - for (int i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - - beginTransparencyGroup(state, bbox, nullptr, gFalse, gFalse, gFalse); - baseMatrix[4] -= transpGroupStack->tx; - baseMatrix[5] -= transpGroupStack->ty; - - ctm = state->getCTM(); - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - imgMaskData.imgStr = new ImageStream(str, width, 1, 1); - imgMaskData.imgStr->reset(); - imgMaskData.invert = invert ? 0 : 1; - imgMaskData.width = width; - imgMaskData.height = height; - imgMaskData.y = 0; - - transpGroupStack->softmask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); - maskSplash = new Splash(transpGroupStack->softmask, vectorAntialias); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskColor[0] = 0xff; - maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); - delete maskSplash; - delete imgMaskData.imgStr; - str->close(); -} - -void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { - double bbox[4] = {0,0,1,1}; // dummy - - /* transfer mask to alpha channel! */ - // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight()); - // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight()); - if (transpGroupStack->softmask != nullptr) { - Guchar *dest = bitmap->getAlphaPtr(); - Guchar *src = transpGroupStack->softmask->getDataPtr(); - for (int c= 0; c < transpGroupStack->softmask->getRowSize() * transpGroupStack->softmask->getHeight(); c++) { - dest[c] = src[c]; - } - delete transpGroupStack->softmask; - transpGroupStack->softmask = nullptr; - } - endTransparencyGroup(state); - baseMatrix[4] += transpGroupStack->tx; - baseMatrix[5] += transpGroupStack->ty; - paintTransparencyGroup(state, bbox); -} - -struct SplashOutImageData { - ImageStream *imgStr; - GfxImageColorMap *colorMap; - SplashColorPtr lookup; - int *maskColors; - SplashColorMode colorMode; - int width, height, y; - ImageStream *maskStr; - GfxImageColorMap *maskColorMap; - SplashColor matteColor; -}; - -#ifdef USE_CMS -GBool SplashOutputDev::useIccImageSrc(void *data) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - - if (!imgData->lookup && imgData->colorMap->getColorSpace()->getMode() == csICCBased) { - GfxICCBasedColorSpace *colorSpace = (GfxICCBasedColorSpace *) imgData->colorMap->getColorSpace(); - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceGray) - return gTrue; - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceRGB) - return gTrue; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceCMYK) - return gTrue; - break; -#endif - } - } - - return gFalse; -} -#endif - -// Clip x to lie in [0, 255]. -static inline Guchar clip255(int x) { - return x < 0 ? 0 : x > 255 ? 255 : x; -} - -GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, - Guchar * /*alphaLine*/) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - Guchar *p; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - int nComps, x; - - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { - int destComps = 1; - if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) - destComps = 3; - else if (imgData->colorMode == splashModeXBGR8) - destComps = 4; -#ifdef SPLASH_CMYK - else if (imgData->colorMode == splashModeCMYK8) - destComps = 4; - else if (imgData->colorMode == splashModeDeviceN8) - destComps = SPOT_NCOMPS + 4; -#endif - memset(colorLine, 0, imgData->width * destComps); - return gFalse; - } - - nComps = imgData->colorMap->getNumPixelComps(); - - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - *q++ = imgData->lookup[*p]; - } - break; - case splashModeRGB8: - case splashModeBGR8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - } - break; - case splashModeXBGR8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - } - break; - case splashModeDeviceN8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = col[cp]; - } - break; -#endif - } - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - if (imgData->colorMap->useRGBLine()) { - imgData->colorMap->getRGBLine(p, (Guchar *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - } - } - break; - case splashModeXBGR8: - if (imgData->colorMap->useRGBLine()) { - imgData->colorMap->getRGBXLine(p, (Guchar *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - *q++ = 255; - } - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - if (imgData->colorMap->useCMYKLine()) { - imgData->colorMap->getCMYKLine(p, (Guchar *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - } - } - break; - case splashModeDeviceN8: - if (imgData->colorMap->useDeviceNLine()) { - imgData->colorMap->getDeviceNLine(p, (Guchar *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getDeviceN(p, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = colToByte(deviceN.c[cp]); - } - } - break; -#endif - } - } - - if (imgData->maskStr != nullptr && (p = imgData->maskStr->getLine()) != nullptr) { - int destComps = splashColorModeNComps[imgData->colorMode]; - int convComps = (imgData->colorMode == splashModeXBGR8) ? 3 : destComps; - imgData->maskColorMap->getGrayLine(p, p, imgData->width); - for (x = 0, q = colorLine; x < imgData->width; ++x, p++, q += destComps) { - for (int cp = 0; cp < convComps; cp++) { - q[cp] = (*p) ? clip255(imgData->matteColor[cp] + (int) (q[cp] - imgData->matteColor[cp]) * 255 / *p) : imgData->matteColor[cp]; - } - } - } - ++imgData->y; - return gTrue; -} - -#ifdef USE_CMS -GBool SplashOutputDev::iccImageSrc(void *data, SplashColorPtr colorLine, - Guchar * /*alphaLine*/) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - Guchar *p; - int nComps; - - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { - int destComps = 1; - if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) - destComps = 3; - else if (imgData->colorMode == splashModeXBGR8) - destComps = 4; -#ifdef SPLASH_CMYK - else if (imgData->colorMode == splashModeCMYK8) - destComps = 4; - else if (imgData->colorMode == splashModeDeviceN8) - destComps = SPOT_NCOMPS + 4; -#endif - memset(colorLine, 0, imgData->width * destComps); - return gFalse; - } - - if (imgData->colorMode == splashModeXBGR8) { - SplashColorPtr q; - int x; - for (x = 0, q = colorLine; x < imgData->width; ++x) { - *q++ = *p++; - *q++ = *p++; - *q++ = *p++; - *q++ = 255; - } - } else { - nComps = imgData->colorMap->getNumPixelComps(); - memcpy(colorLine, p, imgData->width * nComps); - } - - ++imgData->y; - return gTrue; -} - -void SplashOutputDev::iccTransform(void *data, SplashBitmap *bitmap) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - int nComps = imgData->colorMap->getNumPixelComps(); - - Guchar *colorLine = (Guchar *) gmalloc(nComps * bitmap->getWidth()); - Guchar *rgbxLine = (imgData->colorMode == splashModeXBGR8) ? (Guchar *) gmalloc(3 * bitmap->getWidth()) : nullptr; - for (int i = 0; i < bitmap->getHeight(); i++) { - Guchar *p = bitmap->getDataPtr() + i * bitmap->getRowSize(); - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGrayLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; - case splashModeRGB8: - case splashModeBGR8: - imgData->colorMap->getRGBLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData->colorMap->getCMYKLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; -#endif - case splashModeXBGR8: - Guchar *q; - Guchar *b = p; - int x; - for (x = 0, q = rgbxLine; x < bitmap->getWidth(); ++x, b+=4) { - *q++ = b[2]; - *q++ = b[1]; - *q++ = b[0]; - } - imgData->colorMap->getRGBLine(rgbxLine, colorLine, bitmap->getWidth()); - b = p; - for (x = 0, q = colorLine; x < bitmap->getWidth(); ++x, b+=4) { - b[2] = *q++; - b[1] = *q++; - b[0] = *q++; - } - break; - } - } - gfree(colorLine); - if (rgbxLine != nullptr) - gfree(rgbxLine); -} -#endif - -GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, - Guchar *alphaLine) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - Guchar *p, *aq; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - Guchar alpha; - int nComps, x, i; - - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { - return gFalse; - } - - nComps = imgData->colorMap->getNumPixelComps(); - - for (x = 0, q = colorLine, aq = alphaLine; - x < imgData->width; - ++x, p += nComps) { - alpha = 0; - for (i = 0; i < nComps; ++i) { - if (p[i] < imgData->maskColors[2*i] || - p[i] > imgData->maskColors[2*i+1]) { - alpha = 0xff; - break; - } - } - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - *q++ = imgData->lookup[*p]; - break; - case splashModeRGB8: - case splashModeBGR8: - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - break; - case splashModeXBGR8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - break; - case splashModeDeviceN8: - col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = col[cp]; - break; -#endif - } - *aq++ = alpha; - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - if (imgData->colorMode == splashModeXBGR8) *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - imgData->colorMap->getDeviceN(p, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = colToByte(deviceN.c[cp]); - break; -#endif - } - *aq++ = alpha; - } - } - - ++imgData->y; - return gTrue; -} - -struct TilingSplashOutBitmap { - SplashBitmap *bitmap; - SplashPattern *pattern; - SplashColorMode colorMode; - int paintType; - int repeatX; - int repeatY; - int y; -}; - -GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine, - Guchar *alphaLine) { - TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data; - - if (imgData->y == imgData->bitmap->getHeight()) { - imgData->repeatY--; - if (imgData->repeatY == 0) - return gFalse; - imgData->y = 0; - } - - if (imgData->paintType == 1) { - const SplashColorMode cMode = imgData->bitmap->getMode(); - SplashColorPtr q = colorLine; - // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel - // for the others we can use raw access - if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) { - for (int m = 0; m < imgData->repeatX; m++) { - for (int x = 0; x < imgData->bitmap->getWidth(); x++) { - imgData->bitmap->getPixel(x, imgData->y, q); - q += splashColorModeNComps[cMode]; - } - } - } else { - const int n = imgData->bitmap->getRowSize(); - SplashColorPtr p; - for (int m = 0; m < imgData->repeatX; m++) { - p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize(); - for (int x = 0; x < n; ++x) { - *q++ = *p++; - } - } - } - if (alphaLine != nullptr) { - SplashColorPtr aq = alphaLine; - SplashColorPtr p; - const int n = imgData->bitmap->getWidth() - 1; - for (int m = 0; m < imgData->repeatX; m++) { - p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth(); - for (int x = 0; x < n; ++x) { - *aq++ = *p++; - } - // This is a hack, because of how Splash antialias works if we overwrite the - // last alpha pixel of the tile most/all of the files look much better - *aq++ = (n == 0) ? *p : *(p - 1); - } - } - } else { - SplashColor col, pat; - SplashColorPtr dest = colorLine; - for (int m = 0; m < imgData->repeatX; m++) { - for (int x = 0; x < imgData->bitmap->getWidth(); x++) { - imgData->bitmap->getPixel(x, imgData->y, col); - imgData->pattern->getColor(x, imgData->y, pat); - for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) { -#ifdef SPLASH_CMYK - if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8) - dest[i] = div255(pat[i] * (255 - col[0])); - else -#endif - dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0])); - } - dest += splashColorModeNComps[imgData->colorMode]; - } - } - if (alphaLine != nullptr) { - const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y; - SplashColorPtr aq = alphaLine; - SplashColorPtr p; - const int n = imgData->bitmap->getWidth(); - for (int m = 0; m < imgData->repeatX; m++) { - p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth(); - for (int x = 0; x < n; ++x) { - *aq++ = *p++; - } - } - } - } - ++imgData->y; - return gTrue; -} - -void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - int *maskColors, GBool inlineImg) { - double *ctm; - SplashCoord mat[6]; - SplashOutImageData imgData; - SplashColorMode srcMode; - SplashImageSource src; - SplashICCTransform tf; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GBool grayIndexed = gFalse; - GfxColor deviceN; -#endif - Guchar pix; - int n, i; - - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - imgData.imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgData.imgStr->reset(); - imgData.colorMap = colorMap; - imgData.maskColors = maskColors; - imgData.colorMode = colorMode; - imgData.width = width; - imgData.height = height; - imgData.maskStr = nullptr; - imgData.maskColorMap = nullptr; - imgData.y = 0; - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - imgData.lookup = nullptr; - if (colorMap->getNumPixelComps() == 1) { - n = 1 << colorMap->getBits(); - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData.lookup = (SplashColorPtr)gmalloc(n); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getGray(&pix, &gray); - imgData.lookup[i] = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 3); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[3*i] = colToByte(rgb.r); - imgData.lookup[3*i+1] = colToByte(rgb.g); - imgData.lookup[3*i+2] = colToByte(rgb.b); - } - break; - case splashModeXBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[4*i] = colToByte(rgb.r); - imgData.lookup[4*i+1] = colToByte(rgb.g); - imgData.lookup[4*i+2] = colToByte(rgb.b); - imgData.lookup[4*i+3] = 255; - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getCMYK(&pix, &cmyk); - if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { - grayIndexed = gFalse; - } - imgData.lookup[4*i] = colToByte(cmyk.c); - imgData.lookup[4*i+1] = colToByte(cmyk.m); - imgData.lookup[4*i+2] = colToByte(cmyk.y); - imgData.lookup[4*i+3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); - grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; - imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getCMYK(&pix, &cmyk); - if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { - grayIndexed = gFalse; - } - colorMap->getDeviceN(&pix, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - imgData.lookup[(SPOT_NCOMPS+4)*i +cp] = colToByte(deviceN.c[cp]); - } - break; -#endif - } - } - -#ifdef SPLASH_CMYK - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), NULL, grayIndexed); -#else - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); -#endif - - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else { - srcMode = colorMode; - } -#ifdef USE_CMS - src = maskColors ? &alphaImageSrc : useIccImageSrc(&imgData) ? &iccImageSrc : &imageSrc; - tf = maskColors == nullptr && useIccImageSrc(&imgData) ? &iccTransform : nullptr; -#else - src = maskColors ? &alphaImageSrc : &imageSrc; - tf = NULL; -#endif - splash->drawImage(src, tf, &imgData, srcMode, maskColors ? gTrue : gFalse, - width, height, mat, interpolate); - if (inlineImg) { - while (imgData.y < height) { - imgData.imgStr->getLine(); - ++imgData.y; - } - } - - gfree(imgData.lookup); - delete imgData.imgStr; - str->close(); -} - -struct SplashOutMaskedImageData { - ImageStream *imgStr; - GfxImageColorMap *colorMap; - SplashBitmap *mask; - SplashColorPtr lookup; - SplashColorMode colorMode; - int width, height, y; -}; - -GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, - Guchar *alphaLine) { - SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; - Guchar *p, *aq; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - Guchar alpha; - Guchar *maskPtr; - int maskBit; - int nComps, x; - - if (imgData->y == imgData->height) { - return gFalse; - } - if (!(p = imgData->imgStr->getLine())) { - return gFalse; - } - - nComps = imgData->colorMap->getNumPixelComps(); - - maskPtr = imgData->mask->getDataPtr() + - imgData->y * imgData->mask->getRowSize(); - maskBit = 0x80; - for (x = 0, q = colorLine, aq = alphaLine; - x < imgData->width; - ++x, p += nComps) { - alpha = (*maskPtr & maskBit) ? 0xff : 0x00; - if (!(maskBit >>= 1)) { - ++maskPtr; - maskBit = 0x80; - } - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - *q++ = imgData->lookup[*p]; - break; - case splashModeRGB8: - case splashModeBGR8: - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - break; - case splashModeXBGR8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - break; - case splashModeDeviceN8: - col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = col[cp]; - break; -#endif - } - *aq++ = alpha; - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - if (imgData->colorMode == splashModeXBGR8) *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - imgData->colorMap->getDeviceN(p, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = colToByte(deviceN.c[cp]); - break; -#endif - } - *aq++ = alpha; - } - } - - ++imgData->y; - return gTrue; -} - -void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, - Stream *str, int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, int maskWidth, - int maskHeight, GBool maskInvert, - GBool maskInterpolate) { - GfxImageColorMap *maskColorMap; - double *ctm; - SplashCoord mat[6]; - SplashOutMaskedImageData imgData; - SplashOutImageMaskData imgMaskData; - SplashColorMode srcMode; - SplashBitmap *maskBitmap; - Splash *maskSplash; - SplashColor maskColor; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - Guchar pix; - int n, i; - -#ifdef SPLASH_CMYK - colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - - // If the mask is higher resolution than the image, use - // drawSoftMaskedImage() instead. - if (maskWidth > width || maskHeight > height) { - Object maskDecode(new Array((xref) ? xref : doc->getXRef())); - maskDecode.arrayAdd(Object(maskInvert ? 0 : 1)); - maskDecode.arrayAdd(Object(maskInvert ? 1 : 0)); - maskColorMap = new GfxImageColorMap(1, &maskDecode, - new GfxDeviceGrayColorSpace()); - drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, - maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); - delete maskColorMap; - - } else { - //----- scale the mask image to the same size as the source image - - mat[0] = (SplashCoord)width; - mat[1] = 0; - mat[2] = 0; - mat[3] = (SplashCoord)height; - mat[4] = 0; - mat[5] = 0; - imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); - imgMaskData.imgStr->reset(); - imgMaskData.invert = maskInvert ? 0 : 1; - imgMaskData.width = maskWidth; - imgMaskData.height = maskHeight; - imgMaskData.y = 0; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse); - if (!maskBitmap->getDataPtr()) { - delete maskBitmap; - width = height = 1; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse); - } - maskSplash = new Splash(maskBitmap, gFalse); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskColor[0] = 0xff; - maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, - maskWidth, maskHeight, mat, gFalse); - delete imgMaskData.imgStr; - maskStr->close(); - delete maskSplash; - - //----- draw the source image - - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) { - delete maskBitmap; - return; - } - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - imgData.imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgData.imgStr->reset(); - imgData.colorMap = colorMap; - imgData.mask = maskBitmap; - imgData.colorMode = colorMode; - imgData.width = width; - imgData.height = height; - imgData.y = 0; - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - imgData.lookup = nullptr; - if (colorMap->getNumPixelComps() == 1) { - n = 1 << colorMap->getBits(); - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData.lookup = (SplashColorPtr)gmalloc(n); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getGray(&pix, &gray); - imgData.lookup[i] = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 3); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[3*i] = colToByte(rgb.r); - imgData.lookup[3*i+1] = colToByte(rgb.g); - imgData.lookup[3*i+2] = colToByte(rgb.b); - } - break; - case splashModeXBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[4*i] = colToByte(rgb.r); - imgData.lookup[4*i+1] = colToByte(rgb.g); - imgData.lookup[4*i+2] = colToByte(rgb.b); - imgData.lookup[4*i+3] = 255; - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getCMYK(&pix, &cmyk); - imgData.lookup[4*i] = colToByte(cmyk.c); - imgData.lookup[4*i+1] = colToByte(cmyk.m); - imgData.lookup[4*i+2] = colToByte(cmyk.y); - imgData.lookup[4*i+3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getDeviceN(&pix, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]); - } - break; -#endif - } - } - - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else { - srcMode = colorMode; - } - splash->drawImage(&maskedImageSrc, nullptr, &imgData, srcMode, gTrue, - width, height, mat, interpolate); - delete maskBitmap; - gfree(imgData.lookup); - delete imgData.imgStr; - str->close(); - } -} - -void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, - Stream *str, int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - GBool maskInterpolate) { - double *ctm; - SplashCoord mat[6]; - SplashOutImageData imgData; - SplashOutImageData imgMaskData; - SplashColorMode srcMode; - SplashBitmap *maskBitmap; - Splash *maskSplash; - SplashColor maskColor; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - Guchar pix; - int n, i; - -#ifdef SPLASH_CMYK - colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - //----- set up the soft mask - - if (maskColorMap->getMatteColor() != nullptr) { - const int maskChars = maskWidth * maskHeight; - Guchar *data = (Guchar *) gmalloc(maskChars); - maskStr->reset(); - const int readChars = maskStr->doGetChars(maskChars, data); - if (unlikely(readChars < maskChars)) { - memset(&data[readChars], 0, maskChars - readChars); - } - maskStr->close(); - maskStr = new AutoFreeMemStream((char *)data, 0, maskChars, maskStr->getDictObject()->copy()); - } - imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, - maskColorMap->getNumPixelComps(), - maskColorMap->getBits()); - imgMaskData.imgStr->reset(); - imgMaskData.colorMap = maskColorMap; - imgMaskData.maskColors = nullptr; - imgMaskData.colorMode = splashModeMono8; - imgMaskData.width = maskWidth; - imgMaskData.height = maskHeight; - imgMaskData.y = 0; - imgMaskData.maskStr = nullptr; - imgMaskData.maskColorMap = nullptr; - n = 1 << maskColorMap->getBits(); - imgMaskData.lookup = (SplashColorPtr)gmalloc(n); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - maskColorMap->getGray(&pix, &gray); - imgMaskData.lookup[i] = colToByte(gray); - } - maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, gFalse); - maskSplash = new Splash(maskBitmap, vectorAntialias); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskSplash->drawImage(&imageSrc, nullptr, &imgMaskData, splashModeMono8, gFalse, - maskWidth, maskHeight, mat, maskInterpolate); - delete imgMaskData.imgStr; - if (maskColorMap->getMatteColor() == nullptr) { - maskStr->close(); - } - gfree(imgMaskData.lookup); - delete maskSplash; - splash->setSoftMask(maskBitmap); - - //----- draw the source image - - imgData.imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgData.imgStr->reset(); - imgData.colorMap = colorMap; - imgData.maskColors = nullptr; - imgData.colorMode = colorMode; - imgData.width = width; - imgData.height = height; - imgData.maskStr = nullptr; - imgData.maskColorMap = nullptr; - if (maskColorMap->getMatteColor() != nullptr) { - getMatteColor(colorMode, colorMap, maskColorMap->getMatteColor(), imgData.matteColor); - imgData.maskColorMap = maskColorMap; - imgData.maskStr = new ImageStream(maskStr, maskWidth, - maskColorMap->getNumPixelComps(), - maskColorMap->getBits()); - imgData.maskStr->reset(); - } - imgData.y = 0; - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - imgData.lookup = nullptr; - if (colorMap->getNumPixelComps() == 1) { - n = 1 << colorMap->getBits(); - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData.lookup = (SplashColorPtr)gmalloc(n); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getGray(&pix, &gray); - imgData.lookup[i] = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 3); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[3*i] = colToByte(rgb.r); - imgData.lookup[3*i+1] = colToByte(rgb.g); - imgData.lookup[3*i+2] = colToByte(rgb.b); - } - break; - case splashModeXBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[4*i] = colToByte(rgb.r); - imgData.lookup[4*i+1] = colToByte(rgb.g); - imgData.lookup[4*i+2] = colToByte(rgb.b); - imgData.lookup[4*i+3] = 255; - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getCMYK(&pix, &cmyk); - imgData.lookup[4*i] = colToByte(cmyk.c); - imgData.lookup[4*i+1] = colToByte(cmyk.m); - imgData.lookup[4*i+2] = colToByte(cmyk.y); - imgData.lookup[4*i+3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getDeviceN(&pix, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]); - } - break; -#endif - } - } - - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else { - srcMode = colorMode; - } - splash->drawImage(&imageSrc, nullptr, &imgData, srcMode, gFalse, width, height, mat, interpolate); - splash->setSoftMask(nullptr); - gfree(imgData.lookup); - delete imgData.maskStr; - delete imgData.imgStr; - if (maskColorMap->getMatteColor() != nullptr) { - maskStr->close(); - delete maskStr; - } - str->close(); -} - -GBool SplashOutputDev::checkTransparencyGroup(GfxState *state, GBool knockout) { - if (state->getFillOpacity() != 1 || - state->getStrokeOpacity() != 1 || - state->getAlphaIsShape() || - state->getBlendMode() != gfxBlendNormal || - splash->getSoftMask() != nullptr || - knockout) - return gTrue; - return transpGroupStack != nullptr && transpGroupStack->shape != nullptr; -} - -void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, - GfxColorSpace *blendingColorSpace, - GBool isolated, GBool knockout, - GBool forSoftMask) { - SplashTransparencyGroup *transpGroup; - SplashColor color; - double xMin, yMin, xMax, yMax, x, y; - int tx, ty, w, h, i; - - // transform the bbox - state->transform(bbox[0], bbox[1], &x, &y); - xMin = xMax = x; - yMin = yMax = y; - state->transform(bbox[0], bbox[3], &x, &y); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - state->transform(bbox[2], bbox[1], &x, &y); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - state->transform(bbox[2], bbox[3], &x, &y); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - tx = (int)floor(xMin); - if (tx < 0) { - tx = 0; - } else if (tx >= bitmap->getWidth()) { - tx = bitmap->getWidth() - 1; - } - ty = (int)floor(yMin); - if (ty < 0) { - ty = 0; - } else if (ty >= bitmap->getHeight()) { - ty = bitmap->getHeight() - 1; - } - w = (int)ceil(xMax) - tx + 1; - if (tx + w > bitmap->getWidth()) { - w = bitmap->getWidth() - tx; - } - if (w < 1) { - w = 1; - } - h = (int)ceil(yMax) - ty + 1; - if (ty + h > bitmap->getHeight()) { - h = bitmap->getHeight() - ty; - } - if (h < 1) { - h = 1; - } - - // push a new stack entry - transpGroup = new SplashTransparencyGroup(); - transpGroup->softmask = nullptr; - transpGroup->tx = tx; - transpGroup->ty = ty; - transpGroup->blendingColorSpace = blendingColorSpace; - transpGroup->isolated = isolated; - transpGroup->shape = (knockout && !isolated) ? SplashBitmap::copy(bitmap) : nullptr; - transpGroup->knockout = (knockout && isolated); - transpGroup->knockoutOpacity = 1.0; - transpGroup->next = transpGroupStack; - transpGroupStack = transpGroup; - - // save state - transpGroup->origBitmap = bitmap; - transpGroup->origSplash = splash; - transpGroup->fontAA = fontEngine->getAA(); - - //~ this handles the blendingColorSpace arg for soft masks, but - //~ not yet for transparency groups - - // switch to the blending color space - if (forSoftMask && isolated && blendingColorSpace) { - if (blendingColorSpace->getMode() == csDeviceGray || - blendingColorSpace->getMode() == csCalGray || - (blendingColorSpace->getMode() == csICCBased && - blendingColorSpace->getNComps() == 1)) { - colorMode = splashModeMono8; - } else if (blendingColorSpace->getMode() == csDeviceRGB || - blendingColorSpace->getMode() == csCalRGB || - (blendingColorSpace->getMode() == csICCBased && - blendingColorSpace->getNComps() == 3)) { - //~ does this need to use BGR8? - colorMode = splashModeRGB8; -#ifdef SPLASH_CMYK - } else if (blendingColorSpace->getMode() == csDeviceCMYK || - (blendingColorSpace->getMode() == csICCBased && - blendingColorSpace->getNComps() == 4)) { - colorMode = splashModeCMYK8; -#endif - } - } - - // create the temporary bitmap - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, - bitmapTopDown, bitmap->getSeparationList()); - if (!bitmap->getDataPtr()) { - delete bitmap; - w = h = 1; - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, bitmapTopDown); - } - splash = new Splash(bitmap, vectorAntialias, - transpGroup->origSplash->getScreen()); - if (transpGroup->next != nullptr && transpGroup->next->knockout) { - fontEngine->setAA(gFalse); - } - splash->setThinLineMode(transpGroup->origSplash->getThinLineMode()); - splash->setMinLineWidth(s_minLineWidth); - //~ Acrobat apparently copies at least the fill and stroke colors, and - //~ maybe other state(?) -- but not the clipping path (and not sure - //~ what else) - //~ [this is likely the same situation as in type3D1()] - splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); - splash->setStrokePattern( - transpGroup->origSplash->getStrokePattern()->copy()); - if (isolated) { - for (i = 0; i < splashMaxColorComps; ++i) { - color[i] = 0; - } - if (colorMode == splashModeXBGR8) color[3] = 255; - splash->clear(color, 0); - } else { - SplashBitmap *shape = (knockout) ? transpGroup->shape : - (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->shape : transpGroup->origBitmap; - int shapeTx = (knockout) ? tx : - (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->tx + tx : tx; - int shapeTy = (knockout) ? ty : - (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->ty + ty : ty; - splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); - splash->setInNonIsolatedGroup(shape, shapeTx, shapeTy); - } - transpGroup->tBitmap = bitmap; - state->shiftCTMAndClip(-tx, -ty); - updateCTM(state, 0, 0, 0, 0, 0, 0); - ++nestCount; -} - -void SplashOutputDev::endTransparencyGroup(GfxState *state) { - // restore state - --nestCount; - delete splash; - bitmap = transpGroupStack->origBitmap; - colorMode = bitmap->getMode(); - splash = transpGroupStack->origSplash; - state->shiftCTMAndClip(transpGroupStack->tx, transpGroupStack->ty); - updateCTM(state, 0, 0, 0, 0, 0, 0); -} - -void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) { - SplashBitmap *tBitmap; - SplashTransparencyGroup *transpGroup; - GBool isolated; - int tx, ty; - - tx = transpGroupStack->tx; - ty = transpGroupStack->ty; - tBitmap = transpGroupStack->tBitmap; - isolated = transpGroupStack->isolated; - - // paint the transparency group onto the parent bitmap - // - the clip path was set in the parent's state) - if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { - SplashCoord knockoutOpacity = (transpGroupStack->next != nullptr) ? transpGroupStack->next->knockoutOpacity - : transpGroupStack->knockoutOpacity; - splash->setOverprintMask(0xffffffff, gFalse); - splash->composite(tBitmap, 0, 0, tx, ty, - tBitmap->getWidth(), tBitmap->getHeight(), - gFalse, !isolated, transpGroupStack->next != nullptr && transpGroupStack->next->knockout, knockoutOpacity); - fontEngine->setAA(transpGroupStack->fontAA); - if (transpGroupStack->next != nullptr && transpGroupStack->next->shape != nullptr) { - transpGroupStack->next->knockout = gTrue; - } - } - - // pop the stack - transpGroup = transpGroupStack; - transpGroupStack = transpGroup->next; - if (transpGroupStack != nullptr && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) { - transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity; - } - delete transpGroup->shape; - delete transpGroup; - - delete tBitmap; -} - -void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, - GBool alpha, Function *transferFunc, - GfxColor *backdropColor) { - SplashBitmap *softMask, *tBitmap; - Splash *tSplash; - SplashTransparencyGroup *transpGroup; - SplashColor color; - SplashColorPtr p; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - double lum, lum2; - int tx, ty, x, y; - - tx = transpGroupStack->tx; - ty = transpGroupStack->ty; - tBitmap = transpGroupStack->tBitmap; - - // composite with backdrop color - if (!alpha && tBitmap->getMode() != splashModeMono1) { - //~ need to correctly handle the case where no blending color - //~ space is given - if (transpGroupStack->blendingColorSpace) { - tSplash = new Splash(tBitmap, vectorAntialias, - transpGroupStack->origSplash->getScreen()); - switch (tBitmap->getMode()) { - case splashModeMono1: - // transparency is not supported in mono1 mode - break; - case splashModeMono8: - transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); - color[0] = colToByte(gray); - tSplash->compositeBackground(color); - break; - case splashModeXBGR8: - color[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb); - color[0] = colToByte(rgb.r); - color[1] = colToByte(rgb.g); - color[2] = colToByte(rgb.b); - tSplash->compositeBackground(color); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk); - color[0] = colToByte(cmyk.c); - color[1] = colToByte(cmyk.m); - color[2] = colToByte(cmyk.y); - color[3] = colToByte(cmyk.k); - tSplash->compositeBackground(color); - break; - case splashModeDeviceN8: - transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN); - for (int cp=0; cp < SPOT_NCOMPS+4; cp++) - color[cp] = colToByte(deviceN.c[cp]); - tSplash->compositeBackground(color); - break; -#endif - } - delete tSplash; - } - } - - softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, gFalse); - unsigned char fill = 0; - if (transpGroupStack->blendingColorSpace) { - transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); - fill = colToByte(gray); - } - memset(softMask->getDataPtr(), fill, - softMask->getRowSize() * softMask->getHeight()); - p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; - int xMax = tBitmap->getWidth(); - int yMax = tBitmap->getHeight(); - if (xMax > bitmap->getWidth() - tx) xMax = bitmap->getWidth() - tx; - if (yMax > bitmap->getHeight() - ty) yMax = bitmap->getHeight() - ty; - for (y = 0; y < yMax; ++y) { - for (x = 0; x < xMax; ++x) { - if (alpha) { - if (transferFunc) { - lum = tBitmap->getAlpha(x, y) / 255.0; - transferFunc->transform(&lum, &lum2); - p[x] = (int)(lum2 * 255.0 + 0.5); - } else - p[x] = tBitmap->getAlpha(x, y); - } else { - tBitmap->getPixel(x, y, color); - // convert to luminosity - switch (tBitmap->getMode()) { - case splashModeMono1: - case splashModeMono8: - lum = color[0] / 255.0; - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - lum = (0.3 / 255.0) * color[0] + - (0.59 / 255.0) * color[1] + - (0.11 / 255.0) * color[2]; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - lum = (1 - color[3] / 255.0) - - (0.3 / 255.0) * color[0] - - (0.59 / 255.0) * color[1] - - (0.11 / 255.0) * color[2]; - if (lum < 0) { - lum = 0; - } - break; -#endif - } - if (transferFunc) { - transferFunc->transform(&lum, &lum2); - } else { - lum2 = lum; - } - p[x] = (int)(lum2 * 255.0 + 0.5); - } - } - p += softMask->getRowSize(); - } - splash->setSoftMask(softMask); - - // pop the stack - transpGroup = transpGroupStack; - transpGroupStack = transpGroup->next; - delete transpGroup; - - delete tBitmap; -} - -void SplashOutputDev::clearSoftMask(GfxState *state) { - splash->setSoftMask(nullptr); -} - -void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) { - splashColorCopy(paperColor, paperColorA); -} - -int SplashOutputDev::getBitmapWidth() { - return bitmap->getWidth(); -} - -int SplashOutputDev::getBitmapHeight() { - return bitmap->getHeight(); -} - -SplashBitmap *SplashOutputDev::takeBitmap() { - SplashBitmap *ret; - - ret = bitmap; - bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - return ret; -} - -void SplashOutputDev::getModRegion(int *xMin, int *yMin, - int *xMax, int *yMax) { - splash->getModRegion(xMin, yMin, xMax, yMax); -} - -void SplashOutputDev::clearModRegion() { - splash->clearModRegion(); -} - -#if 1 //~tmp: turn off anti-aliasing temporarily -GBool SplashOutputDev::getVectorAntialias() { - return splash->getVectorAntialias(); -} - -void SplashOutputDev::setVectorAntialias(GBool vaa) { - vaa = vaa && colorMode != splashModeMono1; - vectorAntialias = vaa; - splash->setVectorAntialias(vaa); -} -#endif - -void SplashOutputDev::setFreeTypeHinting(GBool enable, GBool enableSlightHintingA) -{ - enableFreeTypeHinting = enable; - enableSlightHinting = enableSlightHintingA; -} - -GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str, - double *ptm, int paintType, int /*tilingType*/, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) -{ - PDFRectangle box; - Gfx *gfx; - Splash *formerSplash = splash; - SplashBitmap *formerBitmap = bitmap; - double width, height; - int surface_width, surface_height, result_width, result_height, i; - int repeatX, repeatY; - SplashCoord matc[6]; - Matrix m1; - double *ctm, savedCTM[6]; - double kx, ky, sx, sy; - GBool retValue = gFalse; - - width = bbox[2] - bbox[0]; - height = bbox[3] - bbox[1]; - - if (xStep != width || yStep != height) - return gFalse; - - // calculate offsets - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - savedCTM[i] = ctm[i]; - } - state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]); - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) { - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return gFalse; - } - } - matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4]; - matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5]; - if (splashAbs(ctm[1]) > splashAbs(ctm[0])) { - kx = -ctm[1]; - ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1]; - } else { - kx = ctm[0]; - ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0]; - } - result_width = (int) ceil(fabs(kx * width * (x1 - x0))); - result_height = (int) ceil(fabs(ky * height * (y1 - y0))); - kx = state->getHDPI() / 72.0; - ky = state->getVDPI() / 72.0; - m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx; - m1.m[1] = 0; - m1.m[2] = 0; - m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky; - m1.m[4] = 0; - m1.m[5] = 0; - m1.transform(width, height, &kx, &ky); - surface_width = (int) ceil (fabs(kx)); - surface_height = (int) ceil (fabs(ky)); - - sx = (double) result_width / (surface_width * (x1 - x0)); - sy = (double) result_height / (surface_height * (y1 - y0)); - m1.m[0] *= sx; - m1.m[3] *= sy; - m1.transform(width, height, &kx, &ky); - - if(fabs(kx) < 1 && fabs(ky) < 1) { - kx = std::min<double>(kx, ky); - ky = 2 / kx; - m1.m[0] *= ky; - m1.m[3] *= ky; - m1.transform(width, height, &kx, &ky); - surface_width = (int) ceil (fabs(kx)); - surface_height = (int) ceil (fabs(ky)); - repeatX = x1 - x0; - repeatY = y1 - y0; - } else { - if ((unsigned long) surface_width * surface_height > 0x800000L) { - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return gFalse; - } - while(fabs(kx) > 16384 || fabs(ky) > 16384) { - // limit pattern bitmap size - m1.m[0] /= 2; - m1.m[3] /= 2; - m1.transform(width, height, &kx, &ky); - } - surface_width = (int) ceil (fabs(kx)); - surface_height = (int) ceil (fabs(ky)); - // adjust repeat values to completely fill region - repeatX = result_width / surface_width; - repeatY = result_height / surface_height; - if (surface_width * repeatX < result_width) - repeatX++; - if (surface_height * repeatY < result_height) - repeatY++; - if (x1 - x0 > repeatX) - repeatX = x1 - x0; - if (y1 - y0 > repeatY) - repeatY = y1 - y0; - } - // restore CTM and calculate rotate and scale with rounded matric - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]); - ctm = state->getCTM(); - matc[0] = ctm[0]; - matc[1] = ctm[1]; - matc[2] = ctm[2]; - matc[3] = ctm[3]; - - if (surface_width == 0 || surface_height == 0 || repeatX * repeatY <= 4) { - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return gFalse; - } - m1.transform(bbox[0], bbox[1], &kx, &ky); - m1.m[4] = -kx; - m1.m[5] = -ky; - - bitmap = new SplashBitmap(surface_width, surface_height, 1, - (paintType == 1) ? colorMode : splashModeMono8, gTrue); - if (bitmap->getDataPtr() == nullptr) { - SplashBitmap *tBitmap = bitmap; - bitmap = formerBitmap; - delete tBitmap; - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return gFalse; - } - splash = new Splash(bitmap, gTrue); - if (paintType == 2) { - SplashColor clearColor; -#ifdef SPLASH_CMYK - clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF; -#else - clearColor[0] = 0xFF; -#endif - splash->clear(clearColor, 0); - } else { - splash->clear(paperColor, 0); - } - splash->setThinLineMode(formerSplash->getThinLineMode()); - splash->setMinLineWidth(s_minLineWidth); - - box.x1 = bbox[0]; box.y1 = bbox[1]; - box.x2 = bbox[2]; box.y2 = bbox[3]; - gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); - // set pattern transformation matrix - gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); - updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); - gfx->display(str); - delete splash; - splash = formerSplash; - TilingSplashOutBitmap imgData; - imgData.bitmap = bitmap; - imgData.paintType = paintType; - imgData.pattern = splash->getFillPattern(); - imgData.colorMode = colorMode; - imgData.y = 0; - imgData.repeatX = repeatX; - imgData.repeatY = repeatY; - SplashBitmap *tBitmap = bitmap; - bitmap = formerBitmap; - result_width = tBitmap->getWidth() * imgData.repeatX; - result_height = tBitmap->getHeight() * imgData.repeatY; - - if (splashAbs(matc[1]) > splashAbs(matc[0])) { - kx = -matc[1]; - ky = matc[2] - (matc[0] * matc[3]) / matc[1]; - } else { - kx = matc[0]; - ky = matc[3] - (matc[1] * matc[2]) / matc[0]; - } - kx = result_width / (fabs(kx) + 1); - ky = result_height / (fabs(ky) + 1); - state->concatCTM(kx, 0, 0, ky, 0, 0); - ctm = state->getCTM(); - matc[0] = ctm[0]; - matc[1] = ctm[1]; - matc[2] = ctm[2]; - matc[3] = ctm[3]; - GBool minorAxisZero = matc[1] == 0 && matc[2] == 0; - if (matc[0] > 0 && minorAxisZero && matc[3] > 0) { - // draw the tiles - for (int y = 0; y < imgData.repeatY; ++y) { - for (int x = 0; x < imgData.repeatX; ++x) { - x0 = splashFloor(matc[4]) + x * tBitmap->getWidth(); - y0 = splashFloor(matc[5]) + y * tBitmap->getHeight(); - splash->blitImage(tBitmap, gTrue, x0, y0); - } - } - retValue = gTrue; - } else { - retValue = splash->drawImage(&tilingBitmapSrc, nullptr, &imgData, colorMode, gTrue, result_width, result_height, matc, gFalse, gTrue) == splashOk; - } - delete tBitmap; - delete gfx; - return retValue; -} - -GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) -{ - GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode(); - GBool bDirectColorTranslation = gFalse; // triggers an optimization. - switch (colorMode) { - case splashModeRGB8: - bDirectColorTranslation = (shadingMode == csDeviceRGB); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - bDirectColorTranslation = (shadingMode == csDeviceCMYK); - break; -#endif - default: - break; - } - // restore vector antialias because we support it here - if (shading->isParameterized()) { - SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading); - GBool vaa = getVectorAntialias(); - GBool retVal = gFalse; - setVectorAntialias(gTrue); - retVal = splash->gouraudTriangleShadedFill(splashShading); - setVectorAntialias(vaa); - delete splashShading; - return retVal; - } - return gFalse; -} - -GBool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) { - double xMin, yMin, xMax, yMax; - SplashPath *path; - GBool vaa = getVectorAntialias(); - // restore vector antialias because we support it here - setVectorAntialias(gTrue); - - GBool retVal = gFalse; - // get the clip region bbox - if (pattern->getShading()->getHasBBox()) { - pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); - } else { - state->getClipBBox(&xMin, &yMin, &xMax, &yMax); - - xMin = floor (xMin); - yMin = floor (yMin); - xMax = ceil (xMax); - yMax = ceil (yMax); - - { - Matrix ctm, ictm; - double x[4], y[4]; - int i; - - state->getCTM(&ctm); - ctm.invertTo(&ictm); - - ictm.transform(xMin, yMin, &x[0], &y[0]); - ictm.transform(xMax, yMin, &x[1], &y[1]); - ictm.transform(xMin, yMax, &x[2], &y[2]); - ictm.transform(xMax, yMax, &x[3], &y[3]); - - xMin = xMax = x[0]; - yMin = yMax = y[0]; - for (i = 1; i < 4; i++) { - xMin = std::min<double>(xMin, x[i]); - yMin = std::min<double>(yMin, y[i]); - xMax = std::max<double>(xMax, x[i]); - yMax = std::max<double>(yMax, y[i]); - } - } - } - - // fill the region - state->moveTo(xMin, yMin); - state->lineTo(xMax, yMin); - state->lineTo(xMax, yMax); - state->lineTo(xMin, yMax); - state->closePath(); - path = convertPath(state, state->getPath(), gTrue); - -#ifdef SPLASH_CMYK - pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk); - state->clearPath(); - setVectorAntialias(vaa); - delete path; - - return retVal; -} - -GBool SplashOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { - SplashFunctionPattern *pattern = new SplashFunctionPattern(colorMode, state, shading); - double xMin, yMin, xMax, yMax; - SplashPath *path; - GBool vaa = getVectorAntialias(); - // restore vector antialias because we support it here - setVectorAntialias(gTrue); - - GBool retVal = gFalse; - // get the clip region bbox - if (pattern->getShading()->getHasBBox()) { - pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); - } else { - state->getClipBBox(&xMin, &yMin, &xMax, &yMax); - - xMin = floor (xMin); - yMin = floor (yMin); - xMax = ceil (xMax); - yMax = ceil (yMax); - - { - Matrix ctm, ictm; - double x[4], y[4]; - int i; - - state->getCTM(&ctm); - ctm.invertTo(&ictm); - - ictm.transform(xMin, yMin, &x[0], &y[0]); - ictm.transform(xMax, yMin, &x[1], &y[1]); - ictm.transform(xMin, yMax, &x[2], &y[2]); - ictm.transform(xMax, yMax, &x[3], &y[3]); - - xMin = xMax = x[0]; - yMin = yMax = y[0]; - for (i = 1; i < 4; i++) { - xMin = std::min<double>(xMin, x[i]); - yMin = std::min<double>(yMin, y[i]); - xMax = std::max<double>(xMax, x[i]); - yMax = std::max<double>(yMax, y[i]); - } - } - } - - // fill the region - state->moveTo(xMin, yMin); - state->lineTo(xMax, yMin); - state->lineTo(xMax, yMax); - state->lineTo(xMin, yMax); - state->closePath(); - path = convertPath(state, state->getPath(), gTrue); - -#ifdef SPLASH_CMYK - pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk); - state->clearPath(); - setVectorAntialias(vaa); - delete path; - - delete pattern; - - return retVal; -} - -GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { - SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading); - GBool retVal = univariateShadedFill(state, pattern, tMin, tMax); - - delete pattern; - - return retVal; -} - -GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) { - SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading); - GBool retVal = univariateShadedFill(state, pattern, tMin, tMax); - - delete pattern; - - return retVal; -} |