comctl32: Fix a typo in comment.
[wine.git] / dlls / winemac.drv / image.c
blob4090350c4483acc6070e8ce0794afad7f3cbe5d9
1 /*
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
21 #include "config.h"
23 #include "macdrv.h"
24 #include "winuser.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(image);
28 #include "pshpack1.h"
30 typedef struct
32 BYTE bWidth;
33 BYTE bHeight;
34 BYTE bColorCount;
35 BYTE bReserved;
36 WORD wPlanes;
37 WORD wBitCount;
38 DWORD dwBytesInRes;
39 WORD nID;
40 } GRPICONDIRENTRY;
42 typedef struct
44 WORD idReserved;
45 WORD idType;
46 WORD idCount;
47 GRPICONDIRENTRY idEntries[1];
48 } GRPICONDIR;
50 #include "poppack.h"
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;
62 BOOL has_alpha = FALSE;
63 DWORD *ptr;
64 CGBitmapInfo alpha_format;
65 CGColorSpaceRef colorspace;
66 CFDataRef data;
67 CGDataProviderRef provider;
68 CGImageRef cgimage;
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);
76 return NULL;
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;
83 if (has_alpha)
84 alpha_format = kCGImageAlphaFirst;
85 else
86 alpha_format = kCGImageAlphaNoneSkipFirst;
88 colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
89 if (!colorspace)
91 WARN("failed to create colorspace\n");
92 return NULL;
95 data = CFDataCreate(NULL, (UInt8*)color_bits, color_size);
96 if (!data)
98 WARN("failed to create data\n");
99 CGColorSpaceRelease(colorspace);
100 return NULL;
103 provider = CGDataProviderCreateWithCFData(data);
104 CFRelease(data);
105 if (!provider)
107 WARN("failed to create data provider\n");
108 CGColorSpaceRelease(colorspace);
109 return NULL;
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);
117 if (!cgimage)
119 WARN("failed to create image\n");
120 return NULL;
123 /* if no alpha channel was drawn then generate it from the mask */
124 if (!has_alpha)
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);
136 return NULL;
139 data = CFDataCreate(NULL, (UInt8*)mask_bits, mask_size);
140 if (!data)
142 WARN("failed to create data\n");
143 CGImageRelease(cgimage);
144 return NULL;
147 provider = CGDataProviderCreateWithCFData(data);
148 CFRelease(data);
149 if (!provider)
151 WARN("failed to create data provider\n");
152 CGImageRelease(cgimage);
153 return NULL;
156 cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
157 CGDataProviderRelease(provider);
158 if (!cgmask)
160 WARN("failed to create mask\n");
161 CGImageRelease(cgimage);
162 return NULL;
165 temp = CGImageCreateWithMask(cgimage, cgmask);
166 CGImageRelease(cgmask);
167 CGImageRelease(cgimage);
168 if (!temp)
170 WARN("failed to create masked image\n");
171 return NULL;
173 cgimage = temp;
176 return cgimage;
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;
188 HDC hdc;
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)
199 ICONINFO info;
200 BITMAP bm;
202 if (!GetIconInfo(icon, &info))
203 return NULL;
205 GetObjectW(info.hbmMask, sizeof(bm), &bm);
206 if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
207 width = bm.bmWidth;
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);
230 if (!hbmColor)
232 WARN("failed to create DIB section for cursor color data\n");
233 goto cleanup;
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);
248 if (!hbmMask)
250 WARN("failed to create DIB section for cursor mask data\n");
251 goto cleanup;
254 ret = create_cgimage_from_icon_bitmaps(hdc, icon, hbmColor, color_bits, color_size, hbmMask,
255 mask_bits, mask_size, width, height, 0);
257 cleanup:
258 if (hbmColor) DeleteObject(hbmColor);
259 if (hbmMask) DeleteObject(hbmMask);
260 DeleteDC(hdc);
261 return ret;
265 /***********************************************************************
266 * get_first_resource
268 * Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
269 * which just gets the handle for the first resource and stops further
270 * enumeration.
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);
277 return FALSE;
281 /***********************************************************************
282 * create_app_icon_images
284 CFArrayRef create_app_icon_images(void)
286 HRSRC res_info;
287 HGLOBAL res_data;
288 GRPICONDIR *icon_dir;
289 CFMutableArrayRef images = NULL;
290 int i;
292 TRACE("()\n");
294 res_info = NULL;
295 EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
296 if (!res_info)
298 WARN("found no RT_GROUP_ICON resource\n");
299 return NULL;
302 if (!(res_data = LoadResource(NULL, res_info)))
304 WARN("failed to load RT_GROUP_ICON resource\n");
305 return NULL;
308 if (!(icon_dir = LockResource(res_data)))
310 WARN("failed to lock RT_GROUP_ICON resource\n");
311 goto cleanup;
314 images = CFArrayCreateMutable(NULL, icon_dir->idCount, &kCFTypeArrayCallBacks);
315 if (!images)
317 WARN("failed to create images array\n");
318 goto cleanup;
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;
326 int j;
327 LPCWSTR name;
328 HGLOBAL icon_res_data;
329 BYTE *icon_bits;
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;
350 break;
354 if (found_better_bpp) continue;
356 name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
357 res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
358 if (!res_info)
360 WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
361 continue;
364 icon_res_data = LoadResource(NULL, res_info);
365 if (!icon_res_data)
367 WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
368 continue;
371 icon_bits = LockResource(icon_res_data);
372 if (icon_bits)
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);
380 if (data)
382 CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
383 CFRelease(data);
384 if (provider)
386 cgimage = CGImageCreateWithPNGDataProvider(provider, NULL, FALSE,
387 kCGRenderingIntentDefault);
388 CGDataProviderRelease(provider);
393 if (!cgimage)
395 HICON icon;
396 icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
397 TRUE, 0x00030000, width, height, 0);
398 if (icon)
400 cgimage = create_cgimage_from_icon(icon, width, height);
401 DestroyIcon(icon);
403 else
404 WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
407 if (cgimage)
409 CFArrayAppendValue(images, cgimage);
410 CGImageRelease(cgimage);
413 else
414 WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
416 FreeResource(icon_res_data);
419 cleanup:
420 if (images && !CFArrayGetCount(images))
422 CFRelease(images);
423 images = NULL;
425 FreeResource(res_data);
427 return images;