Add a null pointer check to CPDF_CalRGB::v_Load().
[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     if (!pDict)
352         return FALSE;
353
354     CPDF_Array* pParam = pDict->GetArray(FX_BSTRC("WhitePoint"));
355     int i;
356     for (i = 0; i < 3; i ++) {
357         m_WhitePoint[i] = pParam ? pParam->GetNumber(i) : 0;
358     }
359     pParam = pDict->GetArray(FX_BSTRC("BlackPoint"));
360     for (i = 0; i < 3; i ++) {
361         m_BlackPoint[i] = pParam ? pParam->GetNumber(i) : 0;
362     }
363     pParam = pDict->GetArray(FX_BSTRC("Gamma"));
364     if (pParam) {
365         m_bGamma = TRUE;
366         for (i = 0; i < 3; i ++) {
367             m_Gamma[i] = pParam->GetNumber(i);
368         }
369     } else {
370         m_bGamma = FALSE;
371     }
372     pParam = pDict->GetArray(FX_BSTRC("Matrix"));
373     if (pParam) {
374         m_bMatrix = TRUE;
375         for (i = 0; i < 9; i ++) {
376             m_Matrix[i] = pParam->GetNumber(i);
377         }
378     } else {
379         m_bMatrix = FALSE;
380     }
381     return TRUE;
382 }
383 FX_BOOL CPDF_CalRGB::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
384 {
385     FX_FLOAT A_ = pBuf[0];
386     FX_FLOAT B_ = pBuf[1];
387     FX_FLOAT C_ = pBuf[2];
388     if (m_bGamma) {
389         A_ = (FX_FLOAT)FXSYS_pow(A_, m_Gamma[0]);
390         B_ = (FX_FLOAT)FXSYS_pow(B_, m_Gamma[1]);
391         C_ = (FX_FLOAT)FXSYS_pow(C_, m_Gamma[2]);
392     }
393     FX_FLOAT X, Y, Z;
394     if (m_bMatrix) {
395         X = m_Matrix[0] * A_ + m_Matrix[3] * B_ + m_Matrix[6] * C_;
396         Y = m_Matrix[1] * A_ + m_Matrix[4] * B_ + m_Matrix[7] * C_;
397         Z = m_Matrix[2] * A_ + m_Matrix[5] * B_ + m_Matrix[8] * C_;
398     } else {
399         X = A_;
400         Y = B_;
401         Z = C_;
402     }
403     XYZ_to_sRGB_WhitePoint(X, Y, Z, R, G, B, m_WhitePoint[0], m_WhitePoint[1], m_WhitePoint[2]);
404     return TRUE;
405 }
406 FX_BOOL CPDF_CalRGB::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
407 {
408     pBuf[0] = R;
409     pBuf[1] = G;
410     pBuf[2] = B;
411     return TRUE;
412 }
413 void CPDF_CalRGB::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
414 {
415     if (bTransMask) {
416         FX_FLOAT Cal[3];
417         FX_FLOAT R, G, B;
418         for(int i = 0; i < pixels; i ++) {
419             Cal[0] = ((FX_FLOAT)pSrcBuf[2]) / 255;
420             Cal[1] = ((FX_FLOAT)pSrcBuf[1]) / 255;
421             Cal[2] = ((FX_FLOAT)pSrcBuf[0]) / 255;
422             GetRGB(Cal, R, G, B);
423             pDestBuf[0] = FXSYS_round(B * 255);
424             pDestBuf[1] = FXSYS_round(G * 255);
425             pDestBuf[2] = FXSYS_round(R * 255);
426             pSrcBuf += 3;
427             pDestBuf += 3;
428         }
429     }
430     ReverseRGB(pDestBuf, pSrcBuf, pixels);
431 }
432 class CPDF_LabCS : public CPDF_ColorSpace
433 {
434 public:
435     explicit CPDF_LabCS(CPDF_Document* pDoc)
436         : CPDF_ColorSpace(pDoc, PDFCS_LAB, 3) {
437     }
438     void GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const override;
439     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
440     FX_BOOL     GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
441     FX_BOOL     SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const override;
442     void TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width,
443                             int image_height, FX_BOOL bTransMask = FALSE) const;
444
445     FX_FLOAT m_WhitePoint[3];
446     FX_FLOAT m_BlackPoint[3];
447     FX_FLOAT m_Ranges[4];
448 };
449 FX_BOOL CPDF_LabCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
450 {
451     CPDF_Dictionary* pDict = pArray->GetDict(1);
452     if (!pDict) {
453         return FALSE;
454     }
455     CPDF_Array* pParam = pDict->GetArray(FX_BSTRC("WhitePoint"));
456     int i;
457     for (i = 0; i < 3; i ++) {
458         m_WhitePoint[i] = pParam ? pParam->GetNumber(i) : 0;
459     }
460     pParam = pDict->GetArray(FX_BSTRC("BlackPoint"));
461     for (i = 0; i < 3; i ++) {
462         m_BlackPoint[i] = pParam ? pParam->GetNumber(i) : 0;
463     }
464     pParam = pDict->GetArray(FX_BSTRC("Range"));
465     const FX_FLOAT def_ranges[4] = { -100 * 1.0f, 100 * 1.0f, -100 * 1.0f, 100 * 1.0f};
466     for (i = 0; i < 4; i ++) {
467         m_Ranges[i] = pParam ? pParam->GetNumber(i) : def_ranges[i];
468     }
469     return TRUE;
470 }
471 void CPDF_LabCS::GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const
472 {
473     assert(iComponent < 3);
474     value = 0;
475     if (iComponent == 0) {
476         min = 0;
477         max = 100 * 1.0f;
478     } else {
479         min = m_Ranges[iComponent * 2 - 2];
480         max = m_Ranges[iComponent * 2 - 1];
481         if (value < min) {
482             value = min;
483         } else if (value > max) {
484             value = max;
485         }
486     }
487 }
488 FX_BOOL CPDF_LabCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
489 {
490     FX_FLOAT Lstar = pBuf[0];
491     FX_FLOAT astar = pBuf[1];
492     FX_FLOAT bstar = pBuf[2];
493     FX_FLOAT M = (Lstar + 16.0f) / 116.0f;
494     FX_FLOAT L = M + astar / 500.0f;
495     FX_FLOAT N = M - bstar / 200.0f;
496     FX_FLOAT X, Y, Z;
497     if (L < 0.2069f) {
498         X = 0.957f * 0.12842f * (L - 0.1379f);
499     } else {
500         X = 0.957f * L * L * L;
501     }
502     if (M < 0.2069f) {
503         Y = 0.12842f * (M - 0.1379f);
504     } else {
505         Y = M * M * M;
506     }
507     if (N < 0.2069f) {
508         Z = 1.0889f * 0.12842f * (N - 0.1379f);
509     } else {
510         Z = 1.0889f * N * N * N;
511     }
512     XYZ_to_sRGB(X, Y, Z, R, G, B);
513     return TRUE;
514 }
515 FX_BOOL CPDF_LabCS::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
516 {
517     return FALSE;
518 }
519 void CPDF_LabCS::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
520 {
521     for (int i = 0; i < pixels; i ++) {
522         FX_FLOAT lab[3];
523         FX_FLOAT R, G, B;
524         lab[0] = (pSrcBuf[0] * 100 / 255.0f);
525         lab[1] = (FX_FLOAT)(pSrcBuf[1] - 128);
526         lab[2] = (FX_FLOAT)(pSrcBuf[2] - 128);
527         GetRGB(lab, R, G, B);
528         pDestBuf[0] = (int32_t)(B * 255);
529         pDestBuf[1] = (int32_t)(G * 255);
530         pDestBuf[2] = (int32_t)(R * 255);
531         pDestBuf += 3;
532         pSrcBuf += 3;
533     }
534 }
535 CPDF_IccProfile::CPDF_IccProfile(const uint8_t* pData, FX_DWORD dwSize):
536     m_bsRGB(FALSE),
537     m_pTransform(NULL),
538     m_nSrcComponents(0)
539 {
540     if (dwSize == 3144 && FXSYS_memcmp(pData + 0x190, "sRGB IEC61966-2.1", 17) == 0) {
541         m_bsRGB = TRUE;
542         m_nSrcComponents = 3;
543     }
544     else if (CPDF_ModuleMgr::Get()->GetIccModule()) {
545         m_pTransform = CPDF_ModuleMgr::Get()->GetIccModule()->CreateTransform_sRGB(pData, dwSize, m_nSrcComponents);
546     }
547 }
548 CPDF_IccProfile::~CPDF_IccProfile()
549 {
550     if (m_pTransform) {
551         CPDF_ModuleMgr::Get()->GetIccModule()->DestroyTransform(m_pTransform);
552     }
553 }
554 class CPDF_ICCBasedCS : public CPDF_ColorSpace
555 {
556 public:
557     explicit CPDF_ICCBasedCS(CPDF_Document* pDoc)
558         : CPDF_ColorSpace(pDoc, PDFCS_ICCBASED, 0),
559           m_pAlterCS(nullptr),
560           m_pProfile(nullptr),
561           m_pCache(nullptr),
562           m_pRanges(nullptr),
563           m_bOwn(FALSE) {
564     }
565     ~CPDF_ICCBasedCS() override;
566
567     FX_BOOL     v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
568     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
569     FX_BOOL     SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const override;
570     FX_BOOL     v_GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const override;
571     void EnableStdConversion(FX_BOOL bEnabled) override;
572     void TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width,
573                             int image_height, FX_BOOL bTransMask = FALSE) const override;
574
575     CPDF_ColorSpace* m_pAlterCS;
576     CPDF_IccProfile* m_pProfile;
577     uint8_t* m_pCache;
578     FX_FLOAT* m_pRanges;
579     FX_BOOL     m_bOwn;
580 };
581
582 CPDF_ICCBasedCS::~CPDF_ICCBasedCS()
583 {
584     if (m_pCache) {
585         FX_Free(m_pCache);
586     }
587     if (m_pRanges) {
588         FX_Free(m_pRanges);
589     }
590     if (m_pAlterCS && m_bOwn) {
591         m_pAlterCS->ReleaseCS();
592     }
593     if (m_pProfile && m_pDocument) {
594         m_pDocument->GetPageData()->ReleaseIccProfile(m_pProfile);
595     }
596 }
597
598 FX_BOOL CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
599 {
600     CPDF_Stream* pStream = pArray->GetStream(1);
601     if (pStream == NULL) {
602         return FALSE;
603     }
604     m_pProfile = pDoc->LoadIccProfile(pStream);
605     if (!m_pProfile) {
606         return FALSE;
607     }
608     m_nComponents = m_pProfile->GetComponents(); //Try using the nComponents from ICC profile
609     CPDF_Dictionary* pDict = pStream->GetDict();
610     if (m_pProfile->m_pTransform == NULL) { // No valid ICC profile or using sRGB
611         CPDF_Object* pAlterCSObj = pDict ? pDict->GetElementValue(FX_BSTRC("Alternate")) : NULL;
612         if (pAlterCSObj) {
613             CPDF_ColorSpace* pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj);
614             if (pAlterCS) {
615                 if (m_nComponents == 0) { // NO valid ICC profile
616                     if (pAlterCS->CountComponents() > 0) { // Use Alternative colorspace
617                         m_nComponents = pAlterCS->CountComponents();
618                         m_pAlterCS = pAlterCS;
619                         m_bOwn = TRUE;
620                     }
621                     else { // No valid alternative colorspace
622                         pAlterCS->ReleaseCS();
623                         int32_t nDictComponents = pDict ? pDict->GetInteger(FX_BSTRC("N")) : 0;
624                         if (nDictComponents != 1 && nDictComponents != 3 && nDictComponents != 4) {
625                             return FALSE;
626                         }
627                         m_nComponents = nDictComponents;
628                     }
629
630                 }
631                 else { // Using sRGB
632                     if (pAlterCS->CountComponents() != m_nComponents) {
633                         pAlterCS->ReleaseCS();
634                     }
635                     else {
636                         m_pAlterCS = pAlterCS;
637                         m_bOwn = TRUE;
638                     }
639                 }
640             }
641         }
642         if (!m_pAlterCS) {
643             if (m_nComponents == 1) {
644                 m_pAlterCS = GetStockCS(PDFCS_DEVICEGRAY);
645             }
646             else if (m_nComponents == 3) {
647                 m_pAlterCS = GetStockCS(PDFCS_DEVICERGB);
648             }
649             else if (m_nComponents == 4) {
650                 m_pAlterCS = GetStockCS(PDFCS_DEVICECMYK);
651             }
652         }
653     }
654     CPDF_Array* pRanges = pDict->GetArray(FX_BSTRC("Range"));
655     m_pRanges = FX_Alloc2D(FX_FLOAT, m_nComponents, 2);
656     for (int i = 0; i < m_nComponents * 2; i ++) {
657         if (pRanges) {
658             m_pRanges[i] = pRanges->GetNumber(i);
659         } else if (i % 2) {
660             m_pRanges[i] = 1.0f;
661         } else {
662             m_pRanges[i] = 0;
663         }
664     }
665     return TRUE;
666 }
667 FX_BOOL CPDF_ICCBasedCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
668 {
669     if (m_pProfile && m_pProfile->m_bsRGB) {
670         R = pBuf[0];
671         G = pBuf[1];
672         B = pBuf[2];
673         return TRUE;
674     }
675     ICodec_IccModule *pIccModule = CPDF_ModuleMgr::Get()->GetIccModule();
676     if (m_pProfile->m_pTransform == NULL || pIccModule == NULL) {
677         if (m_pAlterCS) {
678             m_pAlterCS->GetRGB(pBuf, R, G, B);
679         } else {
680             R = G = B = 0.0f;
681         }
682         return TRUE;
683     }
684     FX_FLOAT rgb[3];
685     pIccModule->SetComponents(m_nComponents);
686     pIccModule->Translate(m_pProfile->m_pTransform, pBuf, rgb);
687     R = rgb[0];
688     G = rgb[1];
689     B = rgb[2];
690     return TRUE;
691 }
692 FX_BOOL CPDF_ICCBasedCS::v_GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const
693 {
694     if (m_nComponents != 4) {
695         return FALSE;
696     }
697     c = pBuf[0];
698     m = pBuf[1];
699     y = pBuf[2];
700     k = pBuf[3];
701     return TRUE;
702 }
703 FX_BOOL CPDF_ICCBasedCS::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
704 {
705     return FALSE;
706 }
707 void CPDF_ICCBasedCS::EnableStdConversion(FX_BOOL bEnabled)
708 {
709     CPDF_ColorSpace::EnableStdConversion(bEnabled);
710     if (m_pAlterCS) {
711         m_pAlterCS->EnableStdConversion(bEnabled);
712     }
713 }
714 void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels, int image_width, int image_height, FX_BOOL bTransMask) const
715 {
716     if (m_pProfile->m_bsRGB) {
717         ReverseRGB(pDestBuf, pSrcBuf, pixels);
718     } else if (m_pProfile->m_pTransform) {
719         int nMaxColors = 1;
720         for (int i = 0; i < m_nComponents; i ++) {
721             nMaxColors *= 52;
722         }
723         if (m_nComponents > 3 || image_width * image_height < nMaxColors * 3 / 2) {
724             CPDF_ModuleMgr::Get()->GetIccModule()->TranslateScanline(m_pProfile->m_pTransform, pDestBuf, pSrcBuf, pixels);
725         } else {
726             if (m_pCache == NULL) {
727                 ((CPDF_ICCBasedCS*)this)->m_pCache = FX_Alloc2D(uint8_t, nMaxColors, 3);
728                 uint8_t* temp_src = FX_Alloc2D(uint8_t, nMaxColors, m_nComponents);
729                 uint8_t* pSrc = temp_src;
730                 for (int i = 0; i < nMaxColors; i ++) {
731                     FX_DWORD color = i;
732                     FX_DWORD order = nMaxColors / 52;
733                     for (int c = 0; c < m_nComponents; c ++) {
734                         *pSrc++ = (uint8_t)(color / order * 5);
735                         color %= order;
736                         order /= 52;
737                     }
738                 }
739                 CPDF_ModuleMgr::Get()->GetIccModule()->TranslateScanline(m_pProfile->m_pTransform, m_pCache, temp_src, nMaxColors);
740                 FX_Free(temp_src);
741             }
742             for (int i = 0; i < pixels; i ++) {
743                 int index = 0;
744                 for (int c = 0; c < m_nComponents; c ++) {
745                     index = index * 52 + (*pSrcBuf) / 5;
746                     pSrcBuf ++;
747                 }
748                 index *= 3;
749                 *pDestBuf++ = m_pCache[index];
750                 *pDestBuf++ = m_pCache[index + 1];
751                 *pDestBuf++ = m_pCache[index + 2];
752             }
753         }
754     } else if (m_pAlterCS) {
755         m_pAlterCS->TranslateImageLine(pDestBuf, pSrcBuf, pixels, image_width, image_height);
756     }
757 }
758 class CPDF_IndexedCS : public CPDF_ColorSpace
759 {
760 public:
761     explicit CPDF_IndexedCS(CPDF_Document* pDoc)
762         : CPDF_ColorSpace(pDoc, PDFCS_INDEXED, 1),
763           m_pBaseCS(nullptr),
764           m_pCountedBaseCS(nullptr),
765           m_pCompMinMax(nullptr) {
766     }
767     ~CPDF_IndexedCS() override;
768
769     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
770     FX_BOOL     GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
771     CPDF_ColorSpace* GetBaseCS() const override;
772     void EnableStdConversion(FX_BOOL bEnabled) override;
773
774     CPDF_ColorSpace* m_pBaseCS;
775     CPDF_CountedColorSpace* m_pCountedBaseCS;
776     int m_nBaseComponents;
777     int m_MaxIndex;
778     CFX_ByteString m_Table;
779     FX_FLOAT* m_pCompMinMax;
780 };
781 CPDF_IndexedCS::~CPDF_IndexedCS()
782 {
783     if (m_pCompMinMax) {
784         FX_Free(m_pCompMinMax);
785     }
786     CPDF_ColorSpace* pCS = m_pCountedBaseCS ? m_pCountedBaseCS->get() : NULL;
787     if (pCS && m_pDocument) {
788         m_pDocument->GetPageData()->ReleaseColorSpace(pCS->GetArray());
789     }
790 }
791 FX_BOOL CPDF_IndexedCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
792 {
793     if (pArray->GetCount() < 4) {
794         return FALSE;
795     }
796     CPDF_Object* pBaseObj = pArray->GetElementValue(1);
797     if (pBaseObj == m_pArray) {
798         return FALSE;
799     }
800     CPDF_DocPageData* pDocPageData = pDoc->GetPageData();
801     m_pBaseCS = pDocPageData->GetColorSpace(pBaseObj, NULL);
802     if (m_pBaseCS == NULL) {
803         return FALSE;
804     }
805     m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
806     m_nBaseComponents = m_pBaseCS->CountComponents();
807     m_pCompMinMax = FX_Alloc2D(FX_FLOAT, m_nBaseComponents, 2);
808     FX_FLOAT defvalue;
809     for (int i = 0; i < m_nBaseComponents; i ++) {
810         m_pBaseCS->GetDefaultValue(i, defvalue, m_pCompMinMax[i * 2], m_pCompMinMax[i * 2 + 1]);
811         m_pCompMinMax[i * 2 + 1] -= m_pCompMinMax[i * 2];
812     }
813     m_MaxIndex = pArray->GetInteger(2);
814     CPDF_Object* pTableObj = pArray->GetElementValue(3);
815     if (pTableObj == NULL) {
816         return FALSE;
817     }
818     if (pTableObj->GetType() == PDFOBJ_STRING) {
819         m_Table = ((CPDF_String*)pTableObj)->GetString();
820     } else if (pTableObj->GetType() == PDFOBJ_STREAM) {
821         CPDF_StreamAcc acc;
822         acc.LoadAllData((CPDF_Stream*)pTableObj, FALSE);
823         m_Table = CFX_ByteStringC(acc.GetData(), acc.GetSize());
824     }
825     return TRUE;
826 }
827
828 FX_BOOL CPDF_IndexedCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
829 {
830     int index = (int32_t)(*pBuf);
831     if (index < 0 || index > m_MaxIndex) {
832         return FALSE;
833     }
834     if (m_nBaseComponents) {
835         if (index == INT_MAX || (index + 1) > INT_MAX / m_nBaseComponents ||
836                 (index + 1)*m_nBaseComponents > (int)m_Table.GetLength()) {
837             R = G = B = 0;
838             return FALSE;
839         }
840     }
841     CFX_FixedBufGrow<FX_FLOAT, 16> Comps(m_nBaseComponents);
842     FX_FLOAT* comps = Comps;
843     const uint8_t* pTable = m_Table;
844     for (int i = 0; i < m_nBaseComponents; i ++) {
845         comps[i] = m_pCompMinMax[i * 2] + m_pCompMinMax[i * 2 + 1] * pTable[index * m_nBaseComponents + i] / 255;
846     }
847     m_pBaseCS->GetRGB(comps, R, G, B);
848     return TRUE;
849 }
850 CPDF_ColorSpace*CPDF_IndexedCS::GetBaseCS() const
851 {
852     return m_pBaseCS;
853 }
854 void CPDF_IndexedCS::EnableStdConversion(FX_BOOL bEnabled)
855 {
856     CPDF_ColorSpace::EnableStdConversion(bEnabled);
857     if (m_pBaseCS) {
858         m_pBaseCS->EnableStdConversion(bEnabled);
859     }
860 }
861 #define MAX_PATTERN_COLORCOMPS  16
862 typedef struct _PatternValue {
863     CPDF_Pattern*       m_pPattern;
864     CPDF_CountedPattern*        m_pCountedPattern;
865     int                         m_nComps;
866     FX_FLOAT            m_Comps[MAX_PATTERN_COLORCOMPS];
867 } PatternValue;
868 CPDF_PatternCS::~CPDF_PatternCS()
869 {
870     CPDF_ColorSpace* pCS = m_pCountedBaseCS ? m_pCountedBaseCS->get() : NULL;
871     if (pCS && m_pDocument) {
872             m_pDocument->GetPageData()->ReleaseColorSpace(pCS->GetArray());
873     }
874 }
875 FX_BOOL CPDF_PatternCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
876 {
877     CPDF_Object* pBaseCS = pArray->GetElementValue(1);
878     if (pBaseCS == m_pArray) {
879         return FALSE;
880     }
881     CPDF_DocPageData* pDocPageData = pDoc->GetPageData();
882     m_pBaseCS = pDocPageData->GetColorSpace(pBaseCS, NULL);
883     if (m_pBaseCS) {
884         if (m_pBaseCS->GetFamily() == PDFCS_PATTERN) {
885             return FALSE;
886         }
887         m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
888         m_nComponents = m_pBaseCS->CountComponents() + 1;
889         if (m_pBaseCS->CountComponents() > MAX_PATTERN_COLORCOMPS) {
890             return FALSE;
891         }
892     } else {
893         m_nComponents = 1;
894     }
895     return TRUE;
896 }
897 FX_BOOL CPDF_PatternCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
898 {
899     if (m_pBaseCS) {
900         ASSERT(m_pBaseCS->GetFamily() != PDFCS_PATTERN);
901         PatternValue* pvalue = (PatternValue*)pBuf;
902         if (m_pBaseCS->GetRGB(pvalue->m_Comps, R, G, B)) {
903             return TRUE;
904         }
905     }
906     R = G = B = 0.75f;
907     return FALSE;
908 }
909 CPDF_ColorSpace* CPDF_PatternCS::GetBaseCS() const
910 {
911     return m_pBaseCS;
912 }
913 class CPDF_SeparationCS : public CPDF_ColorSpace
914 {
915 public:
916     CPDF_SeparationCS(CPDF_Document* pDoc)
917             : CPDF_ColorSpace(pDoc, PDFCS_SEPARATION, 1),
918               m_pAltCS(nullptr),
919               m_pFunc(nullptr) {
920     }
921     ~CPDF_SeparationCS() override;
922     void GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const override;
923     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
924     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
925     void EnableStdConversion(FX_BOOL bEnabled) override;
926
927     CPDF_ColorSpace* m_pAltCS;
928     CPDF_Function* m_pFunc;
929     enum { None, All, Colorant } m_Type;
930 };
931 CPDF_SeparationCS::~CPDF_SeparationCS()
932 {
933     if (m_pAltCS) {
934         m_pAltCS->ReleaseCS();
935     }
936     delete m_pFunc;
937 }
938 void CPDF_SeparationCS::GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const
939 {
940     value = 1.0f;
941     min = 0;
942     max = 1.0f;
943 }
944 FX_BOOL CPDF_SeparationCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
945 {
946     CFX_ByteString name = pArray->GetString(1);
947     if (name == FX_BSTRC("None")) {
948         m_Type = None;
949     } else {
950         m_Type = Colorant;
951         CPDF_Object* pAltCS = pArray->GetElementValue(2);
952         if (pAltCS == m_pArray) {
953             return FALSE;
954         }
955         m_pAltCS = Load(pDoc, pAltCS);
956         if (!m_pAltCS) {
957             return FALSE;
958         }
959         CPDF_Object* pFuncObj = pArray->GetElementValue(3);
960         if (pFuncObj && pFuncObj->GetType() != PDFOBJ_NAME) {
961             m_pFunc = CPDF_Function::Load(pFuncObj);
962         }
963         if (m_pFunc && m_pFunc->CountOutputs() < m_pAltCS->CountComponents()) {
964             delete m_pFunc;
965             m_pFunc = NULL;
966         }
967     }
968     return TRUE;
969 }
970 FX_BOOL CPDF_SeparationCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
971 {
972     if (m_Type == None) {
973         return FALSE;
974     }
975     if (m_pFunc == NULL) {
976         if (m_pAltCS == NULL) {
977             return FALSE;
978         }
979         int nComps = m_pAltCS->CountComponents();
980         CFX_FixedBufGrow<FX_FLOAT, 16> results(nComps);
981         for (int i = 0; i < nComps; i ++) {
982             results[i] = *pBuf;
983         }
984         m_pAltCS->GetRGB(results, R, G, B);
985         return TRUE;
986     }
987     CFX_FixedBufGrow<FX_FLOAT, 16> results(m_pFunc->CountOutputs());
988     int nresults = 0;
989     m_pFunc->Call(pBuf, 1, results, nresults);
990     if (nresults == 0) {
991         return FALSE;
992     }
993     if (m_pAltCS) {
994         m_pAltCS->GetRGB(results, R, G, B);
995         return TRUE;
996     } else {
997         R = G = B = 0;
998         return FALSE;
999     }
1000 }
1001 void CPDF_SeparationCS::EnableStdConversion(FX_BOOL bEnabled)
1002 {
1003     CPDF_ColorSpace::EnableStdConversion(bEnabled);
1004     if (m_pAltCS) {
1005         m_pAltCS->EnableStdConversion(bEnabled);
1006     }
1007 }
1008 class CPDF_DeviceNCS : public CPDF_ColorSpace
1009 {
1010 public:
1011     CPDF_DeviceNCS(CPDF_Document* pDoc)
1012             : CPDF_ColorSpace(pDoc, PDFCS_DEVICEN, 0),
1013               m_pAltCS(nullptr),
1014               m_pFunc(nullptr) {
1015     }
1016     ~CPDF_DeviceNCS() override;
1017     void GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const override;
1018     FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
1019     FX_BOOL GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const override;
1020     void EnableStdConversion(FX_BOOL bEnabled) override;
1021
1022     CPDF_ColorSpace* m_pAltCS;
1023     CPDF_Function* m_pFunc;
1024 };
1025 CPDF_DeviceNCS::~CPDF_DeviceNCS()
1026 {
1027     delete m_pFunc;
1028     if (m_pAltCS) {
1029         m_pAltCS->ReleaseCS();
1030     }
1031 }
1032 void CPDF_DeviceNCS::GetDefaultValue(int iComponent, FX_FLOAT& value, FX_FLOAT& min, FX_FLOAT& max) const
1033 {
1034     value = 1.0f;
1035     min = 0;
1036     max = 1.0f;
1037 }
1038 FX_BOOL CPDF_DeviceNCS::v_Load(CPDF_Document* pDoc, CPDF_Array* pArray)
1039 {
1040     CPDF_Object* pObj = pArray->GetElementValue(1);
1041     if (!pObj) {
1042         return FALSE;
1043     }
1044     if (pObj->GetType() != PDFOBJ_ARRAY) {
1045         return FALSE;
1046     }
1047     m_nComponents = ((CPDF_Array*)pObj)->GetCount();
1048     CPDF_Object* pAltCS = pArray->GetElementValue(2);
1049     if (!pAltCS || pAltCS == m_pArray) {
1050         return FALSE;
1051     }
1052     m_pAltCS = Load(pDoc, pAltCS);
1053     m_pFunc = CPDF_Function::Load(pArray->GetElementValue(3));
1054     if (m_pAltCS == NULL || m_pFunc == NULL) {
1055         return FALSE;
1056     }
1057     if (m_pFunc->CountOutputs() < m_pAltCS->CountComponents()) {
1058         return FALSE;
1059     }
1060     return TRUE;
1061 }
1062 FX_BOOL CPDF_DeviceNCS::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B) const
1063 {
1064     if (m_pFunc == NULL) {
1065         return FALSE;
1066     }
1067     CFX_FixedBufGrow<FX_FLOAT, 16> results(m_pFunc->CountOutputs());
1068     int nresults = 0;
1069     m_pFunc->Call(pBuf, m_nComponents, results, nresults);
1070     if (nresults == 0) {
1071         return FALSE;
1072     }
1073     m_pAltCS->GetRGB(results, R, G, B);
1074     return TRUE;
1075 }
1076 void CPDF_DeviceNCS::EnableStdConversion(FX_BOOL bEnabled)
1077 {
1078     CPDF_ColorSpace::EnableStdConversion(bEnabled);
1079     if (m_pAltCS) {
1080         m_pAltCS->EnableStdConversion(bEnabled);
1081     }
1082 }
1083 CPDF_ColorSpace* CPDF_ColorSpace::GetStockCS(int family)
1084 {
1085     return CPDF_ModuleMgr::Get()->GetPageModule()->GetStockCS(family);;
1086 }
1087 CPDF_ColorSpace* _CSFromName(const CFX_ByteString& name)
1088 {
1089     if (name == FX_BSTRC("DeviceRGB") || name == FX_BSTRC("RGB")) {
1090         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
1091     }
1092     if (name == FX_BSTRC("DeviceGray") || name == FX_BSTRC("G")) {
1093         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
1094     }
1095     if (name == FX_BSTRC("DeviceCMYK") || name == FX_BSTRC("CMYK")) {
1096         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
1097     }
1098     if (name == FX_BSTRC("Pattern")) {
1099         return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
1100     }
1101     return NULL;
1102 }
1103 CPDF_ColorSpace* CPDF_ColorSpace::Load(CPDF_Document* pDoc, CPDF_Object* pObj)
1104 {
1105     if (pObj == NULL) {
1106         return NULL;
1107     }
1108     if (pObj->GetType() == PDFOBJ_NAME) {
1109         return _CSFromName(pObj->GetString());
1110     }
1111     if (pObj->GetType() == PDFOBJ_STREAM) {
1112         CPDF_Dictionary *pDict = ((CPDF_Stream *)pObj)->GetDict();
1113         if (!pDict) {
1114             return NULL;
1115         }
1116         CPDF_ColorSpace *pRet = NULL;
1117         FX_POSITION pos = pDict->GetStartPos();
1118         while (pos) {
1119             CFX_ByteString bsKey;
1120             CPDF_Object *pValue = pDict->GetNextElement(pos, bsKey);
1121             if (pValue && pValue->GetType() == PDFOBJ_NAME) {
1122                 pRet = _CSFromName(pValue->GetString());
1123             }
1124             if (pRet) {
1125                 return pRet;
1126             }
1127         }
1128         return NULL;
1129     }
1130     if (pObj->GetType() != PDFOBJ_ARRAY) {
1131         return NULL;
1132     }
1133     CPDF_Array* pArray = (CPDF_Array*)pObj;
1134     if (pArray->GetCount() == 0) {
1135         return NULL;
1136     }
1137     CPDF_Object *pFamilyObj = pArray->GetElementValue(0);
1138     if (!pFamilyObj) {
1139         return NULL;
1140     }
1141     CFX_ByteString familyname = pFamilyObj->GetString();
1142     if (pArray->GetCount() == 1) {
1143         return _CSFromName(familyname);
1144     }
1145     CPDF_ColorSpace* pCS = NULL;
1146     FX_DWORD id = familyname.GetID();
1147     if (id == FXBSTR_ID('C', 'a', 'l', 'G')) {
1148         pCS = new CPDF_CalGray(pDoc);
1149     } else if (id == FXBSTR_ID('C', 'a', 'l', 'R')) {
1150         pCS = new CPDF_CalRGB(pDoc);
1151     } else if (id == FXBSTR_ID('L', 'a', 'b', 0)) {
1152         pCS = new CPDF_LabCS(pDoc);
1153     } else if (id == FXBSTR_ID('I', 'C', 'C', 'B')) {
1154         pCS = new CPDF_ICCBasedCS(pDoc);
1155     } else if (id == FXBSTR_ID('I', 'n', 'd', 'e') || id == FXBSTR_ID('I', 0, 0, 0)) {
1156         pCS = new CPDF_IndexedCS(pDoc);
1157     } else if (id == FXBSTR_ID('S', 'e', 'p', 'a')) {
1158         pCS = new CPDF_SeparationCS(pDoc);
1159     } else if (id == FXBSTR_ID('D', 'e', 'v', 'i')) {
1160         pCS = new CPDF_DeviceNCS(pDoc);
1161     } else if (id == FXBSTR_ID('P', 'a', 't', 't')) {
1162         pCS = new CPDF_PatternCS(pDoc);
1163     } else {
1164         return NULL;
1165     }
1166     pCS->m_pArray = pArray;
1167     if (!pCS->v_Load(pDoc, pArray)) {
1168         pCS->ReleaseCS();
1169         return NULL;
1170     }
1171     return pCS;
1172 }
1173 void CPDF_ColorSpace::ReleaseCS()
1174 {
1175     if (this == GetStockCS(PDFCS_DEVICERGB)) {
1176         return;
1177     }
1178     if (this == GetStockCS(PDFCS_DEVICEGRAY)) {
1179         return;
1180     }
1181     if (this == GetStockCS(PDFCS_DEVICECMYK)) {
1182         return;
1183     }
1184     if (this == GetStockCS(PDFCS_PATTERN)) {
1185         return;
1186     }
1187     delete this;
1188 }
1189 int CPDF_ColorSpace::GetBufSize() const
1190 {
1191     if (m_Family == PDFCS_PATTERN) {
1192         return sizeof(PatternValue);
1193     }
1194     return m_nComponents * sizeof(FX_FLOAT);
1195 }
1196 FX_FLOAT* CPDF_ColorSpace::CreateBuf()
1197 {
1198     int size = GetBufSize();
1199     uint8_t* pBuf = FX_Alloc(uint8_t, size);
1200     return (FX_FLOAT*)pBuf;
1201 }
1202 FX_BOOL CPDF_ColorSpace::sRGB() const
1203 {
1204     if (m_Family == PDFCS_DEVICERGB) {
1205         return TRUE;
1206     }
1207     if (m_Family != PDFCS_ICCBASED) {
1208         return FALSE;
1209     }
1210     CPDF_ICCBasedCS* pCS = (CPDF_ICCBasedCS*)this;
1211     return pCS->m_pProfile->m_bsRGB;
1212 }
1213 FX_BOOL CPDF_ColorSpace::GetCMYK(FX_FLOAT* pBuf, FX_FLOAT& c, FX_FLOAT& m, FX_FLOAT& y, FX_FLOAT& k) const
1214 {
1215     if (v_GetCMYK(pBuf, c, m, y, k)) {
1216         return TRUE;
1217     }
1218     FX_FLOAT R, G, B;
1219     if (!GetRGB(pBuf, R, G, B)) {
1220         return FALSE;
1221     }
1222     sRGB_to_AdobeCMYK(R, G, B, c, m, y, k);
1223     return TRUE;
1224 }
1225 FX_BOOL CPDF_ColorSpace::SetCMYK(FX_FLOAT* pBuf, FX_FLOAT c, FX_FLOAT m, FX_FLOAT y, FX_FLOAT k) const
1226 {
1227     if (v_SetCMYK(pBuf, c, m, y, k)) {
1228         return TRUE;
1229     }
1230     FX_FLOAT R, G, B;
1231     AdobeCMYK_to_sRGB(c, m, y, k, R, G, B);
1232     return SetRGB(pBuf, R, G, B);
1233 }
1234 void CPDF_ColorSpace::GetDefaultColor(FX_FLOAT* buf) const
1235 {
1236     if (buf == NULL || m_Family == PDFCS_PATTERN) {
1237         return;
1238     }
1239     FX_FLOAT min, max;
1240     for (int i = 0; i < m_nComponents; i ++) {
1241         GetDefaultValue(i, buf[i], min, max);
1242     }
1243 }
1244 int CPDF_ColorSpace::GetMaxIndex() const
1245 {
1246     if (m_Family != PDFCS_INDEXED) {
1247         return 0;
1248     }
1249     CPDF_IndexedCS* pCS = (CPDF_IndexedCS*)this;
1250     return pCS->m_MaxIndex;
1251 }
1252 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
1253 {
1254     CFX_FixedBufGrow<FX_FLOAT, 16> srcbuf(m_nComponents);
1255     FX_FLOAT* src = srcbuf;
1256     FX_FLOAT R, G, B;
1257     for (int i = 0; i < pixels; i ++) {
1258         for (int j = 0; j < m_nComponents; j ++)
1259             if (m_Family == PDFCS_INDEXED) {
1260                 src[j] = (FX_FLOAT)(*src_buf ++);
1261             } else {
1262                 src[j] = (FX_FLOAT)(*src_buf ++) / 255;
1263             }
1264         GetRGB(src, R, G, B);
1265         *dest_buf ++ = (int32_t)(B * 255);
1266         *dest_buf ++ = (int32_t)(G * 255);
1267         *dest_buf ++ = (int32_t)(R * 255);
1268     }
1269 }
1270 void CPDF_ColorSpace::EnableStdConversion(FX_BOOL bEnabled)
1271 {
1272     if (bEnabled) {
1273         m_dwStdConversion ++;
1274     } else if (m_dwStdConversion) {
1275         m_dwStdConversion --;
1276     }
1277 }
1278 CPDF_Color::CPDF_Color(int family)
1279 {
1280     m_pCS = CPDF_ColorSpace::GetStockCS(family);
1281     int nComps = 3;
1282     if (family == PDFCS_DEVICEGRAY) {
1283         nComps = 1;
1284     } else if (family == PDFCS_DEVICECMYK) {
1285         nComps = 4;
1286     }
1287     m_pBuffer = FX_Alloc(FX_FLOAT, nComps);
1288     for (int i = 0; i < nComps; i ++) {
1289         m_pBuffer[i] = 0;
1290     }
1291 }
1292 CPDF_Color::~CPDF_Color()
1293 {
1294     ReleaseBuffer();
1295     ReleaseColorSpace();
1296 }
1297 void CPDF_Color::ReleaseBuffer()
1298 {
1299     if (!m_pBuffer) {
1300         return;
1301     }
1302     if (m_pCS->GetFamily() == PDFCS_PATTERN) {
1303         PatternValue* pvalue = (PatternValue*)m_pBuffer;
1304         CPDF_Pattern* pPattern = pvalue->m_pCountedPattern ? pvalue->m_pCountedPattern->get() : NULL;
1305         if (pPattern && pPattern->m_pDocument) {
1306             CPDF_DocPageData *pPageData = pPattern->m_pDocument->GetPageData();
1307             if (pPageData) {
1308                 pPageData->ReleasePattern(pPattern->m_pPatternObj);
1309             }
1310         }
1311     }
1312     FX_Free(m_pBuffer);
1313     m_pBuffer = NULL;
1314 }
1315 void CPDF_Color::ReleaseColorSpace()
1316 {
1317     if (m_pCS && m_pCS->m_pDocument && m_pCS->GetArray()) {
1318         m_pCS->m_pDocument->GetPageData()->ReleaseColorSpace(m_pCS->GetArray());
1319         m_pCS = NULL;
1320     }
1321 }
1322 void CPDF_Color::SetColorSpace(CPDF_ColorSpace* pCS)
1323 {
1324     if (m_pCS == pCS) {
1325         if (m_pBuffer == NULL) {
1326             m_pBuffer = pCS->CreateBuf();
1327         }
1328         ReleaseColorSpace();
1329         m_pCS = pCS;
1330         return;
1331     }
1332     ReleaseBuffer();
1333     ReleaseColorSpace();
1334     m_pCS = pCS;
1335     if (m_pCS) {
1336         m_pBuffer = pCS->CreateBuf();
1337         pCS->GetDefaultColor(m_pBuffer);
1338     }
1339 }
1340 void CPDF_Color::SetValue(FX_FLOAT* comps)
1341 {
1342     if (m_pBuffer == NULL) {
1343         return;
1344     }
1345     if (m_pCS->GetFamily() != PDFCS_PATTERN) {
1346         FXSYS_memcpy(m_pBuffer, comps, m_pCS->CountComponents() * sizeof(FX_FLOAT));
1347     }
1348 }
1349 void CPDF_Color::SetValue(CPDF_Pattern* pPattern, FX_FLOAT* comps, int ncomps)
1350 {
1351     if (ncomps > MAX_PATTERN_COLORCOMPS) {
1352         return;
1353     }
1354     if (m_pCS == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1355         if (m_pBuffer) {
1356             FX_Free(m_pBuffer);
1357         }
1358         m_pCS = CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
1359         m_pBuffer = m_pCS->CreateBuf();
1360     }
1361     CPDF_DocPageData *pDocPageData = NULL;
1362     PatternValue* pvalue = (PatternValue*)m_pBuffer;
1363     if (pvalue->m_pPattern && pvalue->m_pPattern->m_pDocument) {
1364         pDocPageData = pvalue->m_pPattern->m_pDocument->GetPageData();
1365         if (pDocPageData) {
1366             pDocPageData->ReleasePattern(pvalue->m_pPattern->m_pPatternObj);
1367         }
1368     }
1369     pvalue->m_nComps = ncomps;
1370     pvalue->m_pPattern = pPattern;
1371     if (ncomps) {
1372         FXSYS_memcpy(pvalue->m_Comps, comps, ncomps * sizeof(FX_FLOAT));
1373     }
1374     pvalue->m_pCountedPattern = NULL;
1375     if (pPattern && pPattern->m_pDocument)
1376     {
1377         if (!pDocPageData) {
1378             pDocPageData = pPattern->m_pDocument->GetPageData();
1379         }
1380         pvalue->m_pCountedPattern = pDocPageData->FindPatternPtr(pPattern->m_pPatternObj);
1381     }
1382 }
1383 void CPDF_Color::Copy(const CPDF_Color* pSrc)
1384 {
1385     ReleaseBuffer();
1386     ReleaseColorSpace();
1387     m_pCS = pSrc->m_pCS;
1388     if (m_pCS && m_pCS->m_pDocument) {
1389         CPDF_Array* pArray = m_pCS->GetArray();
1390         if (pArray) {
1391             m_pCS = m_pCS->m_pDocument->GetPageData()->GetCopiedColorSpace(pArray);
1392         }
1393     }
1394     if (m_pCS == NULL) {
1395         return;
1396     }
1397     m_pBuffer = m_pCS->CreateBuf();
1398     FXSYS_memcpy(m_pBuffer, pSrc->m_pBuffer, m_pCS->GetBufSize());
1399     if (m_pCS->GetFamily() == PDFCS_PATTERN) {
1400         PatternValue* pvalue = (PatternValue*)m_pBuffer;
1401         if (pvalue->m_pPattern && pvalue->m_pPattern->m_pDocument) {
1402             pvalue->m_pPattern = pvalue->m_pPattern->m_pDocument->GetPageData()->GetPattern(pvalue->m_pPattern->m_pPatternObj, FALSE, &pvalue->m_pPattern->m_ParentMatrix);
1403         }
1404     }
1405 }
1406 FX_BOOL CPDF_Color::GetRGB(int& R, int& G, int& B) const
1407 {
1408     if (m_pCS == NULL || m_pBuffer == NULL) {
1409         return FALSE;
1410     }
1411     FX_FLOAT r=0.0f, g=0.0f, b=0.0f;
1412     if (!m_pCS->GetRGB(m_pBuffer, r, g, b)) {
1413         return FALSE;
1414     }
1415     R = (int32_t)(r * 255 + 0.5f);
1416     G = (int32_t)(g * 255 + 0.5f);
1417     B = (int32_t)(b * 255 + 0.5f);
1418     return TRUE;
1419 }
1420 CPDF_Pattern* CPDF_Color::GetPattern() const
1421 {
1422     if (m_pBuffer == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1423         return NULL;
1424     }
1425     PatternValue* pvalue = (PatternValue*)m_pBuffer;
1426     return pvalue->m_pPattern;
1427 }
1428 CPDF_ColorSpace* CPDF_Color::GetPatternCS() const
1429 {
1430     if (m_pBuffer == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1431         return NULL;
1432     }
1433     return m_pCS->GetBaseCS();
1434 }
1435 FX_FLOAT* CPDF_Color::GetPatternColor() const
1436 {
1437     if (m_pBuffer == NULL || m_pCS->GetFamily() != PDFCS_PATTERN) {
1438         return NULL;
1439     }
1440     PatternValue* pvalue = (PatternValue*)m_pBuffer;
1441     return pvalue->m_nComps ? pvalue->m_Comps : NULL;
1442 }
1443 FX_BOOL CPDF_Color::IsEqual(const CPDF_Color& other) const
1444 {
1445     if (m_pCS != other.m_pCS || m_pCS == NULL) {
1446         return FALSE;
1447     }
1448     return FXSYS_memcmp(m_pBuffer, other.m_pBuffer, m_pCS->GetBufSize()) == 0;
1449 }