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

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

Issue 2652503002: Use SkICC in gfx::ICCProfile and gfx::ColorSpace (Closed)
Patch Set: Clean up negatives Created 3 years, 11 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
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/SkData.h"
13 #include "third_party/skia/include/core/SkICC.h"
12 #include "ui/gfx/color_transform.h" 14 #include "ui/gfx/color_transform.h"
13 15
14 namespace gfx { 16 namespace gfx {
15 17
16 namespace { 18 namespace {
17 const size_t kMinProfileLength = 128; 19 const size_t kMinProfileLength = 128;
18 const size_t kMaxProfileLength = 4 * 1024 * 1024; 20 const size_t kMaxProfileLength = 4 * 1024 * 1024;
19 21
20 // Allow keeping around a maximum of 8 cached ICC profiles. Beware that 22 // Allow keeping around a maximum of 8 cached ICC profiles. Beware that
21 // we will do a linear search thorugh currently-cached ICC profiles, 23 // we will do a linear search thorugh currently-cached ICC profiles,
(...skipping 14 matching lines...) Expand all
36 } // namespace 38 } // namespace
37 39
38 ICCProfile::ICCProfile() = default; 40 ICCProfile::ICCProfile() = default;
39 ICCProfile::ICCProfile(ICCProfile&& other) = default; 41 ICCProfile::ICCProfile(ICCProfile&& other) = default;
40 ICCProfile::ICCProfile(const ICCProfile& other) = default; 42 ICCProfile::ICCProfile(const ICCProfile& other) = default;
41 ICCProfile& ICCProfile::operator=(ICCProfile&& other) = default; 43 ICCProfile& ICCProfile::operator=(ICCProfile&& other) = default;
42 ICCProfile& ICCProfile::operator=(const ICCProfile& other) = default; 44 ICCProfile& ICCProfile::operator=(const ICCProfile& other) = default;
43 ICCProfile::~ICCProfile() = default; 45 ICCProfile::~ICCProfile() = default;
44 46
45 bool ICCProfile::operator==(const ICCProfile& other) const { 47 bool ICCProfile::operator==(const ICCProfile& other) const {
46 if (type_ != other.type_) 48 return data_ == data_;
47 return false;
48 switch (type_) {
49 case Type::INVALID:
50 return true;
51 case Type::FROM_COLOR_SPACE:
52 return color_space_ == other.color_space_;
53 case Type::FROM_DATA:
54 return data_ == other.data_;
55 }
56 return false;
57 } 49 }
58 50
59 bool ICCProfile::operator!=(const ICCProfile& other) const { 51 bool ICCProfile::operator!=(const ICCProfile& other) const {
60 return !(*this == other); 52 return data_ != data_;
dcheng 2017/01/23 23:26:46 Nit: probably best to leave it defined in terms of
ccameron 2017/01/24 07:09:57 Good point -- done.
61 } 53 }
62 54
63 // static 55 // static
64 ICCProfile ICCProfile::FromData(const char* data, size_t size) { 56 ICCProfile ICCProfile::FromData(const void* data, size_t size) {
65 ICCProfile icc_profile; 57 if (!IsValidProfileLength(size)) {
66 if (IsValidProfileLength(size)) { 58 if (size != 0)
67 icc_profile.type_ = Type::FROM_DATA; 59 DLOG(ERROR) << "Invalid ICC profile length: " << size << ".";
68 icc_profile.data_.insert(icc_profile.data_.begin(), data, data + size);
69 } else {
70 return ICCProfile(); 60 return ICCProfile();
71 } 61 }
72 62
73 Cache& cache = g_cache.Get(); 63 uint64_t new_profile_id = 0;
74 base::AutoLock lock(cache.lock); 64 const char* data_as_char = reinterpret_cast<const char*>(data);
75 65 {
76 // Linearly search the cached ICC profiles to find one with the same data. 66 // Linearly search the cached ICC profiles to find one with the same data.
77 // If it exists, re-use its id and touch it in the cache. 67 // If it exists, re-use its id and touch it in the cache.
78 for (auto iter = cache.id_to_icc_profile_mru.begin(); 68 Cache& cache = g_cache.Get();
79 iter != cache.id_to_icc_profile_mru.end(); ++iter) { 69 base::AutoLock lock(cache.lock);
80 if (icc_profile.data_ == iter->second.data_) { 70 for (auto iter = cache.id_to_icc_profile_mru.begin();
81 icc_profile = iter->second; 71 iter != cache.id_to_icc_profile_mru.end(); ++iter) {
82 cache.id_to_icc_profile_mru.Get(icc_profile.id_); 72 const std::vector<char>& iter_data = iter->second.data_;
83 return icc_profile; 73 if (iter_data.size() != size || memcmp(data, iter_data.data(), size))
74 continue;
75 auto found = cache.id_to_icc_profile_mru.Get(iter->second.id_);
76 return found->second;
84 } 77 }
78 new_profile_id = cache.next_unused_id++;
85 } 79 }
86 80
87 // Create a new cached id and add it to the cache. 81 // Create a new cached id and add it to the cache.
88 icc_profile.id_ = cache.next_unused_id++; 82 ICCProfile icc_profile;
89 icc_profile.color_space_ = 83 icc_profile.id_ = new_profile_id;
90 ColorSpace(ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::CUSTOM, 84 icc_profile.data_.insert(icc_profile.data_.begin(), data_as_char,
91 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); 85 data_as_char + size);
92 icc_profile.color_space_.icc_profile_id_ = icc_profile.id_; 86 icc_profile.ComputeColorSpaceAndCache();
93 icc_profile.color_space_.sk_color_space_ = SkColorSpace::MakeICC(data, size);
94 cache.id_to_icc_profile_mru.Put(icc_profile.id_, icc_profile);
95 return icc_profile; 87 return icc_profile;
96 } 88 }
97 89
98 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) 90 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11)
99 // static 91 // static
100 ICCProfile ICCProfile::FromBestMonitor() { 92 ICCProfile ICCProfile::FromBestMonitor() {
101 return ICCProfile(); 93 return ICCProfile();
102 } 94 }
103 #endif 95 #endif
104 96
105 // static 97 // static
106 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) { 98 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) {
107 if (color_space == gfx::ColorSpace()) 99 if (color_space == gfx::ColorSpace())
108 return ICCProfile(); 100 return ICCProfile();
109 101
110 // If |color_space| was created from an ICC profile, retrieve that exact 102 // If |color_space| was created from an ICC profile, retrieve that exact
111 // profile. 103 // profile.
112 if (color_space.icc_profile_id_) { 104 if (color_space.icc_profile_id_) {
113 Cache& cache = g_cache.Get(); 105 Cache& cache = g_cache.Get();
114 base::AutoLock lock(cache.lock); 106 base::AutoLock lock(cache.lock);
115
116 auto found = cache.id_to_icc_profile_mru.Get(color_space.icc_profile_id_); 107 auto found = cache.id_to_icc_profile_mru.Get(color_space.icc_profile_id_);
117 if (found != cache.id_to_icc_profile_mru.end()) { 108 if (found != cache.id_to_icc_profile_mru.end())
118 return found->second; 109 return found->second;
119 }
120 } 110 }
121 111
122 // TODO(ccameron): Support constructing ICC profiles from arbitrary ColorSpace 112 // Otherwise, construct an ICC profile based on the best approximated
123 // objects. 113 // primaries and matrix.
124 ICCProfile icc_profile; 114 SkMatrix44 to_XYZD50_matrix;
125 icc_profile.type_ = gfx::ICCProfile::Type::FROM_COLOR_SPACE; 115 if (!color_space.GetPrimaryMatrix(&to_XYZD50_matrix)) {
126 icc_profile.color_space_ = color_space; 116 DLOG(ERROR) << "Failed to get ColorSpace primary matrix for ICCProfile.";
127 return icc_profile; 117 return ICCProfile();
118 }
119
120 SkColorSpaceTransferFn fn;
121 if (!color_space.GetTransferFunction(&fn)) {
122 DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile.";
123 return ICCProfile();
124 }
125
126 sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix);
127 if (!data) {
128 DLOG(ERROR) << "Failed to create SkICC.";
129 return ICCProfile();
130 }
131
132 return FromData(data->data(), data->size());
128 } 133 }
129 134
130 ICCProfile ICCProfile::FromSkColorSpace(sk_sp<SkColorSpace> color_space) { 135 ICCProfile ICCProfile::FromSkColorSpace(sk_sp<SkColorSpace> color_space) {
131 ICCProfile icc_profile; 136 ICCProfile icc_profile;
132 137
133 Cache& cache = g_cache.Get(); 138 Cache& cache = g_cache.Get();
134 base::AutoLock lock(cache.lock); 139 base::AutoLock lock(cache.lock);
135 140
136 // Linearly search the cached ICC profiles to find one with the same data. 141 // Linearly search the cached ICC profiles to find one with the same data.
137 // If it exists, re-use its id and touch it in the cache. 142 // If it exists, re-use its id and touch it in the cache.
138 for (auto iter = cache.id_to_icc_profile_mru.begin(); 143 for (auto iter = cache.id_to_icc_profile_mru.begin();
139 iter != cache.id_to_icc_profile_mru.end(); ++iter) { 144 iter != cache.id_to_icc_profile_mru.end(); ++iter) {
140 sk_sp<SkColorSpace> iter_color_space = 145 sk_sp<SkColorSpace> iter_color_space =
141 iter->second.color_space_.ToSkColorSpace(); 146 iter->second.color_space_.ToSkColorSpace();
142 if (SkColorSpace::Equals(color_space.get(), iter_color_space.get())) { 147 if (SkColorSpace::Equals(color_space.get(), iter_color_space.get())) {
143 icc_profile = iter->second; 148 icc_profile = iter->second;
144 cache.id_to_icc_profile_mru.Get(icc_profile.id_); 149 cache.id_to_icc_profile_mru.Get(icc_profile.id_);
145 return icc_profile; 150 return icc_profile;
146 } 151 }
147 } 152 }
148 153
149 // TODO(ccameron): Support constructing ICC profiles from arbitrary 154 // TODO(ccameron): Support constructing ICC profiles from arbitrary
150 // SkColorSpace objects. 155 // SkColorSpace objects.
156 DLOG(ERROR) << "Failed to find ICC profile matching SkColorSpace.";
151 return icc_profile; 157 return icc_profile;
152 } 158 }
153 159
154 const std::vector<char>& ICCProfile::GetData() const { 160 const std::vector<char>& ICCProfile::GetData() const {
155 return data_; 161 return data_;
156 } 162 }
157 163
158 ColorSpace ICCProfile::GetColorSpace() const { 164 const ColorSpace& ICCProfile::GetColorSpace() const {
159 if (type_ == Type::INVALID) 165 // Move this ICC profile to the most recently used end of the cache,
160 return gfx::ColorSpace(); 166 // inserting if needed.
161 if (type_ == Type::FROM_COLOR_SPACE) 167 if (id_) {
162 return color_space_; 168 Cache& cache = g_cache.Get();
169 base::AutoLock lock(cache.lock);
170 auto found = cache.id_to_icc_profile_mru.Get(id_);
171 if (found == cache.id_to_icc_profile_mru.end())
172 found = cache.id_to_icc_profile_mru.Put(id_, *this);
173 }
174 return color_space_;
175 }
163 176
164 ColorSpace color_space = color_space_; 177 void ICCProfile::ComputeColorSpaceAndCache() {
178 if (!id_)
179 return;
165 180
166 // Move this ICC profile to the most recently used end of the cache. 181 // If this already exists in the cache, just update its |color_space_|.
167 { 182 {
168 Cache& cache = g_cache.Get(); 183 Cache& cache = g_cache.Get();
169 base::AutoLock lock(cache.lock); 184 base::AutoLock lock(cache.lock);
170
171 auto found = cache.id_to_icc_profile_mru.Get(id_); 185 auto found = cache.id_to_icc_profile_mru.Get(id_);
172 if (found == cache.id_to_icc_profile_mru.end()) 186 if (found != cache.id_to_icc_profile_mru.end()) {
173 cache.id_to_icc_profile_mru.Put(id_, *this); 187 color_space_ = found->second.color_space_;
188 return;
189 }
174 } 190 }
175 191
176 ColorSpace unity_colorspace( 192 // Compute the color space.
177 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::LINEAR, 193 color_space_ = gfx::ColorSpace(
194 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::CUSTOM,
178 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); 195 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
179 unity_colorspace.custom_primary_matrix_[0] = 1.0f; 196 color_space_.icc_profile_id_ = id_;
180 unity_colorspace.custom_primary_matrix_[1] = 0.0f; 197 color_space_.sk_color_space_ =
181 unity_colorspace.custom_primary_matrix_[2] = 0.0f; 198 SkColorSpace::MakeICC(data_.data(), data_.size());
182 unity_colorspace.custom_primary_matrix_[3] = 0.0f;
183 199
184 unity_colorspace.custom_primary_matrix_[4] = 0.0f; 200 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size());
185 unity_colorspace.custom_primary_matrix_[5] = 1.0f; 201 if (sk_icc) {
186 unity_colorspace.custom_primary_matrix_[6] = 0.0f; 202 bool result;
187 unity_colorspace.custom_primary_matrix_[7] = 0.0f; 203 SkMatrix44 to_XYZD50_matrix;
204 result = sk_icc->toXYZD50(&to_XYZD50_matrix);
205 if (result) {
206 for (int row = 0; row < 3; ++row) {
207 for (int col = 0; col < 3; ++col) {
208 color_space_.custom_primary_matrix_[3 * row + col] =
209 to_XYZD50_matrix.get(row, col);
210 }
211 }
212 } else {
213 // Just say that the primaries were the sRGB primaries if we can't
214 // extract them.
215 color_space_.primaries_ = ColorSpace::PrimaryID::BT709;
216 DLOG(ERROR) << "Unable to handle ICCProfile primaries.";
217 }
218 SkColorSpaceTransferFn fn;
219 result = sk_icc->isNumericalTransferFn(&fn);
220 if (result) {
221 color_space_.custom_transfer_params_[0] = fn.fA;
222 color_space_.custom_transfer_params_[1] = fn.fB;
223 color_space_.custom_transfer_params_[2] = fn.fC;
224 color_space_.custom_transfer_params_[3] = fn.fD;
225 color_space_.custom_transfer_params_[4] = fn.fE;
226 color_space_.custom_transfer_params_[5] = fn.fF;
227 color_space_.custom_transfer_params_[6] = fn.fG;
228 } else {
229 // Just say that the transfer function was sRGB if we cannot read it.
230 // TODO(ccameron): Use a least squares approximation of the transfer
231 // function when it is not numerical.
232 color_space_.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
233 DLOG(ERROR) << "Unable to handle ICCProfile transfer function.";
234 }
235 }
188 236
189 unity_colorspace.custom_primary_matrix_[8] = 0.0f; 237 // Add to the cache.
190 unity_colorspace.custom_primary_matrix_[9] = 0.0f; 238 {
191 unity_colorspace.custom_primary_matrix_[10] = 1.0f; 239 Cache& cache = g_cache.Get();
192 unity_colorspace.custom_primary_matrix_[11] = 0.0f; 240 base::AutoLock lock(cache.lock);
193 241 cache.id_to_icc_profile_mru.Put(id_, *this);
194 // This will look up and use the ICC profile. 242 }
195 std::unique_ptr<ColorTransform> transform(ColorTransform::NewColorTransform(
196 color_space, unity_colorspace, ColorTransform::Intent::INTENT_ABSOLUTE));
197
198 ColorTransform::TriStim tmp[4];
199 tmp[0].set_x(1.0f);
200 tmp[1].set_y(1.0f);
201 tmp[2].set_z(1.0f);
202 transform->transform(tmp, arraysize(tmp));
203
204 color_space.custom_primary_matrix_[0] = tmp[0].x() - tmp[3].x();
205 color_space.custom_primary_matrix_[1] = tmp[1].x() - tmp[3].x();
206 color_space.custom_primary_matrix_[2] = tmp[2].x() - tmp[3].x();
207 color_space.custom_primary_matrix_[3] = tmp[3].x();
208
209 color_space.custom_primary_matrix_[4] = tmp[0].y() - tmp[3].y();
210 color_space.custom_primary_matrix_[5] = tmp[1].y() - tmp[3].y();
211 color_space.custom_primary_matrix_[6] = tmp[2].y() - tmp[3].y();
212 color_space.custom_primary_matrix_[7] = tmp[3].y();
213
214 color_space.custom_primary_matrix_[8] = tmp[0].z() - tmp[3].z();
215 color_space.custom_primary_matrix_[9] = tmp[1].z() - tmp[3].z();
216 color_space.custom_primary_matrix_[10] = tmp[2].z() - tmp[3].z();
217 color_space.custom_primary_matrix_[11] = tmp[3].z();
218
219 return color_space;
220 } 243 }
221 244
222 // static 245 // static
223 bool ICCProfile::IsValidProfileLength(size_t length) { 246 bool ICCProfile::IsValidProfileLength(size_t length) {
224 return length >= kMinProfileLength && length <= kMaxProfileLength; 247 return length >= kMinProfileLength && length <= kMaxProfileLength;
225 } 248 }
226 249
227 } // namespace gfx 250 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698