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