summaryrefslogtreecommitdiffstats
path: root/ui/gfx/color_profile_win.cc
blob: 030e8a63f019808d6778d127329421bb9aec02f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// 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