xref: /aosp_15_r20/external/pdfium/core/fpdfapi/page/cpdf_devicecs.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "core/fpdfapi/page/cpdf_devicecs.h"
8 
9 #include <algorithm>
10 
11 #include "core/fpdfapi/parser/cpdf_array.h"
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fpdfapi/parser/cpdf_document.h"
14 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
15 #include "core/fpdfapi/parser/cpdf_string.h"
16 #include "core/fxcodec/fx_codec.h"
17 #include "core/fxge/dib/cfx_cmyk_to_srgb.h"
18 #include "third_party/base/check.h"
19 #include "third_party/base/notreached.h"
20 
21 namespace {
22 
NormalizeChannel(float fVal)23 float NormalizeChannel(float fVal) {
24   return std::clamp(fVal, 0.0f, 1.0f);
25 }
26 
27 }  // namespace
28 
CPDF_DeviceCS(Family family)29 CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) {
30   DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB ||
31          family == Family::kDeviceCMYK);
32   SetComponentsForStockCS(ComponentsForFamily(GetFamily()));
33 }
34 
35 CPDF_DeviceCS::~CPDF_DeviceCS() = default;
36 
v_Load(CPDF_Document * pDoc,const CPDF_Array * pArray,std::set<const CPDF_Object * > * pVisited)37 uint32_t CPDF_DeviceCS::v_Load(CPDF_Document* pDoc,
38                                const CPDF_Array* pArray,
39                                std::set<const CPDF_Object*>* pVisited) {
40   // Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is
41   // never loaded by CPDF_ColorSpace.
42   NOTREACHED_NORETURN();
43 }
44 
GetRGB(pdfium::span<const float> pBuf,float * R,float * G,float * B) const45 bool CPDF_DeviceCS::GetRGB(pdfium::span<const float> pBuf,
46                            float* R,
47                            float* G,
48                            float* B) const {
49   switch (GetFamily()) {
50     case Family::kDeviceGray:
51       *R = NormalizeChannel(pBuf[0]);
52       *G = *R;
53       *B = *R;
54       return true;
55     case Family::kDeviceRGB:
56       *R = NormalizeChannel(pBuf[0]);
57       *G = NormalizeChannel(pBuf[1]);
58       *B = NormalizeChannel(pBuf[2]);
59       return true;
60     case Family::kDeviceCMYK:
61       if (IsStdConversionEnabled()) {
62         float k = pBuf[3];
63         *R = 1.0f - std::min(1.0f, pBuf[0] + k);
64         *G = 1.0f - std::min(1.0f, pBuf[1] + k);
65         *B = 1.0f - std::min(1.0f, pBuf[2] + k);
66       } else {
67         std::tie(*R, *G, *B) = AdobeCMYK_to_sRGB(
68             NormalizeChannel(pBuf[0]), NormalizeChannel(pBuf[1]),
69             NormalizeChannel(pBuf[2]), NormalizeChannel(pBuf[3]));
70       }
71       return true;
72     default:
73       NOTREACHED_NORETURN();
74   }
75 }
76 
TranslateImageLine(pdfium::span<uint8_t> dest_span,pdfium::span<const uint8_t> src_span,int pixels,int image_width,int image_height,bool bTransMask) const77 void CPDF_DeviceCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
78                                        pdfium::span<const uint8_t> src_span,
79                                        int pixels,
80                                        int image_width,
81                                        int image_height,
82                                        bool bTransMask) const {
83   uint8_t* pDestBuf = dest_span.data();
84   const uint8_t* pSrcBuf = src_span.data();
85   switch (GetFamily()) {
86     case Family::kDeviceGray:
87       for (int i = 0; i < pixels; i++) {
88         // Compiler can not conclude that src/dest don't overlap, avoid
89         // duplicate loads.
90         const uint8_t pix = pSrcBuf[i];
91         *pDestBuf++ = pix;
92         *pDestBuf++ = pix;
93         *pDestBuf++ = pix;
94       }
95       break;
96     case Family::kDeviceRGB:
97       fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
98       break;
99     case Family::kDeviceCMYK:
100       if (bTransMask) {
101         for (int i = 0; i < pixels; i++) {
102           // Compiler can't conclude src/dest don't overlap, avoid interleaved
103           // loads and stores.
104           const uint8_t s0 = pSrcBuf[0];
105           const uint8_t s1 = pSrcBuf[1];
106           const uint8_t s2 = pSrcBuf[2];
107           const int k = 255 - pSrcBuf[3];
108           pDestBuf[0] = ((255 - s0) * k) / 255;
109           pDestBuf[1] = ((255 - s1) * k) / 255;
110           pDestBuf[2] = ((255 - s2) * k) / 255;
111           pDestBuf += 3;
112           pSrcBuf += 4;
113         }
114       } else {
115         if (IsStdConversionEnabled()) {
116           for (int i = 0; i < pixels; i++) {
117             // Compiler can't conclude src/dest don't overlap, avoid
118             // interleaved loads and stores.
119             const uint8_t s0 = pSrcBuf[0];
120             const uint8_t s1 = pSrcBuf[1];
121             const uint8_t s2 = pSrcBuf[2];
122             const uint8_t k = pSrcBuf[3];
123             pDestBuf[2] = 255 - std::min(255, s0 + k);
124             pDestBuf[1] = 255 - std::min(255, s1 + k);
125             pDestBuf[0] = 255 - std::min(255, s2 + k);
126             pSrcBuf += 4;
127             pDestBuf += 3;
128           }
129         } else {
130           for (int i = 0; i < pixels; i++) {
131             std::tie(pDestBuf[2], pDestBuf[1], pDestBuf[0]) =
132                 AdobeCMYK_to_sRGB1(pSrcBuf[0], pSrcBuf[1], pSrcBuf[2],
133                                    pSrcBuf[3]);
134             pSrcBuf += 4;
135             pDestBuf += 3;
136           }
137         }
138       }
139       break;
140     default:
141       NOTREACHED_NORETURN();
142   }
143 }
144