blob: 030e8a63f019808d6778d127329421bb9aec02f8 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gfx/color_profile.h"
#include <windows.h>
#include <stddef.h>
#include <map>
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
namespace gfx {
class ColorProfileCache {
public:
// A thread-safe cache of color profiles keyed by windows device name.
ColorProfileCache() {}
bool Find(const std::wstring& device, std::vector<char>* profile) {
base::AutoLock lock(lock_);
DeviceColorProfile::const_iterator it = cache_.find(device);
if (it == cache_.end())
return false;
*profile = it->second;
return true;
}
void Insert(const std::wstring& device, const std::vector<char>& profile) {
base::AutoLock lock(lock_);
cache_[device] = profile;
}
bool Erase(const std::wstring& device) {
base::AutoLock lock(lock_);
DeviceColorProfile::iterator it = cache_.find(device);
if (it == cache_.end())
return false;
cache_.erase(device);
return true;
}
void Clear() {
base::AutoLock lock(lock_);
cache_.clear();
}
private:
typedef std::map<std::wstring, std::vector<char> > DeviceColorProfile;
DeviceColorProfile cache_;
base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(ColorProfileCache);
};
base::LazyInstance<ColorProfileCache>::Leaky g_color_profile_cache =
LAZY_INSTANCE_INITIALIZER;
inline ColorProfileCache& GetColorProfileCache() {
return g_color_profile_cache.Get();
}
bool GetDisplayColorProfile(const gfx::Rect& bounds,
std::vector<char>* profile) {
DCHECK(profile->empty());
RECT rect = bounds.ToRECT();
HMONITOR handle = ::MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
if (bounds.IsEmpty() || !handle)
return false;
MONITORINFOEX monitor;
monitor.cbSize = sizeof(MONITORINFOEX);
CHECK(::GetMonitorInfo(handle, &monitor));
if (GetColorProfileCache().Find(monitor.szDevice, profile))
return true;
HDC hdc = ::CreateDC(monitor.szDevice, NULL, NULL, NULL);
DWORD path_length = MAX_PATH;
WCHAR path[MAX_PATH + 1];
BOOL result = ::GetICMProfile(hdc, &path_length, path);
::DeleteDC(hdc);
if (!result)
return false;
base::FilePath file_name = base::FilePath(path).BaseName();
if (file_name != base::FilePath(L"sRGB Color Space Profile.icm")) {
std::string data;
if (base::ReadFileToString(base::FilePath(path), &data))
profile->assign(data.data(), data.data() + data.size());
size_t length = profile->size();
if (gfx::InvalidColorProfileLength(length))
profile->clear();
}
GetColorProfileCache().Insert(monitor.szDevice, *profile);
return true;
}
void ReadColorProfile(std::vector<char>* profile) {
// TODO: support multiple monitors.
HDC screen_dc = GetDC(NULL);
DWORD path_len = MAX_PATH;
WCHAR path[MAX_PATH + 1];
BOOL result = GetICMProfile(screen_dc, &path_len, path);
ReleaseDC(NULL, screen_dc);
if (!result)
return;
std::string profileData;
if (!base::ReadFileToString(base::FilePath(path), &profileData))
return;
size_t length = profileData.size();
if (gfx::InvalidColorProfileLength(length))
return;
profile->assign(profileData.data(), profileData.data() + length);
}
} // namespace gfx