// Copyright 2014 PDFium Authors. All rights reserved. // 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/iccmodule.h" #include #include #include #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" namespace fxcodec { namespace { // For use with std::unique_ptr. struct CmsProfileDeleter { inline void operator()(cmsHPROFILE p) { cmsCloseProfile(p); } }; using ScopedCmsProfile = std::unique_ptr; bool Check3Components(cmsColorSpaceSignature cs) { switch (cs) { case cmsSigGrayData: case cmsSigCmykData: return false; default: return true; } } } // namespace CLcmsCmm::CLcmsCmm(cmsHTRANSFORM hTransform, int srcComponents, bool bIsLab, bool bNormal) : m_hTransform(hTransform), m_nSrcComponents(srcComponents), m_bLab(bIsLab), m_bNormal(bNormal) {} CLcmsCmm::~CLcmsCmm() { cmsDeleteTransform(m_hTransform); } // static std::unique_ptr IccModule::CreateTransformSRGB( pdfium::span span) { ScopedCmsProfile srcProfile(cmsOpenProfileFromMem(span.data(), span.size())); if (!srcProfile) return nullptr; ScopedCmsProfile dstProfile(cmsCreate_sRGBProfile()); if (!dstProfile) return nullptr; cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get()); uint32_t nSrcComponents = cmsChannelsOf(srcCS); // According to PDF spec, number of components must be 1, 3, or 4. if (nSrcComponents != 1 && nSrcComponents != 3 && nSrcComponents != 4) 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; const int intent = 0; switch (dstCS) { case cmsSigRgbData: hTransform = cmsCreateTransform(srcProfile.get(), srcFormat, dstProfile.get(), TYPE_BGR_8, intent, 0); break; case cmsSigGrayData: case cmsSigCmykData: // Check3Components() already filtered these types. NOTREACHED(); break; default: break; } if (!hTransform) return nullptr; return pdfium::MakeUnique(hTransform, nSrcComponents, bLab, bNormal); } // static void IccModule::Translate(CLcmsCmm* pTransform, uint32_t nSrcComponents, const float* pSrcValues, float* pDestValues) { if (!pTransform) return; uint8_t output[4]; // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will // apply some member of m_hTransform to the input. We need to go over all the // places which set transform to verify that only |nSrcComponents| are used. if (pTransform->IsLab()) { std::vector inputs(std::max(nSrcComponents, 16u)); for (uint32_t i = 0; i < nSrcComponents; ++i) inputs[i] = pSrcValues[i]; cmsDoTransform(pTransform->transform(), inputs.data(), output, 1); } else { std::vector inputs(std::max(nSrcComponents, 16u)); for (uint32_t i = 0; i < nSrcComponents; ++i) { inputs[i] = pdfium::clamp(static_cast(pSrcValues[i] * 255.0f), 0, 255); } cmsDoTransform(pTransform->transform(), inputs.data(), output, 1); } pDestValues[0] = output[2] / 255.0f; pDestValues[1] = output[1] / 255.0f; pDestValues[2] = output[0] / 255.0f; } // static void IccModule::TranslateScanline(CLcmsCmm* pTransform, unsigned char* pDest, const unsigned char* pSrc, int32_t pixels) { if (pTransform) cmsDoTransform(pTransform->transform(), pSrc, pDest, pixels); } } // namespace fxcodec