| //======================================================================== |
| // |
| // GfxState.cc |
| // |
| // Copyright 1996-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 Kristian Høgsberg <krh@redhat.com> |
| // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net> |
| // Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2006-2022, 2024 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2009, 2012 Koji Otani <sho@bbr.jp> |
| // Copyright (C) 2009, 2011-2016, 2020, 2023 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2009, 2019 Christian Persch <chpe@gnome.org> |
| // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com> |
| // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com> |
| // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com> |
| // Copyright (C) 2012, 2020 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com> |
| // Copyright (C) 2013 Hib Eris <hib@hiberis.nl> |
| // Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it> |
| // Copyright (C) 2015, 2020 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2016 Marek Kasik <mkasik@redhat.com> |
| // Copyright (C) 2017, 2019, 2022 Oliver Sander <oliver.sander@tu-dresden.de> |
| // 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 Volker Krause <vkrause@kde.org> |
| // Copyright (C) 2018, 2019 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com> |
| // Copyright (C) 2020, 2021 Philipp Knechtges <philipp-dev@knechtges.com> |
| // Copyright (C) 2020 Lluís Batlle i Rossell <viric@viric.name> |
| // Copyright (C) 2024 Athul Raj Kollareth <krathul3152@gmail.com> |
| // |
| // 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> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <cstddef> |
| #include <cmath> |
| #include <cstring> |
| #include "goo/gfile.h" |
| #include "goo/gmem.h" |
| #include "Error.h" |
| #include "Object.h" |
| #include "Array.h" |
| #include "Page.h" |
| #include "Gfx.h" |
| #include "GfxState.h" |
| #include "GfxState_helpers.h" |
| #include "GfxFont.h" |
| #include "GlobalParams.h" |
| #include "PopplerCache.h" |
| #include "OutputDev.h" |
| #include "splash/SplashTypes.h" |
| |
| //------------------------------------------------------------------------ |
| |
| // Max depth of nested color spaces. This is used to catch infinite |
| // loops in the color space object structure. |
| #define colorSpaceRecursionLimit 8 |
| |
| //------------------------------------------------------------------------ |
| |
| bool Matrix::invertTo(Matrix *other) const |
| { |
| const double det_denominator = determinant(); |
| if (unlikely(det_denominator == 0)) { |
| *other = { 1, 0, 0, 1, 0, 0 }; |
| return false; |
| } |
| |
| const double det = 1 / det_denominator; |
| other->m[0] = m[3] * det; |
| other->m[1] = -m[1] * det; |
| other->m[2] = -m[2] * det; |
| other->m[3] = m[0] * det; |
| other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det; |
| other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det; |
| |
| return true; |
| } |
| |
| void Matrix::translate(double tx, double ty) |
| { |
| double x0 = tx * m[0] + ty * m[2] + m[4]; |
| double y0 = tx * m[1] + ty * m[3] + m[5]; |
| m[4] = x0; |
| m[5] = y0; |
| } |
| |
| void Matrix::scale(double sx, double sy) |
| { |
| m[0] *= sx; |
| m[1] *= sx; |
| m[2] *= sy; |
| m[3] *= sy; |
| } |
| |
| void Matrix::transform(double x, double y, double *tx, double *ty) const |
| { |
| double temp_x, temp_y; |
| |
| temp_x = m[0] * x + m[2] * y + m[4]; |
| temp_y = m[1] * x + m[3] * y + m[5]; |
| |
| *tx = temp_x; |
| *ty = temp_y; |
| } |
| |
| // Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis |
| double Matrix::norm() const |
| { |
| double f, g, h, i, j; |
| |
| i = m[0] * m[0] + m[1] * m[1]; |
| j = m[2] * m[2] + m[3] * m[3]; |
| |
| f = 0.5 * (i + j); |
| g = 0.5 * (i - j); |
| h = m[0] * m[2] + m[1] * m[3]; |
| |
| return sqrt(f + hypot(g, h)); |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| struct GfxBlendModeInfo |
| { |
| const char *name; |
| GfxBlendMode mode; |
| }; |
| |
| static const GfxBlendModeInfo gfxBlendModeNames[] = { { "Normal", gfxBlendNormal }, { "Compatible", gfxBlendNormal }, |
| { "Multiply", gfxBlendMultiply }, { "Screen", gfxBlendScreen }, |
| { "Overlay", gfxBlendOverlay }, { "Darken", gfxBlendDarken }, |
| { "Lighten", gfxBlendLighten }, { "ColorDodge", gfxBlendColorDodge }, |
| { "ColorBurn", gfxBlendColorBurn }, { "HardLight", gfxBlendHardLight }, |
| { "SoftLight", gfxBlendSoftLight }, { "Difference", gfxBlendDifference }, |
| { "Exclusion", gfxBlendExclusion }, { "Hue", gfxBlendHue }, |
| { "Saturation", gfxBlendSaturation }, { "Color", gfxBlendColor }, |
| { "Luminosity", gfxBlendLuminosity } }; |
| |
| #define nGfxBlendModeNames ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo)))) |
| |
| //------------------------------------------------------------------------ |
| // |
| // NB: This must match the GfxColorSpaceMode enum defined in |
| // GfxState.h |
| static const char *gfxColorSpaceModeNames[] = { "DeviceGray", "CalGray", "DeviceRGB", "CalRGB", "DeviceCMYK", "Lab", "ICCBased", "Indexed", "Separation", "DeviceN", "Pattern" }; |
| |
| #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *))) |
| |
| #ifdef USE_CMS |
| |
| static const std::map<unsigned int, unsigned int>::size_type CMSCACHE_LIMIT = 2048; |
| |
| # include <lcms2.h> |
| # define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION |
| |
| static void lcmsprofiledeleter(void *profile) |
| { |
| cmsCloseProfile(profile); |
| } |
| |
| GfxLCMSProfilePtr make_GfxLCMSProfilePtr(void *profile) |
| { |
| if (profile == nullptr) { |
| return GfxLCMSProfilePtr(); |
| } |
| return GfxLCMSProfilePtr(profile, lcmsprofiledeleter); |
| } |
| |
| void GfxColorTransform::doTransform(void *in, void *out, unsigned int size) |
| { |
| cmsDoTransform(transform, in, out, size); |
| } |
| |
| // transformA should be a cmsHTRANSFORM |
| GfxColorTransform::GfxColorTransform(void *transformA, int cmsIntentA, unsigned int inputPixelTypeA, unsigned int transformPixelTypeA) |
| { |
| transform = transformA; |
| cmsIntent = cmsIntentA; |
| inputPixelType = inputPixelTypeA; |
| transformPixelType = transformPixelTypeA; |
| } |
| |
| GfxColorTransform::~GfxColorTransform() |
| { |
| cmsDeleteTransform(transform); |
| } |
| |
| // convert color space signature to cmsColor type |
| static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs); |
| static unsigned int getCMSNChannels(cmsColorSpaceSignature cs); |
| |
| #endif |
| |
| //------------------------------------------------------------------------ |
| // GfxColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxColorSpace::GfxColorSpace() |
| { |
| overprintMask = 0x0f; |
| mapping = nullptr; |
| } |
| |
| GfxColorSpace::~GfxColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion) |
| { |
| Object obj1; |
| |
| if (recursion > colorSpaceRecursionLimit) { |
| error(errSyntaxError, -1, "Loop detected in color space objects"); |
| return {}; |
| } |
| |
| if (csObj->isName()) { |
| if (csObj->isName("DeviceGray") || csObj->isName("G")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultGray"); |
| if (objCS.isNull()) { |
| return state->copyDefaultGrayColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultGrayColorSpace(); |
| } |
| } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultRGB"); |
| if (objCS.isNull()) { |
| return state->copyDefaultRGBColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultRGBColorSpace(); |
| } |
| } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultCMYK"); |
| if (objCS.isNull()) { |
| return state->copyDefaultCMYKColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultCMYKColorSpace(); |
| } |
| } else if (csObj->isName("Pattern")) { |
| return std::make_unique<GfxPatternColorSpace>(nullptr); |
| } else { |
| error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName()); |
| } |
| } else if (csObj->isArray() && csObj->arrayGetLength() > 0) { |
| obj1 = csObj->arrayGet(0); |
| if (obj1.isName("DeviceGray") || obj1.isName("G")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultGray"); |
| if (objCS.isNull()) { |
| return state->copyDefaultGrayColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultGrayColorSpace(); |
| } |
| } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultRGB"); |
| if (objCS.isNull()) { |
| return state->copyDefaultRGBColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultRGBColorSpace(); |
| } |
| } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultCMYK"); |
| if (objCS.isNull()) { |
| return state->copyDefaultCMYKColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultCMYKColorSpace(); |
| } |
| } else if (obj1.isName("CalGray")) { |
| return GfxCalGrayColorSpace::parse(csObj->getArray(), state); |
| } else if (obj1.isName("CalRGB")) { |
| return GfxCalRGBColorSpace::parse(csObj->getArray(), state); |
| } else if (obj1.isName("Lab")) { |
| return GfxLabColorSpace::parse(csObj->getArray(), state); |
| } else if (obj1.isName("ICCBased")) { |
| return GfxICCBasedColorSpace::parse(csObj->getArray(), out, state, recursion); |
| } else if (obj1.isName("Indexed") || obj1.isName("I")) { |
| return GfxIndexedColorSpace::parse(res, csObj->getArray(), out, state, recursion); |
| } else if (obj1.isName("Separation")) { |
| return GfxSeparationColorSpace::parse(res, csObj->getArray(), out, state, recursion); |
| } else if (obj1.isName("DeviceN")) { |
| return GfxDeviceNColorSpace::parse(res, csObj->getArray(), out, state, recursion); |
| } else if (obj1.isName("Pattern")) { |
| return GfxPatternColorSpace::parse(res, csObj->getArray(), out, state, recursion); |
| } else { |
| error(errSyntaxWarning, -1, "Bad color space"); |
| } |
| } else if (csObj->isDict()) { |
| obj1 = csObj->dictLookup("ColorSpace"); |
| if (obj1.isName("DeviceGray")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultGray"); |
| if (objCS.isNull()) { |
| return state->copyDefaultGrayColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultGrayColorSpace(); |
| } |
| } else if (obj1.isName("DeviceRGB")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultRGB"); |
| if (objCS.isNull()) { |
| return state->copyDefaultRGBColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultRGBColorSpace(); |
| } |
| } else if (obj1.isName("DeviceCMYK")) { |
| if (res != nullptr) { |
| Object objCS = res->lookupColorSpace("DefaultCMYK"); |
| if (objCS.isNull()) { |
| return state->copyDefaultCMYKColorSpace(); |
| } else { |
| return GfxColorSpace::parse(nullptr, &objCS, out, state); |
| } |
| } else { |
| return state->copyDefaultCMYKColorSpace(); |
| } |
| } else { |
| error(errSyntaxWarning, -1, "Bad color space dict'"); |
| } |
| } else { |
| error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict"); |
| } |
| return {}; |
| } |
| |
| void GfxColorSpace::createMapping(std::vector<std::unique_ptr<GfxSeparationColorSpace>> *separationList, int maxSepComps) |
| { |
| return; |
| } |
| |
| void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const |
| { |
| int i; |
| |
| for (i = 0; i < getNComps(); ++i) { |
| decodeLow[i] = 0; |
| decodeRange[i] = 1; |
| } |
| } |
| |
| int GfxColorSpace::getNumColorSpaceModes() |
| { |
| return nGfxColorSpaceModes; |
| } |
| |
| const char *GfxColorSpace::getColorSpaceModeName(int idx) |
| { |
| return gfxColorSpaceModeNames[idx]; |
| } |
| |
| #ifdef USE_CMS |
| |
| static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text) |
| { |
| error(errSyntaxWarning, -1, "{0:s}", text); |
| } |
| |
| unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs) |
| { |
| switch (cs) { |
| case cmsSigXYZData: |
| return PT_XYZ; |
| break; |
| case cmsSigLabData: |
| return PT_Lab; |
| break; |
| case cmsSigLuvData: |
| return PT_YUV; |
| break; |
| case cmsSigYCbCrData: |
| return PT_YCbCr; |
| break; |
| case cmsSigYxyData: |
| return PT_Yxy; |
| break; |
| case cmsSigRgbData: |
| return PT_RGB; |
| break; |
| case cmsSigGrayData: |
| return PT_GRAY; |
| break; |
| case cmsSigHsvData: |
| return PT_HSV; |
| break; |
| case cmsSigHlsData: |
| return PT_HLS; |
| break; |
| case cmsSigCmykData: |
| return PT_CMYK; |
| break; |
| case cmsSigCmyData: |
| return PT_CMY; |
| break; |
| case cmsSig2colorData: |
| case cmsSig3colorData: |
| case cmsSig4colorData: |
| case cmsSig5colorData: |
| case cmsSig6colorData: |
| case cmsSig7colorData: |
| case cmsSig8colorData: |
| case cmsSig9colorData: |
| case cmsSig10colorData: |
| case cmsSig11colorData: |
| case cmsSig12colorData: |
| case cmsSig13colorData: |
| case cmsSig14colorData: |
| case cmsSig15colorData: |
| default: |
| break; |
| } |
| return PT_RGB; |
| } |
| |
| unsigned int getCMSNChannels(cmsColorSpaceSignature cs) |
| { |
| switch (cs) { |
| case cmsSigXYZData: |
| case cmsSigLuvData: |
| case cmsSigLabData: |
| case cmsSigYCbCrData: |
| case cmsSigYxyData: |
| case cmsSigRgbData: |
| case cmsSigHsvData: |
| case cmsSigHlsData: |
| case cmsSigCmyData: |
| case cmsSig3colorData: |
| return 3; |
| break; |
| case cmsSigGrayData: |
| return 1; |
| break; |
| case cmsSigCmykData: |
| case cmsSig4colorData: |
| return 4; |
| break; |
| case cmsSig2colorData: |
| return 2; |
| break; |
| case cmsSig5colorData: |
| return 5; |
| break; |
| case cmsSig6colorData: |
| return 6; |
| break; |
| case cmsSig7colorData: |
| return 7; |
| break; |
| case cmsSig8colorData: |
| return 8; |
| break; |
| case cmsSig9colorData: |
| return 9; |
| break; |
| case cmsSig10colorData: |
| return 10; |
| break; |
| case cmsSig11colorData: |
| return 11; |
| break; |
| case cmsSig12colorData: |
| return 12; |
| break; |
| case cmsSig13colorData: |
| return 13; |
| break; |
| case cmsSig14colorData: |
| return 14; |
| break; |
| case cmsSig15colorData: |
| return 15; |
| default: |
| break; |
| } |
| return 3; |
| } |
| #endif |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceGrayColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() { } |
| |
| GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxDeviceGrayColorSpace::copy() const |
| { |
| return std::make_unique<GfxDeviceGrayColorSpace>(); |
| } |
| |
| void GfxDeviceGrayColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| *gray = clip01(color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length) |
| { |
| memcpy(out, in, length); |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| rgb->r = rgb->g = rgb->b = clip01(color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) |
| { |
| int i; |
| |
| for (i = 0; i < length; i++) { |
| out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0); |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| *out++ = in[i]; |
| *out++ = in[i]; |
| *out++ = in[i]; |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| *out++ = in[i]; |
| *out++ = in[i]; |
| *out++ = in[i]; |
| *out++ = 255; |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| *out++ = 0; |
| *out++ = 0; |
| *out++ = 0; |
| *out++ = in[i]; |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| for (int j = 0; j < SPOT_NCOMPS + 4; j++) { |
| out[j] = 0; |
| } |
| out[4] = in[i]; |
| out += (SPOT_NCOMPS + 4); |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| cmyk->c = cmyk->m = cmyk->y = 0; |
| cmyk->k = clip01(gfxColorComp1 - color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| clearGfxColor(deviceN); |
| deviceN->c[3] = clip01(gfxColorComp1 - color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| color->c[0] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxCalGrayColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxCalGrayColorSpace::GfxCalGrayColorSpace() |
| { |
| whiteX = whiteY = whiteZ = 1; |
| blackX = blackY = blackZ = 0; |
| gamma = 1; |
| } |
| |
| GfxCalGrayColorSpace::~GfxCalGrayColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxCalGrayColorSpace::copy() const |
| { |
| auto cs = std::make_unique<GfxCalGrayColorSpace>(); |
| cs->whiteX = whiteX; |
| cs->whiteY = whiteY; |
| cs->whiteZ = whiteZ; |
| cs->blackX = blackX; |
| cs->blackY = blackY; |
| cs->blackZ = blackZ; |
| cs->gamma = gamma; |
| #ifdef USE_CMS |
| cs->transform = transform; |
| #endif |
| return cs; |
| } |
| |
| // This is the inverse of MatrixLMN in Example 4.10 from the PostScript |
| // Language Reference, Third Edition. |
| static const double xyzrgb[3][3] = { { 3.240449, -1.537136, -0.498531 }, { -0.969265, 1.876011, 0.041556 }, { 0.055643, -0.204026, 1.057229 } }; |
| |
| // From the same reference as above, the inverse of the DecodeLMN function. |
| // This is essentially the gamma function of the sRGB profile. |
| static double srgb_gamma_function(double x) |
| { |
| // 0.04045 is what lcms2 uses, but the PS Reference Example 4.10 specifies 0.03928??? |
| // if (x <= 0.04045 / 12.92321) { |
| if (x <= 0.03928 / 12.92321) { |
| return x * 12.92321; |
| } |
| return 1.055 * pow(x, 1.0 / 2.4) - 0.055; |
| } |
| |
| // D65 is the white point of the sRGB profile as it is specified above in the xyzrgb array |
| static const double white_d65_X = 0.9505; |
| static const double white_d65_Y = 1.0; |
| static const double white_d65_Z = 1.0890; |
| |
| #ifdef USE_CMS |
| // D50 is the default white point as used in ICC profiles and in the lcms2 library |
| static const double white_d50_X = 0.96422; |
| static const double white_d50_Y = 1.0; |
| static const double white_d50_Z = 0.82521; |
| |
| static void inline bradford_transform_to_d50(double &X, double &Y, double &Z, const double source_whiteX, const double source_whiteY, const double source_whiteZ) |
| { |
| if (source_whiteX == white_d50_X && source_whiteY == white_d50_Y && source_whiteZ == white_d50_Z) { |
| // early exit if noop |
| return; |
| } |
| // at first apply Bradford matrix |
| double rho_in = 0.8951000 * X + 0.2664000 * Y - 0.1614000 * Z; |
| double gamma_in = -0.7502000 * X + 1.7135000 * Y + 0.0367000 * Z; |
| double beta_in = 0.0389000 * X - 0.0685000 * Y + 1.0296000 * Z; |
| |
| // apply a diagonal matrix with the diagonal entries being the inverse bradford-transformed white point |
| rho_in /= 0.8951000 * source_whiteX + 0.2664000 * source_whiteY - 0.1614000 * source_whiteZ; |
| gamma_in /= -0.7502000 * source_whiteX + 1.7135000 * source_whiteY + 0.0367000 * source_whiteZ; |
| beta_in /= 0.0389000 * source_whiteX - 0.0685000 * source_whiteY + 1.0296000 * source_whiteZ; |
| |
| // now revert the two steps above, but substituting the source white point by the device white point (D50) |
| // Since the white point is known a priori this has been combined into a single operation. |
| X = 0.98332566 * rho_in - 0.15005819 * gamma_in + 0.13095252 * beta_in; |
| Y = 0.43069901 * rho_in + 0.52894900 * gamma_in + 0.04035199 * beta_in; |
| Z = 0.00849698 * rho_in + 0.04086079 * gamma_in + 0.79284618 * beta_in; |
| } |
| #endif |
| |
| static void inline bradford_transform_to_d65(double &X, double &Y, double &Z, const double source_whiteX, const double source_whiteY, const double source_whiteZ) |
| { |
| if (source_whiteX == white_d65_X && source_whiteY == white_d65_Y && source_whiteZ == white_d65_Z) { |
| // early exit if noop |
| return; |
| } |
| // at first apply Bradford matrix |
| double rho_in = 0.8951000 * X + 0.2664000 * Y - 0.1614000 * Z; |
| double gamma_in = -0.7502000 * X + 1.7135000 * Y + 0.0367000 * Z; |
| double beta_in = 0.0389000 * X - 0.0685000 * Y + 1.0296000 * Z; |
| |
| // apply a diagonal matrix with the diagonal entries being the inverse bradford-transformed white point |
| rho_in /= 0.8951000 * source_whiteX + 0.2664000 * source_whiteY - 0.1614000 * source_whiteZ; |
| gamma_in /= -0.7502000 * source_whiteX + 1.7135000 * source_whiteY + 0.0367000 * source_whiteZ; |
| beta_in /= 0.0389000 * source_whiteX - 0.0685000 * source_whiteY + 1.0296000 * source_whiteZ; |
| |
| // now revert the two steps above, but substituting the source white point by the device white point (D65) |
| // Since the white point is known a priori this has been combined into a single operation. |
| X = 0.92918329 * rho_in - 0.15299782 * gamma_in + 0.17428453 * beta_in; |
| Y = 0.40698452 * rho_in + 0.53931108 * gamma_in + 0.05370440 * beta_in; |
| Z = -0.00802913 * rho_in + 0.04166125 * gamma_in + 1.05519788 * beta_in; |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxCalGrayColorSpace::parse(Array *arr, GfxState *state) |
| { |
| Object obj1, obj2; |
| |
| obj1 = arr->get(1); |
| if (!obj1.isDict()) { |
| error(errSyntaxWarning, -1, "Bad CalGray color space"); |
| return {}; |
| } |
| auto cs = std::make_unique<GfxCalGrayColorSpace>(); |
| obj2 = obj1.dictLookup("WhitePoint"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1); |
| cs->whiteY = obj2.arrayGet(1).getNumWithDefaultValue(1); |
| cs->whiteZ = obj2.arrayGet(2).getNumWithDefaultValue(1); |
| } |
| obj2 = obj1.dictLookup("BlackPoint"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->blackX = obj2.arrayGet(0).getNumWithDefaultValue(0); |
| cs->blackY = obj2.arrayGet(1).getNumWithDefaultValue(0); |
| cs->blackZ = obj2.arrayGet(2).getNumWithDefaultValue(0); |
| } |
| |
| cs->gamma = obj1.dictLookup("Gamma").getNumWithDefaultValue(1); |
| |
| #ifdef USE_CMS |
| cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr; |
| #endif |
| return cs; |
| } |
| |
| // convert CalGray to media XYZ color space |
| // (not multiply by the white point) |
| void GfxCalGrayColorSpace::getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const |
| { |
| const double A = colToDbl(color->c[0]); |
| const double xyzColor = pow(A, gamma); |
| *pX = xyzColor; |
| *pY = xyzColor; |
| *pZ = xyzColor; |
| } |
| |
| void GfxCalGrayColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| GfxRGB rgb; |
| |
| #ifdef USE_CMS |
| if (transform && transform->getTransformPixelType() == PT_GRAY) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color, &X, &Y, &Z); |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| *gray = byteToCol(out[0]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); |
| } |
| |
| void GfxCalGrayColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| double X, Y, Z; |
| double r, g, b; |
| |
| getXYZ(color, &X, &Y, &Z); |
| #ifdef USE_CMS |
| if (transform && transform->getTransformPixelType() == PT_RGB) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| return; |
| } |
| #endif |
| bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ); |
| // convert XYZ to RGB, including gamut mapping and gamma correction |
| r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; |
| g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; |
| b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; |
| rgb->r = dblToCol(srgb_gamma_function(clip01(r))); |
| rgb->g = dblToCol(srgb_gamma_function(clip01(g))); |
| rgb->b = dblToCol(srgb_gamma_function(clip01(b))); |
| } |
| |
| void GfxCalGrayColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| #ifdef USE_CMS |
| if (transform && transform->getTransformPixelType() == PT_CMYK) { |
| double in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color, &X, &Y, &Z); |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxCalGrayColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| GfxCMYK cmyk; |
| clearGfxColor(deviceN); |
| getCMYK(color, &cmyk); |
| deviceN->c[0] = cmyk.c; |
| deviceN->c[1] = cmyk.m; |
| deviceN->c[2] = cmyk.y; |
| deviceN->c[3] = cmyk.k; |
| } |
| |
| void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| color->c[0] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceRGBColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() { } |
| |
| GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxDeviceRGBColorSpace::copy() const |
| { |
| return std::make_unique<GfxDeviceRGBColorSpace>(); |
| } |
| |
| void GfxDeviceRGBColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| *gray = clip01((GfxColorComp)(0.3 * color->c[0] + 0.59 * color->c[1] + 0.11 * color->c[2] + 0.5)); |
| } |
| |
| void GfxDeviceRGBColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length) |
| { |
| int i; |
| |
| for (i = 0; i < length; i++) { |
| out[i] = (in[i * 3 + 0] * 19595 + in[i * 3 + 1] * 38469 + in[i * 3 + 2] * 7472) / 65536; |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| rgb->r = clip01(color->c[0]); |
| rgb->g = clip01(color->c[1]); |
| rgb->b = clip01(color->c[2]); |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) |
| { |
| unsigned char *p; |
| int i; |
| |
| for (i = 0, p = in; i < length; i++, p += 3) { |
| out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0); |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = *in++; |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = 255; |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) |
| { |
| GfxColorComp c, m, y, k; |
| |
| for (int i = 0; i < length; i++) { |
| c = byteToCol(255 - *in++); |
| m = byteToCol(255 - *in++); |
| y = byteToCol(255 - *in++); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| *out++ = colToByte(c - k); |
| *out++ = colToByte(m - k); |
| *out++ = colToByte(y - k); |
| *out++ = colToByte(k); |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) |
| { |
| GfxColorComp c, m, y, k; |
| |
| for (int i = 0; i < length; i++) { |
| for (int j = 0; j < SPOT_NCOMPS + 4; j++) { |
| out[j] = 0; |
| } |
| c = byteToCol(255 - *in++); |
| m = byteToCol(255 - *in++); |
| y = byteToCol(255 - *in++); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| out[0] = colToByte(c - k); |
| out[1] = colToByte(m - k); |
| out[2] = colToByte(y - k); |
| out[3] = colToByte(k); |
| out += (SPOT_NCOMPS + 4); |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| GfxColorComp c, m, y, k; |
| |
| c = clip01(gfxColorComp1 - color->c[0]); |
| m = clip01(gfxColorComp1 - color->c[1]); |
| y = clip01(gfxColorComp1 - color->c[2]); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxDeviceRGBColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| GfxCMYK cmyk; |
| clearGfxColor(deviceN); |
| getCMYK(color, &cmyk); |
| deviceN->c[0] = cmyk.c; |
| deviceN->c[1] = cmyk.m; |
| deviceN->c[2] = cmyk.y; |
| deviceN->c[3] = cmyk.k; |
| } |
| |
| void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| color->c[0] = 0; |
| color->c[1] = 0; |
| color->c[2] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxCalRGBColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxCalRGBColorSpace::GfxCalRGBColorSpace() |
| { |
| whiteX = whiteY = whiteZ = 1; |
| blackX = blackY = blackZ = 0; |
| gammaR = gammaG = gammaB = 1; |
| mat[0] = 1; |
| mat[1] = 0; |
| mat[2] = 0; |
| mat[3] = 0; |
| mat[4] = 1; |
| mat[5] = 0; |
| mat[6] = 0; |
| mat[7] = 0; |
| mat[8] = 1; |
| } |
| |
| GfxCalRGBColorSpace::~GfxCalRGBColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxCalRGBColorSpace::copy() const |
| { |
| int i; |
| |
| auto cs = std::make_unique<GfxCalRGBColorSpace>(); |
| cs->whiteX = whiteX; |
| cs->whiteY = whiteY; |
| cs->whiteZ = whiteZ; |
| cs->blackX = blackX; |
| cs->blackY = blackY; |
| cs->blackZ = blackZ; |
| cs->gammaR = gammaR; |
| cs->gammaG = gammaG; |
| cs->gammaB = gammaB; |
| for (i = 0; i < 9; ++i) { |
| cs->mat[i] = mat[i]; |
| } |
| #ifdef USE_CMS |
| cs->transform = transform; |
| #endif |
| return cs; |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxCalRGBColorSpace::parse(Array *arr, GfxState *state) |
| { |
| Object obj1, obj2; |
| int i; |
| |
| obj1 = arr->get(1); |
| if (!obj1.isDict()) { |
| error(errSyntaxWarning, -1, "Bad CalRGB color space"); |
| return {}; |
| } |
| auto cs = std::make_unique<GfxCalRGBColorSpace>(); |
| obj2 = obj1.dictLookup("WhitePoint"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1); |
| cs->whiteY = obj2.arrayGet(1).getNumWithDefaultValue(1); |
| cs->whiteZ = obj2.arrayGet(2).getNumWithDefaultValue(1); |
| } |
| obj2 = obj1.dictLookup("BlackPoint"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->blackX = obj2.arrayGet(0).getNumWithDefaultValue(0); |
| cs->blackY = obj2.arrayGet(1).getNumWithDefaultValue(0); |
| cs->blackZ = obj2.arrayGet(2).getNumWithDefaultValue(0); |
| } |
| obj2 = obj1.dictLookup("Gamma"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->gammaR = obj2.arrayGet(0).getNumWithDefaultValue(1); |
| cs->gammaG = obj2.arrayGet(1).getNumWithDefaultValue(1); |
| cs->gammaB = obj2.arrayGet(2).getNumWithDefaultValue(1); |
| } |
| obj2 = obj1.dictLookup("Matrix"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 9) { |
| for (i = 0; i < 9; ++i) { |
| Object obj3 = obj2.arrayGet(i); |
| if (likely(obj3.isNum())) { |
| cs->mat[i] = obj3.getNum(); |
| } |
| } |
| } |
| |
| #ifdef USE_CMS |
| cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr; |
| #endif |
| return cs; |
| } |
| |
| // convert CalRGB to XYZ color space |
| void GfxCalRGBColorSpace::getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const |
| { |
| double A, B, C; |
| |
| A = pow(colToDbl(color->c[0]), gammaR); |
| B = pow(colToDbl(color->c[1]), gammaG); |
| C = pow(colToDbl(color->c[2]), gammaB); |
| *pX = mat[0] * A + mat[3] * B + mat[6] * C; |
| *pY = mat[1] * A + mat[4] * B + mat[7] * C; |
| *pZ = mat[2] * A + mat[5] * B + mat[8] * C; |
| } |
| |
| void GfxCalRGBColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| GfxRGB rgb; |
| |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color, &X, &Y, &Z); |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| *gray = byteToCol(out[0]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); |
| } |
| |
| void GfxCalRGBColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| double X, Y, Z; |
| double r, g, b; |
| |
| getXYZ(color, &X, &Y, &Z); |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| |
| return; |
| } |
| #endif |
| bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ); |
| // convert XYZ to RGB, including gamut mapping and gamma correction |
| r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; |
| g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; |
| b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; |
| rgb->r = dblToCol(srgb_gamma_function(clip01(r))); |
| rgb->g = dblToCol(srgb_gamma_function(clip01(g))); |
| rgb->b = dblToCol(srgb_gamma_function(clip01(b))); |
| } |
| |
| void GfxCalRGBColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { |
| double in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color, &X, &Y, &Z); |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxCalRGBColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| GfxCMYK cmyk; |
| clearGfxColor(deviceN); |
| getCMYK(color, &cmyk); |
| deviceN->c[0] = cmyk.c; |
| deviceN->c[1] = cmyk.m; |
| deviceN->c[2] = cmyk.y; |
| deviceN->c[3] = cmyk.k; |
| } |
| |
| void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| color->c[0] = 0; |
| color->c[1] = 0; |
| color->c[2] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceCMYKColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() { } |
| |
| GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxDeviceCMYKColorSpace::copy() const |
| { |
| return std::make_unique<GfxDeviceCMYKColorSpace>(); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3] - 0.3 * color->c[0] - 0.59 * color->c[1] - 0.11 * color->c[2] + 0.5)); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| double c, m, y, k, c1, m1, y1, k1, r, g, b; |
| |
| c = colToDbl(color->c[0]); |
| m = colToDbl(color->c[1]); |
| y = colToDbl(color->c[2]); |
| k = colToDbl(color->c[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->r = clip01(dblToCol(r)); |
| rgb->g = clip01(dblToCol(g)); |
| rgb->b = clip01(dblToCol(b)); |
| } |
| |
| static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(unsigned char *&in, double &r, double &g, double &b) |
| { |
| double c, m, y, k, c1, m1, y1, k1; |
| |
| c = byteToDbl(*in++); |
| m = byteToDbl(*in++); |
| y = byteToDbl(*in++); |
| k = byteToDbl(*in++); |
| c1 = 1 - c; |
| m1 = 1 - m; |
| y1 = 1 - y; |
| k1 = 1 - k; |
| cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) |
| { |
| double r, g, b; |
| for (int i = 0; i < length; i++) { |
| GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); |
| *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b)); |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) |
| { |
| double r, g, b; |
| |
| for (int i = 0; i < length; i++) { |
| GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); |
| *out++ = dblToByte(clip01(r)); |
| *out++ = dblToByte(clip01(g)); |
| *out++ = dblToByte(clip01(b)); |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) |
| { |
| double r, g, b; |
| |
| for (int i = 0; i < length; i++) { |
| GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); |
| *out++ = dblToByte(clip01(r)); |
| *out++ = dblToByte(clip01(g)); |
| *out++ = dblToByte(clip01(b)); |
| *out++ = 255; |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = *in++; |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) |
| { |
| for (int i = 0; i < length; i++) { |
| for (int j = 0; j < SPOT_NCOMPS + 4; j++) { |
| out[j] = 0; |
| } |
| out[0] = *in++; |
| out[1] = *in++; |
| out[2] = *in++; |
| out[3] = *in++; |
| out += (SPOT_NCOMPS + 4); |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| cmyk->c = clip01(color->c[0]); |
| cmyk->m = clip01(color->c[1]); |
| cmyk->y = clip01(color->c[2]); |
| cmyk->k = clip01(color->c[3]); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| clearGfxColor(deviceN); |
| deviceN->c[0] = clip01(color->c[0]); |
| deviceN->c[1] = clip01(color->c[1]); |
| deviceN->c[2] = clip01(color->c[2]); |
| deviceN->c[3] = clip01(color->c[3]); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| color->c[0] = 0; |
| color->c[1] = 0; |
| color->c[2] = 0; |
| color->c[3] = gfxColorComp1; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxLabColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxLabColorSpace::GfxLabColorSpace() |
| { |
| whiteX = whiteY = whiteZ = 1; |
| blackX = blackY = blackZ = 0; |
| aMin = bMin = -100; |
| aMax = bMax = 100; |
| } |
| |
| GfxLabColorSpace::~GfxLabColorSpace() { } |
| |
| std::unique_ptr<GfxColorSpace> GfxLabColorSpace::copy() const |
| { |
| auto cs = std::make_unique<GfxLabColorSpace>(); |
| cs->whiteX = whiteX; |
| cs->whiteY = whiteY; |
| cs->whiteZ = whiteZ; |
| cs->blackX = blackX; |
| cs->blackY = blackY; |
| cs->blackZ = blackZ; |
| cs->aMin = aMin; |
| cs->aMax = aMax; |
| cs->bMin = bMin; |
| cs->bMax = bMax; |
| #ifdef USE_CMS |
| cs->transform = transform; |
| #endif |
| return cs; |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxLabColorSpace::parse(Array *arr, GfxState *state) |
| { |
| Object obj1, obj2; |
| |
| obj1 = arr->get(1); |
| if (!obj1.isDict()) { |
| error(errSyntaxWarning, -1, "Bad Lab color space"); |
| return {}; |
| } |
| auto cs = std::make_unique<GfxLabColorSpace>(); |
| bool ok = true; |
| obj2 = obj1.dictLookup("WhitePoint"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->whiteX = obj2.arrayGet(0).getNum(&ok); |
| cs->whiteY = obj2.arrayGet(1).getNum(&ok); |
| cs->whiteZ = obj2.arrayGet(2).getNum(&ok); |
| } |
| obj2 = obj1.dictLookup("BlackPoint"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 3) { |
| cs->blackX = obj2.arrayGet(0).getNum(&ok); |
| cs->blackY = obj2.arrayGet(1).getNum(&ok); |
| cs->blackZ = obj2.arrayGet(2).getNum(&ok); |
| } |
| obj2 = obj1.dictLookup("Range"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 4) { |
| cs->aMin = obj2.arrayGet(0).getNum(&ok); |
| cs->aMax = obj2.arrayGet(1).getNum(&ok); |
| cs->bMin = obj2.arrayGet(2).getNum(&ok); |
| cs->bMax = obj2.arrayGet(3).getNum(&ok); |
| } |
| |
| if (!ok) { |
| error(errSyntaxWarning, -1, "Bad Lab color space"); |
| #ifdef USE_CMS |
| cs->transform = nullptr; |
| #endif |
| return {}; |
| } |
| |
| #ifdef USE_CMS |
| cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr; |
| #endif |
| return cs; |
| } |
| |
| void GfxLabColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| GfxRGB rgb; |
| |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| getXYZ(color, &in[0], &in[1], &in[2]); |
| bradford_transform_to_d50(in[0], in[1], in[2], whiteX, whiteY, whiteZ); |
| transform->doTransform(in, out, 1); |
| *gray = byteToCol(out[0]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); |
| } |
| |
| // convert L*a*b* to media XYZ color space |
| // (not multiply by the white point) |
| void GfxLabColorSpace::getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const |
| { |
| double X, Y, Z; |
| double t1, t2; |
| |
| t1 = (colToDbl(color->c[0]) + 16) / 116; |
| t2 = t1 + colToDbl(color->c[1]) / 500; |
| if (t2 >= (6.0 / 29.0)) { |
| X = t2 * t2 * t2; |
| } else { |
| X = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); |
| } |
| if (t1 >= (6.0 / 29.0)) { |
| Y = t1 * t1 * t1; |
| } else { |
| Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0)); |
| } |
| t2 = t1 - colToDbl(color->c[2]) / 200; |
| if (t2 >= (6.0 / 29.0)) { |
| Z = t2 * t2 * t2; |
| } else { |
| Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); |
| } |
| *pX = X; |
| *pY = Y; |
| *pZ = Z; |
| } |
| |
| void GfxLabColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| double X, Y, Z; |
| |
| getXYZ(color, &X, &Y, &Z); |
| X *= whiteX; |
| Y *= whiteY; |
| Z *= whiteZ; |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| return; |
| } else if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { |
| unsigned char out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| double c, m, y, k, c1, m1, y1, k1, r, g, b; |
| |
| bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); |
| in[0] = X; |
| in[1] = Y; |
| in[2] = Z; |
| transform->doTransform(in, out, 1); |
| c = byteToDbl(out[0]); |
| m = byteToDbl(out[1]); |
| y = byteToDbl(out[2]); |
| k = byteToDbl(out[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->r = clip01(dblToCol(r)); |
| rgb->g = clip01(dblToCol(g)); |
| rgb->b = clip01(dblToCol(b)); |
| return; |
| } |
| #endif |
| bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ); |
| // convert XYZ to RGB, including gamut mapping and gamma correction |
| const double r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; |
| const double g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; |
| const double b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; |
| rgb->r = dblToCol(srgb_gamma_function(clip01(r))); |
| rgb->g = dblToCol(srgb_gamma_function(clip01(g))); |
| rgb->b = dblToCol(srgb_gamma_function(clip01(b))); |
| } |
| |
| void GfxLabColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { |
| double in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| |
| getXYZ(color, &in[0], &in[1], &in[2]); |
| bradford_transform_to_d50(in[0], in[1], in[2], whiteX, whiteY, whiteZ); |
| transform->doTransform(in, out, 1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxLabColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| GfxCMYK cmyk; |
| clearGfxColor(deviceN); |
| getCMYK(color, &cmyk); |
| deviceN->c[0] = cmyk.c; |
| deviceN->c[1] = cmyk.m; |
| deviceN->c[2] = cmyk.y; |
| deviceN->c[3] = cmyk.k; |
| } |
| |
| void GfxLabColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| color->c[0] = 0; |
| if (aMin > 0) { |
| color->c[1] = dblToCol(aMin); |
| } else if (aMax < 0) { |
| color->c[1] = dblToCol(aMax); |
| } else { |
| color->c[1] = 0; |
| } |
| if (bMin > 0) { |
| color->c[2] = dblToCol(bMin); |
| } else if (bMax < 0) { |
| color->c[2] = dblToCol(bMax); |
| } else { |
| color->c[2] = 0; |
| } |
| } |
| |
| void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const |
| { |
| decodeLow[0] = 0; |
| decodeRange[0] = 100; |
| decodeLow[1] = aMin; |
| decodeRange[1] = aMax - aMin; |
| decodeLow[2] = bMin; |
| decodeRange[2] = bMax - bMin; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxICCBasedColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, std::unique_ptr<GfxColorSpace> &&altA, const Ref *iccProfileStreamA) : alt(std::move(altA)) |
| { |
| nComps = nCompsA; |
| iccProfileStream = *iccProfileStreamA; |
| rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0; |
| rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1; |
| #ifdef USE_CMS |
| transform = nullptr; |
| lineTransform = nullptr; |
| psCSA = nullptr; |
| #endif |
| } |
| |
| GfxICCBasedColorSpace::~GfxICCBasedColorSpace() |
| { |
| #ifdef USE_CMS |
| if (psCSA) { |
| gfree(psCSA); |
| } |
| #endif |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxICCBasedColorSpace::copy() const |
| { |
| return copyAsOwnType(); |
| } |
| |
| std::unique_ptr<GfxICCBasedColorSpace> GfxICCBasedColorSpace::copyAsOwnType() const |
| { |
| int i; |
| |
| auto cs = std::make_unique<GfxICCBasedColorSpace>(nComps, alt->copy(), &iccProfileStream); |
| for (i = 0; i < 4; ++i) { |
| cs->rangeMin[i] = rangeMin[i]; |
| cs->rangeMax[i] = rangeMax[i]; |
| } |
| #ifdef USE_CMS |
| cs->profile = profile; |
| cs->transform = transform; |
| cs->lineTransform = lineTransform; |
| #endif |
| return cs; |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState *state, int recursion) |
| { |
| int nCompsA; |
| Dict *dict; |
| Object obj1, obj2; |
| int i; |
| |
| if (arr->getLength() < 2) { |
| error(errSyntaxError, -1, "Bad ICCBased color space"); |
| return {}; |
| } |
| const Object &obj1Ref = arr->getNF(1); |
| const Ref iccProfileStreamA = obj1Ref.isRef() ? obj1Ref.getRef() : Ref::INVALID(); |
| #ifdef USE_CMS |
| // check cache |
| if (out && iccProfileStreamA != Ref::INVALID()) { |
| if (auto *item = out->getIccColorSpaceCache()->lookup(iccProfileStreamA)) { |
| std::unique_ptr<GfxICCBasedColorSpace> cs = item->copyAsOwnType(); |
| int transformIntent = cs->getIntent(); |
| int cmsIntent = INTENT_RELATIVE_COLORIMETRIC; |
| if (state != nullptr) { |
| cmsIntent = state->getCmsRenderingIntent(); |
| } |
| if (transformIntent == cmsIntent) { |
| return cs; |
| } |
| } |
| } |
| #endif |
| obj1 = arr->get(1); |
| if (!obj1.isStream()) { |
| error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)"); |
| return nullptr; |
| } |
| dict = obj1.streamGetDict(); |
| obj2 = dict->lookup("N"); |
| if (!obj2.isInt()) { |
| error(errSyntaxWarning, -1, "Bad ICCBased color space (N)"); |
| return nullptr; |
| } |
| nCompsA = obj2.getInt(); |
| if (nCompsA > 4) { |
| error(errSyntaxError, -1, "ICCBased color space with too many ({0:d} > 4) components", nCompsA); |
| nCompsA = 4; |
| } |
| obj2 = dict->lookup("Alternate"); |
| std::unique_ptr<GfxColorSpace> altA; |
| if (obj2.isNull() || !(altA = GfxColorSpace::parse(nullptr, &obj2, out, state, recursion + 1))) { |
| switch (nCompsA) { |
| case 1: |
| altA = std::make_unique<GfxDeviceGrayColorSpace>(); |
| break; |
| case 3: |
| altA = std::make_unique<GfxDeviceRGBColorSpace>(); |
| break; |
| case 4: |
| altA = std::make_unique<GfxDeviceCMYKColorSpace>(); |
| break; |
| default: |
| error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N"); |
| return nullptr; |
| } |
| } |
| if (altA->getNComps() != nCompsA) { |
| error(errSyntaxWarning, -1, "Bad ICCBased color space - N doesn't match alt color space"); |
| return {}; |
| } |
| auto cs = std::make_unique<GfxICCBasedColorSpace>(nCompsA, std::move(altA), &iccProfileStreamA); |
| obj2 = dict->lookup("Range"); |
| if (obj2.isArray() && obj2.arrayGetLength() == 2 * nCompsA) { |
| for (i = 0; i < nCompsA; ++i) { |
| cs->rangeMin[i] = obj2.arrayGet(2 * i).getNumWithDefaultValue(0); |
| cs->rangeMax[i] = obj2.arrayGet(2 * i + 1).getNumWithDefaultValue(1); |
| } |
| } |
| |
| #ifdef USE_CMS |
| obj1 = arr->get(1); |
| if (!obj1.isStream()) { |
| error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)"); |
| return {}; |
| } |
| Stream *iccStream = obj1.getStream(); |
| |
| const std::vector<unsigned char> profBuf = iccStream->toUnsignedChars(65536, 65536); |
| auto hp = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(profBuf.data(), profBuf.size())); |
| cs->profile = hp; |
| if (!hp) { |
| error(errSyntaxWarning, -1, "read ICCBased color space profile error"); |
| } else { |
| cs->buildTransforms(state); |
| } |
| // put this colorSpace into cache |
| if (out && iccProfileStreamA != Ref::INVALID()) { |
| out->getIccColorSpaceCache()->put(iccProfileStreamA, cs->copyAsOwnType()); |
| } |
| #endif |
| return cs; |
| } |
| |
| #ifdef USE_CMS |
| void GfxICCBasedColorSpace::buildTransforms(GfxState *state) |
| { |
| auto dhp = (state != nullptr && state->getDisplayProfile() != nullptr) ? state->getDisplayProfile() : nullptr; |
| if (!dhp) { |
| dhp = GfxState::sRGBProfile; |
| } |
| unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(profile.get())); |
| unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp.get())); |
| unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp.get())); |
| cmsHTRANSFORM transformA; |
| |
| int cmsIntent = INTENT_RELATIVE_COLORIMETRIC; |
| if (state != nullptr) { |
| cmsIntent = state->getCmsRenderingIntent(); |
| } |
| if ((transformA = cmsCreateTransform(profile.get(), COLORSPACE_SH(cst) | CHANNELS_SH(nComps) | BYTES_SH(1), dhp.get(), COLORSPACE_SH(dcst) | CHANNELS_SH(dNChannels) | BYTES_SH(1), cmsIntent, LCMS_FLAGS)) == nullptr) { |
| error(errSyntaxWarning, -1, "Can't create transform"); |
| transform = nullptr; |
| } else { |
| transform = std::make_shared<GfxColorTransform>(transformA, cmsIntent, cst, dcst); |
| } |
| if (dcst == PT_RGB || dcst == PT_CMYK) { |
| // create line transform only when the display is RGB type color space |
| if ((transformA = cmsCreateTransform(profile.get(), CHANNELS_SH(nComps) | BYTES_SH(1), dhp.get(), (dcst == PT_RGB) ? TYPE_RGB_8 : TYPE_CMYK_8, cmsIntent, LCMS_FLAGS)) == nullptr) { |
| error(errSyntaxWarning, -1, "Can't create transform"); |
| lineTransform = nullptr; |
| } else { |
| lineTransform = std::make_shared<GfxColorTransform>(transformA, cmsIntent, cst, dcst); |
| } |
| } |
| } |
| #endif |
| |
| void GfxICCBasedColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) { |
| unsigned char in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| |
| if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { |
| in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); |
| in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); |
| in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); |
| } else { |
| for (int i = 0; i < nComps; i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| } |
| if (nComps <= 4) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key); |
| if (it != cmsCache.end()) { |
| unsigned int value = it->second; |
| *gray = byteToCol(value & 0xff); |
| return; |
| } |
| } |
| transform->doTransform(in, out, 1); |
| *gray = byteToCol(out[0]); |
| if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| unsigned int value = out[0]; |
| cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value)); |
| } |
| } else { |
| GfxRGB rgb; |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.3 * rgb.r + 0.59 * rgb.g + 0.11 * rgb.b + 0.5)); |
| } |
| #else |
| alt->getGray(color, gray); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) { |
| unsigned char in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| |
| if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { |
| in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); |
| in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); |
| in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); |
| } else { |
| for (int i = 0; i < nComps; i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| } |
| if (nComps <= 4) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key); |
| if (it != cmsCache.end()) { |
| unsigned int value = it->second; |
| rgb->r = byteToCol(value >> 16); |
| rgb->g = byteToCol((value >> 8) & 0xff); |
| rgb->b = byteToCol(value & 0xff); |
| return; |
| } |
| } |
| transform->doTransform(in, out, 1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| unsigned int value = (out[0] << 16) + (out[1] << 8) + out[2]; |
| cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value)); |
| } |
| } else if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { |
| unsigned char in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| double c, m, y, k, c1, m1, y1, k1, r, g, b; |
| |
| if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { |
| in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); |
| in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); |
| in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); |
| } else { |
| for (int i = 0; i < nComps; i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| } |
| if (nComps <= 4) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key); |
| if (it != cmsCache.end()) { |
| unsigned int value = it->second; |
| rgb->r = byteToCol(value >> 16); |
| rgb->g = byteToCol((value >> 8) & 0xff); |
| rgb->b = byteToCol(value & 0xff); |
| return; |
| } |
| } |
| transform->doTransform(in, out, 1); |
| c = byteToDbl(out[0]); |
| m = byteToDbl(out[1]); |
| y = byteToDbl(out[2]); |
| k = byteToDbl(out[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->r = clip01(dblToCol(r)); |
| rgb->g = clip01(dblToCol(g)); |
| rgb->b = clip01(dblToCol(b)); |
| if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| unsigned int value = (dblToByte(r) << 16) + (dblToByte(g) << 8) + dblToByte(b); |
| cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value)); |
| } |
| } else { |
| alt->getRGB(color, rgb); |
| } |
| #else |
| alt->getRGB(color, rgb); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) |
| { |
| #ifdef USE_CMS |
| if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) { |
| unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); |
| lineTransform->doTransform(in, tmp, length); |
| for (int i = 0; i < length; ++i) { |
| unsigned char *current = tmp + (i * 3); |
| out[i] = (current[0] << 16) | (current[1] << 8) | current[2]; |
| } |
| gfree(tmp); |
| } else { |
| alt->getRGBLine(in, out, length); |
| } |
| #else |
| alt->getRGBLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) |
| { |
| #ifdef USE_CMS |
| if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) { |
| unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); |
| lineTransform->doTransform(in, tmp, length); |
| unsigned char *current = tmp; |
| for (int i = 0; i < length; ++i) { |
| *out++ = *current++; |
| *out++ = *current++; |
| *out++ = *current++; |
| } |
| gfree(tmp); |
| } else if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) { |
| unsigned char *tmp = (unsigned char *)gmallocn(4 * length, sizeof(unsigned char)); |
| lineTransform->doTransform(in, tmp, length); |
| unsigned char *current = tmp; |
| double c, m, y, k, c1, m1, y1, k1, r, g, b; |
| for (int i = 0; i < length; ++i) { |
| c = byteToDbl(*current++); |
| m = byteToDbl(*current++); |
| y = byteToDbl(*current++); |
| k = byteToDbl(*current++); |
| c1 = 1 - c; |
| m1 = 1 - m; |
| y1 = 1 - y; |
| k1 = 1 - k; |
| cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); |
| *out++ = dblToByte(r); |
| *out++ = dblToByte(g); |
| *out++ = dblToByte(b); |
| } |
| gfree(tmp); |
| } else { |
| alt->getRGBLine(in, out, length); |
| } |
| #else |
| alt->getRGBLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) |
| { |
| #ifdef USE_CMS |
| if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) { |
| unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); |
| lineTransform->doTransform(in, tmp, length); |
| unsigned char *current = tmp; |
| for (int i = 0; i < length; ++i) { |
| *out++ = *current++; |
| *out++ = *current++; |
| *out++ = *current++; |
| *out++ = 255; |
| } |
| gfree(tmp); |
| } else { |
| alt->getRGBXLine(in, out, length); |
| } |
| #else |
| alt->getRGBXLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) |
| { |
| #ifdef USE_CMS |
| if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) { |
| transform->doTransform(in, out, length); |
| } else if (lineTransform != nullptr && nComps != 4) { |
| GfxColorComp c, m, y, k; |
| unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); |
| getRGBLine(in, tmp, length); |
| unsigned char *p = tmp; |
| for (int i = 0; i < length; i++) { |
| c = byteToCol(255 - *p++); |
| m = byteToCol(255 - *p++); |
| y = byteToCol(255 - *p++); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| *out++ = colToByte(c - k); |
| *out++ = colToByte(m - k); |
| *out++ = colToByte(y - k); |
| *out++ = colToByte(k); |
| } |
| gfree(tmp); |
| } else { |
| alt->getCMYKLine(in, out, length); |
| } |
| #else |
| alt->getCMYKLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) |
| { |
| #ifdef USE_CMS |
| if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) { |
| unsigned char *tmp = (unsigned char *)gmallocn(4 * length, sizeof(unsigned char)); |
| transform->doTransform(in, tmp, length); |
| unsigned char *p = tmp; |
| for (int i = 0; i < length; i++) { |
| for (int j = 0; j < 4; j++) { |
| *out++ = *p++; |
| } |
| for (int j = 4; j < SPOT_NCOMPS + 4; j++) { |
| *out++ = 0; |
| } |
| } |
| gfree(tmp); |
| } else if (lineTransform != nullptr && nComps != 4) { |
| GfxColorComp c, m, y, k; |
| unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); |
| getRGBLine(in, tmp, length); |
| unsigned char *p = tmp; |
| for (int i = 0; i < length; i++) { |
| for (int j = 0; j < SPOT_NCOMPS + 4; j++) { |
| out[j] = 0; |
| } |
| c = byteToCol(255 - *p++); |
| m = byteToCol(255 - *p++); |
| y = byteToCol(255 - *p++); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| out[0] = colToByte(c - k); |
| out[1] = colToByte(m - k); |
| out[2] = colToByte(y - k); |
| out[3] = colToByte(k); |
| out += (SPOT_NCOMPS + 4); |
| } |
| gfree(tmp); |
| } else { |
| alt->getDeviceNLine(in, out, length); |
| } |
| #else |
| alt->getDeviceNLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const |
| { |
| #ifdef USE_CMS |
| if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { |
| unsigned char in[gfxColorMaxComps]; |
| unsigned char out[gfxColorMaxComps]; |
| |
| if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { |
| in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); |
| in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); |
| in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); |
| } else { |
| for (int i = 0; i < nComps; i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| } |
| if (nComps <= 4) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key); |
| if (it != cmsCache.end()) { |
| unsigned int value = it->second; |
| cmyk->c = byteToCol(value >> 24); |
| cmyk->m = byteToCol((value >> 16) & 0xff); |
| cmyk->y = byteToCol((value >> 8) & 0xff); |
| cmyk->k = byteToCol(value & 0xff); |
| return; |
| } |
| } |
| transform->doTransform(in, out, 1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { |
| unsigned int key = 0; |
| for (int j = 0; j < nComps; j++) { |
| key = (key << 8) + in[j]; |
| } |
| unsigned int value = (out[0] << 24) + (out[1] << 16) + (out[2] << 8) + out[3]; |
| cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value)); |
| } |
| } else if (nComps != 4 && transform != nullptr && transform->getTransformPixelType() == PT_RGB) { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } else { |
| alt->getCMYK(color, cmyk); |
| } |
| #else |
| alt->getCMYK(color, cmyk); |
| #endif |
| } |
| |
| bool GfxICCBasedColorSpace::useGetRGBLine() const |
| { |
| #ifdef USE_CMS |
| return lineTransform != nullptr || alt->useGetRGBLine(); |
| #else |
| return alt->useGetRGBLine(); |
| #endif |
| } |
| |
| bool GfxICCBasedColorSpace::useGetCMYKLine() const |
| { |
| #ifdef USE_CMS |
| return lineTransform != nullptr || alt->useGetCMYKLine(); |
| #else |
| return alt->useGetCMYKLine(); |
| #endif |
| } |
| |
| bool GfxICCBasedColorSpace::useGetDeviceNLine() const |
| { |
| #ifdef USE_CMS |
| return lineTransform != nullptr || alt->useGetDeviceNLine(); |
| #else |
| return alt->useGetDeviceNLine(); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const |
| { |
| GfxCMYK cmyk; |
| clearGfxColor(deviceN); |
| getCMYK(color, &cmyk); |
| deviceN->c[0] = cmyk.c; |
| deviceN->c[1] = cmyk.m; |
| deviceN->c[2] = cmyk.y; |
| deviceN->c[3] = cmyk.k; |
| } |
| |
| void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) const |
| { |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| if (rangeMin[i] > 0) { |
| color->c[i] = dblToCol(rangeMin[i]); |
| } else if (rangeMax[i] < 0) { |
| color->c[i] = dblToCol(rangeMax[i]); |
| } else { |
| color->c[i] = 0; |
| } |
| } |
| } |
| |
| void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const |
| { |
| alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel); |
| |
| #if 0 |
| // this is nominally correct, but some PDF files don't set the |
| // correct ranges in the ICCBased dict |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| decodeLow[i] = rangeMin[i]; |
| decodeRange[i] = rangeMax[i] - rangeMin[i]; |
| } |
| #endif |
| } |
| |
| #ifdef USE_CMS |
| char *GfxICCBasedColorSpace::getPostScriptCSA() |
| { |
| # if LCMS_VERSION >= 2070 |
| // The runtime version check of lcms2 is only available from release 2.7 upwards. |
| // The generation of the CSA code only works reliably for version 2.10 and upwards. |
| // Cf. the explanation in the corresponding lcms2 merge request [1], and the original mail thread [2]. |
| // [1] https://github.com/mm2/Little-CMS/pull/214 |
| // [2] https://sourceforge.net/p/lcms/mailman/message/33182987/ |
| if (cmsGetEncodedCMMversion() < 2100) { |
| return nullptr; |
| } |
| |
| int size; |
| |
| if (psCSA) { |
| return psCSA; |
| } |
| |
| if (!profile) { |
| error(errSyntaxWarning, -1, "profile is nullptr"); |
| return nullptr; |
| } |
| |
| void *rawprofile = profile.get(); |
| size = cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, nullptr, 0); |
| if (size == 0) { |
| error(errSyntaxWarning, -1, "PostScript CSA is nullptr"); |
| return nullptr; |
| } |
| |
| psCSA = (char *)gmalloc(size + 1); |
| cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, psCSA, size); |
| psCSA[size] = 0; |
| |
| // TODO REMOVE-ME-IN-THE-FUTURE |
| // until we can depend on https://github.com/mm2/Little-CMS/issues/223 being fixed |
| // lcms returns ps code with , instead of . for some locales. The lcms author says |
| // that there's no room for any , in the rest of the ps code, so replacing all the , with . |
| // is an "acceptable" workaround |
| for (int i = 0; i < size; ++i) { |
| if (psCSA[i] == ',') { |
| psCSA[i] = '.'; |
| } |
| } |
| |
| return psCSA; |
| # else |
| return nullptr; |
| # endif |
| } |
| #endif |
| |
| //------------------------------------------------------------------------ |
| // GfxIndexedColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxIndexedColorSpace::GfxIndexedColorSpace(std::unique_ptr<GfxColorSpace> &&baseA, int indexHighA) : base(std::move(baseA)) |
| { |
| indexHigh = indexHighA; |
| lookup = (unsigned char *)gmallocn((indexHigh + 1) * base->getNComps(), sizeof(unsigned char)); |
| overprintMask = base->getOverprintMask(); |
| } |
| |
| GfxIndexedColorSpace::~GfxIndexedColorSpace() |
| { |
| gfree(lookup); |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxIndexedColorSpace::copy() const |
| { |
| |
| auto cs = std::make_unique<GfxIndexedColorSpace>(base->copy(), indexHigh); |
| memcpy(cs->lookup, lookup, (indexHigh + 1) * base->getNComps() * sizeof(unsigned char)); |
| return cs; |
| } |
| |
| std::unique_ptr<GfxColorSpace> GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) |
| { |
| std::unique_ptr<GfxColorSpace> baseA; |
| int indexHighA; |
| Object obj1; |
| const char *s; |
| int i, j; |
| |
| if (arr->getLength() != 4) { |
| error(errSyntaxWarning, -1, "Bad Indexed color space"); |
| return nullptr; |
| } |
| obj1 = arr->get(1); |
| if (!(baseA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) { |
| error(errSyntaxWarning, -1, "Bad Indexed color space (base color space)"); |
| return {}; |
| } |
| obj1 = arr->get(2); |
| if (!obj1.isInt()) { |
| error(errSyntaxWarning, -1, "Bad Indexed color space (hival)"); |
| return {}; |
| } |
| indexHighA = obj1.getInt(); |
| if (indexHighA < 0 || indexHighA > 255) { |
| // the PDF spec requires indexHigh to be in [0,255] -- allowing |
| // values larger than 255 creates a security hole: if nComps * |
| // indexHigh is greater than 2^31, the loop below may overwrite |
| // past the end of the array |
| int previousValue = indexHighA; |
| if (indexHighA < 0) { |
| indexHighA = 0; |
| } else { |
| indexHighA = 255; |
| } |
| error(errSyntaxWarning, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue, indexHighA); |
| } |
| auto cs = std::make_unique<GfxIndexedColorSpace>(std::move(baseA), indexHighA); |
| obj1 = arr->get(3); |
| const int n = cs->getBase()->getNComps(); |
| if (obj1.isStream()) { |
| obj1.streamReset(); |
| for (i = 0; i <= indexHighA; ++i) { |
| const int readChars = obj1.streamGetChars(n, &cs->lookup[i * n]); |
| for (j = readChars; j < n; ++j) { |
| error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes"); |
| cs->lookup[i * n + j] = 0; |
| } |
| } |
| obj1.streamClose(); |
| } else if (obj1.isString()) { |
| if (obj1.getString()->getLength() < (indexHighA + 1) * n) { |
| error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table string too short)"); |
| goto err3; |
| } |
| s = obj1.getString()->c_str(); |
| for (i = 0; i <= indexHighA; ++i) { |
| for (j = 0; j < n; ++j) { |
| cs->lookup[i * n + j] = (unsigned char)*s++; |
| } |
| } |
| } else { |
| error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table)"); |
| goto err3; |
| } |
| return cs; |
| |
| err3: |
| return {}; |
| } |
| |
| GfxColor *GfxIndexedColorSpace::mapColorToBase(const GfxColor *color, GfxColor *baseColor) const |
| { |
| unsigned char *p; |
| double low[gfxColorMaxComps], range[gfxColorMaxComps]; |
| int n, i; |
| |
| n = base->getNComps(); |
| base->getDefaultRanges(low, range, indexHigh); |
| const int idx = (int)(colToDbl(color->c[0]) + 0.5) * n; |
| if (likely((idx + n - 1 < (indexHigh + 1) * base->getNComps()) && idx >= 0)) { |
| p = &lookup[idx]; |
| for (i = 0; i < n; ++i) { |
| baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]); |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| baseColor->c[i] = 0; |
| } |
| } |
| return baseColor; |
| } |
| |
| void GfxIndexedColorSpace::getGray(const GfxColor *color, GfxGray *gray) const |
| { |
| GfxColor color2; |
| |
| base->getGray(mapColorToBase(color, &color2), gray); |
| } |
| |
| void GfxIndexedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const |
| { |
| GfxColor color2; |
| |
| base->getRGB(mapColorToBase(color, &color2), rgb); |
| } |
| |
| void GfxIndexedColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) |
| { |
| unsigned char *line; |
| int i, j, n; |
| |
| n = base->getNComps(); |
| line = (unsigned char *)gmallocn(length, n); |
| for (i = 0; i < length; i++) { |
| for (j = 0; j < n; j++) { |
| line[i * n + j] = lookup[in[i] * n + j]; |
| } |
| } |
| |
| base->getRGBLine(line, out, length); |
| |
| gfree(line); |
| } |
| |
| void GfxIndexedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) |
| { |
| unsigned char *line; |
| int i, j, n; |
| |
| n = base->getNComps(); |
| line = (unsigned char *)gmallocn(length, n); |
| for (i = 0; i < length; i++) { |
| for (j = 0; j < n; j++) { |
| line[i * n + j] = lookup[in[i] * n + j]; |
| } |
| } |
| |
| base->getRGBLine(line, out, length); |
| |
| gfree(line); |
| } |
| |
| void GfxIndexedColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) |
| { |
| unsigned char *line; |
| int i, j, n; |
| |
| n = base->getNComps(); |
| line = (unsigned char *)gmallocn(length, n); |
| for (i = 0; i < length; i++) { |
| for (j = 0; j < n; j++) { |
| line[i * n + j] = lookup[in[i] * n + j]; |
| } |
| } |
| |
| base->getRGBXLine(line, out, length); |
| |
| gfree(line); |
|