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