1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfapi/page/cpdf_devicecs.h"
8
9 #include <algorithm>
10
11 #include "core/fpdfapi/parser/cpdf_array.h"
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fpdfapi/parser/cpdf_document.h"
14 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
15 #include "core/fpdfapi/parser/cpdf_string.h"
16 #include "core/fxcodec/fx_codec.h"
17 #include "core/fxcrt/check.h"
18 #include "core/fxcrt/compiler_specific.h"
19 #include "core/fxcrt/notreached.h"
20 #include "core/fxge/dib/cfx_cmyk_to_srgb.h"
21
22 namespace {
23
NormalizeChannel(float fVal)24 float NormalizeChannel(float fVal) {
25 return std::clamp(fVal, 0.0f, 1.0f);
26 }
27
28 } // namespace
29
CPDF_DeviceCS(Family family)30 CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) {
31 DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB ||
32 family == Family::kDeviceCMYK);
33 SetComponentsForStockCS(ComponentsForFamily(GetFamily()));
34 }
35
36 CPDF_DeviceCS::~CPDF_DeviceCS() = default;
37
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)38 uint32_t CPDF_DeviceCS::v_Load(CPDF_Document* pDoc,
39 const CPDF_Array* pArray,
40 std::set<const CPDF_Object*>* pVisited) {
41 // Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is
42 // never loaded by CPDF_ColorSpace.
43 NOTREACHED_NORETURN();
44 }
45
GetRGB(pdfium::span<const float> pBuf) const46 std::optional<FX_RGB_STRUCT<float>> CPDF_DeviceCS::GetRGB(
47 pdfium::span<const float> pBuf) const {
48 switch (GetFamily()) {
49 case Family::kDeviceGray: {
50 const float pix = NormalizeChannel(pBuf.front());
51 return FX_RGB_STRUCT<float>{pix, pix, pix};
52 }
53 case Family::kDeviceRGB: {
54 const auto& rgb =
55 fxcrt::reinterpret_span<const FX_RGB_STRUCT<float>>(pBuf).front();
56 return FX_RGB_STRUCT<float>{
57 NormalizeChannel(rgb.red),
58 NormalizeChannel(rgb.green),
59 NormalizeChannel(rgb.blue),
60 };
61 }
62 case Family::kDeviceCMYK: {
63 const auto& cmyk =
64 fxcrt::reinterpret_span<const FX_CMYK_STRUCT<float>>(pBuf).front();
65 if (IsStdConversionEnabled()) {
66 return FX_RGB_STRUCT<float>{
67 1.0f - std::min(1.0f, cmyk.cyan + cmyk.key),
68 1.0f - std::min(1.0f, cmyk.magenta + cmyk.key),
69 1.0f - std::min(1.0f, cmyk.yellow + cmyk.key),
70 };
71 }
72 return AdobeCMYK_to_sRGB(
73 NormalizeChannel(cmyk.cyan), NormalizeChannel(cmyk.magenta),
74 NormalizeChannel(cmyk.yellow), NormalizeChannel(cmyk.key));
75 }
76 default:
77 NOTREACHED_NORETURN();
78 }
79 }
80
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const81 void CPDF_DeviceCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
82 pdfium::span<const uint8_t> src_span,
83 int pixels,
84 int image_width,
85 int image_height,
86 bool bTransMask) const {
87 auto rgb_out = fxcrt::reinterpret_span<FX_RGB_STRUCT<uint8_t>>(dest_span);
88 switch (GetFamily()) {
89 case Family::kDeviceGray:
90 CHECK(!bTransMask); // bTransMask only allowed for CMYK colorspaces.
91 // Compiler can't conclude src/dest don't overlap, avoid interleaved
92 // loads and stores by not using an auto& reference here.
93 for (const auto pix : src_span.first(pixels)) {
94 rgb_out.front().red = pix;
95 rgb_out.front().green = pix;
96 rgb_out.front().blue = pix;
97 rgb_out = rgb_out.subspan(1);
98 }
99 break;
100 case Family::kDeviceRGB:
101 CHECK(!bTransMask); // bTransMask only allowed for CMYK colorspaces.
102 fxcodec::ReverseRGB(dest_span, src_span, pixels);
103 break;
104 case Family::kDeviceCMYK: {
105 auto cmyk_in =
106 fxcrt::reinterpret_span<const FX_CMYK_STRUCT<uint8_t>>(src_span);
107 if (bTransMask) {
108 // Compiler can't conclude src/dest don't overlap, avoid interleaved
109 // loads and stores by not using an auto& reference here.
110 for (const auto cmyk : cmyk_in.first(pixels)) {
111 const int k = 255 - cmyk.key;
112 rgb_out.front().red = ((255 - cmyk.cyan) * k) / 255;
113 rgb_out.front().green = ((255 - cmyk.magenta) * k) / 255;
114 rgb_out.front().blue = ((255 - cmyk.yellow) * k) / 255;
115 rgb_out = rgb_out.subspan(1);
116 }
117 break;
118 }
119 if (IsStdConversionEnabled()) {
120 // Compiler can't conclude src/dest don't overlap, avoid interleaved
121 // loads and stores by not using am auto& reference here,
122 for (const auto cmyk : cmyk_in.first(pixels)) {
123 const uint8_t k = cmyk.key;
124 rgb_out.front().blue = 255 - std::min(255, cmyk.cyan + k);
125 rgb_out.front().green = 255 - std::min(255, cmyk.magenta + k);
126 rgb_out.front().red = 255 - std::min(255, cmyk.yellow + k);
127 rgb_out = rgb_out.subspan(1);
128 }
129 break;
130 }
131 for (const auto& cmyk : cmyk_in.first(pixels)) {
132 // TODO(tsepez): maybe this is a FX_BGR_STRUCT in reality?
133 FX_RGB_STRUCT<uint8_t> rgb =
134 AdobeCMYK_to_sRGB1(cmyk.cyan, cmyk.magenta, cmyk.yellow, cmyk.key);
135 rgb_out.front().red = rgb.blue;
136 rgb_out.front().green = rgb.green;
137 rgb_out.front().blue = rgb.red;
138 rgb_out = rgb_out.subspan(1);
139 }
140 break;
141 }
142 default:
143 NOTREACHED_NORETURN();
144 }
145 }
146