1 // Copyright 2016 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_colorspace.h"
8
9 #include <math.h>
10 #include <stdint.h>
11
12 #include <algorithm>
13 #include <array>
14 #include <limits>
15 #include <memory>
16 #include <optional>
17 #include <type_traits>
18 #include <utility>
19 #include <vector>
20
21 #include "core/fpdfapi/page/cpdf_devicecs.h"
22 #include "core/fpdfapi/page/cpdf_docpagedata.h"
23 #include "core/fpdfapi/page/cpdf_function.h"
24 #include "core/fpdfapi/page/cpdf_iccprofile.h"
25 #include "core/fpdfapi/page/cpdf_indexedcs.h"
26 #include "core/fpdfapi/page/cpdf_pattern.h"
27 #include "core/fpdfapi/page/cpdf_patterncs.h"
28 #include "core/fpdfapi/parser/cpdf_array.h"
29 #include "core/fpdfapi/parser/cpdf_dictionary.h"
30 #include "core/fpdfapi/parser/cpdf_document.h"
31 #include "core/fpdfapi/parser/cpdf_name.h"
32 #include "core/fpdfapi/parser/cpdf_object.h"
33 #include "core/fpdfapi/parser/cpdf_stream.h"
34 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
35 #include "core/fxcodec/fx_codec.h"
36 #include "core/fxcodec/icc/icc_transform.h"
37 #include "core/fxcrt/check.h"
38 #include "core/fxcrt/check_op.h"
39 #include "core/fxcrt/compiler_specific.h"
40 #include "core/fxcrt/containers/contains.h"
41 #include "core/fxcrt/data_vector.h"
42 #include "core/fxcrt/fx_2d_size.h"
43 #include "core/fxcrt/fx_safe_types.h"
44 #include "core/fxcrt/fx_system.h"
45 #include "core/fxcrt/maybe_owned.h"
46 #include "core/fxcrt/notreached.h"
47 #include "core/fxcrt/scoped_set_insertion.h"
48 #include "core/fxcrt/stl_util.h"
49 #include "core/fxcrt/zip.h"
50 #include "core/fxge/dib/fx_dib.h"
51
52 namespace {
53
54 constexpr auto kSRGBSamples1 = fxcrt::ToArray<const uint8_t>({
55 0, 3, 6, 10, 13, 15, 18, 20, 22, 23, 25, 27, 28, 30, 31,
56 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
57 48, 49, 49, 50, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 58,
58 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, 66, 66, 67, 67, 68,
59 68, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 76, 76,
60 77, 77, 78, 78, 79, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83,
61 84, 84, 85, 85, 85, 86, 86, 87, 87, 88, 88, 88, 89, 89, 90,
62 90, 91, 91, 91, 92, 92, 93, 93, 93, 94, 94, 95, 95, 95, 96,
63 96, 97, 97, 97, 98, 98, 98, 99, 99, 99, 100, 100, 101, 101, 101,
64 102, 102, 102, 103, 103, 103, 104, 104, 104, 105, 105, 106, 106, 106, 107,
65 107, 107, 108, 108, 108, 109, 109, 109, 110, 110, 110, 110, 111, 111, 111,
66 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 115, 116, 116,
67 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120,
68 });
69
70 constexpr auto kSRGBSamples2 = fxcrt::ToArray<const uint8_t>({
71 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
72 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149,
73 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162,
74 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173,
75 174, 175, 175, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184,
76 185, 185, 186, 187, 187, 188, 189, 189, 190, 190, 191, 192, 192, 193, 194,
77 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202, 203,
78 203, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212,
79 212, 213, 213, 214, 214, 215, 215, 216, 216, 217, 218, 218, 219, 219, 220,
80 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228,
81 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235,
82 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242,
83 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248, 248, 249, 249,
84 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255,
85 });
86
87 constexpr size_t kBlackWhitePointCount = 3;
88
GetDefaultBlackPoint(pdfium::span<float> pPoints)89 void GetDefaultBlackPoint(pdfium::span<float> pPoints) {
90 static constexpr float kDefaultValue = 0.0f;
91 for (size_t i = 0; i < kBlackWhitePointCount; ++i)
92 pPoints[i] = kDefaultValue;
93 }
94
GetBlackPoint(const CPDF_Dictionary * pDict,pdfium::span<float> pPoints)95 void GetBlackPoint(const CPDF_Dictionary* pDict, pdfium::span<float> pPoints) {
96 RetainPtr<const CPDF_Array> pParam = pDict->GetArrayFor("BlackPoint");
97 if (!pParam || pParam->size() != kBlackWhitePointCount) {
98 GetDefaultBlackPoint(pPoints);
99 return;
100 }
101
102 // Check to make sure all values are non-negative.
103 for (size_t i = 0; i < kBlackWhitePointCount; ++i) {
104 pPoints[i] = pParam->GetFloatAt(i);
105 if (pPoints[i] < 0) {
106 GetDefaultBlackPoint(pPoints);
107 return;
108 }
109 }
110 }
111
GetWhitePoint(const CPDF_Dictionary * pDict,pdfium::span<float> pPoints)112 bool GetWhitePoint(const CPDF_Dictionary* pDict, pdfium::span<float> pPoints) {
113 RetainPtr<const CPDF_Array> pParam = pDict->GetArrayFor("WhitePoint");
114 if (!pParam || pParam->size() != kBlackWhitePointCount)
115 return false;
116
117 for (size_t i = 0; i < kBlackWhitePointCount; ++i)
118 pPoints[i] = pParam->GetFloatAt(i);
119 return pPoints[0] > 0.0f && pPoints[1] == 1.0f && pPoints[2] > 0.0f;
120 }
121
122 class CPDF_CalGray final : public CPDF_ColorSpace {
123 public:
124 CONSTRUCT_VIA_MAKE_RETAIN;
125 ~CPDF_CalGray() override;
126
127 // CPDF_ColorSpace:
128 std::optional<FX_RGB_STRUCT<float>> GetRGB(
129 pdfium::span<const float> pBuf) const override;
130 uint32_t v_Load(CPDF_Document* pDoc,
131 const CPDF_Array* pArray,
132 std::set<const CPDF_Object*>* pVisited) override;
133 void TranslateImageLine(pdfium::span<uint8_t> dest_span,
134 pdfium::span<const uint8_t> src_span,
135 int pixels,
136 int image_width,
137 int image_height,
138 bool bTransMask) const override;
139
140 private:
141 static constexpr float kDefaultGamma = 1.0f;
142
143 CPDF_CalGray();
144
145 float gamma_ = kDefaultGamma;
146 std::array<float, kBlackWhitePointCount> white_point_ = {{1.0f, 1.0f, 1.0f}};
147 std::array<float, kBlackWhitePointCount> black_point_ = {{0.0f, 0.0f, 0.0f}};
148 };
149
150 class CPDF_CalRGB final : public CPDF_ColorSpace {
151 public:
152 CONSTRUCT_VIA_MAKE_RETAIN;
153 ~CPDF_CalRGB() override;
154
155 // CPDF_ColorSpace:
156 std::optional<FX_RGB_STRUCT<float>> GetRGB(
157 pdfium::span<const float> pBuf) const override;
158 void TranslateImageLine(pdfium::span<uint8_t> dest_span,
159 pdfium::span<const uint8_t> src_span,
160 int pixels,
161 int image_width,
162 int image_height,
163 bool bTransMask) const override;
164 uint32_t v_Load(CPDF_Document* pDoc,
165 const CPDF_Array* pArray,
166 std::set<const CPDF_Object*>* pVisited) override;
167
168 private:
169 static constexpr size_t kGammaCount = 3;
170 static constexpr size_t kMatrixCount = 9;
171
172 CPDF_CalRGB();
173
174 std::array<float, kBlackWhitePointCount> white_point_ = {{1.0f, 1.0f, 1.0f}};
175 std::array<float, kBlackWhitePointCount> black_point_ = {{0.0f, 0.0f, 0.0f}};
176 std::optional<std::array<float, kGammaCount>> gamma_;
177 std::optional<std::array<float, kMatrixCount>> matrix_;
178 };
179
180 class CPDF_LabCS final : public CPDF_ColorSpace {
181 public:
182 CONSTRUCT_VIA_MAKE_RETAIN;
183 ~CPDF_LabCS() override;
184
185 // CPDF_ColorSpace:
186 std::optional<FX_RGB_STRUCT<float>> GetRGB(
187 pdfium::span<const float> pBuf) const override;
188 void GetDefaultValue(int iComponent,
189 float* value,
190 float* min,
191 float* max) const override;
192 void TranslateImageLine(pdfium::span<uint8_t> dest_span,
193 pdfium::span<const uint8_t> src_span,
194 int pixels,
195 int image_width,
196 int image_height,
197 bool bTransMask) const override;
198 uint32_t v_Load(CPDF_Document* pDoc,
199 const CPDF_Array* pArray,
200 std::set<const CPDF_Object*>* pVisited) override;
201
202 private:
203 static constexpr size_t kRangesCount = 4;
204
205 CPDF_LabCS();
206
207 std::array<float, kBlackWhitePointCount> white_point_ = {{1.0f, 1.0f, 1.0f}};
208 std::array<float, kBlackWhitePointCount> black_point_ = {{0.0f, 0.0f, 0.0f}};
209 std::array<float, kRangesCount> m_Ranges = {};
210 };
211
212 class CPDF_ICCBasedCS final : public CPDF_BasedCS {
213 public:
214 CONSTRUCT_VIA_MAKE_RETAIN;
215 ~CPDF_ICCBasedCS() override;
216
217 // CPDF_ColorSpace:
218 std::optional<FX_RGB_STRUCT<float>> GetRGB(
219 pdfium::span<const float> pBuf) const override;
220 void TranslateImageLine(pdfium::span<uint8_t> dest_span,
221 pdfium::span<const uint8_t> src_span,
222 int pixels,
223 int image_width,
224 int image_height,
225 bool bTransMask) const override;
226 bool IsNormal() const override;
227 uint32_t v_Load(CPDF_Document* pDoc,
228 const CPDF_Array* pArray,
229 std::set<const CPDF_Object*>* pVisited) override;
230
231 private:
232 CPDF_ICCBasedCS();
233
234 // If no valid ICC profile or using sRGB, try looking for an alternate.
235 bool FindAlternateProfile(CPDF_Document* pDoc,
236 const CPDF_Dictionary* pDict,
237 std::set<const CPDF_Object*>* pVisited,
238 uint32_t nExpectedComponents);
239 static RetainPtr<CPDF_ColorSpace> GetStockAlternateProfile(
240 uint32_t nComponents);
241 static std::vector<float> GetRanges(const CPDF_Dictionary* pDict,
242 uint32_t nComponents);
243
244 RetainPtr<CPDF_IccProfile> profile_;
245 mutable DataVector<uint8_t> cache_;
246 std::vector<float> ranges_;
247 };
248
249 class CPDF_SeparationCS final : public CPDF_BasedCS {
250 public:
251 CONSTRUCT_VIA_MAKE_RETAIN;
252 ~CPDF_SeparationCS() override;
253
254 // CPDF_ColorSpace:
255 std::optional<FX_RGB_STRUCT<float>> GetRGB(
256 pdfium::span<const float> pBuf) const override;
257 void GetDefaultValue(int iComponent,
258 float* value,
259 float* min,
260 float* max) const override;
261 uint32_t v_Load(CPDF_Document* pDoc,
262 const CPDF_Array* pArray,
263 std::set<const CPDF_Object*>* pVisited) override;
264
265 private:
266 CPDF_SeparationCS();
267
268 bool m_IsNoneType = false;
269 std::unique_ptr<const CPDF_Function> m_pFunc;
270 };
271
272 class CPDF_DeviceNCS final : public CPDF_BasedCS {
273 public:
274 CONSTRUCT_VIA_MAKE_RETAIN;
275 ~CPDF_DeviceNCS() override;
276
277 // CPDF_ColorSpace:
278 std::optional<FX_RGB_STRUCT<float>> GetRGB(
279 pdfium::span<const float> pBuf) const override;
280 void GetDefaultValue(int iComponent,
281 float* value,
282 float* min,
283 float* max) const override;
284 uint32_t v_Load(CPDF_Document* pDoc,
285 const CPDF_Array* pArray,
286 std::set<const CPDF_Object*>* pVisited) override;
287
288 private:
289 CPDF_DeviceNCS();
290
291 std::unique_ptr<const CPDF_Function> m_pFunc;
292 };
293
294 class Vector_3by1 {
295 public:
Vector_3by1()296 Vector_3by1() : a(0.0f), b(0.0f), c(0.0f) {}
297
Vector_3by1(float a1,float b1,float c1)298 Vector_3by1(float a1, float b1, float c1) : a(a1), b(b1), c(c1) {}
299
300 float a;
301 float b;
302 float c;
303 };
304
305 class Matrix_3by3 {
306 public:
Matrix_3by3()307 Matrix_3by3()
308 : a(0.0f),
309 b(0.0f),
310 c(0.0f),
311 d(0.0f),
312 e(0.0f),
313 f(0.0f),
314 g(0.0f),
315 h(0.0f),
316 i(0.0f) {}
317
Matrix_3by3(float a1,float b1,float c1,float d1,float e1,float f1,float g1,float h1,float i1)318 Matrix_3by3(float a1,
319 float b1,
320 float c1,
321 float d1,
322 float e1,
323 float f1,
324 float g1,
325 float h1,
326 float i1)
327 : a(a1), b(b1), c(c1), d(d1), e(e1), f(f1), g(g1), h(h1), i(i1) {}
328
Inverse()329 Matrix_3by3 Inverse() {
330 float det = a * (e * i - f * h) - b * (i * d - f * g) + c * (d * h - e * g);
331 if (fabs(det) < std::numeric_limits<float>::epsilon())
332 return Matrix_3by3();
333
334 return Matrix_3by3(
335 (e * i - f * h) / det, -(b * i - c * h) / det, (b * f - c * e) / det,
336 -(d * i - f * g) / det, (a * i - c * g) / det, -(a * f - c * d) / det,
337 (d * h - e * g) / det, -(a * h - b * g) / det, (a * e - b * d) / det);
338 }
339
Multiply(const Matrix_3by3 & m)340 Matrix_3by3 Multiply(const Matrix_3by3& m) {
341 return Matrix_3by3(a * m.a + b * m.d + c * m.g, a * m.b + b * m.e + c * m.h,
342 a * m.c + b * m.f + c * m.i, d * m.a + e * m.d + f * m.g,
343 d * m.b + e * m.e + f * m.h, d * m.c + e * m.f + f * m.i,
344 g * m.a + h * m.d + i * m.g, g * m.b + h * m.e + i * m.h,
345 g * m.c + h * m.f + i * m.i);
346 }
347
TransformVector(const Vector_3by1 & v)348 Vector_3by1 TransformVector(const Vector_3by1& v) {
349 return Vector_3by1(a * v.a + b * v.b + c * v.c, d * v.a + e * v.b + f * v.c,
350 g * v.a + h * v.b + i * v.c);
351 }
352
353 float a;
354 float b;
355 float c;
356 float d;
357 float e;
358 float f;
359 float g;
360 float h;
361 float i;
362 };
363
RGB_Conversion(float colorComponent)364 float RGB_Conversion(float colorComponent) {
365 colorComponent = std::clamp(colorComponent, 0.0f, 1.0f);
366 int scale = std::max(static_cast<int>(colorComponent * 1023), 0);
367 if (scale < 192) {
368 return kSRGBSamples1[scale] / 255.0f;
369 }
370 return kSRGBSamples2[scale / 4 - 48] / 255.0f;
371 }
372
XYZ_to_sRGB(float X,float Y,float Z)373 FX_RGB_STRUCT<float> XYZ_to_sRGB(float X, float Y, float Z) {
374 const float R1 = 3.2410f * X - 1.5374f * Y - 0.4986f * Z;
375 const float G1 = -0.9692f * X + 1.8760f * Y + 0.0416f * Z;
376 const float B1 = 0.0556f * X - 0.2040f * Y + 1.0570f * Z;
377 return {RGB_Conversion(R1), RGB_Conversion(G1), RGB_Conversion(B1)};
378 }
379
XYZ_to_sRGB_WhitePoint(float X,float Y,float Z,float Xw,float Yw,float Zw)380 FX_RGB_STRUCT<float> XYZ_to_sRGB_WhitePoint(float X,
381 float Y,
382 float Z,
383 float Xw,
384 float Yw,
385 float Zw) {
386 // The following RGB_xyz is based on
387 // sRGB value {Rx,Ry}={0.64, 0.33}, {Gx,Gy}={0.30, 0.60}, {Bx,By}={0.15, 0.06}
388
389 constexpr float Rx = 0.64f;
390 constexpr float Ry = 0.33f;
391 constexpr float Gx = 0.30f;
392 constexpr float Gy = 0.60f;
393 constexpr float Bx = 0.15f;
394 constexpr float By = 0.06f;
395 Matrix_3by3 RGB_xyz(Rx, Gx, Bx, Ry, Gy, By, 1 - Rx - Ry, 1 - Gx - Gy,
396 1 - Bx - By);
397 Vector_3by1 whitePoint(Xw, Yw, Zw);
398 Vector_3by1 XYZ(X, Y, Z);
399
400 Vector_3by1 RGB_Sum_XYZ = RGB_xyz.Inverse().TransformVector(whitePoint);
401 Matrix_3by3 RGB_SUM_XYZ_DIAG(RGB_Sum_XYZ.a, 0, 0, 0, RGB_Sum_XYZ.b, 0, 0, 0,
402 RGB_Sum_XYZ.c);
403 Matrix_3by3 M = RGB_xyz.Multiply(RGB_SUM_XYZ_DIAG);
404 Vector_3by1 RGB = M.Inverse().TransformVector(XYZ);
405
406 return {RGB_Conversion(RGB.a), RGB_Conversion(RGB.b), RGB_Conversion(RGB.c)};
407 }
408
409 class StockColorSpaces {
410 public:
StockColorSpaces()411 StockColorSpaces()
412 : gray_(pdfium::MakeRetain<CPDF_DeviceCS>(
413 CPDF_ColorSpace::Family::kDeviceGray)),
414 rgb_(pdfium::MakeRetain<CPDF_DeviceCS>(
415 CPDF_ColorSpace::Family::kDeviceRGB)),
416 cmyk_(pdfium::MakeRetain<CPDF_DeviceCS>(
417 CPDF_ColorSpace::Family::kDeviceCMYK)),
418 pattern_(pdfium::MakeRetain<CPDF_PatternCS>()) {
419 pattern_->InitializeStockPattern();
420 }
421 StockColorSpaces(const StockColorSpaces&) = delete;
422 StockColorSpaces& operator=(const StockColorSpaces&) = delete;
423 ~StockColorSpaces() = default;
424
GetStockCS(CPDF_ColorSpace::Family family)425 RetainPtr<CPDF_ColorSpace> GetStockCS(CPDF_ColorSpace::Family family) {
426 if (family == CPDF_ColorSpace::Family::kDeviceGray) {
427 return gray_;
428 }
429 if (family == CPDF_ColorSpace::Family::kDeviceRGB) {
430 return rgb_;
431 }
432 if (family == CPDF_ColorSpace::Family::kDeviceCMYK) {
433 return cmyk_;
434 }
435 if (family == CPDF_ColorSpace::Family::kPattern) {
436 return pattern_;
437 }
438 NOTREACHED_NORETURN();
439 }
440
441 private:
442 RetainPtr<CPDF_DeviceCS> gray_;
443 RetainPtr<CPDF_DeviceCS> rgb_;
444 RetainPtr<CPDF_DeviceCS> cmyk_;
445 RetainPtr<CPDF_PatternCS> pattern_;
446 };
447
448 StockColorSpaces* g_stock_colorspaces = nullptr;
449
450 } // namespace
451
452 PatternValue::PatternValue() = default;
453
454 PatternValue::PatternValue(const PatternValue& that) = default;
455
456 PatternValue::~PatternValue() = default;
457
SetComps(pdfium::span<const float> comps)458 void PatternValue::SetComps(pdfium::span<const float> comps) {
459 fxcrt::Copy(comps, m_Comps);
460 }
461
462 // static
InitializeGlobals()463 void CPDF_ColorSpace::InitializeGlobals() {
464 CHECK(!g_stock_colorspaces);
465 g_stock_colorspaces = new StockColorSpaces();
466 }
467
468 // static
DestroyGlobals()469 void CPDF_ColorSpace::DestroyGlobals() {
470 delete g_stock_colorspaces;
471 g_stock_colorspaces = nullptr;
472 }
473
474 // static
GetStockCS(Family family)475 RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCS(Family family) {
476 return g_stock_colorspaces->GetStockCS(family);
477 }
478
479 // static
GetStockCSForName(const ByteString & name)480 RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCSForName(
481 const ByteString& name) {
482 if (name == "DeviceRGB" || name == "RGB")
483 return GetStockCS(Family::kDeviceRGB);
484 if (name == "DeviceGray" || name == "G")
485 return GetStockCS(Family::kDeviceGray);
486 if (name == "DeviceCMYK" || name == "CMYK")
487 return GetStockCS(Family::kDeviceCMYK);
488 if (name == "Pattern")
489 return GetStockCS(Family::kPattern);
490 return nullptr;
491 }
492
493 // static
Load(CPDF_Document * pDoc,const CPDF_Object * pObj,std::set<const CPDF_Object * > * pVisited)494 RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(
495 CPDF_Document* pDoc,
496 const CPDF_Object* pObj,
497 std::set<const CPDF_Object*>* pVisited) {
498 if (!pObj)
499 return nullptr;
500
501 if (pdfium::Contains(*pVisited, pObj))
502 return nullptr;
503
504 ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pObj);
505
506 if (pObj->IsName())
507 return GetStockCSForName(pObj->GetString());
508
509 if (const CPDF_Stream* pStream = pObj->AsStream()) {
510 RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
511 CPDF_DictionaryLocker locker(std::move(pDict));
512 for (const auto& it : locker) {
513 RetainPtr<const CPDF_Name> pValue = ToName(it.second);
514 if (pValue) {
515 RetainPtr<CPDF_ColorSpace> pRet =
516 GetStockCSForName(pValue->GetString());
517 if (pRet)
518 return pRet;
519 }
520 }
521 return nullptr;
522 }
523
524 const CPDF_Array* pArray = pObj->AsArray();
525 if (!pArray || pArray->IsEmpty())
526 return nullptr;
527
528 RetainPtr<const CPDF_Object> pFamilyObj = pArray->GetDirectObjectAt(0);
529 if (!pFamilyObj)
530 return nullptr;
531
532 ByteString familyname = pFamilyObj->GetString();
533 if (pArray->size() == 1)
534 return GetStockCSForName(familyname);
535
536 RetainPtr<CPDF_ColorSpace> pCS =
537 CPDF_ColorSpace::AllocateColorSpace(familyname.AsStringView());
538 if (!pCS)
539 return nullptr;
540
541 pCS->m_pArray.Reset(pArray);
542 pCS->m_nComponents = pCS->v_Load(pDoc, pArray, pVisited);
543 if (pCS->m_nComponents == 0)
544 return nullptr;
545
546 return pCS;
547 }
548
549 // static
AllocateColorSpace(ByteStringView bsFamilyName)550 RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::AllocateColorSpace(
551 ByteStringView bsFamilyName) {
552 switch (bsFamilyName.GetID()) {
553 case FXBSTR_ID('C', 'a', 'l', 'G'):
554 return pdfium::MakeRetain<CPDF_CalGray>();
555 case FXBSTR_ID('C', 'a', 'l', 'R'):
556 return pdfium::MakeRetain<CPDF_CalRGB>();
557 case FXBSTR_ID('L', 'a', 'b', 0):
558 return pdfium::MakeRetain<CPDF_LabCS>();
559 case FXBSTR_ID('I', 'C', 'C', 'B'):
560 return pdfium::MakeRetain<CPDF_ICCBasedCS>();
561 case FXBSTR_ID('I', 'n', 'd', 'e'):
562 case FXBSTR_ID('I', 0, 0, 0):
563 return pdfium::MakeRetain<CPDF_IndexedCS>();
564 case FXBSTR_ID('S', 'e', 'p', 'a'):
565 return pdfium::MakeRetain<CPDF_SeparationCS>();
566 case FXBSTR_ID('D', 'e', 'v', 'i'):
567 return pdfium::MakeRetain<CPDF_DeviceNCS>();
568 case FXBSTR_ID('P', 'a', 't', 't'):
569 return pdfium::MakeRetain<CPDF_PatternCS>();
570 default:
571 return nullptr;
572 }
573 }
574
575 // static
ComponentsForFamily(Family family)576 uint32_t CPDF_ColorSpace::ComponentsForFamily(Family family) {
577 switch (family) {
578 case Family::kDeviceGray:
579 return 1;
580 case Family::kDeviceRGB:
581 return 3;
582 case Family::kDeviceCMYK:
583 return 4;
584 default:
585 NOTREACHED_NORETURN();
586 }
587 }
588
CreateBufAndSetDefaultColor() const589 std::vector<float> CPDF_ColorSpace::CreateBufAndSetDefaultColor() const {
590 DCHECK(m_Family != Family::kPattern);
591
592 float min;
593 float max;
594 std::vector<float> buf(m_nComponents);
595 for (uint32_t i = 0; i < m_nComponents; i++)
596 GetDefaultValue(i, &buf[i], &min, &max);
597
598 return buf;
599 }
600
ComponentCount() const601 uint32_t CPDF_ColorSpace::ComponentCount() const {
602 return m_nComponents;
603 }
604
GetDefaultValue(int iComponent,float * value,float * min,float * max) const605 void CPDF_ColorSpace::GetDefaultValue(int iComponent,
606 float* value,
607 float* min,
608 float* max) const {
609 *value = 0.0f;
610 *min = 0.0f;
611 *max = 1.0f;
612 }
613
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const614 void CPDF_ColorSpace::TranslateImageLine(pdfium::span<uint8_t> dest_span,
615 pdfium::span<const uint8_t> src_span,
616 int pixels,
617 int image_width,
618 int image_height,
619 bool bTransMask) const {
620 // Only applies to CMYK colorspaces. None of the colorspaces that use this
621 // generic base implementation are CMYK.
622 CHECK(!bTransMask);
623
624 uint8_t* dest_buf = dest_span.data();
625 const uint8_t* src_buf = src_span.data();
626 std::vector<float> src(m_nComponents);
627 const int divisor = m_Family != Family::kIndexed ? 255 : 1;
628 UNSAFE_TODO({
629 for (int i = 0; i < pixels; i++) {
630 for (uint32_t j = 0; j < m_nComponents; j++) {
631 src[j] = static_cast<float>(*src_buf++) / divisor;
632 }
633 auto rgb = GetRGBOrZerosOnError(src);
634 *dest_buf++ = static_cast<int32_t>(rgb.blue * 255);
635 *dest_buf++ = static_cast<int32_t>(rgb.green * 255);
636 *dest_buf++ = static_cast<int32_t>(rgb.red * 255);
637 }
638 });
639 }
640
EnableStdConversion(bool bEnabled)641 void CPDF_ColorSpace::EnableStdConversion(bool bEnabled) {
642 if (bEnabled)
643 m_dwStdConversion++;
644 else if (m_dwStdConversion)
645 m_dwStdConversion--;
646 }
647
IsNormal() const648 bool CPDF_ColorSpace::IsNormal() const {
649 return GetFamily() == Family::kDeviceGray ||
650 GetFamily() == Family::kDeviceRGB ||
651 GetFamily() == Family::kDeviceCMYK ||
652 GetFamily() == Family::kCalGray || GetFamily() == Family::kCalRGB;
653 }
654
AsPatternCS() const655 const CPDF_PatternCS* CPDF_ColorSpace::AsPatternCS() const {
656 return nullptr;
657 }
658
AsIndexedCS() const659 const CPDF_IndexedCS* CPDF_ColorSpace::AsIndexedCS() const {
660 return nullptr;
661 }
662
CPDF_ColorSpace(Family family)663 CPDF_ColorSpace::CPDF_ColorSpace(Family family) : m_Family(family) {}
664
665 CPDF_ColorSpace::~CPDF_ColorSpace() = default;
666
SetComponentsForStockCS(uint32_t nComponents)667 void CPDF_ColorSpace::SetComponentsForStockCS(uint32_t nComponents) {
668 m_nComponents = nComponents;
669 }
670
CPDF_CalGray()671 CPDF_CalGray::CPDF_CalGray() : CPDF_ColorSpace(Family::kCalGray) {}
672
673 CPDF_CalGray::~CPDF_CalGray() = default;
674
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)675 uint32_t CPDF_CalGray::v_Load(CPDF_Document* pDoc,
676 const CPDF_Array* pArray,
677 std::set<const CPDF_Object*>* pVisited) {
678 RetainPtr<const CPDF_Dictionary> pDict = pArray->GetDictAt(1);
679 if (!pDict)
680 return 0;
681
682 if (!GetWhitePoint(pDict.Get(), white_point_)) {
683 return 0;
684 }
685
686 GetBlackPoint(pDict.Get(), black_point_);
687
688 gamma_ = pDict->GetFloatFor("Gamma");
689 if (gamma_ == 0) {
690 gamma_ = kDefaultGamma;
691 }
692 return 1;
693 }
694
GetRGB(pdfium::span<const float> pBuf) const695 std::optional<FX_RGB_STRUCT<float>> CPDF_CalGray::GetRGB(
696 pdfium::span<const float> pBuf) const {
697 const float gray = pBuf[0];
698 return FX_RGB_STRUCT<float>{gray, gray, gray};
699 }
700
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const701 void CPDF_CalGray::TranslateImageLine(pdfium::span<uint8_t> dest_span,
702 pdfium::span<const uint8_t> src_span,
703 int pixels,
704 int image_width,
705 int image_height,
706 bool bTransMask) const {
707 CHECK(!bTransMask); // Only applies to CMYK colorspaces.
708
709 auto gray_span = src_span.first(pixels);
710 auto rgb_span = fxcrt::reinterpret_span<FX_RGB_STRUCT<uint8_t>>(dest_span);
711 for (auto [gray_ref, rgb_ref] : fxcrt::Zip(gray_span, rgb_span)) {
712 // Compiler can not conclude that src/dest don't overlap.
713 const uint8_t pix = gray_ref;
714 rgb_ref.red = pix;
715 rgb_ref.green = pix;
716 rgb_ref.blue = pix;
717 }
718 }
719
CPDF_CalRGB()720 CPDF_CalRGB::CPDF_CalRGB() : CPDF_ColorSpace(Family::kCalRGB) {}
721
722 CPDF_CalRGB::~CPDF_CalRGB() = default;
723
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)724 uint32_t CPDF_CalRGB::v_Load(CPDF_Document* pDoc,
725 const CPDF_Array* pArray,
726 std::set<const CPDF_Object*>* pVisited) {
727 RetainPtr<const CPDF_Dictionary> pDict = pArray->GetDictAt(1);
728 if (!pDict)
729 return 0;
730
731 if (!GetWhitePoint(pDict.Get(), white_point_)) {
732 return 0;
733 }
734
735 GetBlackPoint(pDict.Get(), black_point_);
736
737 RetainPtr<const CPDF_Array> pGamma = pDict->GetArrayFor("Gamma");
738 if (pGamma) {
739 auto& gamma = gamma_.emplace();
740 for (size_t i = 0; i < std::size(gamma); ++i) {
741 gamma[i] = pGamma->GetFloatAt(i);
742 }
743 }
744
745 RetainPtr<const CPDF_Array> pMatrix = pDict->GetArrayFor("Matrix");
746 if (pMatrix) {
747 auto& matrix = matrix_.emplace();
748 for (size_t i = 0; i < std::size(matrix); ++i) {
749 matrix[i] = pMatrix->GetFloatAt(i);
750 }
751 }
752 return 3;
753 }
754
GetRGB(pdfium::span<const float> pBuf) const755 std::optional<FX_RGB_STRUCT<float>> CPDF_CalRGB::GetRGB(
756 pdfium::span<const float> pBuf) const {
757 float a = pBuf[0];
758 float b = pBuf[1];
759 float c = pBuf[2];
760 if (gamma_.has_value()) {
761 const auto& gamma = gamma_.value();
762 a = powf(a, gamma[0]);
763 b = powf(b, gamma[1]);
764 c = powf(c, gamma[2]);
765 }
766 float x;
767 float y;
768 float z;
769 if (matrix_.has_value()) {
770 const auto& matrix = matrix_.value();
771 x = matrix[0] * a + matrix[3] * b + matrix[6] * c;
772 y = matrix[1] * a + matrix[4] * b + matrix[7] * c;
773 z = matrix[2] * a + matrix[5] * b + matrix[8] * c;
774 } else {
775 x = a;
776 y = b;
777 z = c;
778 }
779 return XYZ_to_sRGB_WhitePoint(x, y, z, white_point_[0], white_point_[1],
780 white_point_[2]);
781 }
782
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const783 void CPDF_CalRGB::TranslateImageLine(pdfium::span<uint8_t> dest_span,
784 pdfium::span<const uint8_t> src_span,
785 int pixels,
786 int image_width,
787 int image_height,
788 bool bTransMask) const {
789 CHECK(!bTransMask); // Only applies to CMYK colorspaces.
790 fxcodec::ReverseRGB(dest_span, src_span, pixels);
791 }
792
CPDF_LabCS()793 CPDF_LabCS::CPDF_LabCS() : CPDF_ColorSpace(Family::kLab) {}
794
795 CPDF_LabCS::~CPDF_LabCS() = default;
796
GetDefaultValue(int iComponent,float * value,float * min,float * max) const797 void CPDF_LabCS::GetDefaultValue(int iComponent,
798 float* value,
799 float* min,
800 float* max) const {
801 DCHECK_LT(iComponent, 3);
802
803 if (iComponent > 0) {
804 float range_min = m_Ranges[iComponent * 2 - 2];
805 float range_max = m_Ranges[iComponent * 2 - 1];
806 if (range_min <= range_max) {
807 *min = range_min;
808 *max = range_max;
809 *value = std::clamp(0.0f, *min, *max);
810 return;
811 }
812 }
813
814 *min = 0.0f;
815 *max = 100.0f;
816 *value = 0.0f;
817 }
818
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)819 uint32_t CPDF_LabCS::v_Load(CPDF_Document* pDoc,
820 const CPDF_Array* pArray,
821 std::set<const CPDF_Object*>* pVisited) {
822 RetainPtr<const CPDF_Dictionary> pDict = pArray->GetDictAt(1);
823 if (!pDict)
824 return 0;
825
826 if (!GetWhitePoint(pDict.Get(), white_point_)) {
827 return 0;
828 }
829
830 GetBlackPoint(pDict.Get(), black_point_);
831
832 RetainPtr<const CPDF_Array> pParam = pDict->GetArrayFor("Range");
833 static constexpr std::array<float, kRangesCount> kDefaultRanges = {
834 {-100.0f, 100.0f, -100.0f, 100.0f}};
835 for (size_t i = 0; i < std::size(kDefaultRanges); ++i) {
836 m_Ranges[i] = pParam ? pParam->GetFloatAt(i) : kDefaultRanges[i];
837 }
838 return 3;
839 }
840
GetRGB(pdfium::span<const float> pBuf) const841 std::optional<FX_RGB_STRUCT<float>> CPDF_LabCS::GetRGB(
842 pdfium::span<const float> pBuf) const {
843 float Lstar = pBuf[0];
844 float astar = pBuf[1];
845 float bstar = pBuf[2];
846 float M = (Lstar + 16.0f) / 116.0f;
847 float L = M + astar / 500.0f;
848 float N = M - bstar / 200.0f;
849 float X;
850 float Y;
851 float Z;
852 if (L < 0.2069f)
853 X = 0.957f * 0.12842f * (L - 0.1379f);
854 else
855 X = 0.957f * L * L * L;
856
857 if (M < 0.2069f)
858 Y = 0.12842f * (M - 0.1379f);
859 else
860 Y = M * M * M;
861
862 if (N < 0.2069f)
863 Z = 1.0889f * 0.12842f * (N - 0.1379f);
864 else
865 Z = 1.0889f * N * N * N;
866
867 return XYZ_to_sRGB(X, Y, Z);
868 }
869
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const870 void CPDF_LabCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
871 pdfium::span<const uint8_t> src_span,
872 int pixels,
873 int image_width,
874 int image_height,
875 bool bTransMask) const {
876 CHECK(!bTransMask); // Only applies to CMYK colorspaces.
877
878 auto bgr_span = fxcrt::reinterpret_span<FX_BGR_STRUCT<uint8_t>>(dest_span);
879 auto lab_span =
880 fxcrt::reinterpret_span<const FX_LAB_STRUCT<uint8_t>>(src_span).first(
881 pixels);
882 for (auto [lab_ref, bgr_ref] : fxcrt::Zip(lab_span, bgr_span)) {
883 const float lab[3] = {
884 static_cast<float>(lab_ref.lightness_star * 100) / 255.0f,
885 static_cast<float>(lab_ref.a_star - 128),
886 static_cast<float>(lab_ref.b_star - 128),
887 };
888 // Better code than the equivalent GetRGBOrZerosOnError() since that
889 // is implemented in a base class and can't devirtualize the GetRGB()
890 // call despite this class being marked final.
891 auto rgb = GetRGB(lab).value_or(FX_RGB_STRUCT<float>{});
892 bgr_ref.blue = static_cast<int32_t>(rgb.blue * 255);
893 bgr_ref.green = static_cast<int32_t>(rgb.green * 255);
894 bgr_ref.red = static_cast<int32_t>(rgb.red * 255);
895 }
896 }
897
CPDF_ICCBasedCS()898 CPDF_ICCBasedCS::CPDF_ICCBasedCS() : CPDF_BasedCS(Family::kICCBased) {}
899
900 CPDF_ICCBasedCS::~CPDF_ICCBasedCS() = default;
901
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)902 uint32_t CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc,
903 const CPDF_Array* pArray,
904 std::set<const CPDF_Object*>* pVisited) {
905 RetainPtr<const CPDF_Stream> pStream = pArray->GetStreamAt(1);
906 if (!pStream)
907 return 0;
908
909 // The PDF 1.7 spec says the number of components must be valid. While some
910 // PDF viewers tolerate invalid values, Acrobat does not, so be consistent
911 // with Acrobat and reject bad values.
912 RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
913 const int32_t nDictComponents = pDict->GetIntegerFor("N");
914 if (!fxcodec::IccTransform::IsValidIccComponents(nDictComponents)) {
915 return 0;
916 }
917
918 // Safe to cast, as the value just got validated.
919 const uint32_t nComponents = static_cast<uint32_t>(nDictComponents);
920 profile_ = CPDF_DocPageData::FromDocument(pDoc)->GetIccProfile(pStream);
921 if (!profile_) {
922 return 0;
923 }
924
925 // If PDFium does not understand the ICC profile format at all, or if it's
926 // SRGB, a profile PDFium recognizes but does not support well, then try the
927 // alternate profile.
928 if (!profile_->IsSupported() &&
929 !FindAlternateProfile(pDoc, pDict.Get(), pVisited, nComponents)) {
930 // If there is no alternate profile, use a stock profile as mentioned in
931 // the PDF 1.7 spec in table 4.16 in the "Alternate" key description.
932 DCHECK(!m_pBaseCS);
933 m_pBaseCS = GetStockAlternateProfile(nComponents);
934 }
935
936 // TODO(crbug.com/pdfium/2136): Use this data to clamp color components.
937 ranges_ = GetRanges(pDict.Get(), nComponents);
938 return nComponents;
939 }
940
GetRGB(pdfium::span<const float> pBuf) const941 std::optional<FX_RGB_STRUCT<float>> CPDF_ICCBasedCS::GetRGB(
942 pdfium::span<const float> pBuf) const {
943 if (profile_->IsSRGB()) {
944 return FX_RGB_STRUCT<float>{pBuf[0], pBuf[1], pBuf[2]};
945 }
946 if (profile_->IsSupported()) {
947 float rgb[3];
948 profile_->Translate(pBuf.first(ComponentCount()), rgb);
949 return FX_RGB_STRUCT<float>{rgb[0], rgb[1], rgb[2]};
950 }
951 if (m_pBaseCS) {
952 return m_pBaseCS->GetRGB(pBuf);
953 }
954 return FX_RGB_STRUCT<float>{};
955 }
956
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const957 void CPDF_ICCBasedCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
958 pdfium::span<const uint8_t> src_span,
959 int pixels,
960 int image_width,
961 int image_height,
962 bool bTransMask) const {
963 CHECK(!bTransMask); // Only applies to CMYK colorspaces.
964
965 if (profile_->IsSRGB()) {
966 fxcodec::ReverseRGB(dest_span, src_span, pixels);
967 return;
968 }
969 if (!profile_->IsSupported()) {
970 if (m_pBaseCS) {
971 m_pBaseCS->TranslateImageLine(dest_span, src_span, pixels, image_width,
972 image_height, false);
973 }
974 return;
975 }
976
977 // |nMaxColors| will not overflow since |nComponents| is limited in size.
978 const uint32_t nComponents = ComponentCount();
979 DCHECK(fxcodec::IccTransform::IsValidIccComponents(nComponents));
980 int nMaxColors = 1;
981 for (uint32_t i = 0; i < nComponents; i++)
982 nMaxColors *= 52;
983
984 bool bTranslate = nComponents > 3;
985 if (!bTranslate) {
986 FX_SAFE_INT32 nPixelCount = image_width;
987 nPixelCount *= image_height;
988 if (nPixelCount.IsValid())
989 bTranslate = nPixelCount.ValueOrDie() < nMaxColors * 3 / 2;
990 }
991 if (bTranslate && profile_->IsSupported()) {
992 profile_->TranslateScanline(dest_span, src_span, pixels);
993 return;
994 }
995 if (cache_.empty()) {
996 cache_.resize(Fx2DSizeOrDie(nMaxColors, 3));
997 DataVector<uint8_t> temp_src(Fx2DSizeOrDie(nMaxColors, nComponents));
998 size_t src_index = 0;
999 for (int i = 0; i < nMaxColors; i++) {
1000 uint32_t color = i;
1001 uint32_t order = nMaxColors / 52;
1002 for (uint32_t c = 0; c < nComponents; c++) {
1003 temp_src[src_index++] = static_cast<uint8_t>(color / order * 5);
1004 color %= order;
1005 order /= 52;
1006 }
1007 }
1008 if (profile_->IsSupported()) {
1009 profile_->TranslateScanline(cache_, temp_src, nMaxColors);
1010 }
1011 }
1012 uint8_t* pDestBuf = dest_span.data();
1013 const uint8_t* pSrcBuf = src_span.data();
1014 UNSAFE_TODO({
1015 for (int i = 0; i < pixels; i++) {
1016 int index = 0;
1017 for (uint32_t c = 0; c < nComponents; c++) {
1018 index = index * 52 + (*pSrcBuf) / 5;
1019 pSrcBuf++;
1020 }
1021 index *= 3;
1022 *pDestBuf++ = cache_[index];
1023 *pDestBuf++ = cache_[index + 1];
1024 *pDestBuf++ = cache_[index + 2];
1025 }
1026 });
1027 }
1028
IsNormal() const1029 bool CPDF_ICCBasedCS::IsNormal() const {
1030 if (profile_->IsSRGB()) {
1031 return true;
1032 }
1033 if (profile_->IsSupported()) {
1034 return profile_->IsNormal();
1035 }
1036 if (m_pBaseCS)
1037 return m_pBaseCS->IsNormal();
1038 return false;
1039 }
1040
FindAlternateProfile(CPDF_Document * pDoc,const CPDF_Dictionary * pDict,std::set<const CPDF_Object * > * pVisited,uint32_t nExpectedComponents)1041 bool CPDF_ICCBasedCS::FindAlternateProfile(
1042 CPDF_Document* pDoc,
1043 const CPDF_Dictionary* pDict,
1044 std::set<const CPDF_Object*>* pVisited,
1045 uint32_t nExpectedComponents) {
1046 RetainPtr<const CPDF_Object> pAlterCSObj =
1047 pDict->GetDirectObjectFor("Alternate");
1048 if (!pAlterCSObj)
1049 return false;
1050
1051 auto pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj.Get(), pVisited);
1052 if (!pAlterCS)
1053 return false;
1054
1055 if (pAlterCS->GetFamily() == Family::kPattern)
1056 return false;
1057
1058 if (pAlterCS->ComponentCount() != nExpectedComponents) {
1059 return false;
1060 }
1061
1062 m_pBaseCS = std::move(pAlterCS);
1063 return true;
1064 }
1065
1066 // static
GetStockAlternateProfile(uint32_t nComponents)1067 RetainPtr<CPDF_ColorSpace> CPDF_ICCBasedCS::GetStockAlternateProfile(
1068 uint32_t nComponents) {
1069 if (nComponents == 1)
1070 return GetStockCS(Family::kDeviceGray);
1071 if (nComponents == 3)
1072 return GetStockCS(Family::kDeviceRGB);
1073 if (nComponents == 4)
1074 return GetStockCS(Family::kDeviceCMYK);
1075 NOTREACHED_NORETURN();
1076 }
1077
1078 // static
GetRanges(const CPDF_Dictionary * pDict,uint32_t nComponents)1079 std::vector<float> CPDF_ICCBasedCS::GetRanges(const CPDF_Dictionary* pDict,
1080 uint32_t nComponents) {
1081 DCHECK(fxcodec::IccTransform::IsValidIccComponents(nComponents));
1082 RetainPtr<const CPDF_Array> pRanges = pDict->GetArrayFor("Range");
1083 if (pRanges && pRanges->size() >= nComponents * 2)
1084 return ReadArrayElementsToVector(pRanges.Get(), nComponents * 2);
1085
1086 std::vector<float> ranges;
1087 ranges.reserve(nComponents * 2);
1088 for (uint32_t i = 0; i < nComponents; i++) {
1089 ranges.push_back(0.0f);
1090 ranges.push_back(1.0f);
1091 }
1092 return ranges;
1093 }
1094
CPDF_SeparationCS()1095 CPDF_SeparationCS::CPDF_SeparationCS() : CPDF_BasedCS(Family::kSeparation) {}
1096
1097 CPDF_SeparationCS::~CPDF_SeparationCS() = default;
1098
GetDefaultValue(int iComponent,float * value,float * min,float * max) const1099 void CPDF_SeparationCS::GetDefaultValue(int iComponent,
1100 float* value,
1101 float* min,
1102 float* max) const {
1103 *value = 1.0f;
1104 *min = 0;
1105 *max = 1.0f;
1106 }
1107
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)1108 uint32_t CPDF_SeparationCS::v_Load(CPDF_Document* pDoc,
1109 const CPDF_Array* pArray,
1110 std::set<const CPDF_Object*>* pVisited) {
1111 m_IsNoneType = pArray->GetByteStringAt(1) == "None";
1112 if (m_IsNoneType)
1113 return 1;
1114
1115 RetainPtr<const CPDF_Object> pAltArray = pArray->GetDirectObjectAt(2);
1116 if (HasSameArray(pAltArray.Get()))
1117 return 0;
1118
1119 m_pBaseCS = Load(pDoc, pAltArray.Get(), pVisited);
1120 if (!m_pBaseCS)
1121 return 0;
1122
1123 if (m_pBaseCS->IsSpecial())
1124 return 0;
1125
1126 RetainPtr<const CPDF_Object> pFuncObj = pArray->GetDirectObjectAt(3);
1127 if (pFuncObj && !pFuncObj->IsName()) {
1128 auto pFunc = CPDF_Function::Load(std::move(pFuncObj));
1129 if (pFunc && pFunc->OutputCount() >= m_pBaseCS->ComponentCount()) {
1130 m_pFunc = std::move(pFunc);
1131 }
1132 }
1133 return 1;
1134 }
1135
GetRGB(pdfium::span<const float> pBuf) const1136 std::optional<FX_RGB_STRUCT<float>> CPDF_SeparationCS::GetRGB(
1137 pdfium::span<const float> pBuf) const {
1138 if (m_IsNoneType) {
1139 return std::nullopt;
1140 }
1141 if (!m_pFunc) {
1142 if (!m_pBaseCS) {
1143 return std::nullopt;
1144 }
1145 std::vector<float> results(m_pBaseCS->ComponentCount(), pBuf[0]);
1146 return m_pBaseCS->GetRGB(results);
1147 }
1148
1149 // Using at least 16 elements due to the call m_pAltCS->GetRGB() below.
1150 std::vector<float> results(std::max(m_pFunc->OutputCount(), 16u));
1151 uint32_t nresults = m_pFunc->Call(pBuf.first(1), results).value_or(0);
1152 if (nresults == 0) {
1153 return std::nullopt;
1154 }
1155 if (m_pBaseCS) {
1156 return m_pBaseCS->GetRGB(results);
1157 }
1158 return std::nullopt;
1159 }
1160
CPDF_DeviceNCS()1161 CPDF_DeviceNCS::CPDF_DeviceNCS() : CPDF_BasedCS(Family::kDeviceN) {}
1162
1163 CPDF_DeviceNCS::~CPDF_DeviceNCS() = default;
1164
GetDefaultValue(int iComponent,float * value,float * min,float * max) const1165 void CPDF_DeviceNCS::GetDefaultValue(int iComponent,
1166 float* value,
1167 float* min,
1168 float* max) const {
1169 *value = 1.0f;
1170 *min = 0;
1171 *max = 1.0f;
1172 }
1173
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)1174 uint32_t CPDF_DeviceNCS::v_Load(CPDF_Document* pDoc,
1175 const CPDF_Array* pArray,
1176 std::set<const CPDF_Object*>* pVisited) {
1177 RetainPtr<const CPDF_Array> pObj = ToArray(pArray->GetDirectObjectAt(1));
1178 if (!pObj)
1179 return 0;
1180
1181 RetainPtr<const CPDF_Object> pAltCS = pArray->GetDirectObjectAt(2);
1182 if (!pAltCS || HasSameArray(pAltCS.Get()))
1183 return 0;
1184
1185 m_pBaseCS = Load(pDoc, pAltCS.Get(), pVisited);
1186 m_pFunc = CPDF_Function::Load(pArray->GetDirectObjectAt(3));
1187 if (!m_pBaseCS || !m_pFunc)
1188 return 0;
1189
1190 if (m_pBaseCS->IsSpecial())
1191 return 0;
1192
1193 if (m_pFunc->OutputCount() < m_pBaseCS->ComponentCount()) {
1194 return 0;
1195 }
1196
1197 return fxcrt::CollectionSize<uint32_t>(*pObj);
1198 }
1199
GetRGB(pdfium::span<const float> pBuf) const1200 std::optional<FX_RGB_STRUCT<float>> CPDF_DeviceNCS::GetRGB(
1201 pdfium::span<const float> pBuf) const {
1202 if (!m_pFunc) {
1203 return std::nullopt;
1204 }
1205 // Using at least 16 elements due to the call m_pAltCS->GetRGB() below.
1206 std::vector<float> results(std::max(m_pFunc->OutputCount(), 16u));
1207 uint32_t nresults =
1208 m_pFunc->Call(pBuf.first(ComponentCount()), results).value_or(0);
1209
1210 if (nresults == 0) {
1211 return std::nullopt;
1212 }
1213 return m_pBaseCS->GetRGB(results);
1214 }
1215