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 "third_party/skia/include/core/SkICC.h" |
14 #include "ui/gfx/color_transform.h" | 13 #include "ui/gfx/color_transform.h" |
15 | 14 |
16 namespace gfx { | 15 namespace gfx { |
17 | 16 |
18 const uint64_t ICCProfile::test_id_adobe_rgb_ = 1; | 17 const uint64_t ICCProfile::test_id_adobe_rgb_ = 1; |
19 const uint64_t ICCProfile::test_id_color_spin_ = 2; | 18 const uint64_t ICCProfile::test_id_color_spin_ = 2; |
20 const uint64_t ICCProfile::test_id_generic_rgb_ = 3; | 19 const uint64_t ICCProfile::test_id_generic_rgb_ = 3; |
21 const uint64_t ICCProfile::test_id_srgb_ = 4; | 20 const uint64_t ICCProfile::test_id_srgb_ = 4; |
22 | 21 |
(...skipping 27 matching lines...) Expand all Loading... |
50 ICCProfile::~ICCProfile() = default; | 49 ICCProfile::~ICCProfile() = default; |
51 | 50 |
52 bool ICCProfile::operator==(const ICCProfile& other) const { | 51 bool ICCProfile::operator==(const ICCProfile& other) const { |
53 return data_ == other.data_; | 52 return data_ == other.data_; |
54 } | 53 } |
55 | 54 |
56 bool ICCProfile::operator!=(const ICCProfile& other) const { | 55 bool ICCProfile::operator!=(const ICCProfile& other) const { |
57 return !(*this == other); | 56 return !(*this == other); |
58 } | 57 } |
59 | 58 |
| 59 bool ICCProfile::IsValid() const { |
| 60 return successfully_parsed_by_sk_icc_; |
| 61 } |
| 62 |
60 // static | 63 // static |
61 ICCProfile ICCProfile::FromData(const void* data, size_t size) { | 64 ICCProfile ICCProfile::FromData(const void* data, size_t size) { |
62 return FromDataWithId(data, size, 0); | 65 return FromDataWithId(data, size, 0); |
63 } | 66 } |
64 | 67 |
65 // static | 68 // static |
66 ICCProfile ICCProfile::FromDataWithId(const void* data, | 69 ICCProfile ICCProfile::FromDataWithId(const void* data, |
67 size_t size, | 70 size_t size, |
68 uint64_t new_profile_id) { | 71 uint64_t new_profile_id) { |
69 if (!IsValidProfileLength(size)) { | 72 if (!IsValidProfileLength(size)) { |
(...skipping 30 matching lines...) Expand all Loading... |
100 } | 103 } |
101 | 104 |
102 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) | 105 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) |
103 // static | 106 // static |
104 ICCProfile ICCProfile::FromBestMonitor() { | 107 ICCProfile ICCProfile::FromBestMonitor() { |
105 return ICCProfile(); | 108 return ICCProfile(); |
106 } | 109 } |
107 #endif | 110 #endif |
108 | 111 |
109 // static | 112 // static |
110 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) { | |
111 if (color_space == gfx::ColorSpace()) | |
112 return ICCProfile(); | |
113 | |
114 // If |color_space| was created from an ICC profile, retrieve that exact | |
115 // profile. | |
116 if (color_space.icc_profile_id_) { | |
117 Cache& cache = g_cache.Get(); | |
118 base::AutoLock lock(cache.lock); | |
119 auto found = cache.id_to_icc_profile_mru.Get(color_space.icc_profile_id_); | |
120 if (found != cache.id_to_icc_profile_mru.end()) | |
121 return found->second; | |
122 } | |
123 | |
124 // Otherwise, construct an ICC profile based on the best approximated | |
125 // primaries and matrix. | |
126 SkMatrix44 to_XYZD50_matrix; | |
127 color_space.GetPrimaryMatrix(&to_XYZD50_matrix); | |
128 SkColorSpaceTransferFn fn; | |
129 if (!color_space.GetTransferFunction(&fn)) { | |
130 DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile."; | |
131 return ICCProfile(); | |
132 } | |
133 | |
134 sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix); | |
135 if (!data) { | |
136 DLOG(ERROR) << "Failed to create SkICC."; | |
137 return ICCProfile(); | |
138 } | |
139 | |
140 // gfx::ColorTransform assumes that this will return an empty profile for any | |
141 // color space that was not constructed from an ICC profile. | |
142 // TODO(ccameron): Fix this assumption. | |
143 // return FromData(data->data(), data->size()); | |
144 return ICCProfile(); | |
145 } | |
146 | |
147 const std::vector<char>& ICCProfile::GetData() const { | 113 const std::vector<char>& ICCProfile::GetData() const { |
148 return data_; | 114 return data_; |
149 } | 115 } |
150 | 116 |
151 const ColorSpace& ICCProfile::GetColorSpace() const { | 117 const ColorSpace& ICCProfile::GetColorSpace() const { |
152 // Move this ICC profile to the most recently used end of the cache, | 118 // Move this ICC profile to the most recently used end of the cache, |
153 // inserting if needed. | 119 // inserting if needed. |
154 if (id_) { | 120 if (id_) { |
155 Cache& cache = g_cache.Get(); | 121 Cache& cache = g_cache.Get(); |
156 base::AutoLock lock(cache.lock); | 122 base::AutoLock lock(cache.lock); |
157 auto found = cache.id_to_icc_profile_mru.Get(id_); | 123 auto found = cache.id_to_icc_profile_mru.Get(id_); |
158 if (found == cache.id_to_icc_profile_mru.end()) | 124 if (found == cache.id_to_icc_profile_mru.end()) |
159 found = cache.id_to_icc_profile_mru.Put(id_, *this); | 125 found = cache.id_to_icc_profile_mru.Put(id_, *this); |
160 } | 126 } |
161 return color_space_; | 127 return color_space_; |
162 } | 128 } |
163 | 129 |
| 130 // static |
| 131 bool ICCProfile::FromId(uint64_t id, |
| 132 bool only_if_needed, |
| 133 ICCProfile* icc_profile) { |
| 134 if (!id) |
| 135 return false; |
| 136 |
| 137 Cache& cache = g_cache.Get(); |
| 138 base::AutoLock lock(cache.lock); |
| 139 |
| 140 auto found = cache.id_to_icc_profile_mru.Get(id); |
| 141 if (found == cache.id_to_icc_profile_mru.end()) |
| 142 return false; |
| 143 |
| 144 const ICCProfile& found_icc_profile = found->second; |
| 145 if (found_icc_profile.color_space_is_accurate_ && only_if_needed) |
| 146 return false; |
| 147 |
| 148 *icc_profile = found_icc_profile; |
| 149 return true; |
| 150 } |
| 151 |
164 void ICCProfile::ComputeColorSpaceAndCache() { | 152 void ICCProfile::ComputeColorSpaceAndCache() { |
165 if (!id_) | 153 if (!id_) |
166 return; | 154 return; |
167 | 155 |
168 // If this already exists in the cache, just update its |color_space_|. | 156 // If this already exists in the cache, just update its |color_space_|. |
169 { | 157 { |
170 Cache& cache = g_cache.Get(); | 158 Cache& cache = g_cache.Get(); |
171 base::AutoLock lock(cache.lock); | 159 base::AutoLock lock(cache.lock); |
172 auto found = cache.id_to_icc_profile_mru.Get(id_); | 160 auto found = cache.id_to_icc_profile_mru.Get(id_); |
173 if (found != cache.id_to_icc_profile_mru.end()) { | 161 if (found != cache.id_to_icc_profile_mru.end()) { |
174 color_space_ = found->second.color_space_; | 162 color_space_ = found->second.color_space_; |
| 163 successfully_parsed_by_sk_icc_ = |
| 164 found->second.successfully_parsed_by_sk_icc_; |
175 return; | 165 return; |
176 } | 166 } |
177 } | 167 } |
178 | 168 |
| 169 color_space_is_accurate_ = true; |
| 170 SkMatrix44 to_XYZD50_matrix; |
| 171 SkColorSpaceTransferFn fn; |
| 172 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); |
| 173 if (sk_icc) { |
| 174 successfully_parsed_by_sk_icc_ = true; |
| 175 if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { |
| 176 // Just say that the primaries were the sRGB primaries if we can't |
| 177 // extract them. |
| 178 gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_matrix); |
| 179 color_space_is_accurate_ = false; |
| 180 DLOG(ERROR) << "Unable to handle ICCProfile primaries."; |
| 181 } |
| 182 if (!sk_icc->isNumericalTransferFn(&fn)) { |
| 183 // Just say that the transfer function was sRGB if we cannot read it. |
| 184 // TODO(ccameron): Use a least squares approximation of the transfer |
| 185 // function when it is not numerical. |
| 186 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn); |
| 187 color_space_is_accurate_ = false; |
| 188 DLOG(ERROR) << "Unable to handle ICCProfile transfer function."; |
| 189 } |
| 190 } else { |
| 191 successfully_parsed_by_sk_icc_ = false; |
| 192 gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_matrix); |
| 193 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn); |
| 194 color_space_is_accurate_ = false; |
| 195 DLOG(ERROR) << "Unable parse ICCProfile."; |
| 196 } |
| 197 |
179 // Compute the color space. | 198 // Compute the color space. |
180 color_space_ = gfx::ColorSpace( | 199 color_space_ = gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, fn); |
181 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::CUSTOM, | |
182 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); | |
183 color_space_.icc_profile_id_ = id_; | 200 color_space_.icc_profile_id_ = id_; |
184 color_space_.icc_profile_sk_color_space_ = | 201 color_space_.icc_profile_sk_color_space_ = |
185 SkColorSpace::MakeICC(data_.data(), data_.size()); | 202 SkColorSpace::MakeICC(data_.data(), data_.size()); |
186 | 203 |
187 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); | |
188 if (sk_icc) { | |
189 bool result; | |
190 SkMatrix44 to_XYZD50_matrix; | |
191 result = sk_icc->toXYZD50(&to_XYZD50_matrix); | |
192 if (result) { | |
193 for (int row = 0; row < 3; ++row) { | |
194 for (int col = 0; col < 3; ++col) { | |
195 color_space_.custom_primary_matrix_[3 * row + col] = | |
196 to_XYZD50_matrix.get(row, col); | |
197 } | |
198 } | |
199 } else { | |
200 // Just say that the primaries were the sRGB primaries if we can't | |
201 // extract them. | |
202 color_space_.primaries_ = ColorSpace::PrimaryID::BT709; | |
203 DLOG(ERROR) << "Unable to handle ICCProfile primaries."; | |
204 } | |
205 SkColorSpaceTransferFn fn; | |
206 result = sk_icc->isNumericalTransferFn(&fn); | |
207 if (result) { | |
208 color_space_.custom_transfer_params_[0] = fn.fA; | |
209 color_space_.custom_transfer_params_[1] = fn.fB; | |
210 color_space_.custom_transfer_params_[2] = fn.fC; | |
211 color_space_.custom_transfer_params_[3] = fn.fD; | |
212 color_space_.custom_transfer_params_[4] = fn.fE; | |
213 color_space_.custom_transfer_params_[5] = fn.fF; | |
214 color_space_.custom_transfer_params_[6] = fn.fG; | |
215 } else { | |
216 // Just say that the transfer function was sRGB if we cannot read it. | |
217 // TODO(ccameron): Use a least squares approximation of the transfer | |
218 // function when it is not numerical. | |
219 color_space_.transfer_ = ColorSpace::TransferID::IEC61966_2_1; | |
220 DLOG(ERROR) << "Unable to handle ICCProfile transfer function."; | |
221 } | |
222 } | |
223 | |
224 // Add to the cache. | 204 // Add to the cache. |
225 { | 205 { |
226 Cache& cache = g_cache.Get(); | 206 Cache& cache = g_cache.Get(); |
227 base::AutoLock lock(cache.lock); | 207 base::AutoLock lock(cache.lock); |
228 cache.id_to_icc_profile_mru.Put(id_, *this); | 208 cache.id_to_icc_profile_mru.Put(id_, *this); |
229 } | 209 } |
230 } | 210 } |
231 | 211 |
232 // static | 212 // static |
233 bool ICCProfile::IsValidProfileLength(size_t length) { | 213 bool ICCProfile::IsValidProfileLength(size_t length) { |
234 return length >= kMinProfileLength && length <= kMaxProfileLength; | 214 return length >= kMinProfileLength && length <= kMaxProfileLength; |
235 } | 215 } |
236 | 216 |
237 } // namespace gfx | 217 } // namespace gfx |
OLD | NEW |