| 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" |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 | 101 |
| 102 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) | 102 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(USE_X11) |
| 103 // static | 103 // static |
| 104 ICCProfile ICCProfile::FromBestMonitor() { | 104 ICCProfile ICCProfile::FromBestMonitor() { |
| 105 return ICCProfile(); | 105 return ICCProfile(); |
| 106 } | 106 } |
| 107 #endif | 107 #endif |
| 108 | 108 |
| 109 // static | 109 // static |
| 110 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) { | 110 ICCProfile ICCProfile::FromColorSpace(const gfx::ColorSpace& color_space) { |
| 111 if (color_space == gfx::ColorSpace()) | 111 if (!color_space.IsValid()) |
| 112 return ICCProfile(); | 112 return ICCProfile(); |
| 113 | 113 |
| 114 // If |color_space| was created from an ICC profile, retrieve that exact | 114 // If |color_space| was created from an ICC profile, retrieve that exact |
| 115 // profile. | 115 // profile. |
| 116 if (color_space.icc_profile_id_) { | 116 ICCProfile result; |
| 117 Cache& cache = g_cache.Get(); | 117 if (FromId(color_space.icc_profile_id_, false, &result)) { |
| 118 base::AutoLock lock(cache.lock); | 118 return result; |
| 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 } | 119 } |
| 123 | 120 |
| 124 // Otherwise, construct an ICC profile based on the best approximated | 121 // Otherwise, construct an ICC profile based on the best approximated |
| 125 // primaries and matrix. | 122 // primaries and matrix. |
| 126 SkMatrix44 to_XYZD50_matrix; | 123 SkMatrix44 to_XYZD50_matrix; |
| 127 color_space.GetPrimaryMatrix(&to_XYZD50_matrix); | 124 color_space.GetPrimaryMatrix(&to_XYZD50_matrix); |
| 128 SkColorSpaceTransferFn fn; | 125 SkColorSpaceTransferFn fn; |
| 129 if (!color_space.GetTransferFunction(&fn)) { | 126 if (!color_space.GetTransferFunction(&fn)) { |
| 130 DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile."; | 127 DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile."; |
| 131 return ICCProfile(); | 128 return ICCProfile(); |
| 132 } | 129 } |
| 133 | |
| 134 sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix); | 130 sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix); |
| 135 if (!data) { | 131 if (!data) { |
| 136 DLOG(ERROR) << "Failed to create SkICC."; | 132 DLOG(ERROR) << "Failed to create SkICC."; |
| 137 return ICCProfile(); | 133 return ICCProfile(); |
| 138 } | 134 } |
| 139 | 135 return FromData(data->data(), data->size()); |
| 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 } | 136 } |
| 146 | 137 |
| 147 const std::vector<char>& ICCProfile::GetData() const { | 138 const std::vector<char>& ICCProfile::GetData() const { |
| 148 return data_; | 139 return data_; |
| 149 } | 140 } |
| 150 | 141 |
| 151 const ColorSpace& ICCProfile::GetColorSpace() const { | 142 const ColorSpace& ICCProfile::GetColorSpace() const { |
| 152 // Move this ICC profile to the most recently used end of the cache, | 143 // Move this ICC profile to the most recently used end of the cache, |
| 153 // inserting if needed. | 144 // inserting if needed. |
| 154 if (id_) { | 145 if (id_) { |
| 155 Cache& cache = g_cache.Get(); | 146 Cache& cache = g_cache.Get(); |
| 156 base::AutoLock lock(cache.lock); | 147 base::AutoLock lock(cache.lock); |
| 157 auto found = cache.id_to_icc_profile_mru.Get(id_); | 148 auto found = cache.id_to_icc_profile_mru.Get(id_); |
| 158 if (found == cache.id_to_icc_profile_mru.end()) | 149 if (found == cache.id_to_icc_profile_mru.end()) |
| 159 found = cache.id_to_icc_profile_mru.Put(id_, *this); | 150 found = cache.id_to_icc_profile_mru.Put(id_, *this); |
| 160 } | 151 } |
| 161 return color_space_; | 152 return color_space_; |
| 162 } | 153 } |
| 163 | 154 |
| 155 // static |
| 156 bool ICCProfile::FromId(uint64_t id, |
| 157 bool only_if_needed, |
| 158 ICCProfile* icc_profile) { |
| 159 if (!id) |
| 160 return false; |
| 161 |
| 162 Cache& cache = g_cache.Get(); |
| 163 base::AutoLock lock(cache.lock); |
| 164 |
| 165 auto found = cache.id_to_icc_profile_mru.Get(id); |
| 166 if (found == cache.id_to_icc_profile_mru.end()) |
| 167 return false; |
| 168 |
| 169 const ICCProfile& found_icc_profile = found->second; |
| 170 if (found_icc_profile.color_space_is_accurate_ && only_if_needed) |
| 171 return false; |
| 172 |
| 173 *icc_profile = found_icc_profile; |
| 174 return true; |
| 175 } |
| 176 |
| 164 void ICCProfile::ComputeColorSpaceAndCache() { | 177 void ICCProfile::ComputeColorSpaceAndCache() { |
| 165 if (!id_) | 178 if (!id_) |
| 166 return; | 179 return; |
| 167 | 180 |
| 168 // If this already exists in the cache, just update its |color_space_|. | 181 // If this already exists in the cache, just update its |color_space_|. |
| 169 { | 182 { |
| 170 Cache& cache = g_cache.Get(); | 183 Cache& cache = g_cache.Get(); |
| 171 base::AutoLock lock(cache.lock); | 184 base::AutoLock lock(cache.lock); |
| 172 auto found = cache.id_to_icc_profile_mru.Get(id_); | 185 auto found = cache.id_to_icc_profile_mru.Get(id_); |
| 173 if (found != cache.id_to_icc_profile_mru.end()) { | 186 if (found != cache.id_to_icc_profile_mru.end()) { |
| 174 color_space_ = found->second.color_space_; | 187 color_space_ = found->second.color_space_; |
| 175 return; | 188 return; |
| 176 } | 189 } |
| 177 } | 190 } |
| 178 | 191 |
| 192 color_space_is_accurate_ = true; |
| 193 SkMatrix44 to_XYZD50_matrix; |
| 194 SkColorSpaceTransferFn fn; |
| 195 sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size()); |
| 196 if (sk_icc) { |
| 197 if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { |
| 198 // Just say that the primaries were the sRGB primaries if we can't |
| 199 // extract them. |
| 200 gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_matrix); |
| 201 color_space_is_accurate_ = false; |
| 202 DLOG(ERROR) << "Unable to handle ICCProfile primaries."; |
| 203 } |
| 204 if (!sk_icc->isNumericalTransferFn(&fn)) { |
| 205 // Just say that the transfer function was sRGB if we cannot read it. |
| 206 // TODO(ccameron): Use a least squares approximation of the transfer |
| 207 // function when it is not numerical. |
| 208 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn); |
| 209 color_space_is_accurate_ = false; |
| 210 DLOG(ERROR) << "Unable to handle ICCProfile transfer function."; |
| 211 } |
| 212 } else { |
| 213 gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_matrix); |
| 214 gfx::ColorSpace::CreateSRGB().GetTransferFunction(&fn); |
| 215 color_space_is_accurate_ = false; |
| 216 DLOG(ERROR) << "Unable parse ICCProfile."; |
| 217 } |
| 218 |
| 179 // Compute the color space. | 219 // Compute the color space. |
| 180 color_space_ = gfx::ColorSpace( | 220 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_; | 221 color_space_.icc_profile_id_ = id_; |
| 184 color_space_.icc_profile_sk_color_space_ = | 222 color_space_.icc_profile_sk_color_space_ = |
| 185 SkColorSpace::MakeICC(data_.data(), data_.size()); | 223 SkColorSpace::MakeICC(data_.data(), data_.size()); |
| 186 | 224 |
| 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. | 225 // Add to the cache. |
| 225 { | 226 { |
| 226 Cache& cache = g_cache.Get(); | 227 Cache& cache = g_cache.Get(); |
| 227 base::AutoLock lock(cache.lock); | 228 base::AutoLock lock(cache.lock); |
| 228 cache.id_to_icc_profile_mru.Put(id_, *this); | 229 cache.id_to_icc_profile_mru.Put(id_, *this); |
| 229 } | 230 } |
| 230 } | 231 } |
| 231 | 232 |
| 232 // static | 233 // static |
| 233 bool ICCProfile::IsValidProfileLength(size_t length) { | 234 bool ICCProfile::IsValidProfileLength(size_t length) { |
| 234 return length >= kMinProfileLength && length <= kMaxProfileLength; | 235 return length >= kMinProfileLength && length <= kMaxProfileLength; |
| 235 } | 236 } |
| 236 | 237 |
| 237 } // namespace gfx | 238 } // namespace gfx |
| OLD | NEW |