blob: 720a6c80f6d600f9d27d0f5178adcf2a201b3ba7 [file] [log] [blame]
/*
* Copyright 2018 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 "SkData.h"
#include "SkMakeUnique.h"
#include "skcms.h"
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_skcms(SkColorSpace* src,
SkColorSpace* dst,
SkTransferFunctionBehavior premul) {
// 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);
}
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);
}
bool skcms_can_parse(const void* buf, size_t len) {
skcms_ICCProfile p;
return skcms_Parse(buf, len, &p);
}