| // Copyright 2014 The PDFium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "core/fxcodec/icc/icc_transform.h" | |
| #include <stdint.h> | |
| #include <algorithm> | |
| #include <memory> | |
| #include "core/fxcrt/data_vector.h" | |
| #include "core/fxcrt/notreached.h" | |
| #include "core/fxcrt/numerics/safe_conversions.h" | |
| #include "core/fxcrt/ptr_util.h" | |
| namespace fxcodec { | |
| namespace { | |
| // For use with std::unique_ptr<cmsHPROFILE>. | |
| struct CmsProfileDeleter { | |
| inline void operator()(cmsHPROFILE p) { cmsCloseProfile(p); } | |
| }; | |
| using ScopedCmsProfile = std::unique_ptr<void, CmsProfileDeleter>; | |
| bool Check3Components(cmsColorSpaceSignature cs) { | |
| switch (cs) { | |
| case cmsSigGrayData: | |
| case cmsSigCmykData: | |
| return false; | |
| default: | |
| return true; | |
| } | |
| } | |
| } // namespace | |
| IccTransform::IccTransform(cmsHTRANSFORM hTransform, | |
| int srcComponents, | |
| bool bIsLab, | |
| bool bNormal) | |
| : transform_(hTransform), | |
| src_components_(srcComponents), | |
| lab_(bIsLab), | |
| normal_(bNormal) {} | |
| IccTransform::~IccTransform() { | |
| cmsDeleteTransform(transform_); | |
| } | |
| // static | |
| std::unique_ptr<IccTransform> IccTransform::CreateTransformSRGB( | |
| pdfium::span<const uint8_t> span) { | |
| ScopedCmsProfile srcProfile(cmsOpenProfileFromMem( | |
| span.data(), pdfium::checked_cast<cmsUInt32Number>(span.size()))); | |
| if (!srcProfile) { | |
| return nullptr; | |
| } | |
| ScopedCmsProfile dstProfile(cmsCreate_sRGBProfile()); | |
| if (!dstProfile) { | |
| return nullptr; | |
| } | |
| cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get()); | |
| uint32_t nSrcComponents = cmsChannelsOf(srcCS); | |
| if (!IsValidIccComponents(nSrcComponents)) { | |
| return nullptr; | |
| } | |
| int srcFormat; | |
| bool bLab = false; | |
| bool bNormal = false; | |
| if (srcCS == cmsSigLabData) { | |
| srcFormat = | |
| COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0); | |
| bLab = true; | |
| } else { | |
| srcFormat = | |
| COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1); | |
| // TODO(thestig): Check to see if lcms2 supports more colorspaces that can | |
| // be considered normal. | |
| bNormal = srcCS == cmsSigGrayData || srcCS == cmsSigRgbData || | |
| srcCS == cmsSigCmykData; | |
| } | |
| cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile.get()); | |
| if (!Check3Components(dstCS)) { | |
| return nullptr; | |
| } | |
| cmsHTRANSFORM hTransform = nullptr; | |
| switch (dstCS) { | |
| case cmsSigRgbData: | |
| hTransform = | |
| cmsCreateTransform(srcProfile.get(), srcFormat, dstProfile.get(), | |
| TYPE_BGR_8, INTENT_PERCEPTUAL, /*dwFlags=*/0); | |
| break; | |
| case cmsSigGrayData: | |
| case cmsSigCmykData: | |
| // Check3Components() already filtered these types. | |
| NOTREACHED(); | |
| default: | |
| break; | |
| } | |
| if (!hTransform) { | |
| return nullptr; | |
| } | |
| // Private ctor. | |
| return pdfium::WrapUnique( | |
| new IccTransform(hTransform, nSrcComponents, bLab, bNormal)); | |
| } | |
| void IccTransform::Translate(pdfium::span<const float> src_values, | |
| pdfium::span<float, 3> dest_values) { | |
| uint8_t output[4]; | |
| // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will | |
| // apply some member of transform_ to the input. We need to go over all the | |
| // places which set transform to verify that only `src_values.size()` | |
| // components are used. | |
| if (lab_) { | |
| DataVector<double> inputs(std::max<size_t>(src_values.size(), 16)); | |
| for (uint32_t i = 0; i < src_values.size(); ++i) { | |
| inputs[i] = src_values[i]; | |
| } | |
| cmsDoTransform(transform_, inputs.data(), output, 1); | |
| } else { | |
| DataVector<uint8_t> inputs(std::max<size_t>(src_values.size(), 16)); | |
| for (size_t i = 0; i < src_values.size(); ++i) { | |
| inputs[i] = | |
| static_cast<int>(std::clamp(src_values[i] * 255.0f, 0.0f, 255.0f)); | |
| } | |
| cmsDoTransform(transform_, inputs.data(), output, 1); | |
| } | |
| dest_values[0] = output[2] / 255.0f; | |
| dest_values[1] = output[1] / 255.0f; | |
| dest_values[2] = output[0] / 255.0f; | |
| } | |
| void IccTransform::TranslateScanline(pdfium::span<uint8_t> pDest, | |
| pdfium::span<const uint8_t> pSrc, | |
| int32_t pixels) { | |
| cmsDoTransform(transform_, pSrc.data(), pDest.data(), pixels); | |
| } | |
| // static | |
| bool IccTransform::IsValidIccComponents(int components) { | |
| // According to PDF spec, number of components must be 1, 3, or 4. | |
| return components == 1 || components == 3 || components == 4; | |
| } | |
| } // namespace fxcodec |