msxml3: Embed user/password in uri used to create a moniker.
[wine] / dlls / winemac.drv / image.c
1 /*
2  * MACDRV image functions
3  *
4  * Copyright 2013 Ken Thomases for CodeWeavers Inc.
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include "macdrv.h"
24 #include "winuser.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(image);
27
28 #include "pshpack1.h"
29
30 typedef struct
31 {
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;
41
42 typedef struct
43 {
44     WORD idReserved;
45     WORD idType;
46     WORD idCount;
47     GRPICONDIRENTRY idEntries[1];
48 } GRPICONDIR;
49
50 #include "poppack.h"
51
52
53 /***********************************************************************
54  *              create_cgimage_from_icon_bitmaps
55  */
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)
60 {
61     int i, has_alpha = FALSE;
62     DWORD *ptr;
63     CGBitmapInfo alpha_format;
64     CGColorSpaceRef colorspace;
65     CFDataRef data;
66     CGDataProviderRef provider;
67     CGImageRef cgimage;
68
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))
73     {
74         WARN("Could not draw frame %d (walk past end of frames).\n", istep);
75         return NULL;
76     }
77
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;
81
82     if (has_alpha)
83         alpha_format = kCGImageAlphaFirst;
84     else
85         alpha_format = kCGImageAlphaNoneSkipFirst;
86
87     colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
88     if (!colorspace)
89     {
90         WARN("failed to create colorspace\n");
91         return NULL;
92     }
93
94     data = CFDataCreate(NULL, (UInt8*)color_bits, color_size);
95     if (!data)
96     {
97         WARN("failed to create data\n");
98         CGColorSpaceRelease(colorspace);
99         return NULL;
100     }
101
102     provider = CGDataProviderCreateWithCFData(data);
103     CFRelease(data);
104     if (!provider)
105     {
106         WARN("failed to create data provider\n");
107         CGColorSpaceRelease(colorspace);
108         return NULL;
109     }
110
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);
116     if (!cgimage)
117     {
118         WARN("failed to create image\n");
119         return NULL;
120     }
121
122     /* if no alpha channel was drawn then generate it from the mask */
123     if (!has_alpha)
124     {
125         unsigned int width_bytes = (width + 31) / 32 * 4;
126         CGImageRef cgmask, temp;
127
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))
132         {
133             WARN("Failed to draw frame mask %d.\n", istep);
134             CGImageRelease(cgimage);
135             return NULL;
136         }
137
138         data = CFDataCreate(NULL, (UInt8*)mask_bits, mask_size);
139         if (!data)
140         {
141             WARN("failed to create data\n");
142             CGImageRelease(cgimage);
143             return NULL;
144         }
145
146         provider = CGDataProviderCreateWithCFData(data);
147         CFRelease(data);
148         if (!provider)
149         {
150             WARN("failed to create data provider\n");
151             CGImageRelease(cgimage);
152             return NULL;
153         }
154
155         cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
156         CGDataProviderRelease(provider);
157         if (!cgmask)
158         {
159             WARN("failed to create mask\n");
160             CGImageRelease(cgimage);
161             return NULL;
162         }
163
164         temp = CGImageCreateWithMask(cgimage, cgmask);
165         CGImageRelease(cgmask);
166         CGImageRelease(cgimage);
167         if (!temp)
168         {
169             WARN("failed to create masked image\n");
170             return NULL;
171         }
172         cgimage = temp;
173     }
174
175     return cgimage;
176 }
177
178
179 /***********************************************************************
180  *              create_cgimage_from_icon
181  *
182  * Create a CGImage from a Windows icon.
183  */
184 CGImageRef create_cgimage_from_icon(HANDLE icon, int width, int height)
185 {
186     CGImageRef ret = NULL;
187     HDC hdc;
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;
193
194     TRACE("icon %p width %d height %d\n", icon, width, height);
195
196     if (!width && !height)
197     {
198         ICONINFO info;
199         BITMAP bm;
200
201         if (!GetIconInfo(icon, &info))
202             return NULL;
203
204         GetObjectW(info.hbmMask, sizeof(bm), &bm);
205         if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
206         width = bm.bmWidth;
207         height = bm.bmHeight;
208
209         DeleteObject(info.hbmColor);
210         DeleteObject(info.hbmMask);
211     }
212
213     hdc = CreateCompatibleDC(0);
214
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);
228     if (!hbmColor)
229     {
230         WARN("failed to create DIB section for cursor color data\n");
231         goto cleanup;
232     }
233
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);
246     if (!hbmMask)
247     {
248         WARN("failed to create DIB section for cursor mask data\n");
249         goto cleanup;
250     }
251
252     ret = create_cgimage_from_icon_bitmaps(hdc, icon, hbmColor, color_bits, color_size, hbmMask,
253                                            mask_bits, mask_size, width, height, 0);
254
255 cleanup:
256     if (hbmColor) DeleteObject(hbmColor);
257     if (hbmMask) DeleteObject(hbmMask);
258     DeleteDC(hdc);
259     return ret;
260 }
261
262
263 /***********************************************************************
264  *              get_first_resource
265  *
266  * Helper for create_app_icon_images().  Enum proc for EnumResourceNamesW()
267  * which just gets the handle for the first resource and stops further
268  * enumeration.
269  */
270 static BOOL CALLBACK get_first_resource(HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR lparam)
271 {
272     HRSRC *res_info = (HRSRC*)lparam;
273
274     *res_info = FindResourceW(module, name, (LPCWSTR)RT_GROUP_ICON);
275     return FALSE;
276 }
277
278
279 /***********************************************************************
280  *              create_app_icon_images
281  */
282 CFArrayRef create_app_icon_images(void)
283 {
284     HRSRC res_info;
285     HGLOBAL res_data;
286     GRPICONDIR *icon_dir;
287     CFMutableArrayRef images = NULL;
288     int i;
289
290     TRACE("()\n");
291
292     res_info = NULL;
293     EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
294     if (!res_info)
295     {
296         WARN("found no RT_GROUP_ICON resource\n");
297         return NULL;
298     }
299
300     if (!(res_data = LoadResource(NULL, res_info)))
301     {
302         WARN("failed to load RT_GROUP_ICON resource\n");
303         return NULL;
304     }
305
306     if (!(icon_dir = LockResource(res_data)))
307     {
308         WARN("failed to lock RT_GROUP_ICON resource\n");
309         goto cleanup;
310     }
311
312     images = CFArrayCreateMutable(NULL, icon_dir->idCount, &kCFTypeArrayCallBacks);
313     if (!images)
314     {
315         WARN("failed to create images array\n");
316         goto cleanup;
317     }
318
319     for (i = 0; i < icon_dir->idCount; i++)
320     {
321         int width = icon_dir->idEntries[i].bWidth;
322         int height = icon_dir->idEntries[i].bHeight;
323         BOOL found_better_bpp = FALSE;
324         int j;
325         LPCWSTR name;
326         HGLOBAL icon_res_data;
327         BYTE *icon_bits;
328
329         if (!width) width = 256;
330         if (!height) height = 256;
331
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++)
337         {
338             int jwidth = icon_dir->idEntries[j].bWidth;
339             int jheight = icon_dir->idEntries[j].bHeight;
340
341             if (!jwidth) jwidth = 256;
342             if (!jheight) jheight = 256;
343
344             if (j != i && jwidth == width && jheight == height &&
345                 icon_dir->idEntries[j].wBitCount > icon_dir->idEntries[i].wBitCount)
346             {
347                 found_better_bpp = TRUE;
348                 break;
349             }
350         }
351
352         if (found_better_bpp) continue;
353
354         name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
355         res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
356         if (!res_info)
357         {
358             WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
359             continue;
360         }
361
362         icon_res_data = LoadResource(NULL, res_info);
363         if (!icon_res_data)
364         {
365             WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
366             continue;
367         }
368
369         icon_bits = LockResource(icon_res_data);
370         if (icon_bits)
371         {
372             static const BYTE png_magic[] = { 0x89, 0x50, 0x4e, 0x47 };
373             CGImageRef cgimage = NULL;
374
375             if (!memcmp(icon_bits, png_magic, sizeof(png_magic)))
376             {
377                 CFDataRef data = CFDataCreate(NULL, (UInt8*)icon_bits, icon_dir->idEntries[i].dwBytesInRes);
378                 if (data)
379                 {
380                     CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
381                     CFRelease(data);
382                     if (provider)
383                     {
384                         cgimage = CGImageCreateWithPNGDataProvider(provider, NULL, FALSE,
385                                                                    kCGRenderingIntentDefault);
386                         CGDataProviderRelease(provider);
387                     }
388                 }
389             }
390
391             if (!cgimage)
392             {
393                 HICON icon;
394                 icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
395                                                 TRUE, 0x00030000, width, height, 0);
396                 if (icon)
397                 {
398                     cgimage = create_cgimage_from_icon(icon, width, height);
399                     DestroyIcon(icon);
400                 }
401                 else
402                     WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
403             }
404
405             if (cgimage)
406             {
407                 CFArrayAppendValue(images, cgimage);
408                 CGImageRelease(cgimage);
409             }
410         }
411         else
412             WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
413
414         FreeResource(icon_res_data);
415     }
416
417 cleanup:
418     if (images && !CFArrayGetCount(images))
419     {
420         CFRelease(images);
421         images = NULL;
422     }
423     FreeResource(res_data);
424
425     return images;
426 }