2 * MACDRV image functions
4 * Copyright 2013 Ken Thomases for CodeWeavers Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 WINE_DEFAULT_DEBUG_CHANNEL(image
);
47 GRPICONDIRENTRY idEntries
[1];
53 /***********************************************************************
54 * create_cgimage_from_icon_bitmaps
56 CGImageRef
create_cgimage_from_icon_bitmaps(HDC hdc
, HANDLE icon
, HBITMAP hbmColor
,
57 unsigned char *color_bits
, int color_size
, HBITMAP hbmMask
,
58 unsigned char *mask_bits
, int mask_size
, int width
,
59 int height
, int istep
)
62 BOOL has_alpha
= FALSE
;
64 CGBitmapInfo alpha_format
;
65 CGColorSpaceRef colorspace
;
67 CGDataProviderRef provider
;
70 /* draw the cursor frame to a temporary buffer then create a CGImage from that */
71 memset(color_bits
, 0x00, color_size
);
72 SelectObject(hdc
, hbmColor
);
73 if (!DrawIconEx(hdc
, 0, 0, icon
, width
, height
, istep
, NULL
, DI_NORMAL
))
75 WARN("Could not draw frame %d (walk past end of frames).\n", istep
);
79 /* check if the cursor frame was drawn with an alpha channel */
80 for (i
= 0, ptr
= (DWORD
*)color_bits
; i
< width
* height
; i
++, ptr
++)
81 if ((has_alpha
= (*ptr
& 0xff000000) != 0)) break;
84 alpha_format
= kCGImageAlphaFirst
;
86 alpha_format
= kCGImageAlphaNoneSkipFirst
;
88 colorspace
= CGColorSpaceCreateWithName(kCGColorSpaceSRGB
);
91 WARN("failed to create colorspace\n");
95 data
= CFDataCreate(NULL
, (UInt8
*)color_bits
, color_size
);
98 WARN("failed to create data\n");
99 CGColorSpaceRelease(colorspace
);
103 provider
= CGDataProviderCreateWithCFData(data
);
107 WARN("failed to create data provider\n");
108 CGColorSpaceRelease(colorspace
);
112 cgimage
= CGImageCreate(width
, height
, 8, 32, width
* 4, colorspace
,
113 alpha_format
| kCGBitmapByteOrder32Little
,
114 provider
, NULL
, FALSE
, kCGRenderingIntentDefault
);
115 CGDataProviderRelease(provider
);
116 CGColorSpaceRelease(colorspace
);
119 WARN("failed to create image\n");
123 /* if no alpha channel was drawn then generate it from the mask */
126 unsigned int width_bytes
= (width
+ 31) / 32 * 4;
127 CGImageRef cgmask
, temp
;
129 /* draw the cursor mask to a temporary buffer */
130 memset(mask_bits
, 0xFF, mask_size
);
131 SelectObject(hdc
, hbmMask
);
132 if (!DrawIconEx(hdc
, 0, 0, icon
, width
, height
, istep
, NULL
, DI_MASK
))
134 WARN("Failed to draw frame mask %d.\n", istep
);
135 CGImageRelease(cgimage
);
139 data
= CFDataCreate(NULL
, (UInt8
*)mask_bits
, mask_size
);
142 WARN("failed to create data\n");
143 CGImageRelease(cgimage
);
147 provider
= CGDataProviderCreateWithCFData(data
);
151 WARN("failed to create data provider\n");
152 CGImageRelease(cgimage
);
156 cgmask
= CGImageMaskCreate(width
, height
, 1, 1, width_bytes
, provider
, NULL
, FALSE
);
157 CGDataProviderRelease(provider
);
160 WARN("failed to create mask\n");
161 CGImageRelease(cgimage
);
165 temp
= CGImageCreateWithMask(cgimage
, cgmask
);
166 CGImageRelease(cgmask
);
167 CGImageRelease(cgimage
);
170 WARN("failed to create masked image\n");
180 /***********************************************************************
181 * create_cgimage_from_icon
183 * Create a CGImage from a Windows icon.
185 CGImageRef
create_cgimage_from_icon(HANDLE icon
, int width
, int height
)
187 CGImageRef ret
= NULL
;
189 char buffer
[FIELD_OFFSET(BITMAPINFO
, bmiColors
[256])];
190 BITMAPINFO
*bitmapinfo
= (BITMAPINFO
*)buffer
;
191 unsigned char *color_bits
, *mask_bits
;
192 HBITMAP hbmColor
= 0, hbmMask
= 0;
193 int color_size
, mask_size
;
195 TRACE("icon %p width %d height %d\n", icon
, width
, height
);
197 if (!width
&& !height
)
202 if (!GetIconInfo(icon
, &info
))
205 GetObjectW(info
.hbmMask
, sizeof(bm
), &bm
);
206 if (!info
.hbmColor
) bm
.bmHeight
= max(1, bm
.bmHeight
/ 2);
208 height
= bm
.bmHeight
;
209 TRACE("new width %d height %d\n", width
, height
);
211 DeleteObject(info
.hbmColor
);
212 DeleteObject(info
.hbmMask
);
215 hdc
= CreateCompatibleDC(0);
217 bitmapinfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
218 bitmapinfo
->bmiHeader
.biWidth
= width
;
219 bitmapinfo
->bmiHeader
.biHeight
= -height
;
220 bitmapinfo
->bmiHeader
.biPlanes
= 1;
221 bitmapinfo
->bmiHeader
.biCompression
= BI_RGB
;
222 bitmapinfo
->bmiHeader
.biXPelsPerMeter
= 0;
223 bitmapinfo
->bmiHeader
.biYPelsPerMeter
= 0;
224 bitmapinfo
->bmiHeader
.biClrUsed
= 0;
225 bitmapinfo
->bmiHeader
.biClrImportant
= 0;
226 bitmapinfo
->bmiHeader
.biBitCount
= 32;
227 color_size
= width
* height
* 4;
228 bitmapinfo
->bmiHeader
.biSizeImage
= color_size
;
229 hbmColor
= CreateDIBSection(hdc
, bitmapinfo
, DIB_RGB_COLORS
, (VOID
**) &color_bits
, NULL
, 0);
232 WARN("failed to create DIB section for cursor color data\n");
236 bitmapinfo
->bmiHeader
.biBitCount
= 1;
237 bitmapinfo
->bmiColors
[0].rgbRed
= 0;
238 bitmapinfo
->bmiColors
[0].rgbGreen
= 0;
239 bitmapinfo
->bmiColors
[0].rgbBlue
= 0;
240 bitmapinfo
->bmiColors
[0].rgbReserved
= 0;
241 bitmapinfo
->bmiColors
[1].rgbRed
= 0xff;
242 bitmapinfo
->bmiColors
[1].rgbGreen
= 0xff;
243 bitmapinfo
->bmiColors
[1].rgbBlue
= 0xff;
244 bitmapinfo
->bmiColors
[1].rgbReserved
= 0;
245 mask_size
= ((width
+ 31) / 32 * 4) * height
;
246 bitmapinfo
->bmiHeader
.biSizeImage
= mask_size
;
247 hbmMask
= CreateDIBSection(hdc
, bitmapinfo
, DIB_RGB_COLORS
, (VOID
**) &mask_bits
, NULL
, 0);
250 WARN("failed to create DIB section for cursor mask data\n");
254 ret
= create_cgimage_from_icon_bitmaps(hdc
, icon
, hbmColor
, color_bits
, color_size
, hbmMask
,
255 mask_bits
, mask_size
, width
, height
, 0);
258 if (hbmColor
) DeleteObject(hbmColor
);
259 if (hbmMask
) DeleteObject(hbmMask
);
265 /***********************************************************************
268 * Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
269 * which just gets the handle for the first resource and stops further
272 static BOOL CALLBACK
get_first_resource(HMODULE module
, LPCWSTR type
, LPWSTR name
, LONG_PTR lparam
)
274 HRSRC
*res_info
= (HRSRC
*)lparam
;
276 *res_info
= FindResourceW(module
, name
, (LPCWSTR
)RT_GROUP_ICON
);
281 /***********************************************************************
282 * create_app_icon_images
284 CFArrayRef
create_app_icon_images(void)
288 GRPICONDIR
*icon_dir
;
289 CFMutableArrayRef images
= NULL
;
295 EnumResourceNamesW(NULL
, (LPCWSTR
)RT_GROUP_ICON
, get_first_resource
, (LONG_PTR
)&res_info
);
298 WARN("found no RT_GROUP_ICON resource\n");
302 if (!(res_data
= LoadResource(NULL
, res_info
)))
304 WARN("failed to load RT_GROUP_ICON resource\n");
308 if (!(icon_dir
= LockResource(res_data
)))
310 WARN("failed to lock RT_GROUP_ICON resource\n");
314 images
= CFArrayCreateMutable(NULL
, icon_dir
->idCount
, &kCFTypeArrayCallBacks
);
317 WARN("failed to create images array\n");
321 for (i
= 0; i
< icon_dir
->idCount
; i
++)
323 int width
= icon_dir
->idEntries
[i
].bWidth
;
324 int height
= icon_dir
->idEntries
[i
].bHeight
;
325 BOOL found_better_bpp
= FALSE
;
328 HGLOBAL icon_res_data
;
331 if (!width
) width
= 256;
332 if (!height
) height
= 256;
334 /* If there's another icon at the same size but with better
335 color depth, skip this one. We end up making CGImages that
336 are all 32 bits per pixel, so Cocoa doesn't get the original
337 color depth info to pick the best representation itself. */
338 for (j
= 0; j
< icon_dir
->idCount
; j
++)
340 int jwidth
= icon_dir
->idEntries
[j
].bWidth
;
341 int jheight
= icon_dir
->idEntries
[j
].bHeight
;
343 if (!jwidth
) jwidth
= 256;
344 if (!jheight
) jheight
= 256;
346 if (j
!= i
&& jwidth
== width
&& jheight
== height
&&
347 icon_dir
->idEntries
[j
].wBitCount
> icon_dir
->idEntries
[i
].wBitCount
)
349 found_better_bpp
= TRUE
;
354 if (found_better_bpp
) continue;
356 name
= MAKEINTRESOURCEW(icon_dir
->idEntries
[i
].nID
);
357 res_info
= FindResourceW(NULL
, name
, (LPCWSTR
)RT_ICON
);
360 WARN("failed to find RT_ICON resource %d with ID %hd\n", i
, icon_dir
->idEntries
[i
].nID
);
364 icon_res_data
= LoadResource(NULL
, res_info
);
367 WARN("failed to load icon %d with ID %hd\n", i
, icon_dir
->idEntries
[i
].nID
);
371 icon_bits
= LockResource(icon_res_data
);
374 static const BYTE png_magic
[] = { 0x89, 0x50, 0x4e, 0x47 };
375 CGImageRef cgimage
= NULL
;
377 if (!memcmp(icon_bits
, png_magic
, sizeof(png_magic
)))
379 CFDataRef data
= CFDataCreate(NULL
, (UInt8
*)icon_bits
, icon_dir
->idEntries
[i
].dwBytesInRes
);
382 CGDataProviderRef provider
= CGDataProviderCreateWithCFData(data
);
386 cgimage
= CGImageCreateWithPNGDataProvider(provider
, NULL
, FALSE
,
387 kCGRenderingIntentDefault
);
388 CGDataProviderRelease(provider
);
396 icon
= CreateIconFromResourceEx(icon_bits
, icon_dir
->idEntries
[i
].dwBytesInRes
,
397 TRUE
, 0x00030000, width
, height
, 0);
400 cgimage
= create_cgimage_from_icon(icon
, width
, height
);
404 WARN("failed to create icon %d from resource with ID %hd\n", i
, icon_dir
->idEntries
[i
].nID
);
409 CFArrayAppendValue(images
, cgimage
);
410 CGImageRelease(cgimage
);
414 WARN("failed to lock RT_ICON resource %d with ID %hd\n", i
, icon_dir
->idEntries
[i
].nID
);
416 FreeResource(icon_res_data
);
420 if (images
&& !CFArrayGetCount(images
))
425 FreeResource(res_data
);