| Index: ui/gfx/color_space.cc
|
| diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
|
| index 7650b1e3a9d274e45bbb538fb08041a19607d863..6b797caee5ac078c971a79c047bbe9f82a31e794 100644
|
| --- a/ui/gfx/color_space.cc
|
| +++ b/ui/gfx/color_space.cc
|
| @@ -2,15 +2,95 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "build/build_config.h"
|
| #include "ui/gfx/color_space.h"
|
|
|
| +#include <map>
|
| +
|
| +#include "base/lazy_instance.h"
|
| +#include "base/synchronization/lock.h"
|
| +
|
| namespace gfx {
|
|
|
| namespace {
|
| static const size_t kMinProfileLength = 128;
|
| static const size_t kMaxProfileLength = 4 * 1024 * 1024;
|
| -}
|
| +} // namespace
|
| +
|
| +// The structure used to look up GlobalData structures.
|
| +struct ColorSpace::Key {
|
| + Key(ColorSpace::Type type, const std::vector<char>& icc_profile)
|
| + : type(type), icc_profile(icc_profile) {}
|
| +
|
| + bool operator<(const Key& other) const {
|
| + if (type < other.type)
|
| + return true;
|
| + if (type > other.type)
|
| + return false;
|
| + if (type != Type::ICC_PROFILE)
|
| + return false;
|
| +
|
| + if (icc_profile.size() < other.icc_profile.size())
|
| + return true;
|
| + if (icc_profile.size() > other.icc_profile.size())
|
| + return false;
|
| + for (size_t i = 0; i < icc_profile.size(); ++i) {
|
| + if (icc_profile[i] < other.icc_profile[i])
|
| + return true;
|
| + if (icc_profile[i] > other.icc_profile[i])
|
| + return false;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + ColorSpace::Type type;
|
| + const std::vector<char> icc_profile;
|
| +};
|
| +
|
| +// Because this structure is shared across gfx::ColorSpace objects on
|
| +// different threads, it needs to be thread-safe.
|
| +class ColorSpace::GlobalData
|
| + : public base::RefCountedThreadSafe<ColorSpace::GlobalData> {
|
| + public:
|
| + static void Get(const Key& key, scoped_refptr<GlobalData>* value) {
|
| + base::AutoLock lock(map_lock_.Get());
|
| + auto insert_result = map_.Get().insert(std::make_pair(key, nullptr));
|
| + if (insert_result.second)
|
| + insert_result.first->second = new GlobalData(key, insert_result.first);
|
| + *value = make_scoped_refptr(insert_result.first->second);
|
| + }
|
| +
|
| + const std::vector<char>& GetICCProfile() const { return icc_profile_; }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<GlobalData>;
|
| +
|
| + GlobalData(const Key& key, std::map<Key, GlobalData*>::iterator iterator)
|
| + : iterator_(iterator) {
|
| + // TODO: Compute the ICC profile for named color spaces.
|
| + if (key.type == Type::ICC_PROFILE)
|
| + icc_profile_ = key.icc_profile;
|
| + }
|
| + ~GlobalData() {
|
| + base::AutoLock lock(map_lock_.Get());
|
| + map_.Get().erase(iterator_);
|
| + }
|
| +
|
| + std::vector<char> icc_profile_;
|
| +
|
| + // In order to remove |this| from |map_| when its last reference goes away,
|
| + // keep in |iterator_| the corresponding iterator in |map_|.
|
| + std::map<Key, GlobalData*>::iterator iterator_;
|
| + // The |map_| tracks the existing GlobalData instances, which are owned by
|
| + // ColorSpace instances. Note that |map_| must be leaky because GlobalData
|
| + // instances will reach back into it at unpredictable times during tear-down.
|
| + static base::LazyInstance<std::map<Key, GlobalData*>>::Leaky map_;
|
| + static base::LazyInstance<base::Lock>::Leaky map_lock_;
|
| +};
|
| +
|
| +base::LazyInstance<std::map<ColorSpace::Key, ColorSpace::GlobalData*>>::Leaky
|
| + ColorSpace::GlobalData::map_ = LAZY_INSTANCE_INITIALIZER;
|
| +base::LazyInstance<base::Lock>::Leaky
|
| + ColorSpace::GlobalData::map_lock_ = LAZY_INSTANCE_INITIALIZER;
|
|
|
| ColorSpace::ColorSpace() = default;
|
| ColorSpace::ColorSpace(ColorSpace&& other) = default;
|
| @@ -19,13 +99,26 @@ ColorSpace& ColorSpace::operator=(const ColorSpace& other) = default;
|
| ColorSpace::~ColorSpace() = default;
|
|
|
| bool ColorSpace::operator==(const ColorSpace& other) const {
|
| - return icc_profile_ == other.icc_profile_;
|
| + if (type_ == Type::ICC_PROFILE && other.type_ == Type::ICC_PROFILE)
|
| + return global_data_ == other.global_data_;
|
| + return type_ == other.type_;
|
| }
|
|
|
| +bool ColorSpace::operator<(const ColorSpace& other) const {
|
| + // Note that this does a pointer-based comparision.
|
| + if (type_ == Type::ICC_PROFILE && other.type_ == Type::ICC_PROFILE)
|
| + return global_data_.get() < other.global_data_.get();
|
| + return type_ < other.type_;
|
| +}
|
| +
|
| +// static
|
| ColorSpace ColorSpace::FromICCProfile(const std::vector<char>& icc_profile) {
|
| ColorSpace color_space;
|
| - if (IsValidProfileLength(icc_profile.size()))
|
| - color_space.icc_profile_ = icc_profile;
|
| + if (IsValidProfileLength(icc_profile.size())) {
|
| + color_space.type_ = Type::ICC_PROFILE;
|
| + Key key(Type::ICC_PROFILE, icc_profile);
|
| + GlobalData::Get(key, &color_space.global_data_);
|
| + }
|
| return color_space;
|
| }
|
|
|
| @@ -36,6 +129,14 @@ ColorSpace ColorSpace::FromBestMonitor() {
|
| }
|
| #endif
|
|
|
| +const std::vector<char>& ColorSpace::GetICCProfile() const {
|
| + if (!global_data_) {
|
| + Key key(type_, std::vector<char>());
|
| + GlobalData::Get(key, &global_data_);
|
| + }
|
| + return global_data_->GetICCProfile();
|
| +}
|
| +
|
| // static
|
| bool ColorSpace::IsValidProfileLength(size_t length) {
|
| return length >= kMinProfileLength && length <= kMaxProfileLength;
|
|
|