1 // Copyright 2014 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.
5 #include "ui/views/corewm/cursor_height_provider_win.h"
11 #include "base/basictypes.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/win/scoped_hdc.h"
16 typedef scoped_ptr
<uint32_t> PixelData
;
17 typedef std::map
<HCURSOR
, int> HeightStorage
;
19 const uint32_t kBitsPeruint32
= sizeof(uint32_t) * 8;
20 // All bits are 1 for transparent portion of monochromatic mask.
21 const uint32_t kTransparentMask
= 0xffffffff;
22 // This is height of default pointer arrow in Windows 7.
23 const int kDefaultHeight
= 20;
24 // Masks are monochromatic.
25 const size_t kNumberOfColors
= 2;
26 const size_t KHeaderAndPalette
=
27 sizeof(BITMAPINFOHEADER
) + kNumberOfColors
* sizeof(RGBQUAD
);
29 static HeightStorage
* cached_heights
= NULL
;
31 // Extracts the pixel data of provided bitmap
32 PixelData
GetBitmapData(HBITMAP handle
, const BITMAPINFO
& info
, HDC hdc
) {
34 // Masks are monochromatic.
35 DCHECK_EQ(info
.bmiHeader
.biBitCount
, 1);
36 if (info
.bmiHeader
.biBitCount
!= 1)
39 // When getting pixel data palette is appended to memory pointed by
40 // BITMAPINFO passed so allocate additional memory to store additional data.
41 scoped_ptr
<BITMAPINFO
> header(
42 reinterpret_cast<BITMAPINFO
*>(new char[KHeaderAndPalette
]));
43 memcpy(header
.get(), &(info
.bmiHeader
), sizeof(info
.bmiHeader
));
45 data
.reset(new uint32_t[info
.bmiHeader
.biSizeImage
/ sizeof(uint32_t)]);
47 int result
= GetDIBits(hdc
,
50 info
.bmiHeader
.biHeight
,
61 // Checks if the specifed row is transparent in provided bitmap.
62 bool IsRowTransparent(const PixelData
& data
,
63 const uint32_t row_size
,
64 const uint32_t last_byte_mask
,
66 // Set the padding bits to 1 to make mask matching easier.
67 *(data
.get() + (y
+ 1) * row_size
- 1) |= last_byte_mask
;
68 for (uint32_t i
= y
* row_size
; i
< (y
+ 1) * row_size
; ++i
) {
69 if (*(data
.get() + i
) != kTransparentMask
)
75 // Gets the vertical offset between specified cursor's hotpoint and it's bottom.
77 // Gets the cursor image data and extract cursor's visible height.
78 // Based on that get's what should be the vertical offset between cursor's
79 // hot point and the tooltip.
80 int CalculateCursorHeight(HCURSOR cursor_handle
) {
81 base::win::ScopedGetDC
hdc(NULL
);
84 GetIconInfo(cursor_handle
, &icon
);
86 BITMAPINFO bitmap_info
= {};
87 bitmap_info
.bmiHeader
.biSize
= sizeof(bitmap_info
.bmiHeader
);
88 if (GetDIBits(hdc
, icon
.hbmMask
, 0, 0, NULL
, &bitmap_info
, DIB_RGB_COLORS
) ==
90 return kDefaultHeight
;
92 // Rows are padded to full DWORDs. OR with this mask will set them to 1
93 // to simplify matching with |transparent_mask|.
94 uint32_t last_byte_mask
= 0xFFFFFFFF;
95 const unsigned char bits_to_shift
= sizeof(last_byte_mask
) * 8 -
96 (bitmap_info
.bmiHeader
.biWidth
% kBitsPeruint32
);
97 if (bits_to_shift
!= kBitsPeruint32
)
98 last_byte_mask
= (last_byte_mask
<< bits_to_shift
);
102 const uint32_t row_size
=
103 (bitmap_info
.bmiHeader
.biWidth
+ kBitsPeruint32
- 1) / kBitsPeruint32
;
104 PixelData
data(GetBitmapData(icon
.hbmMask
, bitmap_info
, hdc
));
106 return kDefaultHeight
;
108 const int cursor_height
= GetSystemMetrics(SM_CYCURSOR
);
109 // Crash data seems to indicate cursor_height may be bigger than the bitmap.
110 int i
= std::max(0, static_cast<int>(bitmap_info
.bmiHeader
.biHeight
) -
112 for (; i
< bitmap_info
.bmiHeader
.biHeight
; ++i
) {
113 if (!IsRowTransparent(data
, row_size
, last_byte_mask
, i
)) {
118 return bitmap_info
.bmiHeader
.biHeight
- i
- icon
.yHotspot
;
126 int GetCurrentCursorVisibleHeight() {
127 CURSORINFO cursor
= {0};
128 cursor
.cbSize
= sizeof(cursor
);
129 GetCursorInfo(&cursor
);
131 if (cached_heights
== NULL
)
132 cached_heights
= new HeightStorage
;
134 HeightStorage::const_iterator cached_height
=
135 cached_heights
->find(cursor
.hCursor
);
136 if (cached_height
!= cached_heights
->end())
137 return cached_height
->second
;
139 const int height
= CalculateCursorHeight(cursor
.hCursor
);
140 (*cached_heights
)[cursor
.hCursor
] = height
;
145 } // namespace corewm