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" | |
14 #include "ui/gfx/color_transform.h" | 12 #include "ui/gfx/color_transform.h" |
15 | 13 |
16 namespace gfx { | 14 namespace gfx { |
17 | 15 |
18 namespace { | 16 namespace { |
19 const size_t kMinProfileLength = 128; | 17 const size_t kMinProfileLength = 128; |
20 const size_t kMaxProfileLength = 4 * 1024 * 1024; | 18 const size_t kMaxProfileLength = 4 * 1024 * 1024; |
21 | 19 |
22 // Allow keeping around a maximum of 8 cached ICC profiles. Beware that | 20 // Allow keeping around a maximum of 8 cached ICC profiles. Beware that |
23 // we will do a linear search thorugh currently-cached ICC profiles, | 21 // we will do a linear search thorugh currently-cached ICC profiles, |
(...skipping 14 matching lines...) Expand all Loading... |
38 } // namespace | 36 } // namespace |
39 | 37 |
40 ICCProfile::ICCProfile() = default; | 38 ICCProfile::ICCProfile() = default; |
41 ICCProfile::ICCProfile(ICCProfile&& other) = default; | 39 ICCProfile::ICCProfile(ICCProfile&& other) = default; |
42 ICCProfile::ICCProfile(const ICCProfile& other) = default; | 40 ICCProfile::ICCProfile(const ICCProfile& other) = default; |
43 ICCProfile& ICCProfile::operator=(ICCProfile&& other) = default; | 41 ICCProfile& ICCProfile::operator=(ICCProfile&& other) = default; |
44 ICCProfile& ICCProfile::operator=(const ICCProfile& other) = default; | 42 ICCProfile& ICCProfile::operator=(const ICCProfile& other) = default; |
45 ICCProfile::~ICCProfile() = default; | 43 ICCProfile::~ICCProfile() = default; |
46 | 44 |
47 bool ICCProfile::operator==(const ICCProfile& other) const { | 45 bool ICCProfile::operator==(const ICCProfile& other) const { |
48 return data_ == data_; | 46 if (type_ != other.type_) |
| 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; |
49 } | 57 } |
50 | 58 |
51 bool ICCProfile::operator!=(const ICCProfile& other) const { | 59 bool ICCProfile::operator!=(const ICCProfile& other) const { |
52 return !(*this == other); | 60 return !(*this == other); |
53 } | 61 } |
54 | 62 |
55 // static | 63 // static |
56 ICCProfile ICCProfile::FromData(const void* data, size_t size) { | 64 ICCProfile ICCProfile::FromData(const char* data, size_t size) { |
57 if (!IsValidProfileLength(size)) { | 65 ICCProfile icc_profile; |
58 if (size != 0) | 66 if (IsValidProfileLength(size)) { |
59 DLOG(ERROR) << "Invalid ICC profile length: " << size << "."; | 67 icc_profile.type_ = Type::FROM_DATA; |
| 68 icc_profile.data_.insert(icc_profile.data_.begin(), data, data + size); |
| 69 } else { |
60 return ICCProfile(); | 70 return ICCProfile(); |
61 } | 71 } |
62 | 72 |
63 uint64_t new_profile_id = 0; | 73 Cache& cache = g_cache.Get(); |
64 const char* data_as_char = reinterpret_cast<const char*>(data); | 74 base::AutoLock lock(cache.lock); |
65 { | 75 |
66 // Linearly search the cached ICC profiles to find one with the same data. | 76 // Linearly search the cached ICC profiles to find one with the same data. |
67 // If it exists, re-use its id and touch it in the cache. | 77 // If it exists, re-use its id and touch it in the cache. |
68 Cache& cache = g_cache.Get(); | 78 for (auto iter = cache.id_to_icc_profile_mru.begin(); |
69 base::AutoLock lock(cache.lock); | 79 iter != cache.id_to_icc_profile_mru.end(); ++iter) { |
70 for (auto iter = cache.id_to_icc_profile_mru.begin(); | 80 if (icc_profile.data_ == iter->second.data_) { |
71 iter != cache.id_to_icc_profile_mru.end(); ++iter) { | 81 icc_profile = iter->second; |
72 const std::vector<char>& iter_data = iter->second.data_; | 82 cache.id_to_icc_profile_mru.Get(icc_profile.id_); |
73 if (iter_data.size() != size || memcmp(data, iter_data.data(), size)) | 83 return icc_profile; |
74 continue; | |
75 auto found = cache.id_to_icc_profile_mru.Get(iter->second.id_); | |
76 return found->second; | |
77 } | 84 } |
78 new_profile_id = cache.next_unused_id++; | |
79 } | 85 } |
80 | 86 |
81 // Create a new cached id and add it to the cache. | 87 // Create a new cached id and add it to the cache. |
82 ICCProfile icc_profile; | 88 icc_profile.id_ = cache.next_unused_id++; |
83 icc_profile.id_ = new_profile_id; | 89 icc_profile.color_space_ = |
84 icc_profile.data_.insert(icc_profile.data_.begin(), data_as_char, | 90 ColorSpace(ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::CUSTOM, |
85 data_as_char + size); | 91 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
86 icc_profile.ComputeColorSpaceAndCache(); | 92 icc_profile.color_space_.icc_profile_id_ = icc_profile.id_; |
| 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); |
87 return icc_profile; | 95 return icc_profile; |
88 } | 96 } |
89 | 97 |
90 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) | 98 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) |
91 // static | 99 // static |
92 ICCProfile ICCProfile::FromBestMonitor() { | 100 ICCProfile ICCProfile::FromBestMonitor() { |
93 return ICCProfile(); | 101 return ICCProfile(); |
94 } | 102 } |
95 #endif | 103 #endif |
96 | 104 |
97 // static | 105 // static |
98 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) { | 106 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) { |
99 if (color_space == gfx::ColorSpace()) | 107 if (color_space == gfx::ColorSpace()) |
100 return ICCProfile(); | 108 return ICCProfile(); |
101 | 109 |
102 // If |color_space| was created from an ICC profile, retrieve that exact | 110 // If |color_space| was created from an ICC profile, retrieve that exact |
103 // profile. | 111 // profile. |
104 if (color_space.icc_profile_id_) { | 112 if (color_space.icc_profile_id_) { |
105 Cache& cache = g_cache.Get(); | 113 Cache& cache = g_cache.Get(); |
106 base::AutoLock lock(cache.lock); | 114 base::AutoLock lock(cache.lock); |
| 115 |
107 auto found = cache.id_to_icc_profile_mru.Get(color_space.icc_profile_id_); | 116 auto found = cache.id_to_icc_profile_mru.Get(color_space.icc_profile_id_); |
108 if (found != cache.id_to_icc_profile_mru.end()) | 117 if (found != cache.id_to_icc_profile_mru.end()) { |
109 return found->second; | 118 return found->second; |
| 119 } |
110 } | 120 } |
111 | 121 |
112 // Otherwise, construct an ICC profile based on the best approximated | 122 // TODO(ccameron): Support constructing ICC profiles from arbitrary ColorSpace |
113 // primaries and matrix. | 123 // objects. |
114 SkMatrix44 to_XYZD50_matrix; | 124 ICCProfile icc_profile; |
115 color_space.GetPrimaryMatrix(&to_XYZD50_matrix); | 125 icc_profile.type_ = gfx::ICCProfile::Type::FROM_COLOR_SPACE; |
116 SkColorSpaceTransferFn fn; | 126 icc_profile.color_space_ = color_space; |
117 if (!color_space.GetTransferFunction(&fn)) { | 127 return icc_profile; |
118 DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile."; | |
119 return ICCProfile(); | |
120 } | |
121 | |
122 sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix); | |
123 if (!data) { | |
124 DLOG(ERROR) << "Failed to create SkICC."; | |
125 return ICCProfile(); | |
126 } | |
127 | |
128 // gfx::ColorTransform assumes that this will return an empty profile for any | |
129 // color space that was not constructed from an ICC profile. | |
130 // TODO(ccameron): Fix this assumption. | |
131 // return FromData(data->data(), data->size()); | |
132 return ICCProfile(); | |
133 } | 128 } |
134 | 129 |
135 ICCProfile ICCProfile::FromSkColorSpace(sk_sp<SkColorSpace> color_space) { | 130 ICCProfile ICCProfile::FromSkColorSpace(sk_sp<SkColorSpace> color_space) { |
136 ICCProfile icc_profile; | 131 ICCProfile icc_profile; |
137 | 132 |
138 Cache& cache = g_cache.Get(); | 133 Cache& cache = g_cache.Get(); |
139 base::AutoLock lock(cache.lock); | 134 base::AutoLock lock(cache.lock); |
140 | 135 |
141 // Linearly search the cached ICC profiles to find one with the same data. | 136 // Linearly search the cached ICC profiles to find one with the same data. |
142 // If it exists, re-use its id and touch it in the cache. | 137 // If it exists, re-use its id and touch it in the cache. |
143 for (auto iter = cache.id_to_icc_profile_mru.begin(); | 138 for (auto iter = cache.id_to_icc_profile_mru.begin(); |
144 iter != cache.id_to_icc_profile_mru.end(); ++iter) { | 139 iter != cache.id_to_icc_profile_mru.end(); ++iter) { |
145 sk_sp<SkColorSpace> iter_color_space = | 140 sk_sp<SkColorSpace> iter_color_space = |
146 iter->second.color_space_.ToSkColorSpace(); | 141 iter->second.color_space_.ToSkColorSpace(); |
147 if (SkColorSpace::Equals(color_space.get(), iter_color_space.get())) { | 142 if (SkColorSpace::Equals(color_space.get(), iter_color_space.get())) { |
148 icc_profile = iter->second; | 143 icc_profile = iter->second; |
149 cache.id_to_icc_profile_mru.Get(icc_profile.id_); | 144 cache.id_to_icc_profile_mru.Get(icc_profile.id_); |
150 return icc_profile; | 145 return icc_profile; |
151 } | 146 } |
152 } | 147 } |
153 | 148 |
154 // TODO(ccameron): Support constructing ICC profiles from arbitrary | 149 // TODO(ccameron): Support constructing ICC profiles from arbitrary |
155 // SkColorSpace objects. | 150 // SkColorSpace objects. |
156 DLOG(ERROR) << "Failed to find ICC profile matching SkColorSpace."; | |
157 return icc_profile; | 151 return icc_profile; |
158 } | 152 } |
159 | 153 |
160 const std::vector<char>& ICCProfile::GetData() const { | 154 const std::vector<char>& ICCProfile::GetData() const { |
161 return data_; | 155 return data_; |
162 } | 156 } |
163 | 157 |
164 const ColorSpace& ICCProfile::GetColorSpace() const { | 158 ColorSpace ICCProfile::GetColorSpace() const { |
165 // Move this ICC profile to the most recently used end of the cache, | 159 if (type_ == Type::INVALID) |
166 // inserting if needed. | 160 return gfx::ColorSpace(); |
167 if (id_) { | 161 if (type_ == Type::FROM_COLOR_SPACE) |
168 Cache& cache = g_cache.Get(); | 162 return color_space_; |
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 } | |
176 | 163 |
177 void ICCProfile::ComputeColorSpaceAndCache() { | 164 ColorSpace color_space = color_space_; |
178 if (!id_) | |
179 return; | |
180 | 165 |
181 // If this already exists in the cache, just update its |color_space_|. | 166 // Move this ICC profile to the most recently used end of the cache. |
182 { | 167 { |
183 Cache& cache = g_cache.Get(); | 168 Cache& cache = g_cache.Get(); |
184 base::AutoLock lock(cache.lock); | 169 base::AutoLock lock(cache.lock); |
| 170 |
185 auto found = cache.id_to_icc_profile_mru.Get(id_); | 171 auto found = cache.id_to_icc_profile_mru.Get(id_); |
186 if (found != cache.id_to_icc_profile_mru.end()) { | 172 if (found == cache.id_to_icc_profile_mru.end()) |
187 color_space_ = found->second.color_space_; | 173 cache.id_to_icc_profile_mru.Put(id_, *this); |
188 return; | |
189 } | |
190 } | 174 } |
191 | 175 |
192 // Compute the color space. | 176 ColorSpace unity_colorspace( |
193 color_space_ = gfx::ColorSpace( | 177 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::LINEAR, |
194 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::CUSTOM, | |
195 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); | 178 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
196 color_space_.icc_profile_id_ = id_; | 179 unity_colorspace.custom_primary_matrix_[0] = 1.0f; |
197 color_space_.sk_color_space_ = | 180 unity_colorspace.custom_primary_matrix_[1] = 0.0f; |
198 SkColorSpace::MakeICC(data_.data(), data_.size()); | 181 unity_colorspace.custom_primary_matrix_[2] = 0.0f; |
| 182 unity_colorspace.custom_primary_matrix_[3] = 0.0f; |
199 | 183 |
200 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); | 184 unity_colorspace.custom_primary_matrix_[4] = 0.0f; |
201 if (sk_icc) { | 185 unity_colorspace.custom_primary_matrix_[5] = 1.0f; |
202 bool result; | 186 unity_colorspace.custom_primary_matrix_[6] = 0.0f; |
203 SkMatrix44 to_XYZD50_matrix; | 187 unity_colorspace.custom_primary_matrix_[7] = 0.0f; |
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 } | |
236 | 188 |
237 // Add to the cache. | 189 unity_colorspace.custom_primary_matrix_[8] = 0.0f; |
238 { | 190 unity_colorspace.custom_primary_matrix_[9] = 0.0f; |
239 Cache& cache = g_cache.Get(); | 191 unity_colorspace.custom_primary_matrix_[10] = 1.0f; |
240 base::AutoLock lock(cache.lock); | 192 unity_colorspace.custom_primary_matrix_[11] = 0.0f; |
241 cache.id_to_icc_profile_mru.Put(id_, *this); | 193 |
242 } | 194 // This will look up and use the ICC profile. |
| 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; |
243 } | 220 } |
244 | 221 |
245 // static | 222 // static |
246 bool ICCProfile::IsValidProfileLength(size_t length) { | 223 bool ICCProfile::IsValidProfileLength(size_t length) { |
247 return length >= kMinProfileLength && length <= kMaxProfileLength; | 224 return length >= kMinProfileLength && length <= kMaxProfileLength; |
248 } | 225 } |
249 | 226 |
250 } // namespace gfx | 227 } // namespace gfx |
OLD | NEW |