| 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_; |
| 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 return FromData(data->data(), data->size()); |
| 128 } | 128 } |
| 129 | 129 |
| 130 ICCProfile ICCProfile::FromSkColorSpace(sk_sp<SkColorSpace> color_space) { | 130 ICCProfile ICCProfile::FromSkColorSpace(sk_sp<SkColorSpace> color_space) { |
| 131 ICCProfile icc_profile; | 131 ICCProfile icc_profile; |
| 132 | 132 |
| 133 Cache& cache = g_cache.Get(); | 133 Cache& cache = g_cache.Get(); |
| 134 base::AutoLock lock(cache.lock); | 134 base::AutoLock lock(cache.lock); |
| 135 | 135 |
| 136 // 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. |
| 137 // 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. |
| 138 for (auto iter = cache.id_to_icc_profile_mru.begin(); | 138 for (auto iter = cache.id_to_icc_profile_mru.begin(); |
| 139 iter != cache.id_to_icc_profile_mru.end(); ++iter) { | 139 iter != cache.id_to_icc_profile_mru.end(); ++iter) { |
| 140 sk_sp<SkColorSpace> iter_color_space = | 140 sk_sp<SkColorSpace> iter_color_space = |
| 141 iter->second.color_space_.ToSkColorSpace(); | 141 iter->second.color_space_.ToSkColorSpace(); |
| 142 if (SkColorSpace::Equals(color_space.get(), iter_color_space.get())) { | 142 if (SkColorSpace::Equals(color_space.get(), iter_color_space.get())) { |
| 143 icc_profile = iter->second; | 143 icc_profile = iter->second; |
| 144 cache.id_to_icc_profile_mru.Get(icc_profile.id_); | 144 cache.id_to_icc_profile_mru.Get(icc_profile.id_); |
| 145 return icc_profile; | 145 return icc_profile; |
| 146 } | 146 } |
| 147 } | 147 } |
| 148 | 148 |
| 149 // TODO(ccameron): Support constructing ICC profiles from arbitrary | 149 // TODO(ccameron): Support constructing ICC profiles from arbitrary |
| 150 // SkColorSpace objects. | 150 // SkColorSpace objects. |
| 151 DLOG(ERROR) << "Failed to find ICC profile matching SkColorSpace."; |
| 151 return icc_profile; | 152 return icc_profile; |
| 152 } | 153 } |
| 153 | 154 |
| 154 const std::vector<char>& ICCProfile::GetData() const { | 155 const std::vector<char>& ICCProfile::GetData() const { |
| 155 return data_; | 156 return data_; |
| 156 } | 157 } |
| 157 | 158 |
| 158 ColorSpace ICCProfile::GetColorSpace() const { | 159 const ColorSpace& ICCProfile::GetColorSpace() const { |
| 159 if (type_ == Type::INVALID) | 160 // Move this ICC profile to the most recently used end of the cache, |
| 160 return gfx::ColorSpace(); | 161 // inserting if needed. |
| 161 if (type_ == Type::FROM_COLOR_SPACE) | 162 if (id_) { |
| 162 return color_space_; | 163 Cache& cache = g_cache.Get(); |
| 164 base::AutoLock lock(cache.lock); |
| 165 auto found = cache.id_to_icc_profile_mru.Get(id_); |
| 166 if (found == cache.id_to_icc_profile_mru.end()) |
| 167 found = cache.id_to_icc_profile_mru.Put(id_, *this); |
| 168 } |
| 169 return color_space_; |
| 170 } |
| 163 | 171 |
| 164 ColorSpace color_space = color_space_; | 172 void ICCProfile::ComputeColorSpaceAndCache() { |
| 173 if (!id_) |
| 174 return; |
| 165 | 175 |
| 166 // Move this ICC profile to the most recently used end of the cache. | 176 // If this already exists in the cache, just update its |color_space_|. |
| 167 { | 177 { |
| 168 Cache& cache = g_cache.Get(); | 178 Cache& cache = g_cache.Get(); |
| 169 base::AutoLock lock(cache.lock); | 179 base::AutoLock lock(cache.lock); |
| 170 | |
| 171 auto found = cache.id_to_icc_profile_mru.Get(id_); | 180 auto found = cache.id_to_icc_profile_mru.Get(id_); |
| 172 if (found == cache.id_to_icc_profile_mru.end()) | 181 if (found != cache.id_to_icc_profile_mru.end()) { |
| 173 cache.id_to_icc_profile_mru.Put(id_, *this); | 182 color_space_ = found->second.color_space_; |
| 183 return; |
| 184 } |
| 174 } | 185 } |
| 175 | 186 |
| 176 ColorSpace unity_colorspace( | 187 // Compute the color space. |
| 177 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::LINEAR, | 188 color_space_ = gfx::ColorSpace( |
| 189 ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::CUSTOM, |
| 178 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); | 190 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
| 179 unity_colorspace.custom_primary_matrix_[0] = 1.0f; | 191 color_space_.icc_profile_id_ = id_; |
| 180 unity_colorspace.custom_primary_matrix_[1] = 0.0f; | 192 color_space_.sk_color_space_ = |
| 181 unity_colorspace.custom_primary_matrix_[2] = 0.0f; | 193 SkColorSpace::MakeICC(data_.data(), data_.size()); |
| 182 unity_colorspace.custom_primary_matrix_[3] = 0.0f; | |
| 183 | 194 |
| 184 unity_colorspace.custom_primary_matrix_[4] = 0.0f; | 195 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); |
| 185 unity_colorspace.custom_primary_matrix_[5] = 1.0f; | 196 if (sk_icc) { |
| 186 unity_colorspace.custom_primary_matrix_[6] = 0.0f; | 197 bool result; |
| 187 unity_colorspace.custom_primary_matrix_[7] = 0.0f; | 198 SkMatrix44 to_XYZD50_matrix; |
| 199 result = sk_icc->toXYZD50(&to_XYZD50_matrix); |
| 200 if (result) { |
| 201 for (int row = 0; row < 3; ++row) { |
| 202 for (int col = 0; col < 3; ++col) { |
| 203 color_space_.custom_primary_matrix_[3 * row + col] = |
| 204 to_XYZD50_matrix.get(row, col); |
| 205 } |
| 206 } |
| 207 } else { |
| 208 // Just say that the primaries were the sRGB primaries if we can't |
| 209 // extract them. |
| 210 color_space_.primaries_ = ColorSpace::PrimaryID::BT709; |
| 211 DLOG(ERROR) << "Unable to handle ICCProfile primaries."; |
| 212 } |
| 213 SkColorSpaceTransferFn fn; |
| 214 result = sk_icc->isNumericalTransferFn(&fn); |
| 215 if (result) { |
| 216 color_space_.custom_transfer_params_[0] = fn.fA; |
| 217 color_space_.custom_transfer_params_[1] = fn.fB; |
| 218 color_space_.custom_transfer_params_[2] = fn.fC; |
| 219 color_space_.custom_transfer_params_[3] = fn.fD; |
| 220 color_space_.custom_transfer_params_[4] = fn.fE; |
| 221 color_space_.custom_transfer_params_[5] = fn.fF; |
| 222 color_space_.custom_transfer_params_[6] = fn.fG; |
| 223 } else { |
| 224 // Just say that the transfer function was sRGB if we cannot read it. |
| 225 // TODO(ccameron): Use a least squares approximation of the transfer |
| 226 // function when it is not numerical. |
| 227 color_space_.transfer_ = ColorSpace::TransferID::IEC61966_2_1; |
| 228 DLOG(ERROR) << "Unable to handle ICCProfile transfer function."; |
| 229 } |
| 230 } |
| 188 | 231 |
| 189 unity_colorspace.custom_primary_matrix_[8] = 0.0f; | 232 // Add to the cache. |
| 190 unity_colorspace.custom_primary_matrix_[9] = 0.0f; | 233 { |
| 191 unity_colorspace.custom_primary_matrix_[10] = 1.0f; | 234 Cache& cache = g_cache.Get(); |
| 192 unity_colorspace.custom_primary_matrix_[11] = 0.0f; | 235 base::AutoLock lock(cache.lock); |
| 193 | 236 cache.id_to_icc_profile_mru.Put(id_, *this); |
| 194 // This will look up and use the ICC profile. | 237 } |
| 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 } | 238 } |
| 221 | 239 |
| 222 // static | 240 // static |
| 223 bool ICCProfile::IsValidProfileLength(size_t length) { | 241 bool ICCProfile::IsValidProfileLength(size_t length) { |
| 224 return length >= kMinProfileLength && length <= kMaxProfileLength; | 242 return length >= kMinProfileLength && length <= kMaxProfileLength; |
| 225 } | 243 } |
| 226 | 244 |
| 227 } // namespace gfx | 245 } // namespace gfx |
| OLD | NEW |