1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/gfx/color_profile.h"
6
7#include <windows.h>
8#include <map>
9
10#include "base/files/file_util.h"
11#include "base/lazy_instance.h"
12#include "base/synchronization/lock.h"
13
14namespace gfx {
15
16class ColorProfileCache {
17 public:
18  // A thread-safe cache of color profiles keyed by windows device name.
19  ColorProfileCache() {}
20
21  bool Find(const std::wstring& device, std::vector<char>* profile) {
22    base::AutoLock lock(lock_);
23    DeviceColorProfile::const_iterator it = cache_.find(device);
24    if (it == cache_.end())
25      return false;
26    *profile = it->second;
27    return true;
28  }
29
30  void Insert(const std::wstring& device, const std::vector<char>& profile) {
31    base::AutoLock lock(lock_);
32    cache_[device] = profile;
33  }
34
35  bool Erase(const std::wstring& device) {
36    base::AutoLock lock(lock_);
37    DeviceColorProfile::iterator it = cache_.find(device);
38    if (it == cache_.end())
39      return false;
40    cache_.erase(device);
41    return true;
42  }
43
44  void Clear() {
45    base::AutoLock lock(lock_);
46    cache_.clear();
47  }
48
49 private:
50  typedef std::map<std::wstring, std::vector<char> > DeviceColorProfile;
51
52  DeviceColorProfile cache_;
53  base::Lock lock_;
54
55  DISALLOW_COPY_AND_ASSIGN(ColorProfileCache);
56};
57
58base::LazyInstance<ColorProfileCache>::Leaky g_color_profile_cache =
59    LAZY_INSTANCE_INITIALIZER;
60
61inline ColorProfileCache& GetColorProfileCache() {
62  return g_color_profile_cache.Get();
63}
64
65bool GetDisplayColorProfile(const gfx::Rect& bounds,
66                            std::vector<char>* profile) {
67  DCHECK(profile->empty());
68
69  RECT rect = bounds.ToRECT();
70  HMONITOR handle = ::MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
71  if (bounds.IsEmpty() || !handle)
72    return false;
73
74  MONITORINFOEX monitor;
75  monitor.cbSize = sizeof(MONITORINFOEX);
76  CHECK(::GetMonitorInfo(handle, &monitor));
77  if (GetColorProfileCache().Find(monitor.szDevice, profile))
78    return true;
79
80  HDC hdc = ::CreateDC(monitor.szDevice, NULL, NULL, NULL);
81  DWORD path_length = MAX_PATH;
82  WCHAR path[MAX_PATH + 1];
83  BOOL result = ::GetICMProfile(hdc, &path_length, path);
84  ::DeleteDC(hdc);
85  if (!result)
86    return false;
87
88  base::FilePath file_name = base::FilePath(path).BaseName();
89  if (file_name != base::FilePath(L"sRGB Color Space Profile.icm")) {
90    std::string data;
91    if (base::ReadFileToString(base::FilePath(path), &data))
92      profile->assign(data.data(), data.data() + data.size());
93    size_t length = profile->size();
94    if (gfx::InvalidColorProfileLength(length))
95      profile->clear();
96  }
97
98  GetColorProfileCache().Insert(monitor.szDevice, *profile);
99  return true;
100}
101
102void ReadColorProfile(std::vector<char>* profile) {
103  // TODO: support multiple monitors.
104  HDC screen_dc = GetDC(NULL);
105  DWORD path_len = MAX_PATH;
106  WCHAR path[MAX_PATH + 1];
107
108  BOOL result = GetICMProfile(screen_dc, &path_len, path);
109  ReleaseDC(NULL, screen_dc);
110  if (!result)
111    return;
112  std::string profileData;
113  if (!base::ReadFileToString(base::FilePath(path), &profileData))
114    return;
115  size_t length = profileData.size();
116  if (gfx::InvalidColorProfileLength(length))
117    return;
118  profile->assign(profileData.data(), profileData.data() + length);
119}
120
121}  // namespace gfx
122