Commit ac3c37d5 authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

Make ICCProfile no longer store a ColorSpace directly

Make ICCProfile store the primaries and transfer function that it
read or computed directly, rather than storing a ColorSpace object.
Construct the ColorSpace object when requested, rather than storing it.

Store an approximate (or guessed) primary matrix and transfer function
for ICC_BASED ColorSpace objects. When computing the parametric
approximation of an ICC_BASED ColorSpace object, use these values
directly, rather than having to look them up from original ICCProfile.

Bug: 766736
Change-Id: I21d6dfaa38706f814f6c9dd54517806e99366113
Reviewed-on: https://chromium-review.googlesource.com/745311Reviewed-by: default avatarFredrik Hubinette <hubbe@chromium.org>
Commit-Queue: ccameron <ccameron@chromium.org>
Cr-Commit-Position: refs/heads/master@{#512958}
parent 5768afb3
......@@ -51,11 +51,11 @@ ColorSpace::ColorSpace(const ColorSpace& other)
range_(other.range_),
icc_profile_id_(other.icc_profile_id_),
icc_profile_sk_color_space_(other.icc_profile_sk_color_space_) {
if (transfer_ == TransferID::CUSTOM) {
if (transfer_ == TransferID::CUSTOM || transfer_ == TransferID::ICC_BASED) {
memcpy(custom_transfer_params_, other.custom_transfer_params_,
sizeof(custom_transfer_params_));
}
if (primaries_ == PrimaryID::CUSTOM) {
if (primaries_ == PrimaryID::CUSTOM || primaries_ == PrimaryID::ICC_BASED) {
memcpy(custom_primary_matrix_, other.custom_primary_matrix_,
sizeof(custom_primary_matrix_));
}
......@@ -96,13 +96,18 @@ ColorSpace ColorSpace::CreateCustom(const SkMatrix44& to_XYZD50,
ColorSpace result(ColorSpace::PrimaryID::CUSTOM,
ColorSpace::TransferID::CUSTOM, ColorSpace::MatrixID::RGB,
ColorSpace::RangeID::FULL);
result.SetCustomPrimaries(to_XYZD50);
result.SetCustomTransferFunction(fn);
return result;
}
void ColorSpace::SetCustomPrimaries(const SkMatrix44& to_XYZD50) {
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
result.custom_primary_matrix_[3 * row + col] = to_XYZD50.get(row, col);
custom_primary_matrix_[3 * row + col] = to_XYZD50.get(row, col);
}
}
result.SetCustomTransferFunction(fn);
return result;
primaries_ = PrimaryID::CUSTOM;
}
void ColorSpace::SetCustomTransferFunction(const SkColorSpaceTransferFn& fn) {
......@@ -168,13 +173,13 @@ bool ColorSpace::operator==(const ColorSpace& other) const {
if (primaries_ != other.primaries_ || transfer_ != other.transfer_ ||
matrix_ != other.matrix_ || range_ != other.range_)
return false;
if (primaries_ == PrimaryID::CUSTOM) {
if (primaries_ == PrimaryID::CUSTOM || primaries_ == PrimaryID::ICC_BASED) {
if (memcmp(custom_primary_matrix_, other.custom_primary_matrix_,
sizeof(custom_primary_matrix_))) {
return false;
}
}
if (transfer_ == TransferID::CUSTOM) {
if (transfer_ == TransferID::CUSTOM || transfer_ == TransferID::ICC_BASED) {
if (memcmp(custom_transfer_params_, other.custom_transfer_params_,
sizeof(custom_transfer_params_))) {
return false;
......@@ -202,27 +207,25 @@ bool ColorSpace::FullRangeEncodedValues() const {
transfer_ == TransferID::IEC61966_2_4;
}
bool ColorSpace::IsParametric() const {
return primaries_ != PrimaryID::ICC_BASED &&
transfer_ != TransferID::ICC_BASED;
}
ColorSpace ColorSpace::GetParametricApproximation() const {
// If this is parametric already, return it directly.
if (IsParametric())
return *this;
// Query the ICC profile, if available, for the parametric approximation.
ICCProfile icc_profile;
if (icc_profile_id_ && GetICCProfile(&icc_profile)) {
return icc_profile.GetParametricColorSpace();
} else {
DLOG(ERROR)
<< "Unable to acquire ICC profile for parametric approximation.";
ColorSpace result = *this;
// The parametric approximation will not equal the original color space only
// when the primaries or transfer function of the original color space are not
// parametric (that is, they require the original ICC profile to represent
// them).
if (result.primaries_ == PrimaryID::ICC_BASED ||
result.transfer_ == TransferID::ICC_BASED) {
// Ensure that the result not reference the original ICC profile anymore,
// because ICC profile represents a different space from the returned
// approximation.
result.icc_profile_id_ = 0;
result.icc_profile_sk_color_space_ = nullptr;
if (result.primaries_ == PrimaryID::ICC_BASED)
result.primaries_ = PrimaryID::CUSTOM;
if (result.transfer_ == TransferID::ICC_BASED)
result.transfer_ = TransferID::CUSTOM;
}
// Fall back to sRGB if the ICC profile is no longer cached.
return CreateSRGB();
return result;
}
bool ColorSpace::operator!=(const ColorSpace& other) const {
......@@ -246,7 +249,7 @@ bool ColorSpace::operator<(const ColorSpace& other) const {
return true;
if (range_ > other.range_)
return false;
if (primaries_ == PrimaryID::CUSTOM) {
if (primaries_ == PrimaryID::CUSTOM || primaries_ == PrimaryID::ICC_BASED) {
int primary_result =
memcmp(custom_primary_matrix_, other.custom_primary_matrix_,
sizeof(custom_primary_matrix_));
......@@ -255,7 +258,7 @@ bool ColorSpace::operator<(const ColorSpace& other) const {
if (primary_result > 0)
return false;
}
if (transfer_ == TransferID::CUSTOM) {
if (transfer_ == TransferID::CUSTOM || transfer_ == TransferID::ICC_BASED) {
int transfer_result =
memcmp(custom_transfer_params_, other.custom_transfer_params_,
sizeof(custom_transfer_params_));
......@@ -272,14 +275,14 @@ size_t ColorSpace::GetHash() const {
(static_cast<size_t>(transfer_) << 8) |
(static_cast<size_t>(matrix_) << 16) |
(static_cast<size_t>(range_) << 24);
if (primaries_ == PrimaryID::CUSTOM) {
if (primaries_ == PrimaryID::CUSTOM || primaries_ == PrimaryID::ICC_BASED) {
const uint32_t* params =
reinterpret_cast<const uint32_t*>(custom_primary_matrix_);
result ^= params[0];
result ^= params[4];
result ^= params[8];
}
if (transfer_ == TransferID::CUSTOM) {
if (transfer_ == TransferID::CUSTOM || transfer_ == TransferID::ICC_BASED) {
const uint32_t* params =
reinterpret_cast<const uint32_t*>(custom_transfer_params_);
result ^= params[3];
......@@ -314,16 +317,17 @@ std::string ColorSpace::ToString() const {
PRINT_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB)
PRINT_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN)
PRINT_ENUM_CASE(PrimaryID, ICC_BASED)
case PrimaryID::CUSTOM:
PRINT_ENUM_CASE(PrimaryID, CUSTOM)
}
if (primaries_ == PrimaryID::ICC_BASED || primaries_ == PrimaryID::CUSTOM) {
ss << ":[";
for (size_t i = 0; i < 3; ++i) {
ss << "[";
for (size_t i = 0; i < 3; ++i) {
ss << "[";
for (size_t j = 0; j < 3; ++j)
ss << custom_primary_matrix_[3 * i + j] << ",";
ss << "],";
}
ss << "]";
break;
for (size_t j = 0; j < 3; ++j)
ss << custom_primary_matrix_[3 * i + j] << ",";
ss << "],";
}
ss << "]";
}
ss << ", transfer:";
switch (transfer_) {
......@@ -350,13 +354,13 @@ std::string ColorSpace::ToString() const {
PRINT_ENUM_CASE(TransferID, IEC61966_2_1_HDR)
PRINT_ENUM_CASE(TransferID, LINEAR_HDR)
PRINT_ENUM_CASE(TransferID, ICC_BASED)
case TransferID::CUSTOM: {
SkColorSpaceTransferFn fn;
GetTransferFunction(&fn);
ss << fn.fC << "*x + " << fn.fF << " if x < " << fn.fD << " else (";
ss << fn.fA << "*x + " << fn.fB << ")**" << fn.fG << " + " << fn.fE;
break;
}
PRINT_ENUM_CASE(TransferID, CUSTOM)
}
if (transfer_ == TransferID::ICC_BASED || transfer_ == TransferID::CUSTOM) {
SkColorSpaceTransferFn fn;
GetTransferFunction(&fn);
ss << ":(" << fn.fC << "*x + " << fn.fF << " if x < " << fn.fD << " else (";
ss << fn.fA << "*x + " << fn.fB << ")**" << fn.fG << " + " << fn.fE << ")";
}
ss << ", matrix:";
switch (matrix_) {
......@@ -396,15 +400,15 @@ ColorSpace ColorSpace::GetAsFullRangeRGB() const {
}
ColorSpace ColorSpace::GetRasterColorSpace() const {
// Rasterization can only be done into parametric color spaces.
if (!IsParametric())
return GetParametricApproximation();
// Rasterization doesn't support more than 8 bit unorm values. If the output
// space has an extended range, use Display P3 for the rasterization space,
// to get a somewhat wider color gamut.
if (HasExtendedSkTransferFn())
return CreateDisplayP3D65();
return *this;
// Rasterization can only be done into parametric color spaces. Return the
// parametric approximation.
return GetParametricApproximation();
}
ColorSpace ColorSpace::GetBlendingColorSpace() const {
......@@ -416,11 +420,6 @@ ColorSpace ColorSpace::GetBlendingColorSpace() const {
}
sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const {
// If we got a specific SkColorSpace from the ICCProfile that this color space
// was created from, use that.
if (icc_profile_sk_color_space_)
return icc_profile_sk_color_space_;
// Unspecified color spaces correspond to the null SkColorSpace.
if (!IsValid())
return nullptr;
......@@ -435,6 +434,11 @@ sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const {
return nullptr;
}
// If we got a specific SkColorSpace from the ICCProfile that this color space
// was created from, use that.
if (icc_profile_sk_color_space_)
return icc_profile_sk_color_space_;
// Use the named SRGB and linear-SRGB instead of the generic constructors.
if (primaries_ == PrimaryID::BT709) {
if (transfer_ == TransferID::IEC61966_2_1)
......@@ -512,10 +516,14 @@ bool ColorSpace::GetICCProfile(ICCProfile* icc_profile) const {
}
// If this was created from an ICC profile, retrieve that exact profile.
ICCProfile result;
if (ICCProfile::FromId(icc_profile_id_, icc_profile))
return true;
if (primaries_ == PrimaryID::ICC_BASED ||
transfer_ == TransferID::ICC_BASED) {
return false;
}
// Otherwise, construct an ICC profile based on the best approximated
// primaries and matrix.
SkMatrix44 to_XYZD50_matrix;
......@@ -580,11 +588,11 @@ void ColorSpace::GetPrimaryMatrix(SkMatrix44* to_XYZD50) const {
SkColorSpacePrimaries primaries = {0};
switch (primaries_) {
case ColorSpace::PrimaryID::CUSTOM:
case ColorSpace::PrimaryID::ICC_BASED:
to_XYZD50->set3x3RowMajorf(custom_primary_matrix_);
return;
case ColorSpace::PrimaryID::INVALID:
case ColorSpace::PrimaryID::ICC_BASED:
to_XYZD50->setIdentity();
return;
......@@ -751,6 +759,7 @@ bool ColorSpace::GetTransferFunction(SkColorSpaceTransferFn* fn) const {
switch (transfer_) {
case ColorSpace::TransferID::CUSTOM:
case ColorSpace::TransferID::ICC_BASED:
fn->fA = custom_transfer_params_[0];
fn->fB = custom_transfer_params_[1];
fn->fC = custom_transfer_params_[2];
......@@ -818,7 +827,6 @@ bool ColorSpace::GetTransferFunction(SkColorSpaceTransferFn* fn) const {
case ColorSpace::TransferID::SMPTEST2084:
case ColorSpace::TransferID::SMPTEST2084_NON_HDR:
case ColorSpace::TransferID::INVALID:
case ColorSpace::TransferID::ICC_BASED:
break;
}
......
......@@ -53,7 +53,8 @@ class COLOR_SPACE_EXPORT ColorSpace {
CUSTOM,
// For color spaces defined by an ICC profile which cannot be represented
// parametrically. Any ColorTransform using this color space will use the
// ICC profile directly to compute a transform LUT.
// ICC profile directly to compute a transform LUT. A parametric
// approximation of these primaries is stored in |custom_primary_matrix_|.
ICC_BASED,
LAST = ICC_BASED,
};
......@@ -89,7 +90,8 @@ class COLOR_SPACE_EXPORT ColorSpace {
LINEAR_HDR,
// A parametric transfer function defined by |custom_transfer_params_|.
CUSTOM,
// See PrimaryID::ICC_BASED.
// See PrimaryID::ICC_BASED. A parametric approximation of the transfer
// function is stored in |custom_transfer_params_|.
ICC_BASED,
LAST = ICC_BASED,
};
......@@ -171,9 +173,6 @@ class COLOR_SPACE_EXPORT ColorSpace {
// Returns true if the encoded values can be outside of the 0.0-1.0 range.
bool FullRangeEncodedValues() const;
// Returns true if this color space can be represented parametrically.
bool IsParametric() const;
// Return a parametric approximation of this color space (if it is not already
// parametric).
ColorSpace GetParametricApproximation() const;
......@@ -208,6 +207,7 @@ class COLOR_SPACE_EXPORT ColorSpace {
void GetRangeAdjustMatrix(SkMatrix44* matrix) const;
private:
void SetCustomPrimaries(const SkMatrix44& to_XYZD50);
void SetCustomTransferFunction(const SkColorSpaceTransferFn& fn);
// Returns true if the transfer function is defined by an
......@@ -219,12 +219,12 @@ class COLOR_SPACE_EXPORT ColorSpace {
MatrixID matrix_ = MatrixID::INVALID;
RangeID range_ = RangeID::INVALID;
// Only used if primaries_ is PrimaryID::CUSTOM.
// Only used if primaries_ is PrimaryID::CUSTOM or PrimaryID::ICC_BASED.
float custom_primary_matrix_[9] = {0, 0, 0, 0, 0, 0, 0, 0};
// Only used if transfer_ is TransferID::CUSTOM. This array consists of the A
// through G entries of the SkColorSpaceTransferFn structure in alphabetical
// order.
// Only used if transfer_ is TransferID::CUSTOM or PrimaryID::ICC_BASED.
// This array consists of the A through G entries of the
// SkColorSpaceTransferFn structure in alphabetical order.
float custom_transfer_params_[7] = {0, 0, 0, 0, 0, 0, 0};
// This is used to look up the ICCProfile from which this ColorSpace was
......
......@@ -252,8 +252,9 @@ TEST(SimpleColorSpace, ICCProfileOnlyColorSpin) {
const float kEpsilon = 2.5f / 255.f;
ICCProfile icc_profile = ICCProfileForTestingNoAnalyticTrFn();
ColorSpace icc_space = icc_profile.GetColorSpace();
ColorSpace colorspin =
ICCProfileForTestingColorSpin().GetParametricColorSpace();
ColorSpace colorspin = ICCProfileForTestingColorSpin()
.GetColorSpace()
.GetParametricApproximation();
ColorTransform::TriStim input_value(0.25f, 0.5f, 0.75f);
ColorTransform::TriStim transformed_value = input_value;
......
......@@ -54,17 +54,6 @@ class ICCProfileCache {
if (!icc_profile->id_)
icc_profile->id_ = next_unused_id_++;
// Ensure that GetColorSpace() point back to this ICCProfile.
gfx::ColorSpace& color_space = icc_profile->color_space_;
color_space.icc_profile_id_ = icc_profile->id_;
// Ensure that the GetParametricColorSpace() point back to this ICCProfile
// only if the parametric version is accurate.
if (color_space.primaries_ != ColorSpace::PrimaryID::ICC_BASED &&
color_space.transfer_ != ColorSpace::TransferID::ICC_BASED) {
icc_profile->parametric_color_space_.icc_profile_id_ = icc_profile->id_;
}
Entry entry;
entry.icc_profile = *icc_profile;
id_to_icc_profile_mru_.Put(icc_profile->id_, entry);
......@@ -201,76 +190,69 @@ static base::LazyInstance<ICCProfileCache>::DestructorAtExit g_cache =
} // namespace
// static
ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces(
const std::vector<char>& data,
gfx::ColorSpace* parametric_color_space,
float* parametric_tr_fn_max_error,
sk_sp<SkColorSpace>* useable_sk_color_space) {
// Initialize the output parameters as invalid.
*parametric_color_space = gfx::ColorSpace();
*parametric_tr_fn_max_error = 0;
*useable_sk_color_space = nullptr;
ICCProfile::AnalyzeResult ICCProfile::Initialize() {
// Start out with no parametric data.
primaries_ = gfx::ColorSpace::PrimaryID::INVALID;
transfer_ = gfx::ColorSpace::TransferID::INVALID;
// Parse the profile and attempt to create a SkColorSpaceXform out of it.
sk_sp<SkColorSpace> sk_srgb_color_space = SkColorSpace::MakeSRGB();
sk_sp<SkICC> sk_icc = SkICC::Make(data.data(), data.size());
sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size());
if (!sk_icc) {
DLOG(ERROR) << "Failed to parse ICC profile to SkICC.";
return kICCFailedToParse;
}
sk_sp<SkColorSpace> sk_icc_color_space =
SkColorSpace::MakeICC(data.data(), data.size());
if (!sk_icc_color_space) {
sk_color_space_ = SkColorSpace::MakeICC(data_.data(), data_.size());
if (!sk_color_space_) {
DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace.";
return kICCFailedToExtractSkColorSpace;
}
// If our SkColorSpace representation is sRGB then return that.
if (SkColorSpace::Equals(sk_srgb_color_space.get(), sk_color_space_.get())) {
primaries_ = gfx::ColorSpace::PrimaryID::BT709;
transfer_ = gfx::ColorSpace::TransferID::IEC61966_2_1;
return kICCExtractedSRGBColorSpace;
}
std::unique_ptr<SkColorSpaceXform> sk_color_space_xform =
SkColorSpaceXform::New(sk_srgb_color_space.get(),
sk_icc_color_space.get());
SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_color_space_.get());
if (!sk_color_space_xform) {
DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform.";
return kICCFailedToCreateXform;
}
// Because this SkColorSpace can be used to construct a transform, mark it
// as "useable". Mark the "best approximation" as sRGB to start.
*useable_sk_color_space = sk_icc_color_space;
*parametric_color_space = ColorSpace::CreateSRGB();
// If our SkColorSpace representation is sRGB then return that.
if (SkColorSpace::Equals(sk_srgb_color_space.get(),
sk_icc_color_space.get())) {
return kICCExtractedSRGBColorSpace;
}
// Because this SkColorSpace can be used to construct a transform, we can use
// it to create a LUT based color transform, at the very least. If we fail to
// get any better approximation, we'll use sRGB as our approximation.
primaries_ = ColorSpace::PrimaryID::ICC_BASED;
transfer_ = ColorSpace::TransferID::ICC_BASED;
ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_);
ColorSpace::CreateSRGB().GetTransferFunction(&transfer_fn_);
// A primary matrix is required for our parametric approximation.
// A primary matrix is required for our parametric representations. Use it if
// it exists.
SkMatrix44 to_XYZD50_matrix;
if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) {
DLOG(ERROR) << "Failed to extract ICC profile primary matrix.";
return kICCFailedToExtractMatrix;
}
primaries_ = ColorSpace::PrimaryID::CUSTOM;
to_XYZD50_ = to_XYZD50_matrix;
// Try to directly extract a numerical transfer function.
// Try to directly extract a numerical transfer function. Use it if it
// exists.
SkColorSpaceTransferFn exact_tr_fn;
if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) {
*parametric_color_space =
gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, exact_tr_fn);
transfer_ = ColorSpace::TransferID::CUSTOM;
transfer_fn_ = exact_tr_fn;
return kICCExtractedMatrixAndAnalyticTrFn;
}
// If we fail to get a transfer function, use the sRGB transfer function,
// and return false to indicate that the gfx::ColorSpace isn't accurate, but
// we can construct accurate LUT transforms using the underlying
// SkColorSpace.
*parametric_color_space = gfx::ColorSpace::CreateCustom(
to_XYZD50_matrix, ColorSpace::TransferID::IEC61966_2_1);
// Attempt to fit a parametric transfer function to the table data in the
// profile.
SkColorSpaceTransferFn approx_tr_fn;
if (!SkApproximateTransferFn(sk_icc, parametric_tr_fn_max_error,
&approx_tr_fn)) {
if (!SkApproximateTransferFn(sk_icc, &transfer_fn_error_, &approx_tr_fn)) {
DLOG(ERROR) << "Failed approximate transfer function.";
return kICCFailedToConvergeToApproximateTrFn;
}
......@@ -278,16 +260,16 @@ ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces(
// If this converged, but has too high error, use the sRGB transfer function
// from above.
const float kMaxError = 2.f / 256.f;
if (*parametric_tr_fn_max_error >= kMaxError) {
if (transfer_fn_error_ >= kMaxError) {
DLOG(ERROR) << "Failed to accurately approximate transfer function, error: "
<< 256.f * (*parametric_tr_fn_max_error) << "/256";
<< 256.f * transfer_fn_error_ << "/256";
return kICCFailedToApproximateTrFnAccurately;
};
// If the error is sufficiently low, declare that the approximation is
// accurate.
*parametric_color_space =
gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn);
transfer_ = ColorSpace::TransferID::CUSTOM;
transfer_fn_ = approx_tr_fn;
return kICCExtractedMatrixAndApproximatedTrFn;
}
......@@ -346,14 +328,28 @@ const std::vector<char>& ICCProfile::GetData() const {
return data_;
}
const ColorSpace& ICCProfile::GetColorSpace() const {
ColorSpace ICCProfile::GetColorSpace() const {
g_cache.Get().TouchEntry(*this);
return color_space_;
}
const ColorSpace& ICCProfile::GetParametricColorSpace() const {
g_cache.Get().TouchEntry(*this);
return parametric_color_space_;
ColorSpace color_space;
if (!IsValid())
return color_space;
color_space.icc_profile_id_ = id_;
color_space.icc_profile_sk_color_space_ = sk_color_space_;
DCHECK(color_space.icc_profile_sk_color_space_);
color_space.matrix_ = ColorSpace::MatrixID::RGB;
color_space.range_ = ColorSpace::RangeID::FULL;
if (primaries_ == ColorSpace::PrimaryID::CUSTOM ||
primaries_ == ColorSpace::PrimaryID::ICC_BASED) {
color_space.SetCustomPrimaries(to_XYZD50_);
}
color_space.primaries_ = primaries_;
if (transfer_ == ColorSpace::TransferID::CUSTOM ||
transfer_ == ColorSpace::TransferID::ICC_BASED) {
color_space.SetCustomTransferFunction(transfer_fn_);
}
color_space.transfer_ = transfer_;
return color_space;
}
// static
......@@ -376,34 +372,7 @@ void ICCProfile::ComputeColorSpaceAndCache() {
return;
// Parse the ICC profile
sk_sp<SkColorSpace> useable_sk_color_space;
analyze_result_ =
ExtractColorSpaces(data_, &parametric_color_space_,
&parametric_tr_fn_error_, &useable_sk_color_space);
switch (analyze_result_) {
case kICCExtractedSRGBColorSpace:
case kICCExtractedMatrixAndAnalyticTrFn:
case kICCExtractedMatrixAndApproximatedTrFn:
// Successfully and accurately extracted color space.
color_space_ = parametric_color_space_;
break;
case kICCFailedToExtractRawTrFn:
case kICCFailedToExtractMatrix:
case kICCFailedToConvergeToApproximateTrFn:
case kICCFailedToApproximateTrFnAccurately:
// Successfully but extracted a color space, but it isn't accurate enough.
color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED,
ColorSpace::TransferID::ICC_BASED);
color_space_.icc_profile_sk_color_space_ = useable_sk_color_space;
break;
case kICCFailedToParse:
case kICCFailedToExtractSkColorSpace:
case kICCFailedToCreateXform:
// Can't even use this color space as a LUT.
DCHECK(!parametric_color_space_.IsValid());
color_space_ = parametric_color_space_;
break;
}
analyze_result_ = Initialize();
// Add to the cache.
g_cache.Get().InsertAndSetIdIfNeeded(this);
......@@ -429,7 +398,7 @@ void ICCProfile::HistogramDisplay(int64_t display_id) const {
if (nonlinear_fit_converged) {
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.ColorSpace.Destination.NonlinearFitError",
static_cast<int>(parametric_tr_fn_error_ * 255), 0, 127, 16);
static_cast<int>(transfer_fn_error_ * 255), 0, 127, 16);
}
}
......
......@@ -52,12 +52,7 @@ class COLOR_SPACE_EXPORT ICCProfile {
// Return a ColorSpace that references this ICCProfile. ColorTransforms
// created using this ColorSpace will match this ICCProfile precisely.
const ColorSpace& GetColorSpace() const;
// Return a ColorSpace that is the best parametric approximation of this
// ICCProfile. The resulting ColorSpace will reference this ICCProfile only
// if the parametric approximation is almost exact.
const ColorSpace& GetParametricColorSpace() const;
ColorSpace GetColorSpace() const;
const std::vector<char>& GetData() const;
......@@ -103,12 +98,7 @@ class COLOR_SPACE_EXPORT ICCProfile {
size_t size,
uint64_t id);
static AnalyzeResult ExtractColorSpaces(
const std::vector<char>& data,
gfx::ColorSpace* parametric_color_space,
float* parametric_tr_fn_max_error,
sk_sp<SkColorSpace>* useable_sk_color_space);
AnalyzeResult Initialize();
void ComputeColorSpaceAndCache();
// This globally identifies this ICC profile. It is used to look up this ICC
......@@ -120,18 +110,21 @@ class COLOR_SPACE_EXPORT ICCProfile {
// The result of attepting to extract a color space from the color profile.
AnalyzeResult analyze_result_ = kICCFailedToParse;
// |color_space| always links back to this ICC profile, and its SkColorSpace
// is always equal to the SkColorSpace created from this ICCProfile.
gfx::ColorSpace color_space_;
// Results of Skia parsing the ICC profile data.
sk_sp<SkColorSpace> sk_color_space_;
// The best-fit parametric primaries.
gfx::ColorSpace::PrimaryID primaries_;
SkMatrix44 to_XYZD50_;
// |parametric_color_space_| will only link back to this ICC profile if it
// is accurate, and its SkColorSpace will always be parametrically created.
gfx::ColorSpace parametric_color_space_;
// The best-fit parametric transfer function.
gfx::ColorSpace::TransferID transfer_;
SkColorSpaceTransferFn transfer_fn_;
// The L-infinity error of the parametric color space fit. This is undefined
// unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or
// kICCExtractedMatrixAndApproximatedTrFn.
float parametric_tr_fn_error_ = -1;
float transfer_fn_error_ = 0;
FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, BT709toSRGBICC);
FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace);
......
......@@ -32,8 +32,12 @@ TEST(ICCProfile, SRGB) {
EXPECT_EQ(icc_profile.GetColorSpace().ToSkColorSpace().get(),
sk_color_space.get());
// The parametric generating code should recognize that this is SRGB.
EXPECT_EQ(icc_profile.GetParametricColorSpace(), ColorSpace::CreateSRGB());
EXPECT_EQ(icc_profile.GetParametricColorSpace().ToSkColorSpace().get(),
EXPECT_EQ(icc_profile.GetColorSpace().GetParametricApproximation(),
ColorSpace::CreateSRGB());
EXPECT_EQ(icc_profile.GetColorSpace()
.GetParametricApproximation()
.ToSkColorSpace()
.get(),
sk_color_space.get());
// The generated color space should recognize that this is SRGB.
EXPECT_EQ(color_space.ToSkColorSpace().get(), sk_color_space.get());
......@@ -78,7 +82,8 @@ TEST(ICCProfile, ParametricVersusExact) {
// This ICC profile has three transfer functions that differ enough that the
// parametric color space is considered inaccurate.
ICCProfile multi_tr_fn = ICCProfileForTestingNoAnalyticTrFn();
EXPECT_NE(multi_tr_fn.GetColorSpace(), multi_tr_fn.GetParametricColorSpace());
EXPECT_NE(multi_tr_fn.GetColorSpace(),
multi_tr_fn.GetColorSpace().GetParametricApproximation());
ICCProfile multi_tr_fn_color_space;
EXPECT_TRUE(
......@@ -86,35 +91,40 @@ TEST(ICCProfile, ParametricVersusExact) {
EXPECT_EQ(multi_tr_fn_color_space, multi_tr_fn);
ICCProfile multi_tr_fn_parametric_color_space;
EXPECT_TRUE(multi_tr_fn.GetParametricColorSpace().GetICCProfile(
&multi_tr_fn_parametric_color_space));
EXPECT_TRUE(
multi_tr_fn.GetColorSpace().GetParametricApproximation().GetICCProfile(
&multi_tr_fn_parametric_color_space));
EXPECT_NE(multi_tr_fn_parametric_color_space, multi_tr_fn);
// This ICC profile has a transfer function with T(1) that is greater than 1
// in the approximation, but is still close enough to be considered accurate.
ICCProfile overshoot = ICCProfileForTestingOvershoot();
EXPECT_EQ(overshoot.GetColorSpace(), overshoot.GetParametricColorSpace());
EXPECT_EQ(overshoot.GetColorSpace(),
overshoot.GetColorSpace().GetParametricApproximation());
ICCProfile overshoot_color_space;
EXPECT_TRUE(overshoot.GetColorSpace().GetICCProfile(&overshoot_color_space));
EXPECT_EQ(overshoot_color_space, overshoot);
ICCProfile overshoot_parametric_color_space;
EXPECT_TRUE(overshoot.GetParametricColorSpace().GetICCProfile(
&overshoot_parametric_color_space));
EXPECT_TRUE(
overshoot.GetColorSpace().GetParametricApproximation().GetICCProfile(
&overshoot_parametric_color_space));
EXPECT_EQ(overshoot_parametric_color_space, overshoot);
// This ICC profile is precisely represented by the parametric color space.
ICCProfile accurate = ICCProfileForTestingAdobeRGB();
EXPECT_EQ(accurate.GetColorSpace(), accurate.GetParametricColorSpace());
EXPECT_EQ(accurate.GetColorSpace(),
accurate.GetColorSpace().GetParametricApproximation());
ICCProfile accurate_color_space;
EXPECT_TRUE(accurate.GetColorSpace().GetICCProfile(&accurate_color_space));
EXPECT_EQ(accurate_color_space, accurate);
ICCProfile accurate_parametric_color_space;
EXPECT_TRUE(accurate.GetParametricColorSpace().GetICCProfile(
&accurate_parametric_color_space));
EXPECT_TRUE(
accurate.GetColorSpace().GetParametricApproximation().GetICCProfile(
&accurate_parametric_color_space));
EXPECT_EQ(accurate_parametric_color_space, accurate);
// This ICC profile has only an A2B representation. We cannot create an
......@@ -136,12 +146,15 @@ TEST(ICCProfile, GarbageData) {
ICCProfile::FromData(bad_data.data(), bad_data.size());
EXPECT_FALSE(garbage_profile.IsValid());
EXPECT_FALSE(garbage_profile.GetColorSpace().IsValid());
EXPECT_FALSE(garbage_profile.GetParametricColorSpace().IsValid());
EXPECT_FALSE(
garbage_profile.GetColorSpace().GetParametricApproximation().IsValid());
ICCProfile default_ctor_profile;
EXPECT_FALSE(default_ctor_profile.IsValid());
EXPECT_FALSE(default_ctor_profile.GetColorSpace().IsValid());
EXPECT_FALSE(default_ctor_profile.GetParametricColorSpace().IsValid());
EXPECT_FALSE(default_ctor_profile.GetColorSpace()
.GetParametricApproximation()
.IsValid());
}
TEST(ICCProfile, GenericRGB) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment