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