• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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