Process Hacker
icotobmp.c
Go to the documentation of this file.
1 #include <phgui.h>
2 #include <uxtheme.h>
3 
4 // code from http://msdn.microsoft.com/en-us/library/bb757020.aspx
5 
6 typedef HPAINTBUFFER (*_BeginBufferedPaint)(
7  _In_ HDC hdcTarget,
8  _In_ const RECT *prcTarget,
9  _In_ BP_BUFFERFORMAT dwFormat,
10  _In_ BP_PAINTPARAMS *pPaintParams,
11  _Out_ HDC *phdc
12  );
13 
14 typedef HRESULT (*_EndBufferedPaint)(
15  _In_ HPAINTBUFFER hBufferedPaint,
16  _In_ BOOL fUpdateTarget
17  );
18 
19 typedef HRESULT (*_GetBufferedPaintBits)(
20  _In_ HPAINTBUFFER hBufferedPaint,
21  _Out_ RGBQUAD **ppbBuffer,
22  _Out_ int *pcxRow
23  );
24 
25 static BOOLEAN ImportsInitialized = FALSE;
26 static _BeginBufferedPaint BeginBufferedPaint_I = NULL;
27 static _EndBufferedPaint EndBufferedPaint_I = NULL;
28 static _GetBufferedPaintBits GetBufferedPaintBits_I = NULL;
29 
30 static HBITMAP PhpCreateBitmap32(
31  _In_ HDC hdc,
32  _In_ ULONG Width,
33  _In_ ULONG Height,
34  _Outptr_opt_ PVOID *Bits
35  )
36 {
37  BITMAPINFO bitmapInfo;
38 
39  memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
40  bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
41  bitmapInfo.bmiHeader.biPlanes = 1;
42  bitmapInfo.bmiHeader.biCompression = BI_RGB;
43 
44  bitmapInfo.bmiHeader.biWidth = Width;
45  bitmapInfo.bmiHeader.biHeight = Height;
46  bitmapInfo.bmiHeader.biBitCount = 32;
47 
48  return CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, Bits, NULL, 0);
49 }
50 
51 static BOOLEAN PhpHasAlpha(
52  _In_ PULONG Argb,
53  _In_ ULONG Width,
54  _In_ ULONG Height,
55  _In_ ULONG RowWidth
56  )
57 {
58  ULONG delta;
59  ULONG x;
60  ULONG y;
61 
62  delta = RowWidth - Width;
63 
64  for (y = Width; y; y--)
65  {
66  for (x = Height; x; x--)
67  {
68  if (*Argb++ & 0xff000000)
69  return TRUE;
70  }
71 
72  Argb += delta;
73  }
74 
75  return FALSE;
76 }
77 
78 static VOID PhpConvertToPArgb32(
79  _In_ HDC hdc,
80  _Inout_ PULONG Argb,
81  _In_ HBITMAP Bitmap,
82  _In_ ULONG Width,
83  _In_ ULONG Height,
84  _In_ ULONG RowWidth
85  )
86 {
87  BITMAPINFO bitmapInfo;
88  PVOID bits;
89 
90  memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
91  bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
92  bitmapInfo.bmiHeader.biPlanes = 1;
93  bitmapInfo.bmiHeader.biCompression = BI_RGB;
94 
95  bitmapInfo.bmiHeader.biWidth = Width;
96  bitmapInfo.bmiHeader.biHeight = Height;
97  bitmapInfo.bmiHeader.biBitCount = 32;
98 
99  bits = PhAllocate(Width * sizeof(ULONG) * Height);
100 
101  if (GetDIBits(hdc, Bitmap, 0, Height, bits, &bitmapInfo, DIB_RGB_COLORS) == Height)
102  {
103  PULONG argbMask;
104  ULONG delta;
105  ULONG x;
106  ULONG y;
107 
108  argbMask = (PULONG)bits;
109  delta = RowWidth - Width;
110 
111  for (y = Height; y; y--)
112  {
113  for (x = Width; x; x--)
114  {
115  if (*argbMask++)
116  {
117  *Argb++ = 0; // transparent
118  }
119  else
120  {
121  *Argb++ |= 0xff000000; // opaque
122  }
123  }
124 
125  Argb += delta;
126  }
127  }
128 
129  PhFree(bits);
130 }
131 
132 static VOID PhpConvertToPArgb32IfNeeded(
133  _In_ HPAINTBUFFER PaintBuffer,
134  _In_ HDC hdc,
135  _In_ HICON Icon,
136  _In_ ULONG Width,
137  _In_ ULONG Height
138  )
139 {
140  RGBQUAD *quad;
141  ULONG rowWidth;
142 
143  if (SUCCEEDED(GetBufferedPaintBits_I(PaintBuffer, &quad, &rowWidth)))
144  {
145  PULONG argb = (PULONG)quad;
146 
147  if (!PhpHasAlpha(argb, Width, Height, rowWidth))
148  {
149  ICONINFO iconInfo;
150 
151  if (GetIconInfo(Icon, &iconInfo))
152  {
153  if (iconInfo.hbmMask)
154  {
155  PhpConvertToPArgb32(hdc, argb, iconInfo.hbmMask, Width, Height, rowWidth);
156  }
157 
158  DeleteObject(iconInfo.hbmColor);
159  DeleteObject(iconInfo.hbmMask);
160  }
161  }
162  }
163 }
164 
166  _In_ HICON Icon,
167  _In_ ULONG Width,
168  _In_ ULONG Height
169  )
170 {
171  HBITMAP bitmap;
172  RECT iconRectangle;
173  HDC screenHdc;
174  HDC hdc;
175  HBITMAP oldBitmap;
176  BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
177  BP_PAINTPARAMS paintParams = { sizeof(paintParams) };
178  HDC bufferHdc;
179  HPAINTBUFFER paintBuffer;
180 
181  iconRectangle.left = 0;
182  iconRectangle.top = 0;
183  iconRectangle.right = Width;
184  iconRectangle.bottom = Height;
185 
186  if (!ImportsInitialized)
187  {
188  HMODULE uxtheme;
189 
190  uxtheme = GetModuleHandle(L"uxtheme.dll");
191  BeginBufferedPaint_I = (PVOID)GetProcAddress(uxtheme, "BeginBufferedPaint");
192  EndBufferedPaint_I = (PVOID)GetProcAddress(uxtheme, "EndBufferedPaint");
193  GetBufferedPaintBits_I = (PVOID)GetProcAddress(uxtheme, "GetBufferedPaintBits");
194  ImportsInitialized = TRUE;
195  }
196 
197  if (!BeginBufferedPaint_I || !EndBufferedPaint_I || !GetBufferedPaintBits_I)
198  {
199  // Probably XP.
200 
201  screenHdc = GetDC(NULL);
202  hdc = CreateCompatibleDC(screenHdc);
203  bitmap = CreateCompatibleBitmap(screenHdc, Width, Height);
204  ReleaseDC(NULL, screenHdc);
205 
206  oldBitmap = SelectObject(hdc, bitmap);
207  FillRect(hdc, &iconRectangle, (HBRUSH)(COLOR_WINDOW + 1));
208  DrawIconEx(hdc, 0, 0, Icon, Width, Height, 0, NULL, DI_NORMAL);
209  SelectObject(hdc, oldBitmap);
210 
211  DeleteDC(hdc);
212 
213  return bitmap;
214  }
215 
216  screenHdc = GetDC(NULL);
217  hdc = CreateCompatibleDC(screenHdc);
218  bitmap = PhpCreateBitmap32(screenHdc, Width, Height, NULL);
219  ReleaseDC(NULL, screenHdc);
220  oldBitmap = SelectObject(hdc, bitmap);
221 
222  paintParams.dwFlags = BPPF_ERASE;
223  paintParams.pBlendFunction = &blendFunction;
224 
225  paintBuffer = BeginBufferedPaint_I(hdc, &iconRectangle, BPBF_DIB, &paintParams, &bufferHdc);
226  DrawIconEx(bufferHdc, 0, 0, Icon, Width, Height, 0, NULL, DI_NORMAL);
227  // If the icon did not have an alpha channel, we need to convert the buffer to PARGB.
228  PhpConvertToPArgb32IfNeeded(paintBuffer, hdc, Icon, Width, Height);
229  // This will write the buffer contents to the destination bitmap.
230  EndBufferedPaint_I(paintBuffer, TRUE);
231 
232  SelectObject(hdc, oldBitmap);
233  DeleteDC(hdc);
234 
235  return bitmap;
236 }