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