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