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)
61 int i, has_alpha = FALSE;
63 CGBitmapInfo alpha_format;
64 CGColorSpaceRef colorspace;
66 CGDataProviderRef provider;
69 /* draw the cursor frame to a temporary buffer then create a CGImage from that */
70 memset(color_bits, 0x00, color_size);
71 SelectObject(hdc, hbmColor);
72 if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_NORMAL))
74 WARN("Could not draw frame %d (walk past end of frames).\n", istep);
78 /* check if the cursor frame was drawn with an alpha channel */
79 for (i = 0, ptr = (DWORD*)color_bits; i < width * height; i++, ptr++)
80 if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
83 alpha_format = kCGImageAlphaFirst;
85 alpha_format = kCGImageAlphaNoneSkipFirst;
87 colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
90 WARN("failed to create colorspace\n");
94 data = CFDataCreate(NULL, (UInt8*)color_bits, color_size);
97 WARN("failed to create data\n");
98 CGColorSpaceRelease(colorspace);
102 provider = CGDataProviderCreateWithCFData(data);
106 WARN("failed to create data provider\n");
107 CGColorSpaceRelease(colorspace);
111 cgimage = CGImageCreate(width, height, 8, 32, width * 4, colorspace,
112 alpha_format | kCGBitmapByteOrder32Little,
113 provider, NULL, FALSE, kCGRenderingIntentDefault);
114 CGDataProviderRelease(provider);
115 CGColorSpaceRelease(colorspace);
118 WARN("failed to create image\n");
122 /* if no alpha channel was drawn then generate it from the mask */
125 unsigned int width_bytes = (width + 31) / 32 * 4;
126 CGImageRef cgmask, temp;
128 /* draw the cursor mask to a temporary buffer */
129 memset(mask_bits, 0xFF, mask_size);
130 SelectObject(hdc, hbmMask);
131 if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_MASK))
133 WARN("Failed to draw frame mask %d.\n", istep);
134 CGImageRelease(cgimage);
138 data = CFDataCreate(NULL, (UInt8*)mask_bits, mask_size);
141 WARN("failed to create data\n");
142 CGImageRelease(cgimage);
146 provider = CGDataProviderCreateWithCFData(data);
150 WARN("failed to create data provider\n");
151 CGImageRelease(cgimage);
155 cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
156 CGDataProviderRelease(provider);
159 WARN("failed to create mask\n");
160 CGImageRelease(cgimage);
164 temp = CGImageCreateWithMask(cgimage, cgmask);
165 CGImageRelease(cgmask);
166 CGImageRelease(cgimage);
169 WARN("failed to create masked image\n");
179 /***********************************************************************
180 * create_cgimage_from_icon
182 * Create a CGImage from a Windows icon.
184 CGImageRef create_cgimage_from_icon(HANDLE icon, int width, int height)
186 CGImageRef ret = NULL;
188 char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
189 BITMAPINFO *bitmapinfo = (BITMAPINFO*)buffer;
190 unsigned char *color_bits, *mask_bits;
191 HBITMAP hbmColor = 0, hbmMask = 0;
192 int color_size, mask_size;
194 TRACE("icon %p width %d height %d\n", icon, width, height);
196 if (!width && !height)
201 if (!GetIconInfo(icon, &info))
204 GetObjectW(info.hbmMask, sizeof(bm), &bm);
205 if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
207 height = bm.bmHeight;
209 DeleteObject(info.hbmColor);
210 DeleteObject(info.hbmMask);
213 hdc = CreateCompatibleDC(0);
215 bitmapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
216 bitmapinfo->bmiHeader.biWidth = width;
217 bitmapinfo->bmiHeader.biHeight = -height;
218 bitmapinfo->bmiHeader.biPlanes = 1;
219 bitmapinfo->bmiHeader.biCompression = BI_RGB;
220 bitmapinfo->bmiHeader.biXPelsPerMeter = 0;
221 bitmapinfo->bmiHeader.biYPelsPerMeter = 0;
222 bitmapinfo->bmiHeader.biClrUsed = 0;
223 bitmapinfo->bmiHeader.biClrImportant = 0;
224 bitmapinfo->bmiHeader.biBitCount = 32;
225 color_size = width * height * 4;
226 bitmapinfo->bmiHeader.biSizeImage = color_size;
227 hbmColor = CreateDIBSection(hdc, bitmapinfo, DIB_RGB_COLORS, (VOID **) &color_bits, NULL, 0);
230 WARN("failed to create DIB section for cursor color data\n");
234 bitmapinfo->bmiHeader.biBitCount = 1;
235 bitmapinfo->bmiColors[0].rgbRed = 0;
236 bitmapinfo->bmiColors[0].rgbGreen = 0;
237 bitmapinfo->bmiColors[0].rgbBlue = 0;
238 bitmapinfo->bmiColors[0].rgbReserved = 0;
239 bitmapinfo->bmiColors[1].rgbRed = 0xff;
240 bitmapinfo->bmiColors[1].rgbGreen = 0xff;
241 bitmapinfo->bmiColors[1].rgbBlue = 0xff;
242 bitmapinfo->bmiColors[1].rgbReserved = 0;
243 mask_size = ((width + 31) / 32 * 4) * height;
244 bitmapinfo->bmiHeader.biSizeImage = mask_size;
245 hbmMask = CreateDIBSection(hdc, bitmapinfo, DIB_RGB_COLORS, (VOID **) &mask_bits, NULL, 0);
248 WARN("failed to create DIB section for cursor mask data\n");
252 ret = create_cgimage_from_icon_bitmaps(hdc, icon, hbmColor, color_bits, color_size, hbmMask,
253 mask_bits, mask_size, width, height, 0);
256 if (hbmColor) DeleteObject(hbmColor);
257 if (hbmMask) DeleteObject(hbmMask);
263 /***********************************************************************
266 * Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
267 * which just gets the handle for the first resource and stops further
270 static BOOL CALLBACK get_first_resource(HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR lparam)
272 HRSRC *res_info = (HRSRC*)lparam;
274 *res_info = FindResourceW(module, name, (LPCWSTR)RT_GROUP_ICON);
279 /***********************************************************************
280 * create_app_icon_images
282 CFArrayRef create_app_icon_images(void)
286 GRPICONDIR *icon_dir;
287 CFMutableArrayRef images = NULL;
293 EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
296 WARN("found no RT_GROUP_ICON resource\n");
300 if (!(res_data = LoadResource(NULL, res_info)))
302 WARN("failed to load RT_GROUP_ICON resource\n");
306 if (!(icon_dir = LockResource(res_data)))
308 WARN("failed to lock RT_GROUP_ICON resource\n");
312 images = CFArrayCreateMutable(NULL, icon_dir->idCount, &kCFTypeArrayCallBacks);
315 WARN("failed to create images array\n");
319 for (i = 0; i < icon_dir->idCount; i++)
321 int width = icon_dir->idEntries[i].bWidth;
322 int height = icon_dir->idEntries[i].bHeight;
323 BOOL found_better_bpp = FALSE;
326 HGLOBAL icon_res_data;
329 if (!width) width = 256;
330 if (!height) height = 256;
332 /* If there's another icon at the same size but with better
333 color depth, skip this one. We end up making CGImages that
334 are all 32 bits per pixel, so Cocoa doesn't get the original
335 color depth info to pick the best representation itself. */
336 for (j = 0; j < icon_dir->idCount; j++)
338 int jwidth = icon_dir->idEntries[j].bWidth;
339 int jheight = icon_dir->idEntries[j].bHeight;
341 if (!jwidth) jwidth = 256;
342 if (!jheight) jheight = 256;
344 if (j != i && jwidth == width && jheight == height &&
345 icon_dir->idEntries[j].wBitCount > icon_dir->idEntries[i].wBitCount)
347 found_better_bpp = TRUE;
352 if (found_better_bpp) continue;
354 name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
355 res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
358 WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
362 icon_res_data = LoadResource(NULL, res_info);
365 WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
369 icon_bits = LockResource(icon_res_data);
372 static const BYTE png_magic[] = { 0x89, 0x50, 0x4e, 0x47 };
373 CGImageRef cgimage = NULL;
375 if (!memcmp(icon_bits, png_magic, sizeof(png_magic)))
377 CFDataRef data = CFDataCreate(NULL, (UInt8*)icon_bits, icon_dir->idEntries[i].dwBytesInRes);
380 CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
384 cgimage = CGImageCreateWithPNGDataProvider(provider, NULL, FALSE,
385 kCGRenderingIntentDefault);
386 CGDataProviderRelease(provider);
394 icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
395 TRUE, 0x00030000, width, height, 0);
398 cgimage = create_cgimage_from_icon(icon, width, height);
402 WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
407 CFArrayAppendValue(images, cgimage);
408 CGImageRelease(cgimage);
412 WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
414 FreeResource(icon_res_data);
418 if (images && !CFArrayGetCount(images))
423 FreeResource(res_data);