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