Commit fdc0f878 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.

Bug: 766736
Change-Id: I3b0958545a1bfd889a9b88fb8689c6382d26c781
Reviewed-on: https://chromium-review.googlesource.com/752746
Commit-Queue: ccameron <ccameron@chromium.org>
Reviewed-by: default avatarFredrik Hubinette <hubbe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#515084}
parent d2ddf502
...@@ -54,17 +54,6 @@ class ICCProfileCache { ...@@ -54,17 +54,6 @@ class ICCProfileCache {
if (!icc_profile->id_) if (!icc_profile->id_)
icc_profile->id_ = next_unused_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 entry;
entry.icc_profile = *icc_profile; entry.icc_profile = *icc_profile;
id_to_icc_profile_mru_.Put(icc_profile->id_, entry); id_to_icc_profile_mru_.Put(icc_profile->id_, entry);
...@@ -201,76 +190,59 @@ static base::LazyInstance<ICCProfileCache>::DestructorAtExit g_cache = ...@@ -201,76 +190,59 @@ static base::LazyInstance<ICCProfileCache>::DestructorAtExit g_cache =
} // namespace } // namespace
// static ICCProfile::AnalyzeResult ICCProfile::Initialize() {
ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces( // Start out with no parametric data.
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;
// Parse the profile and attempt to create a SkColorSpaceXform out of it. // Parse the profile and attempt to create a SkColorSpaceXform out of it.
sk_sp<SkColorSpace> sk_srgb_color_space = SkColorSpace::MakeSRGB(); 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) { if (!sk_icc) {
DLOG(ERROR) << "Failed to parse ICC profile to SkICC."; DLOG(ERROR) << "Failed to parse ICC profile to SkICC.";
return kICCFailedToParse; return kICCFailedToParse;
} }
sk_sp<SkColorSpace> sk_icc_color_space = sk_color_space_ = SkColorSpace::MakeICC(data_.data(), data_.size());
SkColorSpace::MakeICC(data.data(), data.size()); if (!sk_color_space_) {
if (!sk_icc_color_space) {
DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace."; DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace.";
return kICCFailedToExtractSkColorSpace; return kICCFailedToExtractSkColorSpace;
} }
std::unique_ptr<SkColorSpaceXform> sk_color_space_xform = std::unique_ptr<SkColorSpaceXform> sk_color_space_xform =
SkColorSpaceXform::New(sk_srgb_color_space.get(), SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_color_space_.get());
sk_icc_color_space.get());
if (!sk_color_space_xform) { if (!sk_color_space_xform) {
DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform."; DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform.";
return kICCFailedToCreateXform; return kICCFailedToCreateXform;
} }
// Because this SkColorSpace can be used to construct a transform, mark it // Because this SkColorSpace can be used to construct a transform, we can use
// as "useable". Mark the "best approximation" as sRGB to start. // it to create a LUT based color transform, at the very least. If we fail to
*useable_sk_color_space = sk_icc_color_space; // get any better approximation, we'll use sRGB as our approximation.
*parametric_color_space = ColorSpace::CreateSRGB(); ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_);
ColorSpace::CreateSRGB().GetTransferFunction(&transfer_fn_);
// If our SkColorSpace representation is sRGB then return that. // If our SkColorSpace representation is sRGB then return that.
if (SkColorSpace::Equals(sk_srgb_color_space.get(), if (sk_color_space_->isSRGB())
sk_icc_color_space.get())) {
return kICCExtractedSRGBColorSpace; return kICCExtractedSRGBColorSpace;
}
// 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; SkMatrix44 to_XYZD50_matrix;
if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) {
DLOG(ERROR) << "Failed to extract ICC profile primary matrix."; DLOG(ERROR) << "Failed to extract ICC profile primary matrix.";
return kICCFailedToExtractMatrix; return kICCFailedToExtractMatrix;
} }
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; SkColorSpaceTransferFn exact_tr_fn;
if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) { if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) {
*parametric_color_space = transfer_fn_ = exact_tr_fn;
gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, exact_tr_fn);
return kICCExtractedMatrixAndAnalyticTrFn; 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 // Attempt to fit a parametric transfer function to the table data in the
// profile. // profile.
SkColorSpaceTransferFn approx_tr_fn; SkColorSpaceTransferFn approx_tr_fn;
if (!SkApproximateTransferFn(sk_icc, parametric_tr_fn_max_error, if (!SkApproximateTransferFn(sk_icc, &transfer_fn_error_, &approx_tr_fn)) {
&approx_tr_fn)) {
DLOG(ERROR) << "Failed approximate transfer function."; DLOG(ERROR) << "Failed approximate transfer function.";
return kICCFailedToConvergeToApproximateTrFn; return kICCFailedToConvergeToApproximateTrFn;
} }
...@@ -278,16 +250,15 @@ ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces( ...@@ -278,16 +250,15 @@ ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces(
// If this converged, but has too high error, use the sRGB transfer function // If this converged, but has too high error, use the sRGB transfer function
// from above. // from above.
const float kMaxError = 2.f / 256.f; 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: " 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; return kICCFailedToApproximateTrFnAccurately;
}; };
// If the error is sufficiently low, declare that the approximation is // If the error is sufficiently low, declare that the approximation is
// accurate. // accurate.
*parametric_color_space = transfer_fn_ = approx_tr_fn;
gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn);
return kICCExtractedMatrixAndApproximatedTrFn; return kICCExtractedMatrixAndApproximatedTrFn;
} }
...@@ -306,18 +277,6 @@ bool ICCProfile::operator!=(const ICCProfile& other) const { ...@@ -306,18 +277,6 @@ bool ICCProfile::operator!=(const ICCProfile& other) const {
return !(*this == other); return !(*this == other);
} }
bool ICCProfile::IsValid() const {
switch (analyze_result_) {
case kICCFailedToParse:
case kICCFailedToExtractSkColorSpace:
case kICCFailedToCreateXform:
return false;
default:
break;
}
return true;
}
// static // static
ICCProfile ICCProfile::FromData(const void* data, size_t size) { ICCProfile ICCProfile::FromData(const void* data, size_t size) {
return FromDataWithId(data, size, 0); return FromDataWithId(data, size, 0);
...@@ -346,14 +305,40 @@ const std::vector<char>& ICCProfile::GetData() const { ...@@ -346,14 +305,40 @@ const std::vector<char>& ICCProfile::GetData() const {
return data_; return data_;
} }
const ColorSpace& ICCProfile::GetColorSpace() const { ColorSpace ICCProfile::GetColorSpace() const {
g_cache.Get().TouchEntry(*this); g_cache.Get().TouchEntry(*this);
return color_space_;
if (!is_valid_)
return ColorSpace();
gfx::ColorSpace color_space;
if (is_parametric_) {
color_space = GetParametricColorSpace();
color_space.icc_profile_sk_color_space_ = sk_color_space_;
} else {
color_space.matrix_ = ColorSpace::MatrixID::RGB;
color_space.range_ = ColorSpace::RangeID::FULL;
color_space.primaries_ = ColorSpace::PrimaryID::ICC_BASED;
color_space.transfer_ = ColorSpace::TransferID::ICC_BASED;
color_space.icc_profile_id_ = id_;
color_space.icc_profile_sk_color_space_ = sk_color_space_;
}
return color_space;
} }
const ColorSpace& ICCProfile::GetParametricColorSpace() const { ColorSpace ICCProfile::GetParametricColorSpace() const {
g_cache.Get().TouchEntry(*this); g_cache.Get().TouchEntry(*this);
return parametric_color_space_;
if (!is_valid_)
return ColorSpace();
ColorSpace color_space =
sk_color_space_->isSRGB()
? ColorSpace::CreateSRGB()
: ColorSpace::CreateCustom(to_XYZD50_, transfer_fn_);
if (is_parametric_)
color_space.icc_profile_id_ = id_;
return color_space;
} }
// static // static
...@@ -376,32 +361,29 @@ void ICCProfile::ComputeColorSpaceAndCache() { ...@@ -376,32 +361,29 @@ void ICCProfile::ComputeColorSpaceAndCache() {
return; return;
// Parse the ICC profile // Parse the ICC profile
sk_sp<SkColorSpace> useable_sk_color_space; analyze_result_ = Initialize();
analyze_result_ =
ExtractColorSpaces(data_, &parametric_color_space_,
&parametric_tr_fn_error_, &useable_sk_color_space);
switch (analyze_result_) { switch (analyze_result_) {
case kICCExtractedSRGBColorSpace: case kICCExtractedSRGBColorSpace:
case kICCExtractedMatrixAndAnalyticTrFn: case kICCExtractedMatrixAndAnalyticTrFn:
case kICCExtractedMatrixAndApproximatedTrFn: case kICCExtractedMatrixAndApproximatedTrFn:
// Successfully and accurately extracted color space. // Successfully and accurately extracted color space.
color_space_ = parametric_color_space_; is_valid_ = true;
is_parametric_ = true;
break; break;
case kICCFailedToExtractRawTrFn: case kICCFailedToExtractRawTrFn:
case kICCFailedToExtractMatrix: case kICCFailedToExtractMatrix:
case kICCFailedToConvergeToApproximateTrFn: case kICCFailedToConvergeToApproximateTrFn:
case kICCFailedToApproximateTrFnAccurately: case kICCFailedToApproximateTrFnAccurately:
// Successfully but extracted a color space, but it isn't accurate enough. // Successfully but extracted a color space, but it isn't accurate enough.
color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED, is_valid_ = true;
ColorSpace::TransferID::ICC_BASED); is_parametric_ = false;
color_space_.icc_profile_sk_color_space_ = useable_sk_color_space;
break; break;
case kICCFailedToParse: case kICCFailedToParse:
case kICCFailedToExtractSkColorSpace: case kICCFailedToExtractSkColorSpace:
case kICCFailedToCreateXform: case kICCFailedToCreateXform:
// Can't even use this color space as a LUT. // Can't even use this color space as a LUT.
DCHECK(!parametric_color_space_.IsValid()); is_valid_ = false;
color_space_ = parametric_color_space_; is_parametric_ = false;
break; break;
} }
...@@ -429,7 +411,7 @@ void ICCProfile::HistogramDisplay(int64_t display_id) const { ...@@ -429,7 +411,7 @@ void ICCProfile::HistogramDisplay(int64_t display_id) const {
if (nonlinear_fit_converged) { if (nonlinear_fit_converged) {
UMA_HISTOGRAM_CUSTOM_COUNTS( UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.ColorSpace.Destination.NonlinearFitError", "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);
} }
} }
......
...@@ -41,7 +41,7 @@ class COLOR_SPACE_EXPORT ICCProfile { ...@@ -41,7 +41,7 @@ class COLOR_SPACE_EXPORT ICCProfile {
bool operator!=(const ICCProfile& other) const; bool operator!=(const ICCProfile& other) const;
// Returns true if this profile was successfully parsed by SkICC. // Returns true if this profile was successfully parsed by SkICC.
bool IsValid() const; bool IsValid() const { return is_valid_; }
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
static ICCProfile FromCGColorSpace(CGColorSpaceRef cg_color_space); static ICCProfile FromCGColorSpace(CGColorSpaceRef cg_color_space);
...@@ -52,12 +52,12 @@ class COLOR_SPACE_EXPORT ICCProfile { ...@@ -52,12 +52,12 @@ class COLOR_SPACE_EXPORT ICCProfile {
// Return a ColorSpace that references this ICCProfile. ColorTransforms // Return a ColorSpace that references this ICCProfile. ColorTransforms
// created using this ColorSpace will match this ICCProfile precisely. // created using this ColorSpace will match this ICCProfile precisely.
const ColorSpace& GetColorSpace() const; ColorSpace GetColorSpace() const;
// Return a ColorSpace that is the best parametric approximation of this // Return a ColorSpace that is the best parametric approximation of this
// ICCProfile. The resulting ColorSpace will reference this ICCProfile only // ICCProfile. The resulting ColorSpace will reference this ICCProfile only
// if the parametric approximation is almost exact. // if the parametric approximation is almost exact.
const ColorSpace& GetParametricColorSpace() const; ColorSpace GetParametricColorSpace() const;
const std::vector<char>& GetData() const; const std::vector<char>& GetData() const;
...@@ -103,12 +103,7 @@ class COLOR_SPACE_EXPORT ICCProfile { ...@@ -103,12 +103,7 @@ class COLOR_SPACE_EXPORT ICCProfile {
size_t size, size_t size,
uint64_t id); uint64_t id);
static AnalyzeResult ExtractColorSpaces( AnalyzeResult Initialize();
const std::vector<char>& data,
gfx::ColorSpace* parametric_color_space,
float* parametric_tr_fn_max_error,
sk_sp<SkColorSpace>* useable_sk_color_space);
void ComputeColorSpaceAndCache(); void ComputeColorSpaceAndCache();
// This globally identifies this ICC profile. It is used to look up this ICC // This globally identifies this ICC profile. It is used to look up this ICC
...@@ -120,18 +115,27 @@ class COLOR_SPACE_EXPORT ICCProfile { ...@@ -120,18 +115,27 @@ class COLOR_SPACE_EXPORT ICCProfile {
// The result of attepting to extract a color space from the color profile. // The result of attepting to extract a color space from the color profile.
AnalyzeResult analyze_result_ = kICCFailedToParse; AnalyzeResult analyze_result_ = kICCFailedToParse;
// |color_space| always links back to this ICC profile, and its SkColorSpace // True iff we can create a valid ColorSpace (and ColorTransform) from this
// is always equal to the SkColorSpace created from this ICCProfile. // object. The transform may be LUT-based (using an SkColorSpaceXform to
gfx::ColorSpace color_space_; // compute the lut).
bool is_valid_ = false;
// True iff |to_XYZD50_| and |transfer_fn_| are accurate representations of
// the data in this profile. In this case ColorTransforms created from this
// profile will be analytic and not LUT-based.
bool is_parametric_ = false;
// Results of Skia parsing the ICC profile data.
sk_sp<SkColorSpace> sk_color_space_;
// |parametric_color_space_| will only link back to this ICC profile if it // The best-fit parametric primaries and transfer function.
// is accurate, and its SkColorSpace will always be parametrically created. SkMatrix44 to_XYZD50_;
gfx::ColorSpace parametric_color_space_; SkColorSpaceTransferFn transfer_fn_;
// The L-infinity error of the parametric color space fit. This is undefined // The L-infinity error of the parametric color space fit. This is undefined
// unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or // unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or
// kICCExtractedMatrixAndApproximatedTrFn. // kICCExtractedMatrixAndApproximatedTrFn.
float parametric_tr_fn_error_ = -1; float transfer_fn_error_ = 0;
FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, BT709toSRGBICC); FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, BT709toSRGBICC);
FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace); FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace);
......
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