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