| // 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 <algorithm> | |
| #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* doc, | |
| const CPDF_Array* pArray, | |
| std::set<const CPDF_Object*>* pVisited) { | |
| // Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is | |
| // never loaded by CPDF_ColorSpace. | |
| NOTREACHED(); | |
| } | |
| std::optional<FX_RGB_STRUCT<float>> CPDF_DeviceCS::GetRGB( | |
| pdfium::span<const float> pBuf) const { | |
| switch (GetFamily()) { | |
| case Family::kDeviceGray: { | |
| const float pix = NormalizeChannel(pBuf.front()); | |
| return FX_RGB_STRUCT<float>{pix, pix, pix}; | |
| } | |
| case Family::kDeviceRGB: { | |
| const auto& rgb = | |
| fxcrt::reinterpret_span<const FX_RGB_STRUCT<float>>(pBuf).front(); | |
| return FX_RGB_STRUCT<float>{ | |
| NormalizeChannel(rgb.red), | |
| NormalizeChannel(rgb.green), | |
| NormalizeChannel(rgb.blue), | |
| }; | |
| } | |
| case Family::kDeviceCMYK: { | |
| const auto& cmyk = | |
| fxcrt::reinterpret_span<const FX_CMYK_STRUCT<float>>(pBuf).front(); | |
| if (IsStdConversionEnabled()) { | |
| return FX_RGB_STRUCT<float>{ | |
| 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 AdobeCmykToStandardRgbF( | |
| NormalizeChannel(cmyk.cyan), NormalizeChannel(cmyk.magenta), | |
| NormalizeChannel(cmyk.yellow), NormalizeChannel(cmyk.key)); | |
| } | |
| default: | |
| NOTREACHED(); | |
| } | |
| } | |
| void CPDF_DeviceCS::TranslateImageLine(pdfium::span<uint8_t> dest_span, | |
| pdfium::span<const uint8_t> src_span, | |
| int pixels, | |
| int image_width, | |
| int image_height, | |
| bool bTransMask) const { | |
| auto rgb_out = fxcrt::reinterpret_span<FX_RGB_STRUCT<uint8_t>>(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(static_cast<size_t>(pixels))) { | |
| rgb_out.front().red = pix; | |
| rgb_out.front().green = pix; | |
| rgb_out.front().blue = pix; | |
| rgb_out = rgb_out.subspan<1u>(); | |
| } | |
| 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<const FX_CMYK_STRUCT<uint8_t>>(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(static_cast<size_t>(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<1u>(); | |
| } | |
| 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(static_cast<size_t>(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<1u>(); | |
| } | |
| break; | |
| } | |
| for (const auto& cmyk : cmyk_in.first(static_cast<size_t>(pixels))) { | |
| // TODO(tsepez): maybe this is a FX_BGR_STRUCT in reality? | |
| FX_RGB_STRUCT<uint8_t> rgb = AdobeCmykToStandardRgb( | |
| 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<1u>(); | |
| } | |
| break; | |
| } | |
| default: | |
| NOTREACHED(); | |
| } | |
| } |