ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "ui/gfx/icc_profile.h" |
| 6 | |
| 7 | #include <list> |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 8 | #include <set> |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 9 | |
ccameron | 290a91a | 2017-05-13 19:22:33 | [diff] [blame] | 10 | #include "base/command_line.h" |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 11 | #include "base/containers/mru_cache.h" |
| 12 | #include "base/lazy_instance.h" |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 13 | #include "base/metrics/histogram_macros.h" |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 14 | #include "base/synchronization/lock.h" |
ccameron | b3206e2 | 2017-03-25 02:09:12 | [diff] [blame] | 15 | #include "third_party/skia/include/core/SkColorSpaceXform.h" |
ccameron | 549738b | 2017-01-28 17:39:32 | [diff] [blame] | 16 | #include "third_party/skia/include/core/SkICC.h" |
ccameron | 24c87c3 | 2017-03-14 21:50:42 | [diff] [blame] | 17 | #include "ui/gfx/skia_color_space_util.h" |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 18 | |
| 19 | namespace gfx { |
| 20 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 21 | namespace { |
| 22 | |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 23 | // An MRU cache mapping ColorSpace objects to the ICCProfile that created them. |
| 24 | // This cache serves two purposes. |
| 25 | // Purpose 1: LUT-based color transforms. |
| 26 | // For color profiles that cannot be represented analytically, this can be |
| 27 | // used to look up ICCProfile that created a ColorSpace, so that its |
| 28 | // SkColorSpace can be used to generate a LUT for a ColorTransform. |
| 29 | // Purpose 2: Specify color profiles to IOSurfaces on Mac. |
| 30 | // On Mac, IOSurfaces specify their output color space by raw ICC profile |
| 31 | // data. If the IOSurface ICC profile does not exactly match the output |
| 32 | // monitor's ICC profile, there is a substantial power cost. This structure |
| 33 | // allows us to retrieve the exact ICC profile data that produced a given |
| 34 | // ColorSpace. |
| 35 | using ProfileCacheBase = base::MRUCache<ColorSpace, ICCProfile>; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 36 | class ProfileCache : public ProfileCacheBase { |
| 37 | public: |
| 38 | static const size_t kMaxCachedICCProfiles = 16; |
| 39 | ProfileCache() : ProfileCacheBase(kMaxCachedICCProfiles) {} |
| 40 | }; |
| 41 | base::LazyInstance<ProfileCache>::DestructorAtExit g_cache = |
| 42 | LAZY_INSTANCE_INITIALIZER; |
| 43 | |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 44 | // The next id to assign to a color profile. |
| 45 | uint64_t g_next_unused_id = 1; |
| 46 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 47 | // Lock that must be held to access |g_cache| and |g_next_unused_id|. |
| 48 | base::LazyInstance<base::Lock>::DestructorAtExit g_lock = |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 49 | LAZY_INSTANCE_INITIALIZER; |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 50 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 51 | } // namespace |
| 52 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 53 | ICCProfile::Internals::AnalyzeResult ICCProfile::Internals::Initialize() { |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 54 | // Start out with no parametric data. |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 55 | |
| 56 | // Parse the profile and attempt to create a SkColorSpaceXform out of it. |
| 57 | sk_sp<SkColorSpace> sk_srgb_color_space = SkColorSpace::MakeSRGB(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 58 | sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 59 | if (!sk_icc) { |
| 60 | DLOG(ERROR) << "Failed to parse ICC profile to SkICC."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 61 | return kICCFailedToParse; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 62 | } |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 63 | sk_color_space_ = SkColorSpace::MakeICC(data_.data(), data_.size()); |
| 64 | if (!sk_color_space_) { |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 65 | DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 66 | return kICCFailedToExtractSkColorSpace; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 67 | } |
| 68 | std::unique_ptr<SkColorSpaceXform> sk_color_space_xform = |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 69 | SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_color_space_.get()); |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 70 | if (!sk_color_space_xform) { |
| 71 | DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 72 | return kICCFailedToCreateXform; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 73 | } |
| 74 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 75 | // Because this SkColorSpace can be used to construct a transform, we can use |
| 76 | // it to create a LUT based color transform, at the very least. If we fail to |
| 77 | // get any better approximation, we'll use sRGB as our approximation. |
| 78 | ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_); |
| 79 | ColorSpace::CreateSRGB().GetTransferFunction(&transfer_fn_); |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 80 | |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 81 | // If our SkColorSpace representation is sRGB then return that. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 82 | if (sk_color_space_->isSRGB()) |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 83 | return kICCExtractedSRGBColorSpace; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 84 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 85 | // A primary matrix is required for our parametric representations. Use it if |
| 86 | // it exists. |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 87 | SkMatrix44 to_XYZD50_matrix; |
| 88 | if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { |
| 89 | DLOG(ERROR) << "Failed to extract ICC profile primary matrix."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 90 | return kICCFailedToExtractMatrix; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 91 | } |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 92 | to_XYZD50_ = to_XYZD50_matrix; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 93 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 94 | // Try to directly extract a numerical transfer function. Use it if it |
| 95 | // exists. |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 96 | SkColorSpaceTransferFn exact_tr_fn; |
| 97 | if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) { |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 98 | transfer_fn_ = exact_tr_fn; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 99 | return kICCExtractedMatrixAndAnalyticTrFn; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 100 | } |
| 101 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 102 | // Attempt to fit a parametric transfer function to the table data in the |
| 103 | // profile. |
| 104 | SkColorSpaceTransferFn approx_tr_fn; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 105 | if (!SkApproximateTransferFn(sk_icc, &transfer_fn_error_, &approx_tr_fn)) { |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 106 | DLOG(ERROR) << "Failed approximate transfer function."; |
| 107 | return kICCFailedToConvergeToApproximateTrFn; |
| 108 | } |
| 109 | |
| 110 | // If this converged, but has too high error, use the sRGB transfer function |
| 111 | // from above. |
| 112 | const float kMaxError = 2.f / 256.f; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 113 | if (transfer_fn_error_ >= kMaxError) { |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 114 | DLOG(ERROR) << "Failed to accurately approximate transfer function, error: " |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 115 | << 256.f * transfer_fn_error_ << "/256"; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 116 | return kICCFailedToApproximateTrFnAccurately; |
| 117 | }; |
| 118 | |
| 119 | // If the error is sufficiently low, declare that the approximation is |
| 120 | // accurate. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 121 | transfer_fn_ = approx_tr_fn; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 122 | return kICCExtractedMatrixAndApproximatedTrFn; |
| 123 | } |
Reilly Grant | c46032eb | 2017-08-30 22:53:52 | [diff] [blame] | 124 | |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 125 | ICCProfile::ICCProfile() = default; |
| 126 | ICCProfile::ICCProfile(ICCProfile&& other) = default; |
| 127 | ICCProfile::ICCProfile(const ICCProfile& other) = default; |
| 128 | ICCProfile& ICCProfile::operator=(ICCProfile&& other) = default; |
| 129 | ICCProfile& ICCProfile::operator=(const ICCProfile& other) = default; |
| 130 | ICCProfile::~ICCProfile() = default; |
| 131 | |
| 132 | bool ICCProfile::operator==(const ICCProfile& other) const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 133 | if (!internals_ && !other.internals_) |
| 134 | return true; |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 135 | if (internals_ && other.internals_) { |
| 136 | return internals_->data_ == other.internals_->data_ && |
| 137 | internals_->id_ == other.internals_->id_; |
| 138 | } |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 139 | return false; |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 140 | } |
| 141 | |
ccameron | c21ca23b | 2017-01-20 03:34:01 | [diff] [blame] | 142 | bool ICCProfile::operator!=(const ICCProfile& other) const { |
| 143 | return !(*this == other); |
| 144 | } |
| 145 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 146 | bool ICCProfile::IsValid() const { |
| 147 | return internals_ ? internals_->is_valid_ : false; |
| 148 | } |
| 149 | |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 150 | // static |
ccameron | 549738b | 2017-01-28 17:39:32 | [diff] [blame] | 151 | ICCProfile ICCProfile::FromData(const void* data, size_t size) { |
ccameron | 9b2a0b1b | 2017-02-02 23:29:14 | [diff] [blame] | 152 | return FromDataWithId(data, size, 0); |
| 153 | } |
| 154 | |
| 155 | // static |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 156 | ICCProfile ICCProfile::FromDataWithId(const void* data_as_void, |
ccameron | 9b2a0b1b | 2017-02-02 23:29:14 | [diff] [blame] | 157 | size_t size, |
| 158 | uint64_t new_profile_id) { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 159 | const char* data_as_byte = reinterpret_cast<const char*>(data_as_void); |
| 160 | std::vector<char> new_profile_data(data_as_byte, data_as_byte + size); |
Reilly Grant | c46032eb | 2017-08-30 22:53:52 | [diff] [blame] | 161 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 162 | base::AutoLock lock(g_lock.Get()); |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 163 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 164 | // See if there is already an entry with the same data. If so, return that |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 165 | // entry. |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 166 | for (const auto& iter : g_cache.Get()) { |
| 167 | const ICCProfile& iter_profile = iter.second; |
| 168 | if (new_profile_data == iter_profile.internals_->data_) |
| 169 | return iter_profile; |
| 170 | } |
| 171 | |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 172 | scoped_refptr<Internals> internals = base::MakeRefCounted<Internals>( |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 173 | std::move(new_profile_data), new_profile_id); |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 174 | |
| 175 | ICCProfile new_profile; |
| 176 | new_profile.internals_ = internals; |
| 177 | |
| 178 | g_cache.Get().Put(new_profile.GetColorSpace(), new_profile); |
| 179 | if (internals->is_parametric_) |
| 180 | g_cache.Get().Put(new_profile.GetParametricColorSpace(), new_profile); |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 181 | return new_profile; |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 182 | } |
| 183 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 184 | ColorSpace ICCProfile::GetColorSpace() const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 185 | if (!internals_) |
| 186 | return ColorSpace(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 187 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 188 | if (!internals_->is_valid_) |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 189 | return ColorSpace(); |
| 190 | |
| 191 | gfx::ColorSpace color_space; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 192 | if (internals_->is_parametric_) { |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 193 | color_space = GetParametricColorSpace(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 194 | } else { |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 195 | // TODO(ccameron): Compute a reasonable approximation instead of always |
| 196 | // falling back to sRGB. |
| 197 | color_space = ColorSpace::CreateCustom( |
| 198 | internals_->to_XYZD50_, ColorSpace::TransferID::IEC61966_2_1); |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 199 | color_space.icc_profile_id_ = internals_->id_; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 200 | } |
| 201 | return color_space; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 202 | } |
suzyh | c3de81a | 2017-01-27 00:28:10 | [diff] [blame] | 203 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 204 | ColorSpace ICCProfile::GetParametricColorSpace() const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 205 | if (!internals_) |
| 206 | return ColorSpace(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 207 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 208 | if (!internals_->is_valid_) |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 209 | return ColorSpace(); |
| 210 | |
| 211 | ColorSpace color_space = |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 212 | internals_->sk_color_space_->isSRGB() |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 213 | ? ColorSpace::CreateSRGB() |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 214 | : ColorSpace::CreateCustom(internals_->to_XYZD50_, |
| 215 | internals_->transfer_fn_); |
| 216 | if (internals_->is_parametric_) |
| 217 | color_space.icc_profile_id_ = internals_->id_; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 218 | return color_space; |
ccameron | 24c87c3 | 2017-03-14 21:50:42 | [diff] [blame] | 219 | } |
| 220 | |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 221 | // TODO(ccameron): Change this to ICCProfile::FromColorSpace. |
| 222 | bool ColorSpace::GetICCProfile(ICCProfile* icc_profile) const { |
| 223 | if (!IsValid()) { |
| 224 | DLOG(ERROR) << "Cannot fetch ICCProfile for invalid space."; |
| 225 | return false; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 226 | } |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 227 | if (matrix_ != MatrixID::RGB) { |
| 228 | DLOG(ERROR) << "Not creating non-RGB ICCProfile"; |
| 229 | return false; |
| 230 | } |
| 231 | if (range_ != RangeID::FULL) { |
| 232 | DLOG(ERROR) << "Not creating non-full-range ICCProfile"; |
| 233 | return false; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 234 | } |
| 235 | |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 236 | // Check for an entry in the cache for this color space. |
| 237 | { |
| 238 | base::AutoLock lock(g_lock.Get()); |
| 239 | auto found = g_cache.Get().Get(*this); |
| 240 | if (found != g_cache.Get().end()) { |
| 241 | *icc_profile = found->second; |
| 242 | return true; |
| 243 | } |
| 244 | // If this was an ICC based profile and we don't have the original profile, |
| 245 | // fall through to using the inaccurate approximation. |
| 246 | if (icc_profile_id_) { |
| 247 | DLOG(ERROR) << "Failed to find id-based ColorSpace in ICCProfile cache"; |
| 248 | return false; |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | // Otherwise, construct an ICC profile based on the best approximated |
| 253 | // primaries and matrix. |
| 254 | SkMatrix44 to_XYZD50_matrix; |
| 255 | GetPrimaryMatrix(&to_XYZD50_matrix); |
| 256 | SkColorSpaceTransferFn fn; |
| 257 | if (!GetTransferFunction(&fn)) { |
| 258 | DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile."; |
| 259 | return false; |
| 260 | } |
| 261 | sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix); |
| 262 | if (!data) { |
| 263 | DLOG(ERROR) << "Failed to create SkICC."; |
| 264 | return false; |
| 265 | } |
| 266 | *icc_profile = ICCProfile::FromDataWithId(data->data(), data->size(), 0); |
| 267 | DCHECK(icc_profile->IsValid()); |
| 268 | return true; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 269 | } |
| 270 | |
| 271 | ICCProfile::Internals::Internals(std::vector<char> data, uint64_t id) |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 272 | : data_(std::move(data)), id_(id) { |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 273 | // Early out for empty entries. |
| 274 | if (data_.empty()) |
ccameron | 549738b | 2017-01-28 17:39:32 | [diff] [blame] | 275 | return; |
suzyh | c3de81a | 2017-01-27 00:28:10 | [diff] [blame] | 276 | |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 277 | // Parse the ICC profile |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 278 | analyze_result_ = Initialize(); |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 279 | switch (analyze_result_) { |
| 280 | case kICCExtractedSRGBColorSpace: |
| 281 | case kICCExtractedMatrixAndAnalyticTrFn: |
| 282 | case kICCExtractedMatrixAndApproximatedTrFn: |
| 283 | // Successfully and accurately extracted color space. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 284 | is_valid_ = true; |
| 285 | is_parametric_ = true; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 286 | break; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 287 | case kICCFailedToConvergeToApproximateTrFn: |
| 288 | case kICCFailedToApproximateTrFnAccurately: |
| 289 | // Successfully but extracted a color space, but it isn't accurate enough. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 290 | is_valid_ = true; |
| 291 | is_parametric_ = false; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 292 | break; |
Christopher Cameron | f0209199 | 2017-11-09 23:12:22 | [diff] [blame] | 293 | case kICCFailedToExtractRawTrFn: |
| 294 | case kICCFailedToExtractMatrix: |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 295 | case kICCFailedToParse: |
| 296 | case kICCFailedToExtractSkColorSpace: |
| 297 | case kICCFailedToCreateXform: |
| 298 | // Can't even use this color space as a LUT. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 299 | is_valid_ = false; |
| 300 | is_parametric_ = false; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 301 | break; |
| 302 | } |
Christopher Cameron | deea252 | 2017-11-16 07:10:52 | [diff] [blame^] | 303 | |
| 304 | if (id_) { |
| 305 | // If |id_| has been set here, then it was specified via sending an |
| 306 | // ICCProfile over IPC. Ensure that the computation of |is_valid_| and |
| 307 | // |is_parametric_| match the analysis done in the sending process. |
| 308 | DCHECK(is_valid_ && !is_parametric_); |
| 309 | } else { |
| 310 | // If this profile is not parametric, assign it an id so that we can look it |
| 311 | // up from a ColorSpace. This path should only be hit in the browser |
| 312 | // process. |
| 313 | if (is_valid_ && !is_parametric_) { |
| 314 | id_ = g_next_unused_id++; |
| 315 | } |
| 316 | } |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 317 | } |
| 318 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 319 | ICCProfile::Internals::~Internals() {} |
| 320 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 321 | void ICCProfile::HistogramDisplay(int64_t display_id) const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 322 | if (!internals_) |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 323 | return; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame] | 324 | internals_->HistogramDisplay(display_id); |
| 325 | } |
| 326 | |
| 327 | void ICCProfile::Internals::HistogramDisplay(int64_t display_id) { |
| 328 | // Ensure that we histogram this profile only once per display id. |
| 329 | if (histogrammed_display_ids_.count(display_id)) |
| 330 | return; |
| 331 | histogrammed_display_ids_.insert(display_id); |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 332 | |
| 333 | UMA_HISTOGRAM_ENUMERATION("Blink.ColorSpace.Destination.ICCResult", |
| 334 | analyze_result_, kICCProfileAnalyzeLast); |
| 335 | |
| 336 | // Add histograms for numerical approximation. |
| 337 | bool nonlinear_fit_converged = |
| 338 | analyze_result_ == kICCExtractedMatrixAndApproximatedTrFn || |
| 339 | analyze_result_ == kICCFailedToApproximateTrFnAccurately; |
| 340 | bool nonlinear_fit_did_not_converge = |
| 341 | analyze_result_ == kICCFailedToConvergeToApproximateTrFn; |
| 342 | if (nonlinear_fit_converged || nonlinear_fit_did_not_converge) { |
| 343 | UMA_HISTOGRAM_BOOLEAN("Blink.ColorSpace.Destination.NonlinearFitConverged", |
| 344 | nonlinear_fit_converged); |
| 345 | } |
| 346 | if (nonlinear_fit_converged) { |
| 347 | UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 348 | "Blink.ColorSpace.Destination.NonlinearFitError", |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 349 | static_cast<int>(transfer_fn_error_ * 255), 0, 127, 16); |
ccameron | 549738b | 2017-01-28 17:39:32 | [diff] [blame] | 350 | } |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 351 | } |
| 352 | |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 353 | } // namespace gfx |