// 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/fxcrt/check.h" #include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/notreached.h" #include "core/fxge/dib/cfx_cmyk_to_srgb.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(); } std::optional> CPDF_DeviceCS::GetRGB( pdfium::span pBuf) const { switch (GetFamily()) { case Family::kDeviceGray: { const float pix = NormalizeChannel(pBuf.front()); return FX_RGB_STRUCT{pix, pix, pix}; } case Family::kDeviceRGB: { const auto& rgb = fxcrt::reinterpret_span>(pBuf).front(); return FX_RGB_STRUCT{ NormalizeChannel(rgb.red), NormalizeChannel(rgb.green), NormalizeChannel(rgb.blue), }; } case Family::kDeviceCMYK: { const auto& cmyk = fxcrt::reinterpret_span>(pBuf).front(); if (IsStdConversionEnabled()) { return FX_RGB_STRUCT{ 1.0f - std::min(1.0f, cmyk.cyan + cmyk.key), 1.0f - std::min(1.0f, cmyk.magenta + cmyk.key), 1.0f - std::min(1.0f, cmyk.yellow + cmyk.key), }; } return AdobeCMYK_to_sRGB( NormalizeChannel(cmyk.cyan), NormalizeChannel(cmyk.magenta), NormalizeChannel(cmyk.yellow), NormalizeChannel(cmyk.key)); } 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 { auto rgb_out = fxcrt::reinterpret_span>(dest_span); switch (GetFamily()) { case Family::kDeviceGray: CHECK(!bTransMask); // bTransMask only allowed for CMYK colorspaces. // Compiler can't conclude src/dest don't overlap, avoid interleaved // loads and stores by not using an auto& reference here. for (const auto pix : src_span.first(pixels)) { rgb_out.front().red = pix; rgb_out.front().green = pix; rgb_out.front().blue = pix; rgb_out = rgb_out.subspan(1); } break; case Family::kDeviceRGB: CHECK(!bTransMask); // bTransMask only allowed for CMYK colorspaces. fxcodec::ReverseRGB(dest_span, src_span, pixels); break; case Family::kDeviceCMYK: { auto cmyk_in = fxcrt::reinterpret_span>(src_span); if (bTransMask) { // Compiler can't conclude src/dest don't overlap, avoid interleaved // loads and stores by not using an auto& reference here. for (const auto cmyk : cmyk_in.first(pixels)) { const int k = 255 - cmyk.key; rgb_out.front().red = ((255 - cmyk.cyan) * k) / 255; rgb_out.front().green = ((255 - cmyk.magenta) * k) / 255; rgb_out.front().blue = ((255 - cmyk.yellow) * k) / 255; rgb_out = rgb_out.subspan(1); } break; } if (IsStdConversionEnabled()) { // Compiler can't conclude src/dest don't overlap, avoid interleaved // loads and stores by not using am auto& reference here, for (const auto cmyk : cmyk_in.first(pixels)) { const uint8_t k = cmyk.key; rgb_out.front().blue = 255 - std::min(255, cmyk.cyan + k); rgb_out.front().green = 255 - std::min(255, cmyk.magenta + k); rgb_out.front().red = 255 - std::min(255, cmyk.yellow + k); rgb_out = rgb_out.subspan(1); } break; } for (const auto& cmyk : cmyk_in.first(pixels)) { // TODO(tsepez): maybe this is a FX_BGR_STRUCT in reality? FX_RGB_STRUCT rgb = AdobeCMYK_to_sRGB1(cmyk.cyan, cmyk.magenta, cmyk.yellow, cmyk.key); rgb_out.front().red = rgb.blue; rgb_out.front().green = rgb.green; rgb_out.front().blue = rgb.red; rgb_out = rgb_out.subspan(1); } break; } default: NOTREACHED_NORETURN(); } }