OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |