blob: 213e5b501ed16a67ef988928d7b86882a2e0e2e7 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkColorSpaceXform.h"
#include "SkColorSpaceXformPriv.h"
#include "SkData.h"
#include "SkMakeUnique.h"
#include "../../third_party/skcms/skcms.h"
std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(SkColorSpace* src, SkColorSpace* dst) {
return SkMakeColorSpaceXform(src, dst, SkTransferFunctionBehavior::kRespect);
}
bool SkColorSpaceXform::Apply(SkColorSpace* dstCS, ColorFormat dstFormat, void* dst,
SkColorSpace* srcCS, ColorFormat srcFormat, const void* src,
int len, AlphaOp op) {
SkAlphaType at;
switch (op) {
case kPreserve_AlphaOp: at = kUnpremul_SkAlphaType; break;
case kPremul_AlphaOp: at = kPremul_SkAlphaType; break;
case kSrcIsOpaque_AlphaOp: at = kOpaque_SkAlphaType; break;
}
return SkColorSpaceXform::New(srcCS, dstCS)->apply(dstFormat, dst, srcFormat, src, len, at);
}
class SkColorSpaceXform_skcms : public SkColorSpaceXform {
public:
SkColorSpaceXform_skcms(const skcms_ICCProfile& srcProfile,
const skcms_ICCProfile& dstProfile,
skcms_AlphaFormat premulFormat)
: fSrcProfile(srcProfile)
, fDstProfile(dstProfile)
, fPremulFormat(premulFormat) {
}
bool apply(ColorFormat, void*, ColorFormat, const void*, int, SkAlphaType) const override;
private:
skcms_ICCProfile fSrcProfile;
skcms_ICCProfile fDstProfile;
skcms_AlphaFormat fPremulFormat;
};
static skcms_PixelFormat get_skcms_format(SkColorSpaceXform::ColorFormat fmt) {
switch (fmt) {
case SkColorSpaceXform::kRGBA_8888_ColorFormat:
return skcms_PixelFormat_RGBA_8888;
case SkColorSpaceXform::kBGRA_8888_ColorFormat:
return skcms_PixelFormat_BGRA_8888;
case SkColorSpaceXform::kRGB_U16_BE_ColorFormat:
return skcms_PixelFormat_RGB_161616;
case SkColorSpaceXform::kRGBA_U16_BE_ColorFormat:
return skcms_PixelFormat_RGBA_16161616;
case SkColorSpaceXform::kRGBA_F16_ColorFormat:
return skcms_PixelFormat_RGBA_hhhh;
case SkColorSpaceXform::kRGBA_F32_ColorFormat:
return skcms_PixelFormat_RGBA_ffff;
case SkColorSpaceXform::kBGR_565_ColorFormat:
return skcms_PixelFormat_BGR_565;
default:
SkDEBUGFAIL("Invalid ColorFormat");
return skcms_PixelFormat_RGBA_8888;
}
}
bool SkColorSpaceXform_skcms::apply(ColorFormat dstFormat, void* dst,
ColorFormat srcFormat, const void* src,
int count, SkAlphaType alphaType) const {
skcms_AlphaFormat srcAlpha = skcms_AlphaFormat_Unpremul;
skcms_AlphaFormat dstAlpha = kPremul_SkAlphaType == alphaType ? fPremulFormat
: skcms_AlphaFormat_Unpremul;
return skcms_Transform(src, get_skcms_format(srcFormat), srcAlpha, &fSrcProfile,
dst, get_skcms_format(dstFormat), dstAlpha, &fDstProfile, count);
}
void SkColorSpace::toProfile(skcms_ICCProfile* profile) const {
if (auto blob = this->onProfileData()) {
SkAssertResult(skcms_Parse(blob->data(), blob->size(), profile));
} else {
SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
SkColorSpaceTransferFn tf;
SkAssertResult(this->toXYZD50(&toXYZ) && this->isNumericalTransferFn(&tf));
skcms_Matrix3x3 m = { {
{ toXYZ.get(0, 0), toXYZ.get(0, 1), toXYZ.get(0, 2) },
{ toXYZ.get(1, 0), toXYZ.get(1, 1), toXYZ.get(1, 2) },
{ toXYZ.get(2, 0), toXYZ.get(2, 1), toXYZ.get(2, 2) },
} };
skcms_Init(profile);
skcms_SetTransferFunction(profile, (const skcms_TransferFunction*)&tf);
skcms_SetXYZD50(profile, &m);
}
}
std::unique_ptr<SkColorSpaceXform> SkMakeColorSpaceXform(SkColorSpace* src, SkColorSpace* dst,
SkTransferFunctionBehavior premul) {
if (src && dst && dst->toXYZD50()) {
// Construct skcms_ICCProfiles from each color space. For now, support A2B and XYZ.
// Eventually, only need to support XYZ. Map premul to one of the two premul formats
// in skcms.
skcms_ICCProfile srcProfile, dstProfile;
src->toProfile(&srcProfile);
dst->toProfile(&dstProfile);
if (!skcms_MakeUsableAsDestination(&dstProfile)) {
return nullptr;
}
skcms_AlphaFormat premulFormat = SkTransferFunctionBehavior::kRespect == premul
? skcms_AlphaFormat_PremulLinear
: skcms_AlphaFormat_PremulAsEncoded;
return skstd::make_unique<SkColorSpaceXform_skcms>(srcProfile, dstProfile, premulFormat);
}
return nullptr;
}
sk_sp<SkColorSpace> SkColorSpace::Make(const skcms_ICCProfile& profile) {
if (!profile.has_toXYZD50 || !profile.has_trc) {
return nullptr;
}
if (skcms_ApproximatelyEqualProfiles(&profile, skcms_sRGB_profile())) {
return SkColorSpace::MakeSRGB();
}
SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
toXYZD50.set3x3RowMajorf(&profile.toXYZD50.vals[0][0]);
if (!toXYZD50.invert(nullptr)) {
return nullptr;
}
const skcms_Curve* trc = profile.trc;
if (trc[0].table_entries ||
trc[1].table_entries ||
trc[2].table_entries ||
memcmp(&trc[0].parametric, &trc[1].parametric, sizeof(trc[0].parametric)) ||
memcmp(&trc[0].parametric, &trc[2].parametric, sizeof(trc[0].parametric))) {
return nullptr;
}
SkColorSpaceTransferFn skia_tf;
memcpy(&skia_tf, &profile.trc[0].parametric, sizeof(skia_tf));
return SkColorSpace::MakeRGB(skia_tf, toXYZD50);
}