xref: /aosp_15_r20/external/pdfium/core/fpdfapi/page/cpdf_colorspace.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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