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;
208 TRACE("new width %d height %d\n", width, height);
210 DeleteObject(info.hbmColor);
211 DeleteObject(info.hbmMask);
214 hdc = CreateCompatibleDC(0);
216 bitmapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
217 bitmapinfo->bmiHeader.biWidth = width;
218 bitmapinfo->bmiHeader.biHeight = -height;
219 bitmapinfo->bmiHeader.biPlanes = 1;
220 bitmapinfo->bmiHeader.biCompression = BI_RGB;
221 bitmapinfo->bmiHeader.biXPelsPerMeter = 0;
222 bitmapinfo->bmiHeader.biYPelsPerMeter = 0;
223 bitmapinfo->bmiHeader.biClrUsed = 0;
224 bitmapinfo->bmiHeader.biClrImportant = 0;
225 bitmapinfo->bmiHeader.biBitCount = 32;
226 color_size = width * height * 4;
227 bitmapinfo->bmiHeader.biSizeImage = color_size;
228 hbmColor = CreateDIBSection(hdc, bitmapinfo, DIB_RGB_COLORS, (VOID **) &color_bits, NULL, 0);
231 WARN("failed to create DIB section for cursor color data\n");
235 bitmapinfo->bmiHeader.biBitCount = 1;
236 bitmapinfo->bmiColors[0].rgbRed = 0;
237 bitmapinfo->bmiColors[0].rgbGreen = 0;
238 bitmapinfo->bmiColors[0].rgbBlue = 0;
239 bitmapinfo->bmiColors[0].rgbReserved = 0;
240 bitmapinfo->bmiColors[1].rgbRed = 0xff;
241 bitmapinfo->bmiColors[1].rgbGreen = 0xff;
242 bitmapinfo->bmiColors[1].rgbBlue = 0xff;
243 bitmapinfo->bmiColors[1].rgbReserved = 0;
244 mask_size = ((width + 31) / 32 * 4) * height;
245 bitmapinfo->bmiHeader.biSizeImage = mask_size;
246 hbmMask = CreateDIBSection(hdc, bitmapinfo, DIB_RGB_COLORS, (VOID **) &mask_bits, NULL, 0);
249 WARN("failed to create DIB section for cursor mask data\n");
253 ret = create_cgimage_from_icon_bitmaps(hdc, icon, hbmColor, color_bits, color_size, hbmMask,
254 mask_bits, mask_size, width, height, 0);
257 if (hbmColor) DeleteObject(hbmColor);
258 if (hbmMask) DeleteObject(hbmMask);
264 /***********************************************************************
267 * Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
268 * which just gets the handle for the first resource and stops further
271 static BOOL CALLBACK get_first_resource(HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR lparam)
273 HRSRC *res_info = (HRSRC*)lparam;
275 *res_info = FindResourceW(module, name, (LPCWSTR)RT_GROUP_ICON);
280 /***********************************************************************
281 * create_app_icon_images
283 CFArrayRef create_app_icon_images(void)
287 GRPICONDIR *icon_dir;
288 CFMutableArrayRef images = NULL;
294 EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
297 WARN("found no RT_GROUP_ICON resource\n");
301 if (!(res_data = LoadResource(NULL, res_info)))
303 WARN("failed to load RT_GROUP_ICON resource\n");
307 if (!(icon_dir = LockResource(res_data)))
309 WARN("failed to lock RT_GROUP_ICON resource\n");
313 images = CFArrayCreateMutable(NULL, icon_dir->idCount, &kCFTypeArrayCallBacks);
316 WARN("failed to create images array\n");
320 for (i = 0; i < icon_dir->idCount; i++)
322 int width = icon_dir->idEntries[i].bWidth;
323 int height = icon_dir->idEntries[i].bHeight;
324 BOOL found_better_bpp = FALSE;
327 HGLOBAL icon_res_data;
330 if (!width) width = 256;
331 if (!height) height = 256;
333 /* If there's another icon at the same size but with better
334 color depth, skip this one. We end up making CGImages that
335 are all 32 bits per pixel, so Cocoa doesn't get the original
336 color depth info to pick the best representation itself. */
337 for (j = 0; j < icon_dir->idCount; j++)
339 int jwidth = icon_dir->idEntries[j].bWidth;
340 int jheight = icon_dir->idEntries[j].bHeight;
342 if (!jwidth) jwidth = 256;
343 if (!jheight) jheight = 256;
345 if (j != i && jwidth == width && jheight == height &&
346 icon_dir->idEntries[j].wBitCount > icon_dir->idEntries[i].wBitCount)
348 found_better_bpp = TRUE;
353 if (found_better_bpp) continue;
355 name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
356 res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
359 WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
363 icon_res_data = LoadResource(NULL, res_info);
366 WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
370 icon_bits = LockResource(icon_res_data);
373 static const BYTE png_magic[] = { 0x89, 0x50, 0x4e, 0x47 };
374 CGImageRef cgimage = NULL;
376 if (!memcmp(icon_bits, png_magic, sizeof(png_magic)))
378 CFDataRef data = CFDataCreate(NULL, (UInt8*)icon_bits, icon_dir->idEntries[i].dwBytesInRes);
381 CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
385 cgimage = CGImageCreateWithPNGDataProvider(provider, NULL, FALSE,
386 kCGRenderingIntentDefault);
387 CGDataProviderRelease(provider);
395 icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
396 TRUE, 0x00030000, width, height, 0);
399 cgimage = create_cgimage_from_icon(icon, width, height);
403 WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
408 CFArrayAppendValue(images, cgimage);
409 CGImageRelease(cgimage);
413 WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
415 FreeResource(icon_res_data);
419 if (images && !CFArrayGetCount(images))
424 FreeResource(res_data);