9e6ca67041e1883981eb66447069c550c8c60a4b
[pdfium.git] / core / src / fpdfapi / fpdf_page / fpdf_page_colors.cpp
1 // Copyright 2014 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 "../../../include/fpdfapi/fpdf_page.h"
8 #include "../../../include/fpdfapi/fpdf_module.h"
9 #include "../../../include/fxcodec/fx_codec.h"
10 #include "pageint.h"
11 #include <limits.h>
12
13 namespace {
14
15 void sRGB_to_AdobeCMYK(FX_FLOAT R, FX_FLOAT G, FX_FLOAT B, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k)
16 {
17     c = 1.0f - R;
18     m = 1.0f - G;
19     y = 1.0f - B;
20     k = c;
21     if (m < k) {
22         k = m;
23     }
24     if (y < k) {
25         k = y;
26     }
27 }
28
29 int ComponentsForFamily(int family) {
30     if (family == PDFCS_DEVICERGB)
31         return 3;
32     if (family == PDFCS_DEVICEGRAY)
33         return 1;
34     return 4;
35 }
36
37 }  // namespace
38
39 CPDF_DeviceCS::CPDF_DeviceCS(CPDF_Document* pDoc, int family)
40         : CPDF_ColorSpace(pDoc, family, ComponentsForFamily(family)) {
41 }
42
43 FX_BOOL CPDF_DeviceCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
44 {
45     if (m_Family == PDFCS_DEVICERGB) {
46         R = pBuf[0];
47         if (R < 0) {
48             R = 0;
49         } else if (R > 1) {
50             R = 1;
51         }
52         G = pBuf[1];
53         if (G < 0) {
54             G = 0;
55         } else if (G > 1) {
56             G = 1;
57         }
58         B = pBuf[2];
59         if (B < 0) {
60             B = 0;
61         } else if (B > 1) {
62             B = 1;
63         }
64     } else if (m_Family == PDFCS_DEVICEGRAY) {
65         R = *pBuf;
66         if (R < 0) {
67             R = 0;
68         } else if (R > 1) {
69             R = 1;
70         }
71         G = B = R;
72     } else if (m_Family == PDFCS_DEVICECMYK) {
73         if (!m_dwStdConversion) {
74             AdobeCMYK_to_sRGB(pBuf[0], pBuf[1], pBuf[2], pBuf[3], R, G, B);
75         } else {
76             FX_FLOAT k = pBuf[3];
77             R = 1.0f - FX_MIN(1.0f, pBuf[0] + k);
78             G = 1.0f - FX_MIN(1.0f, pBuf[1] + k);
79             B = 1.0f - FX_MIN(1.0f, pBuf[2] + k);
80         }
81     } else {
82         ASSERT(m_Family == PDFCS_PATTERN);
83         R = G = B = 0;
84         return FALSE;
85     }
86     return TRUE;
87 }
88 FX_BOOL CPDF_DeviceCS::v_GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const
89 {
90     if (m_Family != PDFCS_DEVICECMYK) {
91         return FALSE;
92     }
93     c = pBuf[0];
94     m = pBuf[1];
95     y = pBuf[2];
96     k = pBuf[3];
97     return TRUE;
98 }
99 FX_BOOL CPDF_DeviceCS::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
100 {
101     if (m_Family == PDFCS_DEVICERGB) {
102         pBuf[0] = R;
103         pBuf[1] = G;
104         pBuf[2] = B;
105         return TRUE;
106     } else if (m_Family == PDFCS_DEVICEGRAY) {
107         if (R == G && R == B) {
108             *pBuf = R;
109             return TRUE;
110         } else {
111             return FALSE;
112         }
113     } else if (m_Family == PDFCS_DEVICECMYK) {
114         sRGB_to_AdobeCMYK(R, G, B, pBuf[0], pBuf[1], pBuf[2], pBuf[3]);
115         return TRUE;
116     }
117     return FALSE;
118 }
119 FX_BOOL CPDF_DeviceCS::v_SetCMYK(FX_FLOAT* pBuf, FX_FLOAT c, FX_FLOAT m, FX_FLOAT y, FX_FLOAT k) const
120 {
121     if (m_Family == PDFCS_DEVICERGB) {
122         AdobeCMYK_to_sRGB(c, m, y, k, pBuf[0], pBuf[1], pBuf[2]);
123         return TRUE;
124     } else if (m_Family == PDFCS_DEVICEGRAY) {
125         return FALSE;
126     } else if (m_Family == PDFCS_DEVICECMYK) {
127         pBuf[0] = c;
128         pBuf[1] = m;
129         pBuf[2] = y;
130         pBuf[3] = k;
131         return TRUE;
132     }
133     return FALSE;
134 }
135 static void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels)
136 {
137     if (pDestBuf == pSrcBuf)
138         for (int i = 0; i < pixels; i ++) {
139             uint8_t temp = pDestBuf[2];
140             pDestBuf[2] = pDestBuf[0];
141             pDestBuf[0] = temp;
142             pDestBuf += 3;
143         }
144     else
145         for (int i = 0; i < pixels; i ++) {
146             *pDestBuf ++ = pSrcBuf[2];
147             *pDestBuf ++ = pSrcBuf[1];
148             *pDestBuf ++ = pSrcBuf[0];
149             pSrcBuf += 3;
150         }
151 }
152 void CPDF_DeviceCS::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
153 {
154     if (bTransMask && m_Family == PDFCS_DEVICECMYK) {
155         for (int i = 0; i < pixels; i ++) {
156             int k = 255 - pSrcBuf[3];
157             pDestBuf[0] = ((255 - pSrcBuf[0]) * k) / 255;
158             pDestBuf[1] = ((255 - pSrcBuf[1]) * k) / 255;
159             pDestBuf[2] = ((255 - pSrcBuf[2]) * k) / 255;
160             pDestBuf += 3;
161             pSrcBuf += 4;
162         }
163         return;
164     }
165     if (m_Family == PDFCS_DEVICERGB) {
166         ReverseRGB(pDestBuf, pSrcBuf, pixels);
167     } else if (m_Family == PDFCS_DEVICEGRAY) {
168         for (int i = 0; i < pixels; i ++) {
169             *pDestBuf ++ = pSrcBuf[i];
170             *pDestBuf ++ = pSrcBuf[i];
171             *pDestBuf ++ = pSrcBuf[i];
172         }
173     } else {
174         for (int i = 0; i < pixels; i ++) {
175             if (!m_dwStdConversion) {
176                 AdobeCMYK_to_sRGB1(pSrcBuf[0], pSrcBuf[1], pSrcBuf[2], pSrcBuf[3], pDestBuf[2], pDestBuf[1], pDestBuf[0]);
177             } else {
178                 uint8_t k = pSrcBuf[3];
179                 pDestBuf[2] = 255 - FX_MIN(255, pSrcBuf[0] + k);
180                 pDestBuf[1] = 255 - FX_MIN(255, pSrcBuf[1] + k);
181                 pDestBuf[0] = 255 - FX_MIN(255, pSrcBuf[2] + k);
182             }
183             pSrcBuf += 4;
184             pDestBuf += 3;
185         }
186     }
187 }
188 const uint8_t g_sRGBSamples1[] = {
189     0,   3,   6,  10,  13,  15,  18,  20,  22,  23,  25,  27,  28,  30,  31,  32,
190     34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
191     49,  50,  51,  52,  53,  53,  54,  55,  56,  56,  57,  58,  58,  59,  60,  61,
192     61,  62,  62,  63,  64,  64,  65,  66,  66,  67,  67,  68,  68,  69,  70,  70,
193     71,  71,  72,  72,  73,  73,  74,  74,  75,  76,  76,  77,  77,  78,  78,  79,
194     79,  79,  80,  80,  81,  81,  82,  82,  83,  83,  84,  84,  85,  85,  85,  86,
195     86,  87,  87,  88,  88,  88,  89,  89,  90,  90,  91,  91,  91,  92,  92,  93,
196     93,  93,  94,  94,  95,  95,  95,  96,  96,  97,  97,  97,  98,  98,  98,  99,
197     99,  99, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 104, 104, 104,
198     105, 105, 106, 106, 106, 107, 107, 107, 108, 108, 108, 109, 109, 109, 110, 110,
199     110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115,
200     115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120,
201 };
202 const uint8_t g_sRGBSamples2[] = {
203     120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
204     137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
205     152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164,
206     165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176,
207     177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 185, 185, 186, 187, 187,
208     188, 189, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197,
209     198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207,
210     208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216,
211     216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224,
212     225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
213     233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240,
214     241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248,
215     248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255,
216 };
217
218 static FX_FLOAT RGB_Conversion(FX_FLOAT colorComponent)
219 {
220     if (colorComponent > 1) {
221         colorComponent = 1;
222     }
223     if (colorComponent < 0) {
224         colorComponent = 0;
225     }
226     int scale = (int)(colorComponent * 1023);
227     if (scale < 0) {
228         scale = 0;
229     }
230     if (scale < 192) {
231         colorComponent = (g_sRGBSamples1[scale] / 255.0f);
232     }
233     else {
234         colorComponent = (g_sRGBSamples2[scale / 4 - 48] / 255.0f);
235     }
236     return colorComponent;
237 }
238
239 static void XYZ_to_sRGB(FX_FLOAT X, FX_FLOAT Y, FX_FLOAT Z, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B)
240 {
241     FX_FLOAT R1 = 3.2410f * X - 1.5374f * Y - 0.4986f * Z;
242     FX_FLOAT G1 = -0.9692f * X + 1.8760f * Y + 0.0416f * Z;
243     FX_FLOAT B1 =  0.0556f * X - 0.2040f * Y + 1.0570f * Z;
244
245     R = RGB_Conversion(R1);
246     G = RGB_Conversion(G1);
247     B = RGB_Conversion(B1);
248 }
249
250 static void XYZ_to_sRGB_WhitePoint(FX_FLOAT X, FX_FLOAT Y, FX_FLOAT Z, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B, FX_FLOAT Xw, FX_FLOAT Yw, FX_FLOAT Zw)
251 {
252     // The following RGB_xyz is based on
253     // sRGB value {Rx,Ry}={0.64, 0.33}, {Gx,Gy}={0.30, 0.60}, {Bx,By}={0.15, 0.06}
254
255     FX_FLOAT Rx = 0.64f, Ry = 0.33f;
256     FX_FLOAT Gx = 0.30f, Gy = 0.60f;
257     FX_FLOAT Bx = 0.15f, By = 0.06f;
258     CFX_Matrix_3by3 RGB_xyz(Rx, Gx, Bx, Ry, Gy, By, 1 - Rx - Ry, 1 - Gx - Gy, 1 - Bx - By);
259     CFX_Vector_3by1 whitePoint(Xw, Yw, Zw);
260     CFX_Vector_3by1 XYZ(X, Y, Z);
261
262     CFX_Vector_3by1 RGB_Sum_XYZ = RGB_xyz.Inverse().TransformVector(whitePoint);
263     CFX_Matrix_3by3 RGB_SUM_XYZ_DIAG(RGB_Sum_XYZ.a, 0, 0, 0, RGB_Sum_XYZ.b, 0, 0, 0, RGB_Sum_XYZ.c);
264     CFX_Matrix_3by3 M = RGB_xyz.Multiply(RGB_SUM_XYZ_DIAG);
265     CFX_Vector_3by1 RGB = M.Inverse().TransformVector(XYZ);
266
267     R = RGB_Conversion(RGB.a);
268     G = RGB_Conversion(RGB.b);
269     B = RGB_Conversion(RGB.c);
270 }
271 class CPDF_CalGray : public CPDF_ColorSpace
272 {
273 public:
274     explicit CPDF_CalGray(CPDF_Document* pDoc)
275         : CPDF_ColorSpace(pDoc, PDFCS_CALGRAY, 1) {
276     }
277     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
278     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
279     FX_BOOL     SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const override;
280     void TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width,
281                             int image_height, FX_BOOL bTransMask = FALSE) const override;
282
283 private:
284     FX_FLOAT m_WhitePoint[3];
285     FX_FLOAT m_BlackPoint[3];
286     FX_FLOAT m_Gamma;
287 };
288
289 FX_BOOL CPDF_CalGray::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
290 {
291     CPDF_Dictionary* pDict = pArray->GetDict(1);
292     CPDF_Array* pParam = pDict->GetArray(FX_BSTRC("WhitePoint"));
293     int i;
294     for (i = 0; i < 3; i ++) {
295         m_WhitePoint[i] = pParam ? pParam->GetNumber(i) : 0;
296     }
297     pParam = pDict->GetArray(FX_BSTRC("BlackPoint"));
298     for (i = 0; i < 3; i ++) {
299         m_BlackPoint[i] = pParam ? pParam->GetNumber(i) : 0;
300     }
301     m_Gamma = pDict->GetNumber(FX_BSTRC("Gamma"));
302     if (m_Gamma == 0) {
303         m_Gamma = 1.0f;
304     }
305     return TRUE;
306 }
307 FX_BOOL CPDF_CalGray::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
308 {
309     R = G = B = *pBuf;
310     return TRUE;
311 }
312 FX_BOOL CPDF_CalGray::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
313 {
314     if (R == G && R == B) {
315         *pBuf = R;
316         return TRUE;
317     } else {
318         return FALSE;
319     }
320 }
321 void CPDF_CalGray::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
322 {
323     for (int i = 0; i < pixels; i ++) {
324         *pDestBuf ++ = pSrcBuf[i];
325         *pDestBuf ++ = pSrcBuf[i];
326         *pDestBuf ++ = pSrcBuf[i];
327     }
328 }
329 class CPDF_CalRGB : public CPDF_ColorSpace
330 {
331 public:
332     explicit CPDF_CalRGB(CPDF_Document* pDoc)
333         : CPDF_ColorSpace(pDoc, PDFCS_CALRGB, 3) {
334     }
335     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
336     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
337     FX_BOOL SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const override;
338     void TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width,
339                             int image_height, FX_BOOL bTransMask = FALSE) const override;
340
341     FX_FLOAT m_WhitePoint[3];
342     FX_FLOAT m_BlackPoint[3];
343     FX_FLOAT m_Gamma[3];
344     FX_FLOAT m_Matrix[9];
345     FX_BOOL m_bGamma;
346     FX_BOOL m_bMatrix;
347 };
348 FX_BOOL CPDF_CalRGB::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
349 {
350     CPDF_Dictionary* pDict = pArray->GetDict(1);
351     CPDF_Array* pParam = pDict->GetArray(FX_BSTRC("WhitePoint"));
352     int i;
353     for (i = 0; i < 3; i ++) {
354         m_WhitePoint[i] = pParam ? pParam->GetNumber(i) : 0;
355     }
356     pParam = pDict->GetArray(FX_BSTRC("BlackPoint"));
357     for (i = 0; i < 3; i ++) {
358         m_BlackPoint[i] = pParam ? pParam->GetNumber(i) : 0;
359     }
360     pParam = pDict->GetArray(FX_BSTRC("Gamma"));
361     if (pParam) {
362         m_bGamma = TRUE;
363         for (i = 0; i < 3; i ++) {
364             m_Gamma[i] = pParam->GetNumber(i);
365         }
366     } else {
367         m_bGamma = FALSE;
368     }
369     pParam = pDict->GetArray(FX_BSTRC("Matrix"));
370     if (pParam) {
371         m_bMatrix = TRUE;
372         for (i = 0; i < 9; i ++) {
373             m_Matrix[i] = pParam->GetNumber(i);
374         }
375     } else {
376         m_bMatrix = FALSE;
377     }
378     return TRUE;
379 }
380 FX_BOOL CPDF_CalRGB::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
381 {
382     FX_FLOAT A_ = pBuf[0];
383     FX_FLOAT B_ = pBuf[1];
384     FX_FLOAT C_ = pBuf[2];
385     if (m_bGamma) {
386         A_ = (FX_FLOAT)FXSYS_pow(A_, m_Gamma[0]);
387         B_ = (FX_FLOAT)FXSYS_pow(B_, m_Gamma[1]);
388         C_ = (FX_FLOAT)FXSYS_pow(C_, m_Gamma[2]);
389     }
390     FX_FLOAT X, Y, Z;
391     if (m_bMatrix) {
392         X = m_Matrix[0] * A_ + m_Matrix[3] * B_ + m_Matrix[6] * C_;
393         Y = m_Matrix[1] * A_ + m_Matrix[4] * B_ + m_Matrix[7] * C_;
394         Z = m_Matrix[2] * A_ + m_Matrix[5] * B_ + m_Matrix[8] * C_;
395     } else {
396         X = A_;
397         Y = B_;
398         Z = C_;
399     }
400     XYZ_to_sRGB_WhitePoint(X, Y, Z, R, G, B, m_WhitePoint[0], m_WhitePoint[1], m_WhitePoint[2]);
401     return TRUE;
402 }
403 FX_BOOL CPDF_CalRGB::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
404 {
405     pBuf[0] = R;
406     pBuf[1] = G;
407     pBuf[2] = B;
408     return TRUE;
409 }
410 void CPDF_CalRGB::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
411 {
412     if (bTransMask) {
413         FX_FLOAT Cal[3];
414         FX_FLOAT R, G, B;
415         for(int i = 0; i < pixels; i ++) {
416             Cal[0] = ((FX_FLOAT)pSrcBuf[2]) / 255;
417             Cal[1] = ((FX_FLOAT)pSrcBuf[1]) / 255;
418             Cal[2] = ((FX_FLOAT)pSrcBuf[0]) / 255;
419             GetRGB(Cal, R, G, B);
420             pDestBuf[0] = FXSYS_round(B * 255);
421             pDestBuf[1] = FXSYS_round(G * 255);
422             pDestBuf[2] = FXSYS_round(R * 255);
423             pSrcBuf += 3;
424             pDestBuf += 3;
425         }
426     }
427     ReverseRGB(pDestBuf, pSrcBuf, pixels);
428 }
429 class CPDF_LabCS : public CPDF_ColorSpace
430 {
431 public:
432     explicit CPDF_LabCS(CPDF_Document* pDoc)
433         : CPDF_ColorSpace(pDoc, PDFCS_LAB, 3) {
434     }
435     void GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const override;
436     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
437     FX_BOOL     GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
438     FX_BOOL     SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const override;
439     void TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width,
440                             int image_height, FX_BOOL bTransMask = FALSE) const;
441
442     FX_FLOAT m_WhitePoint[3];
443     FX_FLOAT m_BlackPoint[3];
444     FX_FLOAT m_Ranges[4];
445 };
446 FX_BOOL CPDF_LabCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
447 {
448     CPDF_Dictionary* pDict = pArray->GetDict(1);
449     if (!pDict) {
450         return FALSE;
451     }
452     CPDF_Array* pParam = pDict->GetArray(FX_BSTRC("WhitePoint"));
453     int i;
454     for (i = 0; i < 3; i ++) {
455         m_WhitePoint[i] = pParam ? pParam->GetNumber(i) : 0;
456     }
457     pParam = pDict->GetArray(FX_BSTRC("BlackPoint"));
458     for (i = 0; i < 3; i ++) {
459         m_BlackPoint[i] = pParam ? pParam->GetNumber(i) : 0;
460     }
461     pParam = pDict->GetArray(FX_BSTRC("Range"));
462     const FX_FLOAT def_ranges[4] = { -100 * 1.0f, 100 * 1.0f, -100 * 1.0f, 100 * 1.0f};
463     for (i = 0; i < 4; i ++) {
464         m_Ranges[i] = pParam ? pParam->GetNumber(i) : def_ranges[i];
465     }
466     return TRUE;
467 }
468 void CPDF_LabCS::GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const
469 {
470     assert(iComponent < 3);
471     value = 0;
472     if (iComponent == 0) {
473         min = 0;
474         max = 100 * 1.0f;
475     } else {
476         min = m_Ranges[iComponent * 2 - 2];
477         max = m_Ranges[iComponent * 2 - 1];
478         if (value < min) {
479             value = min;
480         } else if (value > max) {
481             value = max;
482         }
483     }
484 }
485 FX_BOOL CPDF_LabCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
486 {
487     FX_FLOAT Lstar = pBuf[0];
488     FX_FLOAT astar = pBuf[1];
489     FX_FLOAT bstar = pBuf[2];
490     FX_FLOAT M = (Lstar + 16.0f) / 116.0f;
491     FX_FLOAT L = M + astar / 500.0f;
492     FX_FLOAT N = M - bstar / 200.0f;
493     FX_FLOAT X, Y, Z;
494     if (L < 0.2069f) {
495         X = 0.957f * 0.12842f * (L - 0.1379f);
496     } else {
497         X = 0.957f * L * L * L;
498     }
499     if (M < 0.2069f) {
500         Y = 0.12842f * (M - 0.1379f);
501     } else {
502         Y = M * M * M;
503     }
504     if (N < 0.2069f) {
505         Z = 1.0889f * 0.12842f * (N - 0.1379f);
506     } else {
507         Z = 1.0889f * N * N * N;
508     }
509     XYZ_to_sRGB(X, Y, Z, R, G, B);
510     return TRUE;
511 }
512 FX_BOOL CPDF_LabCS::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
513 {
514     return FALSE;
515 }
516 void CPDF_LabCS::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
517 {
518     for (int i = 0; i < pixels; i ++) {
519         FX_FLOAT lab[3];
520         FX_FLOAT R, G, B;
521         lab[0] = (pSrcBuf[0] * 100 / 255.0f);
522         lab[1] = (FX_FLOAT)(pSrcBuf[1] - 128);
523         lab[2] = (FX_FLOAT)(pSrcBuf[2] - 128);
524         GetRGB(lab, R, G, B);
525         pDestBuf[0] = (int32_t)(B * 255);
526         pDestBuf[1] = (int32_t)(G * 255);
527         pDestBuf[2] = (int32_t)(R * 255);
528         pDestBuf += 3;
529         pSrcBuf += 3;
530     }
531 }
532 CPDF_IccProfile::CPDF_IccProfile(const uint8_t* pData, FX_DWORD dwSize):
533     m_bsRGB(FALSE),
534     m_pTransform(NULL),
535     m_nSrcComponents(0)
536 {
537     if (dwSize == 3144 && FXSYS_memcmp(pData + 0x190, "sRGB IEC61966-2.1", 17) == 0) {
538         m_bsRGB = TRUE;
539         m_nSrcComponents = 3;
540     }
541     else if (CPDF_ModuleMgr::Get()->GetIccModule()) {
542         m_pTransform = CPDF_ModuleMgr::Get()->GetIccModule()->CreateTransform_sRGB(pData, dwSize, m_nSrcComponents);
543     }
544 }
545 CPDF_IccProfile::~CPDF_IccProfile()
546 {
547     if (m_pTransform) {
548         CPDF_ModuleMgr::Get()->GetIccModule()->DestroyTransform(m_pTransform);
549     }
550 }
551 class CPDF_ICCBasedCS : public CPDF_ColorSpace
552 {
553 public:
554     explicit CPDF_ICCBasedCS(CPDF_Document* pDoc)
555         : CPDF_ColorSpace(pDoc, PDFCS_ICCBASED, 0),
556           m_pAlterCS(nullptr),
557           m_pProfile(nullptr),
558           m_pCache(nullptr),
559           m_pRanges(nullptr),
560           m_bOwn(FALSE) {
561     }
562     ~CPDF_ICCBasedCS() override;
563
564     FX_BOOL     v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
565     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
566     FX_BOOL     SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const override;
567     FX_BOOL     v_GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const override;
568     void EnableStdConversion(FX_BOOL bEnabled) override;
569     void TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width,
570                             int image_height, FX_BOOL bTransMask = FALSE) const override;
571
572     CPDF_ColorSpace* m_pAlterCS;
573     CPDF_IccProfile* m_pProfile;
574     uint8_t* m_pCache;
575     FX_FLOAT* m_pRanges;
576     FX_BOOL     m_bOwn;
577 };
578
579 CPDF_ICCBasedCS::~CPDF_ICCBasedCS()
580 {
581     if (m_pCache) {
582         FX_Free(m_pCache);
583     }
584     if (m_pRanges) {
585         FX_Free(m_pRanges);
586     }
587     if (m_pAlterCS && m_bOwn) {
588         m_pAlterCS->ReleaseCS();
589     }
590     if (m_pProfile && m_pDocument) {
591         m_pDocument->GetPageData()->ReleaseIccProfile(m_pProfile);
592     }
593 }
594
595 FX_BOOL CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
596 {
597     CPDF_Stream* pStream = pArray->GetStream(1);
598     if (pStream == NULL) {
599         return FALSE;
600     }
601     m_pProfile = pDoc->LoadIccProfile(pStream);
602     if (!m_pProfile) {
603         return FALSE;
604     }
605     m_nComponents = m_pProfile->GetComponents(); //Try using the nComponents from ICC profile
606     CPDF_Dictionary* pDict = pStream->GetDict();
607     if (m_pProfile->m_pTransform == NULL) { // No valid ICC profile or using sRGB
608         CPDF_Object* pAlterCSObj = pDict ? pDict->GetElementValue(FX_BSTRC("Alternate")) : NULL;
609         if (pAlterCSObj) {
610             CPDF_ColorSpace* pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj);
611             if (pAlterCS) {
612                 if (m_nComponents == 0) { // NO valid ICC profile
613                     if (pAlterCS->CountComponents() > 0) { // Use Alternative colorspace
614                         m_nComponents = pAlterCS->CountComponents();
615                         m_pAlterCS = pAlterCS;
616                         m_bOwn = TRUE;
617                     }
618                     else { // No valid alternative colorspace
619                         pAlterCS->ReleaseCS();
620                         int32_t nDictComponents = pDict ? pDict->GetInteger(FX_BSTRC("N")) : 0;
621                         if (nDictComponents != 1 && nDictComponents != 3 && nDictComponents != 4) {
622                             return FALSE;
623                         }
624                         m_nComponents = nDictComponents;
625                     }
626
627                 }
628                 else { // Using sRGB
629                     if (pAlterCS->CountComponents() != m_nComponents) {
630                         pAlterCS->ReleaseCS();
631                     }
632                     else {
633                         m_pAlterCS = pAlterCS;
634                         m_bOwn = TRUE;
635                     }
636                 }
637             }
638         }
639         if (!m_pAlterCS) {
640             if (m_nComponents == 1) {
641                 m_pAlterCS = GetStockCS(PDFCS_DEVICEGRAY);
642             }
643             else if (m_nComponents == 3) {
644                 m_pAlterCS = GetStockCS(PDFCS_DEVICERGB);
645             }
646             else if (m_nComponents == 4) {
647                 m_pAlterCS = GetStockCS(PDFCS_DEVICECMYK);
648             }
649         }
650     }
651     CPDF_Array* pRanges = pDict->GetArray(FX_BSTRC("Range"));
652     m_pRanges = FX_Alloc2D(FX_FLOAT, m_nComponents, 2);
653     for (int i = 0; i < m_nComponents * 2; i ++) {
654         if (pRanges) {
655             m_pRanges[i] = pRanges->GetNumber(i);
656         } else if (i % 2) {
657             m_pRanges[i] = 1.0f;
658         } else {
659             m_pRanges[i] = 0;
660         }
661     }
662     return TRUE;
663 }
664 FX_BOOL CPDF_ICCBasedCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
665 {
666     if (m_pProfile && m_pProfile->m_bsRGB) {
667         R = pBuf[0];
668         G = pBuf[1];
669         B = pBuf[2];
670         return TRUE;
671     }
672     ICodec_IccModule *pIccModule = CPDF_ModuleMgr::Get()->GetIccModule();
673     if (m_pProfile->m_pTransform == NULL || pIccModule == NULL) {
674         if (m_pAlterCS) {
675             m_pAlterCS->GetRGB(pBuf, R, G, B);
676         } else {
677             R = G = B = 0.0f;
678         }
679         return TRUE;
680     }
681     FX_FLOAT rgb[3];
682     pIccModule->SetComponents(m_nComponents);
683     pIccModule->Translate(m_pProfile->m_pTransform, pBuf, rgb);
684     R = rgb[0];
685     G = rgb[1];
686     B = rgb[2];
687     return TRUE;
688 }
689 FX_BOOL CPDF_ICCBasedCS::v_GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const
690 {
691     if (m_nComponents != 4) {
692         return FALSE;
693     }
694     c = pBuf[0];
695     m = pBuf[1];
696     y = pBuf[2];
697     k = pBuf[3];
698     return TRUE;
699 }
700 FX_BOOL CPDF_ICCBasedCS::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
701 {
702     return FALSE;
703 }
704 void CPDF_ICCBasedCS::EnableStdConversion(FX_BOOL bEnabled)
705 {
706     CPDF_ColorSpace::EnableStdConversion(bEnabled);
707     if (m_pAlterCS) {
708         m_pAlterCS->EnableStdConversion(bEnabled);
709     }
710 }
711 void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
712 {
713     if (m_pProfile->m_bsRGB) {
714         ReverseRGB(pDestBuf, pSrcBuf, pixels);
715     } else if (m_pProfile->m_pTransform) {
716         int nMaxColors = 1;
717         for (int i = 0; i < m_nComponents; i ++) {
718             nMaxColors *= 52;
719         }
720         if (m_nComponents > 3 || image_width * image_height < nMaxColors * 3 / 2) {
721             CPDF_ModuleMgr::Get()->GetIccModule()->TranslateScanline(m_pProfile->m_pTransform, pDestBuf, pSrcBuf, pixels);
722         } else {
723             if (m_pCache == NULL) {
724                 ((CPDF_ICCBasedCS*)this)->m_pCache = FX_Alloc2D(uint8_t, nMaxColors, 3);
725                 uint8_t* temp_src = FX_Alloc2D(uint8_t, nMaxColors, m_nComponents);
726                 uint8_t* pSrc = temp_src;
727                 for (int i = 0; i < nMaxColors; i ++) {
728                     FX_DWORD color = i;
729                     FX_DWORD order = nMaxColors / 52;
730                     for (int c = 0; c < m_nComponents; c ++) {
731                         *pSrc++ = (uint8_t)(color / order * 5);
732                         color %= order;
733                         order /= 52;
734                     }
735                 }
736                 CPDF_ModuleMgr::Get()->GetIccModule()->TranslateScanline(m_pProfile->m_pTransform, m_pCache, temp_src, nMaxColors);
737                 FX_Free(temp_src);
738             }
739             for (int i = 0; i < pixels; i ++) {
740                 int index = 0;
741                 for (int c = 0; c < m_nComponents; c ++) {
742                     index = index * 52 + (*pSrcBuf) / 5;
743                     pSrcBuf ++;
744                 }
745                 index *= 3;
746                 *pDestBuf++ = m_pCache[index];
747                 *pDestBuf++ = m_pCache[index + 1];
748                 *pDestBuf++ = m_pCache[index + 2];
749             }
750         }
751     } else if (m_pAlterCS) {
752         m_pAlterCS->TranslateImageLine(pDestBuf, pSrcBuf, pixels, image_width, image_height);
753     }
754 }
755 class CPDF_IndexedCS : public CPDF_ColorSpace
756 {
757 public:
758     explicit CPDF_IndexedCS(CPDF_Document* pDoc)
759         : CPDF_ColorSpace(pDoc, PDFCS_INDEXED, 1),
760           m_pBaseCS(nullptr),
761           m_pCountedBaseCS(nullptr),
762           m_pCompMinMax(nullptr) {
763     }
764     ~CPDF_IndexedCS() override;
765
766     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
767     FX_BOOL     GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
768     CPDF_ColorSpace* GetBaseCS() const override;
769     void EnableStdConversion(FX_BOOL bEnabled) override;
770
771     CPDF_ColorSpace* m_pBaseCS;
772     CPDF_CountedColorSpace* m_pCountedBaseCS;
773     int m_nBaseComponents;
774     int m_MaxIndex;
775     CFX_ByteString m_Table;
776     FX_FLOAT* m_pCompMinMax;
777 };
778 CPDF_IndexedCS::~CPDF_IndexedCS()
779 {
780     if (m_pCompMinMax) {
781         FX_Free(m_pCompMinMax);
782     }
783     CPDF_ColorSpace* pCS = m_pCountedBaseCS ? m_pCountedBaseCS->get() : NULL;
784     if (pCS && m_pDocument) {
785         m_pDocument->GetPageData()->ReleaseColorSpace(pCS->GetArray());
786     }
787 }
788 FX_BOOL CPDF_IndexedCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
789 {
790     if (pArray->GetCount() < 4) {
791         return FALSE;
792     }
793     CPDF_Object* pBaseObj = pArray->GetElementValue(1);
794     if (pBaseObj == m_pArray) {
795         return FALSE;
796     }
797     CPDF_DocPageData* pDocPageData = pDoc->GetPageData();
798     m_pBaseCS = pDocPageData->GetColorSpace(pBaseObj, NULL);
799     if (m_pBaseCS == NULL) {
800         return FALSE;
801     }
802     m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
803     m_nBaseComponents = m_pBaseCS->CountComponents();
804     m_pCompMinMax = FX_Alloc2D(FX_FLOAT, m_nBaseComponents, 2);
805     FX_FLOAT defvalue;
806     for (int i = 0; i < m_nBaseComponents; i ++) {
807         m_pBaseCS->GetDefaultValue(i, defvalue, m_pCompMinMax[i * 2], m_pCompMinMax[i * 2 + 1]);
808         m_pCompMinMax[i * 2 + 1] -= m_pCompMinMax[i * 2];
809     }
810     m_MaxIndex = pArray->GetInteger(2);
811     CPDF_Object* pTableObj = pArray->GetElementValue(3);
812     if (pTableObj == NULL) {
813         return FALSE;
814     }
815     if (pTableObj->GetType() == PDFOBJ_STRING) {
816         m_Table = ((CPDF_String*)pTableObj)->GetString();
817     } else if (pTableObj->GetType() == PDFOBJ_STREAM) {
818         CPDF_StreamAcc acc;
819         acc.LoadAllData((CPDF_Stream*)pTableObj, FALSE);
820         m_Table = CFX_ByteStringC(acc.GetData(), acc.GetSize());
821     }
822     return TRUE;
823 }
824
825 FX_BOOL CPDF_IndexedCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
826 {
827     int index = (int32_t)(*pBuf);
828     if (index < 0 || index > m_MaxIndex) {
829         return FALSE;
830     }
831     if (m_nBaseComponents) {
832         if (index == INT_MAX || (index + 1) > INT_MAX / m_nBaseComponents ||
833                 (index + 1)*m_nBaseComponents > (int)m_Table.GetLength()) {
834             R = G = B = 0;
835             return FALSE;
836         }
837     }
838     CFX_FixedBufGrow<FX_FLOAT, 16> Comps(m_nBaseComponents);
839     FX_FLOAT* comps = Comps;
840     const uint8_t* pTable = m_Table;
841     for (int i = 0; i < m_nBaseComponents; i ++) {
842         comps[i] = m_pCompMinMax[i * 2] + m_pCompMinMax[i * 2 + 1] * pTable[index * m_nBaseComponents + i] / 255;
843     }
844     m_pBaseCS->GetRGB(comps, R, G, B);
845     return TRUE;
846 }
847 CPDF_ColorSpace*CPDF_IndexedCS::GetBaseCS() const
848 {
849     return m_pBaseCS;
850 }
851 void CPDF_IndexedCS::EnableStdConversion(FX_BOOL bEnabled)
852 {
853     CPDF_ColorSpace::EnableStdConversion(bEnabled);
854     if (m_pBaseCS) {
855         m_pBaseCS->EnableStdConversion(bEnabled);
856     }
857 }
858 #define MAX_PATTERN_COLORCOMPS  16
859 typedef struct _PatternValue {
860     CPDF_Pattern*       m_pPattern;
861     CPDF_CountedPattern*        m_pCountedPattern;
862     int                         m_nComps;
863     FX_FLOAT            m_Comps[MAX_PATTERN_COLORCOMPS];
864 } PatternValue;
865 CPDF_PatternCS::~CPDF_PatternCS()
866 {
867     CPDF_ColorSpace* pCS = m_pCountedBaseCS ? m_pCountedBaseCS->get() : NULL;
868     if (pCS && m_pDocument) {
869             m_pDocument->GetPageData()->ReleaseColorSpace(pCS->GetArray());
870     }
871 }
872 FX_BOOL CPDF_PatternCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
873 {
874     CPDF_Object* pBaseCS = pArray->GetElementValue(1);
875     if (pBaseCS == m_pArray) {
876         return FALSE;
877     }
878     CPDF_DocPageData* pDocPageData = pDoc->GetPageData();
879     m_pBaseCS = pDocPageData->GetColorSpace(pBaseCS, NULL);
880     if (m_pBaseCS) {
881         if (m_pBaseCS->GetFamily() == PDFCS_PATTERN) {
882             return FALSE;
883         }
884         m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
885         m_nComponents = m_pBaseCS->CountComponents() + 1;
886         if (m_pBaseCS->CountComponents() > MAX_PATTERN_COLORCOMPS) {
887             return FALSE;
888         }
889     } else {
890         m_nComponents = 1;
891     }
892     return TRUE;
893 }
894 FX_BOOL CPDF_PatternCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
895 {
896     if (m_pBaseCS) {
897         ASSERT(m_pBaseCS->GetFamily() != PDFCS_PATTERN);
898         PatternValue* pvalue = (PatternValue*)pBuf;
899         if (m_pBaseCS->GetRGB(pvalue->m_Comps, R, G, B)) {
900             return TRUE;
901         }
902     }
903     R = G = B = 0.75f;
904     return FALSE;
905 }
906 CPDF_ColorSpace* CPDF_PatternCS::GetBaseCS() const
907 {
908     return m_pBaseCS;
909 }
910 class CPDF_SeparationCS : public CPDF_ColorSpace
911 {
912 public:
913     CPDF_SeparationCS(CPDF_Document* pDoc)
914             : CPDF_ColorSpace(pDoc, PDFCS_SEPARATION, 1),
915               m_pAltCS(nullptr),
916               m_pFunc(nullptr) {
917     }
918     ~CPDF_SeparationCS() override;
919     void GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const override;
920     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
921     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
922     void EnableStdConversion(FX_BOOL bEnabled) override;
923
924     CPDF_ColorSpace* m_pAltCS;
925     CPDF_Function* m_pFunc;
926     enum { None, All, Colorant } m_Type;
927 };
928 CPDF_SeparationCS::~CPDF_SeparationCS()
929 {
930     if (m_pAltCS) {
931         m_pAltCS->ReleaseCS();
932     }
933     delete m_pFunc;
934 }
935 void CPDF_SeparationCS::GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const
936 {
937     value = 1.0f;
938     min = 0;
939     max = 1.0f;
940 }
941 FX_BOOL CPDF_SeparationCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
942 {
943     CFX_ByteString name = pArray->GetString(1);
944     if (name == FX_BSTRC("None")) {
945         m_Type = None;
946     } else {
947         m_Type = Colorant;
948         CPDF_Object* pAltCS = pArray->GetElementValue(2);
949         if (pAltCS == m_pArray) {
950             return FALSE;
951         }
952         m_pAltCS = Load(pDoc, pAltCS);
953         if (!m_pAltCS) {
954             return FALSE;
955         }
956         CPDF_Object* pFuncObj = pArray->GetElementValue(3);
957         if (pFuncObj && pFuncObj->GetType() != PDFOBJ_NAME) {
958             m_pFunc = CPDF_Function::Load(pFuncObj);
959         }
960         if (m_pFunc && m_pFunc->CountOutputs() < m_pAltCS->CountComponents()) {
961             delete m_pFunc;
962             m_pFunc = NULL;
963         }
964     }
965     return TRUE;
966 }
967 FX_BOOL CPDF_SeparationCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
968 {
969     if (m_Type == None) {
970         return FALSE;
971     }
972     if (m_pFunc == NULL) {
973         if (m_pAltCS == NULL) {
974             return FALSE;
975         }
976         int nComps = m_pAltCS->CountComponents();
977         CFX_FixedBufGrow<FX_FLOAT, 16> results(nComps);
978         for (int i = 0; i < nComps; i ++) {
979             results[i] = *pBuf;
980         }
981         m_pAltCS->GetRGB(results, R, G, B);
982         return TRUE;
983     }
984     CFX_FixedBufGrow<FX_FLOAT, 16> results(m_pFunc->CountOutputs());
985     int nresults = 0;
986     m_pFunc->Call(pBuf, 1, results, nresults);
987     if (nresults == 0) {
988         return FALSE;
989     }
990     if (m_pAltCS) {
991         m_pAltCS->GetRGB(results, R, G, B);
992         return TRUE;
993     } else {
994         R = G = B = 0;
995         return FALSE;
996     }
997 }
998 void CPDF_SeparationCS::EnableStdConversion(FX_BOOL bEnabled)
999 {
1000     CPDF_ColorSpace::EnableStdConversion(bEnabled);
1001     if (m_pAltCS) {
1002         m_pAltCS->EnableStdConversion(bEnabled);
1003     }
1004 }
1005 class CPDF_DeviceNCS : public CPDF_ColorSpace
1006 {
1007 public:
1008     CPDF_DeviceNCS(CPDF_Document* pDoc)
1009             : CPDF_ColorSpace(pDoc, PDFCS_DEVICEN, 0),
1010               m_pAltCS(nullptr),
1011               m_pFunc(nullptr) {
1012     }
1013     ~CPDF_DeviceNCS() override;
1014     void GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const override;
1015     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
1016     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
1017     void EnableStdConversion(FX_BOOL bEnabled) override;
1018
1019     CPDF_ColorSpace* m_pAltCS;
1020     CPDF_Function* m_pFunc;
1021 };
1022 CPDF_DeviceNCS::~CPDF_DeviceNCS()
1023 {
1024     delete m_pFunc;
1025     if (m_pAltCS) {
1026         m_pAltCS->ReleaseCS();
1027     }
1028 }
1029 void CPDF_DeviceNCS::GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const
1030 {
1031     value = 1.0f;
1032     min = 0;
1033     max = 1.0f;
1034 }
1035 FX_BOOL CPDF_DeviceNCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
1036 {
1037     CPDF_Object* pObj = pArray->GetElementValue(1);
1038     if (!pObj) {
1039         return FALSE;
1040     }
1041     if (pObj->GetType() != PDFOBJ_ARRAY) {
1042         return FALSE;
1043     }
1044     m_nComponents = ((CPDF_Array*)pObj)->GetCount();
1045     CPDF_Object* pAltCS = pArray->GetElementValue(2);
1046     if (!pAltCS || pAltCS == m_pArray) {
1047         return FALSE;
1048     }
1049     m_pAltCS = Load(pDoc, pAltCS);
1050     m_pFunc = CPDF_Function::Load(pArray->GetElementValue(3));
1051     if (m_pAltCS == NULL || m_pFunc == NULL) {
1052         return FALSE;
1053     }
1054     if (m_pFunc->CountOutputs() < m_pAltCS->CountComponents()) {
1055         return FALSE;
1056     }
1057     return TRUE;
1058 }
1059 FX_BOOL CPDF_DeviceNCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
1060 {
1061     if (m_pFunc == NULL) {
1062         return FALSE;
1063     }
1064     CFX_FixedBufGrow<FX_FLOAT, 16> results(m_pFunc->CountOutputs());
1065     int nresults = 0;
1066     m_pFunc->Call(pBuf, m_nComponents, results, nresults);
1067     if (nresults == 0) {
1068         return FALSE;
1069     }
1070     m_pAltCS->GetRGB(results, R, G, B);
1071     return TRUE;
1072 }
1073 void CPDF_DeviceNCS::EnableStdConversion(FX_BOOL bEnabled)
1074 {
1075     CPDF_ColorSpace::EnableStdConversion(bEnabled);
1076     if (m_pAltCS) {
1077         m_pAltCS->EnableStdConversion(bEnabled);
1078     }
1079 }
1080 CPDF_ColorSpace* CPDF_ColorSpace::GetStockCS(int family)
1081 {
1082     return CPDF_ModuleMgr::Get()->GetPageModule()->GetStockCS(family);;
1083 }
1084 CPDF_ColorSpace* _CSFromName(const CFX_ByteString& name)
1085 {
1086     if (name == FX_BSTRC("DeviceRGB") || name == FX_BSTRC("RGB")) {
1087         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
1088     }
1089     if (name == FX_BSTRC("DeviceGray") || name == FX_BSTRC("G")) {
1090         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
1091     }
1092     if (name == FX_BSTRC("DeviceCMYK") || name == FX_BSTRC("CMYK")) {
1093         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
1094     }
1095     if (name == FX_BSTRC("Pattern")) {
1096         return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
1097     }
1098     return NULL;
1099 }
1100 CPDF_ColorSpace* CPDF_ColorSpace::Load(CPDF_Document* pDoc, CPDF_Object* pObj)
1101 {
1102     if (pObj == NULL) {
1103         return NULL;
1104     }
1105     if (pObj->GetType() == PDFOBJ_NAME) {
1106         return _CSFromName(pObj->GetString());
1107     }
1108     if (pObj->GetType() == PDFOBJ_STREAM) {
1109         CPDF_Dictionary *pDict = ((CPDF_Stream *)pObj)->GetDict();
1110         if (!pDict) {
1111             return NULL;
1112         }
1113         CPDF_ColorSpace *pRet = NULL;
1114         FX_POSITION pos = pDict->GetStartPos();
1115         while (pos) {
1116             CFX_ByteString bsKey;
1117             CPDF_Object *pValue = pDict->GetNextElement(pos, bsKey);
1118             if (pValue && pValue->GetType() == PDFOBJ_NAME) {
1119                 pRet = _CSFromName(pValue->GetString());
1120             }
1121             if (pRet) {
1122                 return pRet;
1123             }
1124         }
1125         return NULL;
1126     }
1127     if (pObj->GetType() != PDFOBJ_ARRAY) {
1128         return NULL;
1129     }
1130     CPDF_Array* pArray = (CPDF_Array*)pObj;
1131     if (pArray->GetCount() == 0) {
1132         return NULL;
1133     }
1134     CPDF_Object *pFamilyObj = pArray->GetElementValue(0);
1135     if (!pFamilyObj) {
1136         return NULL;
1137     }
1138     CFX_ByteString familyname = pFamilyObj->GetString();
1139     if (pArray->GetCount() == 1) {
1140         return _CSFromName(familyname);
1141     }
1142     CPDF_ColorSpace* pCS = NULL;
1143     FX_DWORD id = familyname.GetID();
1144     if (id == FXBSTR_ID('C', 'a', 'l', 'G')) {
1145         pCS = new CPDF_CalGray(pDoc);
1146     } else if (id == FXBSTR_ID('C', 'a', 'l', 'R')) {
1147         pCS = new CPDF_CalRGB(pDoc);
1148     } else if (id == FXBSTR_ID('L', 'a', 'b', 0)) {
1149         pCS = new CPDF_LabCS(pDoc);
1150     } else if (id == FXBSTR_ID('I', 'C', 'C', 'B')) {
1151         pCS = new CPDF_ICCBasedCS(pDoc);
1152     } else if (id == FXBSTR_ID('I', 'n', 'd', 'e') || id == FXBSTR_ID('I', 0, 0, 0)) {
1153         pCS = new CPDF_IndexedCS(pDoc);
1154     } else if (id == FXBSTR_ID('S', 'e', 'p', 'a')) {
1155         pCS = new CPDF_SeparationCS(pDoc);
1156     } else if (id == FXBSTR_ID('D', 'e', 'v', 'i')) {
1157         pCS = new CPDF_DeviceNCS(pDoc);
1158     } else if (id == FXBSTR_ID('P', 'a', 't', 't')) {
1159         pCS = new CPDF_PatternCS(pDoc);
1160     } else {
1161         return NULL;
1162     }
1163     pCS->m_pArray = pArray;
1164     if (!pCS->v_Load(pDoc, pArray)) {
1165         pCS->ReleaseCS();
1166         return NULL;
1167     }
1168     return pCS;
1169 }
1170 void CPDF_ColorSpace::ReleaseCS()
1171 {
1172     if (this == GetStockCS(PDFCS_DEVICERGB)) {
1173         return;
1174     }
1175     if (this == GetStockCS(PDFCS_DEVICEGRAY)) {
1176         return;
1177     }
1178     if (this == GetStockCS(PDFCS_DEVICECMYK)) {
1179         return;
1180     }
1181     if (this == GetStockCS(PDFCS_PATTERN)) {
1182         return;
1183     }
1184     delete this;
1185 }
1186 int CPDF_ColorSpace::GetBufSize() const
1187 {
1188     if (m_Family == PDFCS_PATTERN) {
1189         return sizeof(PatternValue);
1190     }
1191     return m_nComponents * sizeof(FX_FLOAT);
1192 }
1193 FX_FLOAT* CPDF_ColorSpace::CreateBuf()
1194 {
1195     int size = GetBufSize();
1196     uint8_t* pBuf = FX_Alloc(uint8_t, size);
1197     return (FX_FLOAT*)pBuf;
1198 }
1199 FX_BOOL CPDF_ColorSpace::sRGB() const
1200 {
1201     if (m_Family == PDFCS_DEVICERGB) {
1202         return TRUE;
1203     }
1204     if (m_Family != PDFCS_ICCBASED) {
1205         return FALSE;
1206     }
1207     CPDF_ICCBasedCS* pCS = (CPDF_ICCBasedCS*)this;
1208     return pCS->m_pProfile->m_bsRGB;
1209 }
1210 FX_BOOL CPDF_ColorSpace::GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const
1211 {
1212     if (v_GetCMYK(pBuf, c, m, y, k)) {
1213         return TRUE;
1214     }
1215     FX_FLOAT R, G, B;
1216     if (!GetRGB(pBuf, R, G, B)) {
1217         return FALSE;
1218     }
1219     sRGB_to_AdobeCMYK(R, G, B, c, m, y, k);
1220     return TRUE;
1221 }
1222 FX_BOOL CPDF_ColorSpace::SetCMYK(FX_FLOAT* pBuf, FX_FLOAT c, FX_FLOAT m, FX_FLOAT y, FX_FLOAT k) const
1223 {
1224     if (v_SetCMYK(pBuf, c, m, y, k)) {
1225         return TRUE;
1226     }
1227     FX_FLOAT R, G, B;
1228     AdobeCMYK_to_sRGB(c, m, y, k, R, G, B);
1229     return SetRGB(pBuf, R, G, B);
1230 }
1231 void CPDF_ColorSpace::GetDefaultColor(FX_FLOAT* buf) const
1232 {
1233     if (buf == NULL || m_Family == PDFCS_PATTERN) {
1234         return;
1235     }
1236     FX_FLOAT min, max;
1237     for (int i = 0; i < m_nComponents; i ++) {
1238         GetDefaultValue(i, buf[i], min, max);
1239     }
1240 }
1241 int CPDF_ColorSpace::GetMaxIndex() const
1242 {
1243     if (m_Family != PDFCS_INDEXED) {
1244         return 0;
1245     }
1246     CPDF_IndexedCS* pCS = (CPDF_IndexedCS*)this;
1247     return pCS->m_MaxIndex;
1248 }
1249 void CPDF_ColorSpace::TranslateImageLine(uint8_t* dest_buf, const uint8_t* src_buf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
1250 {
1251     CFX_FixedBufGrow<FX_FLOAT, 16> srcbuf(m_nComponents);
1252     FX_FLOAT* src = srcbuf;
1253     FX_FLOAT R, G, B;
1254     for (int i = 0; i < pixels; i ++) {
1255         for (int j = 0; j < m_nComponents; j ++)
1256             if (m_Family == PDFCS_INDEXED) {
1257                 src[j] = (FX_FLOAT)(*src_buf ++);
1258             } else {
1259                 src[j] = (FX_FLOAT)(*src_buf ++) / 255;
1260             }
1261         GetRGB(src, R, G, B);
1262         *dest_buf ++ = (int32_t)(B * 255);
1263         *dest_buf ++ = (int32_t)(G * 255);
1264         *dest_buf ++ = (int32_t)(R * 255);
1265     }
1266 }
1267 void CPDF_ColorSpace::EnableStdConversion(FX_BOOL bEnabled)
1268 {
1269     if (bEnabled) {
1270         m_dwStdConversion ++;
1271     } else if (m_dwStdConversion) {
1272         m_dwStdConversion --;
1273     }
1274 }
1275 CPDF_Color::CPDF_Color(int family)
1276 {
1277     m_pCS = CPDF_ColorSpace::GetStockCS(family);
1278     int nComps = 3;
1279     if (family == PDFCS_DEVICEGRAY) {
1280         nComps = 1;
1281     } else if (family == PDFCS_DEVICECMYK) {
1282         nComps = 4;
1283     }
1284     m_pBuffer = FX_Alloc(FX_FLOAT, nComps);
1285     for (int i = 0; i < nComps; i ++) {
1286         m_pBuffer[i] = 0;
1287     }
1288 }
1289 CPDF_Color::~CPDF_Color()
1290 {
1291     ReleaseBuffer();
1292     ReleaseColorSpace();
1293 }
1294 void CPDF_Color::ReleaseBuffer()
1295 {
1296     if (!m_pBuffer) {
1297         return;
1298     }
1299     if (m_pCS->GetFamily() == PDFCS_PATTERN) {
1300         PatternValue* pvalue = (PatternValue*)m_pBuffer;
1301         CPDF_Pattern* pPattern = pvalue->m_pCountedPattern ? pvalue->m_pCountedPattern->get() : NULL;
1302         if (pPattern && pPattern->m_pDocument) {
1303             CPDF_DocPageData *pPageData = pPattern->m_pDocument->GetPageData();
1304             if (pPageData) {
1305                 pPageData->ReleasePattern(pPattern->m_pPatternObj);
1306             }
1307         }
1308     }
1309     FX_Free(m_pBuffer);
1310     m_pBuffer = NULL;
1311 }
1312 void CPDF_Color::ReleaseColorSpace()
1313 {
1314     if (m_pCS && m_pCS->m_pDocument && m_pCS->GetArray()) {
1315         m_pCS->m_pDocument->GetPageData()->ReleaseColorSpace(m_pCS->GetArray());
1316         m_pCS = NULL;
1317     }
1318 }
1319 void CPDF_Color::SetColorSpace(CPDF_ColorSpace* pCS)
1320 {
1321     if (m_pCS == pCS) {
1322         if (m_pBuffer == NULL) {
1323             m_pBuffer = pCS->CreateBuf();
1324         }
1325         ReleaseColorSpace();
1326         m_pCS = pCS;
1327         return;
1328     }
1329     ReleaseBuffer();
1330     ReleaseColorSpace();
1331     m_pCS = pCS;
1332     if (m_pCS) {
1333         m_pBuffer = pCS->CreateBuf();
1334         pCS->GetDefaultColor(m_pBuffer);
1335     }
1336 }
1337 void CPDF_Color::SetValue(FX_FLOAT* comps)
1338 {
1339     if (m_pBuffer == NULL) {
1340         return;
1341     }
1342     if (m_pCS->GetFamily() != PDFCS_PATTERN) {
1343         FXSYS_memcpy(m_pBuffer, comps, m_pCS->CountComponents() * sizeof(FX_FLOAT));
1344     }
1345 }
1346 void CPDF_Color::SetValue(CPDF_Pattern* pPattern, FX_FLOAT* comps, int ncomps)
1347 {
1348     if (ncomps > MAX_PATTERN_COLORCOMPS) {
1349         return;
1350     }
1351     if (m_pCS == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1352         if (m_pBuffer) {
1353             FX_Free(m_pBuffer);
1354         }
1355         m_pCS = CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
1356         m_pBuffer = m_pCS->CreateBuf();
1357     }
1358     CPDF_DocPageData *pDocPageData = NULL;
1359     PatternValue* pvalue = (PatternValue*)m_pBuffer;
1360     if (pvalue->m_pPattern && pvalue->m_pPattern->m_pDocument) {
1361         pDocPageData = pvalue->m_pPattern->m_pDocument->GetPageData();
1362         if (pDocPageData) {
1363             pDocPageData->ReleasePattern(pvalue->m_pPattern->m_pPatternObj);
1364         }
1365     }
1366     pvalue->m_nComps = ncomps;
1367     pvalue->m_pPattern = pPattern;
1368     if (ncomps) {
1369         FXSYS_memcpy(pvalue->m_Comps, comps, ncomps * sizeof(FX_FLOAT));
1370     }
1371     pvalue->m_pCountedPattern = NULL;
1372     if (pPattern && pPattern->m_pDocument)
1373     {
1374         if (!pDocPageData) {
1375             pDocPageData = pPattern->m_pDocument->GetPageData();
1376         }
1377         pvalue->m_pCountedPattern = pDocPageData->FindPatternPtr(pPattern->m_pPatternObj);
1378     }
1379 }
1380 void CPDF_Color::Copy(const CPDF_Color* pSrc)
1381 {
1382     ReleaseBuffer();
1383     ReleaseColorSpace();
1384     m_pCS = pSrc->m_pCS;
1385     if (m_pCS && m_pCS->m_pDocument) {
1386         CPDF_Array* pArray = m_pCS->GetArray();
1387         if (pArray) {
1388             m_pCS = m_pCS->m_pDocument->GetPageData()->GetCopiedColorSpace(pArray);
1389         }
1390     }
1391     if (m_pCS == NULL) {
1392         return;
1393     }
1394     m_pBuffer = m_pCS->CreateBuf();
1395     FXSYS_memcpy(m_pBuffer, pSrc->m_pBuffer, m_pCS->GetBufSize());
1396     if (m_pCS->GetFamily() == PDFCS_PATTERN) {
1397         PatternValue* pvalue = (PatternValue*)m_pBuffer;
1398         if (pvalue->m_pPattern && pvalue->m_pPattern->m_pDocument) {
1399             pvalue->m_pPattern = pvalue->m_pPattern->m_pDocument->GetPageData()->GetPattern(pvalue->m_pPattern->m_pPatternObj, FALSE, &pvalue->m_pPattern->m_ParentMatrix);
1400         }
1401     }
1402 }
1403 FX_BOOL CPDF_Color::GetRGB(int& R, int& G, int& B) const
1404 {
1405     if (m_pCS == NULL || m_pBuffer == NULL) {
1406         return FALSE;
1407     }
1408     FX_FLOAT r=0.0f, g=0.0f, b=0.0f;
1409     if (!m_pCS->GetRGB(m_pBuffer, r, g, b)) {
1410         return FALSE;
1411     }
1412     R = (int32_t)(r * 255 + 0.5f);
1413     G = (int32_t)(g * 255 + 0.5f);
1414     B = (int32_t)(b * 255 + 0.5f);
1415     return TRUE;
1416 }
1417 CPDF_Pattern* CPDF_Color::GetPattern() const
1418 {
1419     if (m_pBuffer == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1420         return NULL;
1421     }
1422     PatternValue* pvalue = (PatternValue*)m_pBuffer;
1423     return pvalue->m_pPattern;
1424 }
1425 CPDF_ColorSpace* CPDF_Color::GetPatternCS() const
1426 {
1427     if (m_pBuffer == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1428         return NULL;
1429     }
1430     return m_pCS->GetBaseCS();
1431 }
1432 FX_FLOAT* CPDF_Color::GetPatternColor() const
1433 {
1434     if (m_pBuffer == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1435         return NULL;
1436     }
1437     PatternValue* pvalue = (PatternValue*)m_pBuffer;
1438     return pvalue->m_nComps ? pvalue->m_Comps : NULL;
1439 }
1440 FX_BOOL CPDF_Color::IsEqual(const CPDF_Color& other) const
1441 {
1442     if (m_pCS != other.m_pCS || m_pCS == NULL) {
1443         return FALSE;
1444     }
1445     return FXSYS_memcmp(m_pBuffer, other.m_pBuffer, m_pCS->GetBufSize()) == 0;
1446 }