// Copyright 2014 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fpdfapi/page/cpdf_devicecs.h" #include #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfapi/parser/cpdf_stream_acc.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fxcodec/fx_codec.h" #include "core/fxge/dib/cfx_cmyk_to_srgb.h" #include "third_party/base/check.h" #include "third_party/base/notreached.h" namespace { float NormalizeChannel(float fVal) { return std::clamp(fVal, 0.0f, 1.0f); } } // namespace CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) { DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB || family == Family::kDeviceCMYK); SetComponentsForStockCS(ComponentsForFamily(GetFamily())); } CPDF_DeviceCS::~CPDF_DeviceCS() = default; uint32_t CPDF_DeviceCS::v_Load(CPDF_Document* pDoc, const CPDF_Array* pArray, std::set* pVisited) { // Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is // never loaded by CPDF_ColorSpace. NOTREACHED_NORETURN(); } bool CPDF_DeviceCS::GetRGB(pdfium::span pBuf, float* R, float* G, float* B) const { switch (GetFamily()) { case Family::kDeviceGray: *R = NormalizeChannel(pBuf[0]); *G = *R; *B = *R; return true; case Family::kDeviceRGB: *R = NormalizeChannel(pBuf[0]); *G = NormalizeChannel(pBuf[1]); *B = NormalizeChannel(pBuf[2]); return true; case Family::kDeviceCMYK: if (IsStdConversionEnabled()) { float k = pBuf[3]; *R = 1.0f - std::min(1.0f, pBuf[0] + k); *G = 1.0f - std::min(1.0f, pBuf[1] + k); *B = 1.0f - std::min(1.0f, pBuf[2] + k); } else { std::tie(*R, *G, *B) = AdobeCMYK_to_sRGB( NormalizeChannel(pBuf[0]), NormalizeChannel(pBuf[1]), NormalizeChannel(pBuf[2]), NormalizeChannel(pBuf[3])); } return true; default: NOTREACHED_NORETURN(); } } void CPDF_DeviceCS::TranslateImageLine(pdfium::span dest_span, pdfium::span src_span, int pixels, int image_width, int image_height, bool bTransMask) const { uint8_t* pDestBuf = dest_span.data(); const uint8_t* pSrcBuf = src_span.data(); switch (GetFamily()) { case Family::kDeviceGray: for (int i = 0; i < pixels; i++) { // Compiler can not conclude that src/dest don't overlap, avoid // duplicate loads. const uint8_t pix = pSrcBuf[i]; *pDestBuf++ = pix; *pDestBuf++ = pix; *pDestBuf++ = pix; } break; case Family::kDeviceRGB: fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels); break; case Family::kDeviceCMYK: if (bTransMask) { for (int i = 0; i < pixels; i++) { // Compiler can't conclude src/dest don't overlap, avoid interleaved // loads and stores. const uint8_t s0 = pSrcBuf[0]; const uint8_t s1 = pSrcBuf[1]; const uint8_t s2 = pSrcBuf[2]; const int k = 255 - pSrcBuf[3]; pDestBuf[0] = ((255 - s0) * k) / 255; pDestBuf[1] = ((255 - s1) * k) / 255; pDestBuf[2] = ((255 - s2) * k) / 255; pDestBuf += 3; pSrcBuf += 4; } } else { if (IsStdConversionEnabled()) { for (int i = 0; i < pixels; i++) { // Compiler can't conclude src/dest don't overlap, avoid // interleaved loads and stores. const uint8_t s0 = pSrcBuf[0]; const uint8_t s1 = pSrcBuf[1]; const uint8_t s2 = pSrcBuf[2]; const uint8_t k = pSrcBuf[3]; pDestBuf[2] = 255 - std::min(255, s0 + k); pDestBuf[1] = 255 - std::min(255, s1 + k); pDestBuf[0] = 255 - std::min(255, s2 + k); pSrcBuf += 4; pDestBuf += 3; } } else { for (int i = 0; i < pixels; i++) { std::tie(pDestBuf[2], pDestBuf[1], pDestBuf[0]) = AdobeCMYK_to_sRGB1(pSrcBuf[0], pSrcBuf[1], pSrcBuf[2], pSrcBuf[3]); pSrcBuf += 4; pDestBuf += 3; } } } break; default: NOTREACHED_NORETURN(); } }