xref: /aosp_15_r20/external/pdfium/core/fxcodec/icc/icc_transform.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/fxcodec/icc/icc_transform.h"
8 
9 #include <stdint.h>
10 
11 #include <algorithm>
12 #include <memory>
13 
14 #include "core/fxcrt/data_vector.h"
15 #include "third_party/base/memory/ptr_util.h"
16 #include "third_party/base/notreached.h"
17 #include "third_party/base/numerics/safe_conversions.h"
18 
19 namespace fxcodec {
20 
21 namespace {
22 
23 // For use with std::unique_ptr<cmsHPROFILE>.
24 struct CmsProfileDeleter {
operator ()fxcodec::__anon4eab0d970111::CmsProfileDeleter25   inline void operator()(cmsHPROFILE p) { cmsCloseProfile(p); }
26 };
27 
28 using ScopedCmsProfile = std::unique_ptr<void, CmsProfileDeleter>;
29 
Check3Components(cmsColorSpaceSignature cs)30 bool Check3Components(cmsColorSpaceSignature cs) {
31   switch (cs) {
32     case cmsSigGrayData:
33     case cmsSigCmykData:
34       return false;
35     default:
36       return true;
37   }
38 }
39 
40 }  // namespace
41 
IccTransform(cmsHTRANSFORM hTransform,int srcComponents,bool bIsLab,bool bNormal)42 IccTransform::IccTransform(cmsHTRANSFORM hTransform,
43                            int srcComponents,
44                            bool bIsLab,
45                            bool bNormal)
46     : m_hTransform(hTransform),
47       m_nSrcComponents(srcComponents),
48       m_bLab(bIsLab),
49       m_bNormal(bNormal) {}
50 
~IccTransform()51 IccTransform::~IccTransform() {
52   cmsDeleteTransform(m_hTransform);
53 }
54 
55 // static
CreateTransformSRGB(pdfium::span<const uint8_t> span)56 std::unique_ptr<IccTransform> IccTransform::CreateTransformSRGB(
57     pdfium::span<const uint8_t> span) {
58   ScopedCmsProfile srcProfile(cmsOpenProfileFromMem(
59       span.data(), pdfium::base::checked_cast<cmsUInt32Number>(span.size())));
60   if (!srcProfile)
61     return nullptr;
62 
63   ScopedCmsProfile dstProfile(cmsCreate_sRGBProfile());
64   if (!dstProfile)
65     return nullptr;
66 
67   cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get());
68   uint32_t nSrcComponents = cmsChannelsOf(srcCS);
69 
70   // According to PDF spec, number of components must be 1, 3, or 4.
71   if (nSrcComponents != 1 && nSrcComponents != 3 && nSrcComponents != 4)
72     return nullptr;
73 
74   int srcFormat;
75   bool bLab = false;
76   bool bNormal = false;
77   if (srcCS == cmsSigLabData) {
78     srcFormat =
79         COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0);
80     bLab = true;
81   } else {
82     srcFormat =
83         COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1);
84     // TODO(thestig): Check to see if lcms2 supports more colorspaces that can
85     // be considered normal.
86     bNormal = srcCS == cmsSigGrayData || srcCS == cmsSigRgbData ||
87               srcCS == cmsSigCmykData;
88   }
89   cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile.get());
90   if (!Check3Components(dstCS))
91     return nullptr;
92 
93   cmsHTRANSFORM hTransform = nullptr;
94   switch (dstCS) {
95     case cmsSigRgbData:
96       hTransform =
97           cmsCreateTransform(srcProfile.get(), srcFormat, dstProfile.get(),
98                              TYPE_BGR_8, INTENT_PERCEPTUAL, /*dwFlags=*/0);
99       break;
100     case cmsSigGrayData:
101     case cmsSigCmykData:
102       // Check3Components() already filtered these types.
103       NOTREACHED_NORETURN();
104     default:
105       break;
106   }
107   if (!hTransform)
108     return nullptr;
109 
110   // Private ctor.
111   return pdfium::WrapUnique(
112       new IccTransform(hTransform, nSrcComponents, bLab, bNormal));
113 }
114 
Translate(pdfium::span<const float> pSrcValues,pdfium::span<float> pDestValues)115 void IccTransform::Translate(pdfium::span<const float> pSrcValues,
116                              pdfium::span<float> pDestValues) {
117   uint8_t output[4];
118   // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will
119   // apply some member of m_hTransform to the input. We need to go over all the
120   // places which set transform to verify that only `pSrcValues.size()`
121   // components are used.
122   if (m_bLab) {
123     DataVector<double> inputs(std::max<size_t>(pSrcValues.size(), 16));
124     for (uint32_t i = 0; i < pSrcValues.size(); ++i)
125       inputs[i] = pSrcValues[i];
126     cmsDoTransform(m_hTransform, inputs.data(), output, 1);
127   } else {
128     DataVector<uint8_t> inputs(std::max<size_t>(pSrcValues.size(), 16));
129     for (size_t i = 0; i < pSrcValues.size(); ++i) {
130       inputs[i] = std::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
131     }
132     cmsDoTransform(m_hTransform, inputs.data(), output, 1);
133   }
134   pDestValues[0] = output[2] / 255.0f;
135   pDestValues[1] = output[1] / 255.0f;
136   pDestValues[2] = output[0] / 255.0f;
137 }
138 
TranslateScanline(pdfium::span<uint8_t> pDest,pdfium::span<const uint8_t> pSrc,int32_t pixels)139 void IccTransform::TranslateScanline(pdfium::span<uint8_t> pDest,
140                                      pdfium::span<const uint8_t> pSrc,
141                                      int32_t pixels) {
142   cmsDoTransform(m_hTransform, pSrc.data(), pDest.data(), pixels);
143 }
144 
145 }  // namespace fxcodec
146