Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: ui/gfx/icc_profile.cc

Issue 2742613002: color: Always use parametric color spaces for raster (Closed)
Patch Set: Incorporate review feedback Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/gfx/icc_profile.h ('k') | ui/gfx/icc_profile_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/icc_profile.h" 5 #include "ui/gfx/icc_profile.h"
6 6
7 #include <list> 7 #include <list>
8 8
9 #include "base/containers/mru_cache.h" 9 #include "base/containers/mru_cache.h"
10 #include "base/lazy_instance.h" 10 #include "base/lazy_instance.h"
11 #include "base/synchronization/lock.h" 11 #include "base/synchronization/lock.h"
12 #include "third_party/skia/include/core/SkICC.h" 12 #include "third_party/skia/include/core/SkICC.h"
13 #include "ui/gfx/color_transform.h" 13 #include "ui/gfx/color_transform.h"
14 #include "ui/gfx/skia_color_space_util.h"
14 15
15 namespace gfx { 16 namespace gfx {
16 17
17 const uint64_t ICCProfile::test_id_adobe_rgb_ = 1; 18 const uint64_t ICCProfile::test_id_adobe_rgb_ = 1;
18 const uint64_t ICCProfile::test_id_color_spin_ = 2; 19 const uint64_t ICCProfile::test_id_color_spin_ = 2;
19 const uint64_t ICCProfile::test_id_generic_rgb_ = 3; 20 const uint64_t ICCProfile::test_id_generic_rgb_ = 3;
20 const uint64_t ICCProfile::test_id_srgb_ = 4; 21 const uint64_t ICCProfile::test_id_srgb_ = 4;
21 const uint64_t ICCProfile::test_id_no_analytic_tr_fn_ = 5; 22 const uint64_t ICCProfile::test_id_no_analytic_tr_fn_ = 5;
23 const uint64_t ICCProfile::test_id_a2b_only_ = 6;
22 24
23 namespace { 25 namespace {
24 const size_t kMinProfileLength = 128;
25 const size_t kMaxProfileLength = 4 * 1024 * 1024;
26 26
27 // Allow keeping around a maximum of 8 cached ICC profiles. Beware that 27 // Allow keeping around a maximum of 8 cached ICC profiles. Beware that
28 // we will do a linear search thorugh currently-cached ICC profiles, 28 // we will do a linear search thorugh currently-cached ICC profiles,
29 // when creating a new ICC profile. 29 // when creating a new ICC profile.
30 const size_t kMaxCachedICCProfiles = 8; 30 const size_t kMaxCachedICCProfiles = 8;
31 31
32 struct Cache { 32 struct Cache {
33 Cache() : id_to_icc_profile_mru(kMaxCachedICCProfiles) {} 33 Cache() : id_to_icc_profile_mru(kMaxCachedICCProfiles) {}
34 ~Cache() {} 34 ~Cache() {}
35 35
(...skipping 28 matching lines...) Expand all
64 64
65 // static 65 // static
66 ICCProfile ICCProfile::FromData(const void* data, size_t size) { 66 ICCProfile ICCProfile::FromData(const void* data, size_t size) {
67 return FromDataWithId(data, size, 0); 67 return FromDataWithId(data, size, 0);
68 } 68 }
69 69
70 // static 70 // static
71 ICCProfile ICCProfile::FromDataWithId(const void* data, 71 ICCProfile ICCProfile::FromDataWithId(const void* data,
72 size_t size, 72 size_t size,
73 uint64_t new_profile_id) { 73 uint64_t new_profile_id) {
74 if (!IsValidProfileLength(size)) { 74 if (!size) {
75 if (size != 0) 75 DLOG(ERROR) << "Invalid empty ICC profile.";
76 DLOG(ERROR) << "Invalid ICC profile length: " << size << ".";
77 return ICCProfile(); 76 return ICCProfile();
78 } 77 }
79 78
80 const char* data_as_char = reinterpret_cast<const char*>(data); 79 const char* data_as_char = reinterpret_cast<const char*>(data);
81 { 80 {
82 // Linearly search the cached ICC profiles to find one with the same data. 81 // Linearly search the cached ICC profiles to find one with the same data.
83 // If it exists, re-use its id and touch it in the cache. 82 // If it exists, re-use its id and touch it in the cache.
84 Cache& cache = g_cache.Get(); 83 Cache& cache = g_cache.Get();
85 base::AutoLock lock(cache.lock); 84 base::AutoLock lock(cache.lock);
86 for (auto iter = cache.id_to_icc_profile_mru.begin(); 85 for (auto iter = cache.id_to_icc_profile_mru.begin();
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 if (id_) { 121 if (id_) {
123 Cache& cache = g_cache.Get(); 122 Cache& cache = g_cache.Get();
124 base::AutoLock lock(cache.lock); 123 base::AutoLock lock(cache.lock);
125 auto found = cache.id_to_icc_profile_mru.Get(id_); 124 auto found = cache.id_to_icc_profile_mru.Get(id_);
126 if (found == cache.id_to_icc_profile_mru.end()) 125 if (found == cache.id_to_icc_profile_mru.end())
127 found = cache.id_to_icc_profile_mru.Put(id_, *this); 126 found = cache.id_to_icc_profile_mru.Put(id_, *this);
128 } 127 }
129 return color_space_; 128 return color_space_;
130 } 129 }
131 130
131 const ColorSpace& ICCProfile::GetParametricColorSpace() const {
132 // Move this ICC profile to the most recently used end of the cache,
133 // inserting if needed.
134 if (id_) {
135 Cache& cache = g_cache.Get();
136 base::AutoLock lock(cache.lock);
137 auto found = cache.id_to_icc_profile_mru.Get(id_);
138 if (found == cache.id_to_icc_profile_mru.end())
139 found = cache.id_to_icc_profile_mru.Put(id_, *this);
140 }
141 return parametric_color_space_;
142 }
143
132 // static 144 // static
133 bool ICCProfile::FromId(uint64_t id, 145 bool ICCProfile::FromId(uint64_t id,
134 bool only_if_needed,
135 ICCProfile* icc_profile) { 146 ICCProfile* icc_profile) {
136 if (!id) 147 if (!id)
137 return false; 148 return false;
138 149
139 Cache& cache = g_cache.Get(); 150 Cache& cache = g_cache.Get();
140 base::AutoLock lock(cache.lock); 151 base::AutoLock lock(cache.lock);
141 152
142 auto found = cache.id_to_icc_profile_mru.Get(id); 153 auto found = cache.id_to_icc_profile_mru.Get(id);
143 if (found == cache.id_to_icc_profile_mru.end()) 154 if (found == cache.id_to_icc_profile_mru.end())
144 return false; 155 return false;
145 156
146 const ICCProfile& found_icc_profile = found->second; 157 *icc_profile = found->second;
147 if (found_icc_profile.color_space_is_accurate_ && only_if_needed)
148 return false;
149
150 *icc_profile = found_icc_profile;
151 return true; 158 return true;
152 } 159 }
153 160
154 void ICCProfile::ComputeColorSpaceAndCache() { 161 void ICCProfile::ComputeColorSpaceAndCache() {
155 if (!id_) 162 if (!id_)
156 return; 163 return;
157 164
158 // If this already exists in the cache, just update its |color_space_|. 165 // If this already exists in the cache, just update its |color_space_|.
159 { 166 {
160 Cache& cache = g_cache.Get(); 167 Cache& cache = g_cache.Get();
161 base::AutoLock lock(cache.lock); 168 base::AutoLock lock(cache.lock);
162 auto found = cache.id_to_icc_profile_mru.Get(id_); 169 auto found = cache.id_to_icc_profile_mru.Get(id_);
163 if (found != cache.id_to_icc_profile_mru.end()) { 170 if (found != cache.id_to_icc_profile_mru.end()) {
164 color_space_ = found->second.color_space_; 171 color_space_ = found->second.color_space_;
172 parametric_color_space_ = found->second.parametric_color_space_;
165 successfully_parsed_by_sk_icc_ = 173 successfully_parsed_by_sk_icc_ =
166 found->second.successfully_parsed_by_sk_icc_; 174 found->second.successfully_parsed_by_sk_icc_;
167 return; 175 return;
168 } 176 }
169 } 177 }
170 178
171 color_space_is_accurate_ = true;
172 SkMatrix44 to_XYZD50_matrix;
173 SkColorSpaceTransferFn fn;
174 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); 179 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size());
175 if (sk_icc) { 180 if (sk_icc) {
181 bool parametric_color_space_is_accurate = false;
176 successfully_parsed_by_sk_icc_ = true; 182 successfully_parsed_by_sk_icc_ = true;
177 if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { 183
178 // Just say that the primaries were the sRGB primaries if we can't 184 // Populate |parametric_color_space_| as a primary matrix and analytic
179 // extract them. 185 // transfer function, if possible.
180 gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_matrix); 186 SkMatrix44 to_XYZD50_matrix;
181 color_space_is_accurate_ = false; 187 if (sk_icc->toXYZD50(&to_XYZD50_matrix)) {
182 DLOG(ERROR) << "Unable to handle ICCProfile primaries."; 188 SkColorSpaceTransferFn fn;
189 // First try to get a numerical transfer function from the profile.
190 if (sk_icc->isNumericalTransferFn(&fn)) {
191 parametric_color_space_is_accurate = true;
192 } else {
193 // If that fails, try to approximate the transfer function.
194 float fn_max_error = 0;
195 bool got_approximate_fn =
196 SkApproximateTransferFn(sk_icc, &fn_max_error, &fn);
197 if (got_approximate_fn) {
198 float kMaxError = 3.f / 256.f;
199 if (fn_max_error < kMaxError) {
200 parametric_color_space_is_accurate = true;
201 } else {
202 DLOG(ERROR) << "ICCProfile transfer function approximation "
203 << "inexact, error: " << 256.f * fn_max_error << "/256";
204 }
205 } else {
206 // And if that fails, just say that the transfer function was sRGB.
207 DLOG(ERROR) << "Failed to approximate ICCProfile transfer function.";
208 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn);
209 }
210 }
211 parametric_color_space_ =
212 gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, fn);
213 } else {
214 DLOG(ERROR) << "Failed to extract ICCProfile primary matrix.";
215 // TODO(ccameron): Get an approximate gamut for rasterization.
216 parametric_color_space_ = gfx::ColorSpace::CreateSRGB();
183 } 217 }
184 if (!sk_icc->isNumericalTransferFn(&fn)) { 218
185 // Just say that the transfer function was sRGB if we cannot read it. 219 // If the approximation is accurate, then set |parametric_color_space_| and
186 // TODO(ccameron): Use a least squares approximation of the transfer 220 // |color_space_| to the same value, and link them to |this|. Otherwise, set
187 // function when it is not numerical. 221 // them separately, and do not link |parametric_color_space_| to |this|.
188 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn); 222 if (parametric_color_space_is_accurate) {
189 color_space_is_accurate_ = false; 223 parametric_color_space_.icc_profile_id_ = id_;
190 DLOG(ERROR) << "Unable to handle ICCProfile transfer function."; 224 color_space_ = parametric_color_space_;
225 } else {
226 color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED,
227 ColorSpace::TransferID::ICC_BASED);
228 color_space_.icc_profile_id_ = id_;
229 color_space_.icc_profile_sk_color_space_ =
230 SkColorSpace::MakeICC(data_.data(), data_.size());
191 } 231 }
192 } else { 232 } else {
233 DLOG(ERROR) << "Unable parse ICCProfile.";
193 successfully_parsed_by_sk_icc_ = false; 234 successfully_parsed_by_sk_icc_ = false;
194 gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_matrix);
195 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn);
196 color_space_is_accurate_ = false;
197 DLOG(ERROR) << "Unable parse ICCProfile.";
198 } 235 }
199 236
200 // Compute the color space.
201 color_space_ = gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, fn);
202 color_space_.icc_profile_id_ = id_;
203 color_space_.icc_profile_sk_color_space_ =
204 SkColorSpace::MakeICC(data_.data(), data_.size());
205
206 // Add to the cache. 237 // Add to the cache.
207 { 238 {
208 Cache& cache = g_cache.Get(); 239 Cache& cache = g_cache.Get();
209 base::AutoLock lock(cache.lock); 240 base::AutoLock lock(cache.lock);
210 cache.id_to_icc_profile_mru.Put(id_, *this); 241 cache.id_to_icc_profile_mru.Put(id_, *this);
211 } 242 }
212 } 243 }
213 244
214 // static
215 bool ICCProfile::IsValidProfileLength(size_t length) {
216 return length >= kMinProfileLength && length <= kMaxProfileLength;
217 }
218
219 } // namespace gfx 245 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/icc_profile.h ('k') | ui/gfx/icc_profile_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698