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 | |
ccameron | 9b2a0b1b | 2017-02-02 23:29:14 | [diff] [blame] | 21 | const uint64_t ICCProfile::test_id_adobe_rgb_ = 1; |
| 22 | const uint64_t ICCProfile::test_id_color_spin_ = 2; |
| 23 | const uint64_t ICCProfile::test_id_generic_rgb_ = 3; |
| 24 | const uint64_t ICCProfile::test_id_srgb_ = 4; |
ccameron | 9b2a0b1b | 2017-02-02 23:29:14 | [diff] [blame] | 25 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 26 | namespace { |
| 27 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 28 | const uint64_t kEmptyProfileId = 5; |
| 29 | |
| 30 | // Start from-ICC-data IDs at the end of the hard-coded test id list above. |
| 31 | uint64_t g_next_unused_id = 10; |
| 32 | |
| 33 | using ProfileCacheBase = base::MRUCache<uint64_t, ICCProfile>; |
| 34 | class ProfileCache : public ProfileCacheBase { |
| 35 | public: |
| 36 | static const size_t kMaxCachedICCProfiles = 16; |
| 37 | ProfileCache() : ProfileCacheBase(kMaxCachedICCProfiles) {} |
| 38 | }; |
| 39 | base::LazyInstance<ProfileCache>::DestructorAtExit g_cache = |
| 40 | LAZY_INSTANCE_INITIALIZER; |
| 41 | |
| 42 | // Lock that must be held to access |g_cache| and |g_next_unused_id|. |
| 43 | base::LazyInstance<base::Lock>::DestructorAtExit g_lock = |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 44 | LAZY_INSTANCE_INITIALIZER; |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 45 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 46 | } // namespace |
| 47 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 48 | ICCProfile::Internals::AnalyzeResult ICCProfile::Internals::Initialize() { |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 49 | // Start out with no parametric data. |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 50 | |
| 51 | // Parse the profile and attempt to create a SkColorSpaceXform out of it. |
| 52 | sk_sp<SkColorSpace> sk_srgb_color_space = SkColorSpace::MakeSRGB(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 53 | sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 54 | if (!sk_icc) { |
| 55 | DLOG(ERROR) << "Failed to parse ICC profile to SkICC."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 56 | return kICCFailedToParse; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 57 | } |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 58 | sk_color_space_ = SkColorSpace::MakeICC(data_.data(), data_.size()); |
| 59 | if (!sk_color_space_) { |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 60 | DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 61 | return kICCFailedToExtractSkColorSpace; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 62 | } |
| 63 | std::unique_ptr<SkColorSpaceXform> sk_color_space_xform = |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 64 | SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_color_space_.get()); |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 65 | if (!sk_color_space_xform) { |
| 66 | DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 67 | return kICCFailedToCreateXform; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 68 | } |
| 69 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 70 | // Because this SkColorSpace can be used to construct a transform, we can use |
| 71 | // it to create a LUT based color transform, at the very least. If we fail to |
| 72 | // get any better approximation, we'll use sRGB as our approximation. |
| 73 | ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_); |
| 74 | ColorSpace::CreateSRGB().GetTransferFunction(&transfer_fn_); |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 75 | |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 76 | // If our SkColorSpace representation is sRGB then return that. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 77 | if (sk_color_space_->isSRGB()) |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 78 | return kICCExtractedSRGBColorSpace; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 79 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 80 | // A primary matrix is required for our parametric representations. Use it if |
| 81 | // it exists. |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 82 | SkMatrix44 to_XYZD50_matrix; |
| 83 | if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { |
| 84 | DLOG(ERROR) << "Failed to extract ICC profile primary matrix."; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 85 | return kICCFailedToExtractMatrix; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 86 | } |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 87 | to_XYZD50_ = to_XYZD50_matrix; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 88 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 89 | // Try to directly extract a numerical transfer function. Use it if it |
| 90 | // exists. |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 91 | SkColorSpaceTransferFn exact_tr_fn; |
| 92 | if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) { |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 93 | transfer_fn_ = exact_tr_fn; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 94 | return kICCExtractedMatrixAndAnalyticTrFn; |
Christopher Cameron | a4adb41b | 2017-07-13 06:44:25 | [diff] [blame] | 95 | } |
| 96 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 97 | // Attempt to fit a parametric transfer function to the table data in the |
| 98 | // profile. |
| 99 | SkColorSpaceTransferFn approx_tr_fn; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 100 | if (!SkApproximateTransferFn(sk_icc, &transfer_fn_error_, &approx_tr_fn)) { |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 101 | DLOG(ERROR) << "Failed approximate transfer function."; |
| 102 | return kICCFailedToConvergeToApproximateTrFn; |
| 103 | } |
| 104 | |
| 105 | // If this converged, but has too high error, use the sRGB transfer function |
| 106 | // from above. |
| 107 | const float kMaxError = 2.f / 256.f; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 108 | if (transfer_fn_error_ >= kMaxError) { |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 109 | DLOG(ERROR) << "Failed to accurately approximate transfer function, error: " |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 110 | << 256.f * transfer_fn_error_ << "/256"; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 111 | return kICCFailedToApproximateTrFnAccurately; |
| 112 | }; |
| 113 | |
| 114 | // If the error is sufficiently low, declare that the approximation is |
| 115 | // accurate. |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 116 | transfer_fn_ = approx_tr_fn; |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 117 | return kICCExtractedMatrixAndApproximatedTrFn; |
| 118 | } |
Reilly Grant | c46032eb | 2017-08-30 22:53:52 | [diff] [blame] | 119 | |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 120 | ICCProfile::ICCProfile() = default; |
| 121 | ICCProfile::ICCProfile(ICCProfile&& other) = default; |
| 122 | ICCProfile::ICCProfile(const ICCProfile& other) = default; |
| 123 | ICCProfile& ICCProfile::operator=(ICCProfile&& other) = default; |
| 124 | ICCProfile& ICCProfile::operator=(const ICCProfile& other) = default; |
| 125 | ICCProfile::~ICCProfile() = default; |
| 126 | |
| 127 | bool ICCProfile::operator==(const ICCProfile& other) const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 128 | if (!internals_ && !other.internals_) |
| 129 | return true; |
| 130 | if (internals_ && other.internals_) |
| 131 | return internals_->data_ == other.internals_->data_; |
| 132 | return false; |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 133 | } |
| 134 | |
ccameron | c21ca23b | 2017-01-20 03:34:01 | [diff] [blame] | 135 | bool ICCProfile::operator!=(const ICCProfile& other) const { |
| 136 | return !(*this == other); |
| 137 | } |
| 138 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 139 | bool ICCProfile::IsValid() const { |
| 140 | return internals_ ? internals_->is_valid_ : false; |
| 141 | } |
| 142 | |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 143 | // static |
ccameron | 549738b | 2017-01-28 17:39:32 | [diff] [blame] | 144 | ICCProfile ICCProfile::FromData(const void* data, size_t size) { |
ccameron | 9b2a0b1b | 2017-02-02 23:29:14 | [diff] [blame] | 145 | return FromDataWithId(data, size, 0); |
| 146 | } |
| 147 | |
| 148 | // static |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 149 | ICCProfile ICCProfile::FromDataWithId(const void* data_as_void, |
ccameron | 9b2a0b1b | 2017-02-02 23:29:14 | [diff] [blame] | 150 | size_t size, |
| 151 | uint64_t new_profile_id) { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 152 | const char* data_as_byte = reinterpret_cast<const char*>(data_as_void); |
| 153 | std::vector<char> new_profile_data(data_as_byte, data_as_byte + size); |
Reilly Grant | c46032eb | 2017-08-30 22:53:52 | [diff] [blame] | 154 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 155 | base::AutoLock lock(g_lock.Get()); |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 156 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 157 | if (new_profile_id) { |
| 158 | // If this has specified an id, see if we have an entry for it (and ensure |
| 159 | // that that entry have the same data). |
| 160 | auto found = g_cache.Get().Get(new_profile_id); |
| 161 | if (found != g_cache.Get().end()) { |
| 162 | const ICCProfile& cached_profile = found->second; |
| 163 | DCHECK(new_profile_data == cached_profile.internals_->data_); |
| 164 | return cached_profile; |
| 165 | } |
| 166 | } |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 167 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 168 | // See if there is already an entry with the same data. If so, return that |
| 169 | // entry (even if that means ignoring the profile id that we were provided). |
| 170 | for (const auto& iter : g_cache.Get()) { |
| 171 | const ICCProfile& iter_profile = iter.second; |
| 172 | if (new_profile_data == iter_profile.internals_->data_) |
| 173 | return iter_profile; |
| 174 | } |
| 175 | |
| 176 | // Create a new id for this data if one was not specified. Always ensure that |
| 177 | // the emptry profile have the same id. |
| 178 | if (!new_profile_id) { |
| 179 | if (size == 0) |
| 180 | new_profile_id = kEmptyProfileId; |
| 181 | else |
| 182 | new_profile_id = g_next_unused_id++; |
| 183 | } |
| 184 | |
| 185 | // Create a new profile for this data. |
| 186 | ICCProfile new_profile; |
| 187 | new_profile.internals_ = base::MakeRefCounted<Internals>( |
| 188 | std::move(new_profile_data), new_profile_id); |
| 189 | g_cache.Get().Put(new_profile_id, new_profile); |
| 190 | return new_profile; |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 191 | } |
| 192 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 193 | ColorSpace ICCProfile::GetColorSpace() const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 194 | if (!internals_) |
| 195 | return ColorSpace(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 196 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 197 | TouchCacheEntry(); |
| 198 | |
| 199 | if (!internals_->is_valid_) |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 200 | return ColorSpace(); |
| 201 | |
| 202 | gfx::ColorSpace color_space; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 203 | if (internals_->is_parametric_) { |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 204 | color_space = GetParametricColorSpace(); |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 205 | color_space.icc_profile_sk_color_space_ = internals_->sk_color_space_; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 206 | } else { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 207 | color_space = ColorSpace::CreateCustom(internals_->to_XYZD50_, |
| 208 | ColorSpace::TransferID::ICC_BASED); |
| 209 | color_space.icc_profile_id_ = internals_->id_; |
| 210 | color_space.icc_profile_sk_color_space_ = internals_->sk_color_space_; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 211 | } |
| 212 | return color_space; |
ccameron | e35e232 | 2017-11-02 23:33:46 | [diff] [blame] | 213 | } |
suzyh | c3de81a | 2017-01-27 00:28:10 | [diff] [blame] | 214 | |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 215 | ColorSpace ICCProfile::GetParametricColorSpace() const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 216 | if (!internals_) |
| 217 | return ColorSpace(); |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 218 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 219 | TouchCacheEntry(); |
| 220 | |
| 221 | if (!internals_->is_valid_) |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 222 | return ColorSpace(); |
| 223 | |
| 224 | ColorSpace color_space = |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 225 | internals_->sk_color_space_->isSRGB() |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 226 | ? ColorSpace::CreateSRGB() |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 227 | : ColorSpace::CreateCustom(internals_->to_XYZD50_, |
| 228 | internals_->transfer_fn_); |
| 229 | if (internals_->is_parametric_) |
| 230 | color_space.icc_profile_id_ = internals_->id_; |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 231 | return color_space; |
ccameron | 24c87c3 | 2017-03-14 21:50:42 | [diff] [blame] | 232 | } |
| 233 | |
ccameron | 3b6fe6d | 2017-02-17 06:21:58 | [diff] [blame] | 234 | // static |
| 235 | bool ICCProfile::FromId(uint64_t id, |
ccameron | 3b6fe6d | 2017-02-17 06:21:58 | [diff] [blame] | 236 | ICCProfile* icc_profile) { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 237 | base::AutoLock lock(g_lock.Get()); |
| 238 | |
| 239 | auto found = g_cache.Get().Get(id); |
| 240 | if (found != g_cache.Get().end()) { |
| 241 | *icc_profile = found->second; |
| 242 | return true; |
| 243 | } |
| 244 | *icc_profile = ICCProfile(); |
| 245 | return false; |
ccameron | 3b6fe6d | 2017-02-17 06:21:58 | [diff] [blame] | 246 | } |
| 247 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 248 | void ICCProfile::TouchCacheEntry() const { |
| 249 | if (!internals_) |
| 250 | return; |
| 251 | base::AutoLock lock(g_lock.Get()); |
| 252 | |
| 253 | // Query for an existing cache entry. |
| 254 | auto found = g_cache.Get().Get(internals_->id_); |
| 255 | if (found != g_cache.Get().end()) |
| 256 | return; |
| 257 | |
| 258 | // Check if there are any cache entries with the same data, and refuse to add |
| 259 | // a duplicate entry with the same data but a different id. |
| 260 | // TODO(ccameron): This is a bit odd, but this preserves existing behavior. |
| 261 | for (const auto& iter : g_cache.Get()) { |
| 262 | const ICCProfile& iter_profile = iter.second; |
| 263 | if (internals_->data_ == iter_profile.internals_->data_) |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | // Insert a new cache entry if none existed. |
| 268 | g_cache.Get().Put(internals_->id_, *this); |
| 269 | } |
| 270 | |
| 271 | ICCProfile::Internals::Internals(std::vector<char> data, uint64_t id) |
| 272 | : id_(id), data_(std::move(data)) { |
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 | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 303 | } |
| 304 | |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 305 | ICCProfile::Internals::~Internals() {} |
| 306 | |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 307 | void ICCProfile::HistogramDisplay(int64_t display_id) const { |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 308 | if (!internals_) |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 309 | return; |
Christopher Cameron | 43f1705 | 2017-11-11 01:52:01 | [diff] [blame^] | 310 | internals_->HistogramDisplay(display_id); |
| 311 | } |
| 312 | |
| 313 | void ICCProfile::Internals::HistogramDisplay(int64_t display_id) { |
| 314 | // Ensure that we histogram this profile only once per display id. |
| 315 | if (histogrammed_display_ids_.count(display_id)) |
| 316 | return; |
| 317 | histogrammed_display_ids_.insert(display_id); |
Christopher Cameron | 374b6c4 | 2017-08-31 22:21:23 | [diff] [blame] | 318 | |
| 319 | UMA_HISTOGRAM_ENUMERATION("Blink.ColorSpace.Destination.ICCResult", |
| 320 | analyze_result_, kICCProfileAnalyzeLast); |
| 321 | |
| 322 | // Add histograms for numerical approximation. |
| 323 | bool nonlinear_fit_converged = |
| 324 | analyze_result_ == kICCExtractedMatrixAndApproximatedTrFn || |
| 325 | analyze_result_ == kICCFailedToApproximateTrFnAccurately; |
| 326 | bool nonlinear_fit_did_not_converge = |
| 327 | analyze_result_ == kICCFailedToConvergeToApproximateTrFn; |
| 328 | if (nonlinear_fit_converged || nonlinear_fit_did_not_converge) { |
| 329 | UMA_HISTOGRAM_BOOLEAN("Blink.ColorSpace.Destination.NonlinearFitConverged", |
| 330 | nonlinear_fit_converged); |
| 331 | } |
| 332 | if (nonlinear_fit_converged) { |
| 333 | UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 334 | "Blink.ColorSpace.Destination.NonlinearFitError", |
Christopher Cameron | fdc0f87 | 2017-11-09 03:22:16 | [diff] [blame] | 335 | static_cast<int>(transfer_fn_error_ * 255), 0, 127, 16); |
ccameron | 549738b | 2017-01-28 17:39:32 | [diff] [blame] | 336 | } |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 337 | } |
| 338 | |
ccameron | efdab16 | 2016-07-25 23:00:02 | [diff] [blame] | 339 | } // namespace gfx |