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