xref: /reactos/dll/win32/gdiplus/image.c (revision ebaf247c)
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  * Copyright (C) 2012,2016 Dmitry Timoshkov
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 #include <assert.h>
22 
23 #define NONAMELESSUNION
24 
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "wingdi.h"
29 
30 #define COBJMACROS
31 #include "objbase.h"
32 #include "olectl.h"
33 #include "ole2.h"
34 
35 #include "initguid.h"
36 #include "wincodec.h"
37 #include "gdiplus.h"
38 #include "gdiplus_private.h"
39 #include "wine/debug.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
42 
43 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
44 
45 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
46 #define WMF_PLACEABLE_KEY 0x9ac6cdd7
47 
48 static const struct
49 {
50     const WICPixelFormatGUID *wic_format;
51     PixelFormat gdip_format;
52     /* predefined palette type to use for pixel format conversions */
53     WICBitmapPaletteType palette_type;
54 } pixel_formats[] =
55 {
56     { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
57     { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
58     { &GUID_WICPixelFormat4bppIndexed, PixelFormat4bppIndexed, WICBitmapPaletteTypeFixedHalftone8 },
59     { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 },
60     { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 },
61     { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, WICBitmapPaletteTypeFixedHalftone256 },
62     { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
63     { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
64     { &GUID_WICPixelFormat48bppRGB, PixelFormat48bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
65     { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedHalftone256 },
66     { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, WICBitmapPaletteTypeFixedHalftone256 },
67     { &GUID_WICPixelFormat32bppCMYK, PixelFormat32bppCMYK, WICBitmapPaletteTypeFixedHalftone256 },
68     { &GUID_WICPixelFormat32bppGrayFloat, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedGray256 },
69     { &GUID_WICPixelFormat64bppCMYK, PixelFormat48bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
70     { &GUID_WICPixelFormat64bppRGBA, PixelFormat48bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
71     { NULL }
72 };
73 
74 static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteType palette_type)
75 {
76     HRESULT hr;
77     IWICImagingFactory *factory;
78     IWICPalette *wic_palette;
79     ColorPalette *palette = NULL;
80 
81     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
82     if (hr != S_OK) return NULL;
83 
84     hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
85     if (hr == S_OK)
86     {
87         hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
88         if (frame)
89             hr = IWICBitmapFrameDecode_CopyPalette(frame, wic_palette);
90         if (hr != S_OK)
91         {
92             TRACE("using predefined palette %#x\n", palette_type);
93             hr = IWICPalette_InitializePredefined(wic_palette, palette_type, FALSE);
94         }
95         if (hr == S_OK)
96         {
97             WICBitmapPaletteType type;
98             BOOL alpha;
99             UINT count;
100 
101             IWICPalette_GetColorCount(wic_palette, &count);
102             palette = heap_alloc(2 * sizeof(UINT) + count * sizeof(ARGB));
103             IWICPalette_GetColors(wic_palette, count, palette->Entries, &palette->Count);
104 
105             IWICPalette_GetType(wic_palette, &type);
106             switch(type) {
107                 case WICBitmapPaletteTypeFixedGray4:
108                 case WICBitmapPaletteTypeFixedGray16:
109                 case WICBitmapPaletteTypeFixedGray256:
110                     palette->Flags = PaletteFlagsGrayScale;
111                     break;
112                 case WICBitmapPaletteTypeFixedHalftone8:
113                 case WICBitmapPaletteTypeFixedHalftone27:
114                 case WICBitmapPaletteTypeFixedHalftone64:
115                 case WICBitmapPaletteTypeFixedHalftone125:
116                 case WICBitmapPaletteTypeFixedHalftone216:
117                 case WICBitmapPaletteTypeFixedHalftone252:
118                 case WICBitmapPaletteTypeFixedHalftone256:
119                     palette->Flags = PaletteFlagsHalftone;
120                     break;
121                 default:
122                     palette->Flags = 0;
123             }
124             IWICPalette_HasAlpha(wic_palette, &alpha);
125             if(alpha)
126                 palette->Flags |= PaletteFlagsHasAlpha;
127         }
128         IWICPalette_Release(wic_palette);
129     }
130     IWICImagingFactory_Release(factory);
131     return palette;
132 }
133 
134 GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect* effect,
135     RECT* roi, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
136 {
137     FIXME("(%p %p %p %d %p %p): stub\n", bitmap, effect, roi, useAuxData, auxData, auxDataSize);
138     /*
139      * Note: According to Jose Roca's GDI+ docs, this function is not
140      * implemented in Windows's GDI+.
141      */
142     return NotImplemented;
143 }
144 
145 GpStatus WINGDIPAPI GdipBitmapCreateApplyEffect(GpBitmap** inputBitmaps,
146     INT numInputs, CGpEffect* effect, RECT* roi, RECT* outputRect,
147     GpBitmap** outputBitmap, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
148 {
149     FIXME("(%p %d %p %p %p %p %d %p %p): stub\n", inputBitmaps, numInputs, effect, roi, outputRect, outputBitmap, useAuxData, auxData, auxDataSize);
150     /*
151      * Note: According to Jose Roca's GDI+ docs, this function is not
152      * implemented in Windows's GDI+.
153      */
154     return NotImplemented;
155 }
156 
157 static inline void getpixel_1bppIndexed(BYTE *index, const BYTE *row, UINT x)
158 {
159     *index = (row[x/8]>>(7-x%8)) & 1;
160 }
161 
162 static inline void getpixel_4bppIndexed(BYTE *index, const BYTE *row, UINT x)
163 {
164     if (x & 1)
165         *index = row[x/2]&0xf;
166     else
167         *index = row[x/2]>>4;
168 }
169 
170 static inline void getpixel_8bppIndexed(BYTE *index, const BYTE *row, UINT x)
171 {
172     *index = row[x];
173 }
174 
175 static inline void getpixel_16bppGrayScale(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
176     const BYTE *row, UINT x)
177 {
178     *r = *g = *b = row[x*2+1];
179     *a = 255;
180 }
181 
182 static inline void getpixel_16bppRGB555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
183     const BYTE *row, UINT x)
184 {
185     WORD pixel = *((const WORD*)(row)+x);
186     *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
187     *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
188     *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
189     *a = 255;
190 }
191 
192 static inline void getpixel_16bppRGB565(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
193     const BYTE *row, UINT x)
194 {
195     WORD pixel = *((const WORD*)(row)+x);
196     *r = (pixel>>8&0xf8)|(pixel>>13&0x7);
197     *g = (pixel>>3&0xfc)|(pixel>>9&0x3);
198     *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
199     *a = 255;
200 }
201 
202 static inline void getpixel_16bppARGB1555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
203     const BYTE *row, UINT x)
204 {
205     WORD pixel = *((const WORD*)(row)+x);
206     *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
207     *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
208     *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
209     if ((pixel&0x8000) == 0x8000)
210         *a = 255;
211     else
212         *a = 0;
213 }
214 
215 static inline void getpixel_24bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
216     const BYTE *row, UINT x)
217 {
218     *r = row[x*3+2];
219     *g = row[x*3+1];
220     *b = row[x*3];
221     *a = 255;
222 }
223 
224 static inline void getpixel_32bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
225     const BYTE *row, UINT x)
226 {
227     *r = row[x*4+2];
228     *g = row[x*4+1];
229     *b = row[x*4];
230     *a = 255;
231 }
232 
233 static inline void getpixel_32bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
234     const BYTE *row, UINT x)
235 {
236     *r = row[x*4+2];
237     *g = row[x*4+1];
238     *b = row[x*4];
239     *a = row[x*4+3];
240 }
241 
242 static inline void getpixel_32bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
243     const BYTE *row, UINT x)
244 {
245     *a = row[x*4+3];
246     if (*a == 0)
247         *r = *g = *b = 0;
248     else
249     {
250         *r = row[x*4+2] * 255 / *a;
251         *g = row[x*4+1] * 255 / *a;
252         *b = row[x*4] * 255 / *a;
253     }
254 }
255 
256 static inline void getpixel_48bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
257     const BYTE *row, UINT x)
258 {
259     *r = row[x*6+5];
260     *g = row[x*6+3];
261     *b = row[x*6+1];
262     *a = 255;
263 }
264 
265 static inline void getpixel_64bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
266     const BYTE *row, UINT x)
267 {
268     *r = row[x*8+5];
269     *g = row[x*8+3];
270     *b = row[x*8+1];
271     *a = row[x*8+7];
272 }
273 
274 static inline void getpixel_64bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
275     const BYTE *row, UINT x)
276 {
277     *a = row[x*8+7];
278     if (*a == 0)
279         *r = *g = *b = 0;
280     else
281     {
282         *r = row[x*8+5] * 255 / *a;
283         *g = row[x*8+3] * 255 / *a;
284         *b = row[x*8+1] * 255 / *a;
285     }
286 }
287 
288 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
289     ARGB *color)
290 {
291     BYTE r, g, b, a;
292     BYTE index;
293     BYTE *row;
294 
295     if(!bitmap || !color ||
296        x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
297         return InvalidParameter;
298 
299     row = bitmap->bits+bitmap->stride*y;
300 
301     switch (bitmap->format)
302     {
303         case PixelFormat1bppIndexed:
304             getpixel_1bppIndexed(&index,row,x);
305             break;
306         case PixelFormat4bppIndexed:
307             getpixel_4bppIndexed(&index,row,x);
308             break;
309         case PixelFormat8bppIndexed:
310             getpixel_8bppIndexed(&index,row,x);
311             break;
312         case PixelFormat16bppGrayScale:
313             getpixel_16bppGrayScale(&r,&g,&b,&a,row,x);
314             break;
315         case PixelFormat16bppRGB555:
316             getpixel_16bppRGB555(&r,&g,&b,&a,row,x);
317             break;
318         case PixelFormat16bppRGB565:
319             getpixel_16bppRGB565(&r,&g,&b,&a,row,x);
320             break;
321         case PixelFormat16bppARGB1555:
322             getpixel_16bppARGB1555(&r,&g,&b,&a,row,x);
323             break;
324         case PixelFormat24bppRGB:
325             getpixel_24bppRGB(&r,&g,&b,&a,row,x);
326             break;
327         case PixelFormat32bppRGB:
328             getpixel_32bppRGB(&r,&g,&b,&a,row,x);
329             break;
330         case PixelFormat32bppARGB:
331             getpixel_32bppARGB(&r,&g,&b,&a,row,x);
332             break;
333         case PixelFormat32bppPARGB:
334             getpixel_32bppPARGB(&r,&g,&b,&a,row,x);
335             break;
336         case PixelFormat48bppRGB:
337             getpixel_48bppRGB(&r,&g,&b,&a,row,x);
338             break;
339         case PixelFormat64bppARGB:
340             getpixel_64bppARGB(&r,&g,&b,&a,row,x);
341             break;
342         case PixelFormat64bppPARGB:
343             getpixel_64bppPARGB(&r,&g,&b,&a,row,x);
344             break;
345         default:
346             FIXME("not implemented for format 0x%x\n", bitmap->format);
347             return NotImplemented;
348     }
349 
350     if (bitmap->format & PixelFormatIndexed)
351         *color = bitmap->image.palette->Entries[index];
352     else
353         *color = a<<24|r<<16|g<<8|b;
354 
355     return Ok;
356 }
357 
358 static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, ColorPalette *palette)
359 {
360     BYTE index = 0;
361     int best_distance = 0x7fff;
362     int distance;
363     UINT i;
364 
365     if (!palette) return 0;
366     /* This algorithm scans entire palette,
367        computes difference from desired color (all color components have equal weight)
368        and returns the index of color with least difference.
369 
370        Note: Maybe it could be replaced with a better algorithm for better image quality
371        and performance, though better algorithm would probably need some pre-built lookup
372        tables and thus may actually be slower if this method is called only few times per
373        every image.
374     */
375     for(i=0;i<palette->Count;i++) {
376         ARGB color=palette->Entries[i];
377         distance=abs(b-(color & 0xff)) + abs(g-(color>>8 & 0xff)) + abs(r-(color>>16 & 0xff)) + abs(a-(color>>24 & 0xff));
378         if (distance<best_distance) {
379             best_distance=distance;
380             index=i;
381         }
382     }
383     return index;
384 }
385 
386 static inline void setpixel_8bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
387     BYTE *row, UINT x, ColorPalette *palette)
388 {
389      BYTE index = get_palette_index(r,g,b,a,palette);
390      row[x]=index;
391 }
392 
393 static inline void setpixel_1bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
394     BYTE *row, UINT x, ColorPalette *palette)
395 {
396     row[x/8]  = (row[x/8] & ~(1<<(7-x%8))) | (get_palette_index(r,g,b,a,palette)<<(7-x%8));
397 }
398 
399 static inline void setpixel_4bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
400     BYTE *row, UINT x, ColorPalette *palette)
401 {
402     if (x & 1)
403         row[x/2] = (row[x/2] & 0xf0) | get_palette_index(r,g,b,a,palette);
404     else
405         row[x/2] = (row[x/2] & 0x0f) | get_palette_index(r,g,b,a,palette)<<4;
406 }
407 
408 static inline void setpixel_16bppGrayScale(BYTE r, BYTE g, BYTE b, BYTE a,
409     BYTE *row, UINT x)
410 {
411     *((WORD*)(row)+x) = (r+g+b)*85;
412 }
413 
414 static inline void setpixel_16bppRGB555(BYTE r, BYTE g, BYTE b, BYTE a,
415     BYTE *row, UINT x)
416 {
417     *((WORD*)(row)+x) = (r<<7&0x7c00)|
418                         (g<<2&0x03e0)|
419                         (b>>3&0x001f);
420 }
421 
422 static inline void setpixel_16bppRGB565(BYTE r, BYTE g, BYTE b, BYTE a,
423     BYTE *row, UINT x)
424 {
425     *((WORD*)(row)+x) = (r<<8&0xf800)|
426                          (g<<3&0x07e0)|
427                          (b>>3&0x001f);
428 }
429 
430 static inline void setpixel_16bppARGB1555(BYTE r, BYTE g, BYTE b, BYTE a,
431     BYTE *row, UINT x)
432 {
433     *((WORD*)(row)+x) = (a<<8&0x8000)|
434                         (r<<7&0x7c00)|
435                         (g<<2&0x03e0)|
436                         (b>>3&0x001f);
437 }
438 
439 static inline void setpixel_24bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
440     BYTE *row, UINT x)
441 {
442     row[x*3+2] = r;
443     row[x*3+1] = g;
444     row[x*3] = b;
445 }
446 
447 static inline void setpixel_32bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
448     BYTE *row, UINT x)
449 {
450     *((DWORD*)(row)+x) = (r<<16)|(g<<8)|b;
451 }
452 
453 static inline void setpixel_32bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
454     BYTE *row, UINT x)
455 {
456     *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
457 }
458 
459 static inline void setpixel_32bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
460     BYTE *row, UINT x)
461 {
462     r = r * a / 255;
463     g = g * a / 255;
464     b = b * a / 255;
465     *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
466 }
467 
468 static inline void setpixel_48bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
469     BYTE *row, UINT x)
470 {
471     row[x*6+5] = row[x*6+4] = r;
472     row[x*6+3] = row[x*6+2] = g;
473     row[x*6+1] = row[x*6] = b;
474 }
475 
476 static inline void setpixel_64bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
477     BYTE *row, UINT x)
478 {
479     UINT64 a64=a, r64=r, g64=g, b64=b;
480     *((UINT64*)(row)+x) = (a64<<56)|(a64<<48)|(r64<<40)|(r64<<32)|(g64<<24)|(g64<<16)|(b64<<8)|b64;
481 }
482 
483 static inline void setpixel_64bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
484     BYTE *row, UINT x)
485 {
486     UINT64 a64, r64, g64, b64;
487     a64 = a * 257;
488     r64 = r * a / 255;
489     g64 = g * a / 255;
490     b64 = b * a / 255;
491     *((UINT64*)(row)+x) = (a64<<48)|(r64<<32)|(g64<<16)|b64;
492 }
493 
494 GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y,
495     ARGB color)
496 {
497     BYTE a, r, g, b;
498     BYTE *row;
499 
500     if(!bitmap || x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
501         return InvalidParameter;
502 
503     a = color>>24;
504     r = color>>16;
505     g = color>>8;
506     b = color;
507 
508     row = bitmap->bits + bitmap->stride * y;
509 
510     switch (bitmap->format)
511     {
512         case PixelFormat16bppGrayScale:
513             setpixel_16bppGrayScale(r,g,b,a,row,x);
514             break;
515         case PixelFormat16bppRGB555:
516             setpixel_16bppRGB555(r,g,b,a,row,x);
517             break;
518         case PixelFormat16bppRGB565:
519             setpixel_16bppRGB565(r,g,b,a,row,x);
520             break;
521         case PixelFormat16bppARGB1555:
522             setpixel_16bppARGB1555(r,g,b,a,row,x);
523             break;
524         case PixelFormat24bppRGB:
525             setpixel_24bppRGB(r,g,b,a,row,x);
526             break;
527         case PixelFormat32bppRGB:
528             setpixel_32bppRGB(r,g,b,a,row,x);
529             break;
530         case PixelFormat32bppARGB:
531             setpixel_32bppARGB(r,g,b,a,row,x);
532             break;
533         case PixelFormat32bppPARGB:
534             setpixel_32bppPARGB(r,g,b,a,row,x);
535             break;
536         case PixelFormat48bppRGB:
537             setpixel_48bppRGB(r,g,b,a,row,x);
538             break;
539         case PixelFormat64bppARGB:
540             setpixel_64bppARGB(r,g,b,a,row,x);
541             break;
542         case PixelFormat64bppPARGB:
543             setpixel_64bppPARGB(r,g,b,a,row,x);
544             break;
545         case PixelFormat8bppIndexed:
546             setpixel_8bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
547             break;
548         case PixelFormat4bppIndexed:
549             setpixel_4bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
550             break;
551         case PixelFormat1bppIndexed:
552             setpixel_1bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
553             break;
554         default:
555             FIXME("not implemented for format 0x%x\n", bitmap->format);
556             return NotImplemented;
557     }
558 
559     return Ok;
560 }
561 
562 GpStatus convert_pixels(INT width, INT height,
563     INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
564     INT src_stride, const BYTE *src_bits, PixelFormat src_format,
565     ColorPalette *palette)
566 {
567     INT x, y;
568 
569     if (src_format == dst_format ||
570         (dst_format == PixelFormat32bppRGB && PIXELFORMATBPP(src_format) == 32))
571     {
572         UINT widthbytes = PIXELFORMATBPP(src_format) * width / 8;
573         for (y=0; y<height; y++)
574             memcpy(dst_bits+dst_stride*y, src_bits+src_stride*y, widthbytes);
575         return Ok;
576     }
577 
578 #define convert_indexed_to_rgb(getpixel_function, setpixel_function) do { \
579     for (y=0; y<height; y++) \
580         for (x=0; x<width; x++) { \
581             BYTE index; \
582             ARGB argb; \
583             BYTE *color = (BYTE *)&argb; \
584             getpixel_function(&index, src_bits+src_stride*y, x); \
585             argb = (palette && index < palette->Count) ? palette->Entries[index] : 0; \
586             setpixel_function(color[2], color[1], color[0], color[3], dst_bits+dst_stride*y, x); \
587         } \
588     return Ok; \
589 } while (0);
590 
591 #define convert_rgb_to_rgb(getpixel_function, setpixel_function) do { \
592     for (y=0; y<height; y++) \
593         for (x=0; x<width; x++) { \
594             BYTE r, g, b, a; \
595             getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
596             setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x); \
597         } \
598     return Ok; \
599 } while (0);
600 
601 #define convert_rgb_to_indexed(getpixel_function, setpixel_function) do { \
602     for (y=0; y<height; y++) \
603         for (x=0; x<width; x++) { \
604             BYTE r, g, b, a; \
605             getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
606             setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x, palette); \
607         } \
608     return Ok; \
609 } while (0);
610 
611     switch (src_format)
612     {
613     case PixelFormat1bppIndexed:
614         switch (dst_format)
615         {
616         case PixelFormat16bppGrayScale:
617             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppGrayScale);
618         case PixelFormat16bppRGB555:
619             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB555);
620         case PixelFormat16bppRGB565:
621             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB565);
622         case PixelFormat16bppARGB1555:
623             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppARGB1555);
624         case PixelFormat24bppRGB:
625             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_24bppRGB);
626         case PixelFormat32bppRGB:
627             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppRGB);
628         case PixelFormat32bppARGB:
629             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppARGB);
630         case PixelFormat32bppPARGB:
631             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppPARGB);
632         case PixelFormat48bppRGB:
633             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_48bppRGB);
634         case PixelFormat64bppARGB:
635             convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_64bppARGB);
636         default:
637             break;
638         }
639         break;
640     case PixelFormat4bppIndexed:
641         switch (dst_format)
642         {
643         case PixelFormat16bppGrayScale:
644             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppGrayScale);
645         case PixelFormat16bppRGB555:
646             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB555);
647         case PixelFormat16bppRGB565:
648             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB565);
649         case PixelFormat16bppARGB1555:
650             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppARGB1555);
651         case PixelFormat24bppRGB:
652             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_24bppRGB);
653         case PixelFormat32bppRGB:
654             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppRGB);
655         case PixelFormat32bppARGB:
656             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppARGB);
657         case PixelFormat32bppPARGB:
658             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppPARGB);
659         case PixelFormat48bppRGB:
660             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_48bppRGB);
661         case PixelFormat64bppARGB:
662             convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_64bppARGB);
663         default:
664             break;
665         }
666         break;
667     case PixelFormat8bppIndexed:
668         switch (dst_format)
669         {
670         case PixelFormat16bppGrayScale:
671             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppGrayScale);
672         case PixelFormat16bppRGB555:
673             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB555);
674         case PixelFormat16bppRGB565:
675             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB565);
676         case PixelFormat16bppARGB1555:
677             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppARGB1555);
678         case PixelFormat24bppRGB:
679             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_24bppRGB);
680         case PixelFormat32bppRGB:
681             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppRGB);
682         case PixelFormat32bppARGB:
683             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppARGB);
684         case PixelFormat32bppPARGB:
685             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppPARGB);
686         case PixelFormat48bppRGB:
687             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_48bppRGB);
688         case PixelFormat64bppARGB:
689             convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_64bppARGB);
690         default:
691             break;
692         }
693         break;
694     case PixelFormat16bppGrayScale:
695         switch (dst_format)
696         {
697         case PixelFormat1bppIndexed:
698             convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_1bppIndexed);
699         case PixelFormat8bppIndexed:
700             convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_8bppIndexed);
701         case PixelFormat16bppRGB555:
702             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB555);
703         case PixelFormat16bppRGB565:
704             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB565);
705         case PixelFormat16bppARGB1555:
706             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppARGB1555);
707         case PixelFormat24bppRGB:
708             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_24bppRGB);
709         case PixelFormat32bppRGB:
710             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppRGB);
711         case PixelFormat32bppARGB:
712             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppARGB);
713         case PixelFormat32bppPARGB:
714             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppPARGB);
715         case PixelFormat48bppRGB:
716             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_48bppRGB);
717         case PixelFormat64bppARGB:
718             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_64bppARGB);
719         default:
720             break;
721         }
722         break;
723     case PixelFormat16bppRGB555:
724         switch (dst_format)
725         {
726         case PixelFormat1bppIndexed:
727             convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_1bppIndexed);
728         case PixelFormat8bppIndexed:
729             convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_8bppIndexed);
730         case PixelFormat16bppGrayScale:
731             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppGrayScale);
732         case PixelFormat16bppRGB565:
733             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppRGB565);
734         case PixelFormat16bppARGB1555:
735             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppARGB1555);
736         case PixelFormat24bppRGB:
737             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_24bppRGB);
738         case PixelFormat32bppRGB:
739             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppRGB);
740         case PixelFormat32bppARGB:
741             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppARGB);
742         case PixelFormat32bppPARGB:
743             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppPARGB);
744         case PixelFormat48bppRGB:
745             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_48bppRGB);
746         case PixelFormat64bppARGB:
747             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_64bppARGB);
748         default:
749             break;
750         }
751         break;
752     case PixelFormat16bppRGB565:
753         switch (dst_format)
754         {
755         case PixelFormat1bppIndexed:
756             convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_1bppIndexed);
757         case PixelFormat8bppIndexed:
758             convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_8bppIndexed);
759         case PixelFormat16bppGrayScale:
760             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppGrayScale);
761         case PixelFormat16bppRGB555:
762             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppRGB555);
763         case PixelFormat16bppARGB1555:
764             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppARGB1555);
765         case PixelFormat24bppRGB:
766             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_24bppRGB);
767         case PixelFormat32bppRGB:
768             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppRGB);
769         case PixelFormat32bppARGB:
770             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppARGB);
771         case PixelFormat32bppPARGB:
772             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppPARGB);
773         case PixelFormat48bppRGB:
774             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_48bppRGB);
775         case PixelFormat64bppARGB:
776             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_64bppARGB);
777         default:
778             break;
779         }
780         break;
781     case PixelFormat16bppARGB1555:
782         switch (dst_format)
783         {
784         case PixelFormat1bppIndexed:
785             convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_1bppIndexed);
786         case PixelFormat8bppIndexed:
787             convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_8bppIndexed);
788         case PixelFormat16bppGrayScale:
789             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppGrayScale);
790         case PixelFormat16bppRGB555:
791             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB555);
792         case PixelFormat16bppRGB565:
793             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB565);
794         case PixelFormat24bppRGB:
795             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_24bppRGB);
796         case PixelFormat32bppRGB:
797             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppRGB);
798         case PixelFormat32bppARGB:
799             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppARGB);
800         case PixelFormat32bppPARGB:
801             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppPARGB);
802         case PixelFormat48bppRGB:
803             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_48bppRGB);
804         case PixelFormat64bppARGB:
805             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_64bppARGB);
806         default:
807             break;
808         }
809         break;
810     case PixelFormat24bppRGB:
811         switch (dst_format)
812         {
813         case PixelFormat1bppIndexed:
814             convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_1bppIndexed);
815         case PixelFormat8bppIndexed:
816             convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_8bppIndexed);
817         case PixelFormat16bppGrayScale:
818             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppGrayScale);
819         case PixelFormat16bppRGB555:
820             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB555);
821         case PixelFormat16bppRGB565:
822             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB565);
823         case PixelFormat16bppARGB1555:
824             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppARGB1555);
825         case PixelFormat32bppRGB:
826             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppRGB);
827         case PixelFormat32bppARGB:
828             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppARGB);
829         case PixelFormat32bppPARGB:
830             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppPARGB);
831         case PixelFormat48bppRGB:
832             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_48bppRGB);
833         case PixelFormat64bppARGB:
834             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_64bppARGB);
835         default:
836             break;
837         }
838         break;
839     case PixelFormat32bppRGB:
840         switch (dst_format)
841         {
842         case PixelFormat1bppIndexed:
843             convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_1bppIndexed);
844         case PixelFormat8bppIndexed:
845             convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_8bppIndexed);
846         case PixelFormat16bppGrayScale:
847             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppGrayScale);
848         case PixelFormat16bppRGB555:
849             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB555);
850         case PixelFormat16bppRGB565:
851             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB565);
852         case PixelFormat16bppARGB1555:
853             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppARGB1555);
854         case PixelFormat24bppRGB:
855             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_24bppRGB);
856         case PixelFormat32bppARGB:
857             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppARGB);
858         case PixelFormat32bppPARGB:
859             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppPARGB);
860         case PixelFormat48bppRGB:
861             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_48bppRGB);
862         case PixelFormat64bppARGB:
863             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_64bppARGB);
864         default:
865             break;
866         }
867         break;
868     case PixelFormat32bppARGB:
869         switch (dst_format)
870         {
871         case PixelFormat1bppIndexed:
872             convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_1bppIndexed);
873         case PixelFormat8bppIndexed:
874             convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_8bppIndexed);
875         case PixelFormat16bppGrayScale:
876             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppGrayScale);
877         case PixelFormat16bppRGB555:
878             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB555);
879         case PixelFormat16bppRGB565:
880             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB565);
881         case PixelFormat16bppARGB1555:
882             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppARGB1555);
883         case PixelFormat24bppRGB:
884             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_24bppRGB);
885         case PixelFormat32bppPARGB:
886             convert_32bppARGB_to_32bppPARGB(width, height, dst_bits, dst_stride, src_bits, src_stride);
887             return Ok;
888         case PixelFormat48bppRGB:
889             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_48bppRGB);
890         case PixelFormat64bppARGB:
891             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_64bppARGB);
892         default:
893             break;
894         }
895         break;
896     case PixelFormat32bppPARGB:
897         switch (dst_format)
898         {
899         case PixelFormat1bppIndexed:
900             convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_1bppIndexed);
901         case PixelFormat8bppIndexed:
902             convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_8bppIndexed);
903         case PixelFormat16bppGrayScale:
904             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppGrayScale);
905         case PixelFormat16bppRGB555:
906             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB555);
907         case PixelFormat16bppRGB565:
908             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB565);
909         case PixelFormat16bppARGB1555:
910             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppARGB1555);
911         case PixelFormat24bppRGB:
912             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_24bppRGB);
913         case PixelFormat32bppRGB:
914             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppRGB);
915         case PixelFormat32bppARGB:
916             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppARGB);
917         case PixelFormat48bppRGB:
918             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_48bppRGB);
919         case PixelFormat64bppARGB:
920             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_64bppARGB);
921         default:
922             break;
923         }
924         break;
925     case PixelFormat48bppRGB:
926         switch (dst_format)
927         {
928         case PixelFormat1bppIndexed:
929             convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_1bppIndexed);
930         case PixelFormat8bppIndexed:
931             convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_8bppIndexed);
932         case PixelFormat16bppGrayScale:
933             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppGrayScale);
934         case PixelFormat16bppRGB555:
935             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB555);
936         case PixelFormat16bppRGB565:
937             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB565);
938         case PixelFormat16bppARGB1555:
939             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppARGB1555);
940         case PixelFormat24bppRGB:
941             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_24bppRGB);
942         case PixelFormat32bppRGB:
943             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppRGB);
944         case PixelFormat32bppARGB:
945             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppARGB);
946         case PixelFormat32bppPARGB:
947             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppPARGB);
948         case PixelFormat64bppARGB:
949             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_64bppARGB);
950         default:
951             break;
952         }
953         break;
954     case PixelFormat64bppARGB:
955         switch (dst_format)
956         {
957         case PixelFormat1bppIndexed:
958             convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_1bppIndexed);
959         case PixelFormat8bppIndexed:
960             convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_8bppIndexed);
961         case PixelFormat16bppGrayScale:
962             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppGrayScale);
963         case PixelFormat16bppRGB555:
964             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB555);
965         case PixelFormat16bppRGB565:
966             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB565);
967         case PixelFormat16bppARGB1555:
968             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppARGB1555);
969         case PixelFormat24bppRGB:
970             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_24bppRGB);
971         case PixelFormat32bppRGB:
972             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppRGB);
973         case PixelFormat32bppARGB:
974             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppARGB);
975         case PixelFormat32bppPARGB:
976             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppPARGB);
977         case PixelFormat48bppRGB:
978             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_48bppRGB);
979         default:
980             break;
981         }
982         break;
983     case PixelFormat64bppPARGB:
984         switch (dst_format)
985         {
986         case PixelFormat1bppIndexed:
987             convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_1bppIndexed);
988         case PixelFormat8bppIndexed:
989             convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_8bppIndexed);
990         case PixelFormat16bppGrayScale:
991             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppGrayScale);
992         case PixelFormat16bppRGB555:
993             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB555);
994         case PixelFormat16bppRGB565:
995             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB565);
996         case PixelFormat16bppARGB1555:
997             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppARGB1555);
998         case PixelFormat24bppRGB:
999             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_24bppRGB);
1000         case PixelFormat32bppRGB:
1001             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppRGB);
1002         case PixelFormat32bppARGB:
1003             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppARGB);
1004         case PixelFormat32bppPARGB:
1005             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppPARGB);
1006         case PixelFormat48bppRGB:
1007             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_48bppRGB);
1008         case PixelFormat64bppARGB:
1009             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_64bppARGB);
1010         default:
1011             break;
1012         }
1013         break;
1014     default:
1015         break;
1016     }
1017 
1018 #undef convert_indexed_to_rgb
1019 #undef convert_rgb_to_rgb
1020 
1021     return NotImplemented;
1022 }
1023 
1024 /* This function returns a pointer to an array of pixels that represents the
1025  * bitmap. The *entire* bitmap is locked according to the lock mode specified by
1026  * flags.  It is correct behavior that a user who calls this function with write
1027  * privileges can write to the whole bitmap (not just the area in rect).
1028  *
1029  * FIXME: only used portion of format is bits per pixel. */
1030 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
1031     UINT flags, PixelFormat format, BitmapData* lockeddata)
1032 {
1033     INT bitspp = PIXELFORMATBPP(format);
1034     GpRect act_rect; /* actual rect to be used */
1035     GpStatus stat;
1036     BOOL unlock;
1037 
1038     TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata);
1039 
1040     if(!lockeddata || !bitmap)
1041         return InvalidParameter;
1042     if(!image_lock(&bitmap->image, &unlock))
1043         return ObjectBusy;
1044 
1045     if(rect){
1046         if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
1047           (rect->Y + rect->Height > bitmap->height) || !flags)
1048         {
1049             image_unlock(&bitmap->image, unlock);
1050             return InvalidParameter;
1051         }
1052 
1053         act_rect = *rect;
1054     }
1055     else{
1056         act_rect.X = act_rect.Y = 0;
1057         act_rect.Width  = bitmap->width;
1058         act_rect.Height = bitmap->height;
1059     }
1060 
1061     if(bitmap->lockmode)
1062     {
1063         WARN("bitmap is already locked and cannot be locked again\n");
1064         image_unlock(&bitmap->image, unlock);
1065         return WrongState;
1066     }
1067 
1068     if (bitmap->bits && bitmap->format == format && !(flags & ImageLockModeUserInputBuf))
1069     {
1070         /* no conversion is necessary; just use the bits directly */
1071         lockeddata->Width = act_rect.Width;
1072         lockeddata->Height = act_rect.Height;
1073         lockeddata->PixelFormat = format;
1074         lockeddata->Reserved = flags;
1075         lockeddata->Stride = bitmap->stride;
1076         lockeddata->Scan0 = bitmap->bits + (bitspp / 8) * act_rect.X +
1077                             bitmap->stride * act_rect.Y;
1078 
1079         bitmap->lockmode = flags | ImageLockModeRead;
1080 
1081         image_unlock(&bitmap->image, unlock);
1082         return Ok;
1083     }
1084 
1085     /* Make sure we can convert to the requested format. */
1086     if (flags & ImageLockModeRead)
1087     {
1088         stat = convert_pixels(0, 0, 0, NULL, format, 0, NULL, bitmap->format, NULL);
1089         if (stat == NotImplemented)
1090         {
1091             FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format);
1092             image_unlock(&bitmap->image, unlock);
1093             return NotImplemented;
1094         }
1095     }
1096 
1097     /* If we're opening for writing, make sure we'll be able to write back in
1098      * the original format. */
1099     if (flags & ImageLockModeWrite)
1100     {
1101         stat = convert_pixels(0, 0, 0, NULL, bitmap->format, 0, NULL, format, NULL);
1102         if (stat == NotImplemented)
1103         {
1104             FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format);
1105             image_unlock(&bitmap->image, unlock);
1106             return NotImplemented;
1107         }
1108     }
1109 
1110     lockeddata->Width  = act_rect.Width;
1111     lockeddata->Height = act_rect.Height;
1112     lockeddata->PixelFormat = format;
1113     lockeddata->Reserved = flags;
1114 
1115     if(!(flags & ImageLockModeUserInputBuf))
1116     {
1117         lockeddata->Stride = (((act_rect.Width * bitspp + 7) / 8) + 3) & ~3;
1118 
1119         bitmap->bitmapbits = heap_alloc_zero(lockeddata->Stride * act_rect.Height);
1120 
1121         if (!bitmap->bitmapbits)
1122         {
1123             image_unlock(&bitmap->image, unlock);
1124             return OutOfMemory;
1125         }
1126 
1127         lockeddata->Scan0  = bitmap->bitmapbits;
1128     }
1129 
1130     if (flags & ImageLockModeRead)
1131     {
1132         static BOOL fixme = FALSE;
1133 
1134         if (!fixme && (PIXELFORMATBPP(bitmap->format) * act_rect.X) % 8 != 0)
1135         {
1136             FIXME("Cannot copy rows that don't start at a whole byte.\n");
1137             fixme = TRUE;
1138         }
1139 
1140         stat = convert_pixels(act_rect.Width, act_rect.Height,
1141             lockeddata->Stride, lockeddata->Scan0, format,
1142             bitmap->stride,
1143             bitmap->bits + bitmap->stride * act_rect.Y + PIXELFORMATBPP(bitmap->format) * act_rect.X / 8,
1144             bitmap->format, bitmap->image.palette);
1145 
1146         if (stat != Ok)
1147         {
1148             heap_free(bitmap->bitmapbits);
1149             bitmap->bitmapbits = NULL;
1150             image_unlock(&bitmap->image, unlock);
1151             return stat;
1152         }
1153     }
1154 
1155     bitmap->lockmode = flags | ImageLockModeRead;
1156     bitmap->lockx = act_rect.X;
1157     bitmap->locky = act_rect.Y;
1158 
1159     image_unlock(&bitmap->image, unlock);
1160     return Ok;
1161 }
1162 
1163 GpStatus WINGDIPAPI GdipBitmapSetResolution(GpBitmap* bitmap, REAL xdpi, REAL ydpi)
1164 {
1165     TRACE("(%p, %.2f, %.2f)\n", bitmap, xdpi, ydpi);
1166 
1167     if (!bitmap || xdpi == 0.0 || ydpi == 0.0)
1168         return InvalidParameter;
1169 
1170     bitmap->image.xres = xdpi;
1171     bitmap->image.yres = ydpi;
1172 
1173     return Ok;
1174 }
1175 
1176 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
1177     BitmapData* lockeddata)
1178 {
1179     GpStatus stat;
1180     static BOOL fixme = FALSE;
1181     BOOL unlock;
1182 
1183     TRACE("(%p,%p)\n", bitmap, lockeddata);
1184 
1185     if(!bitmap || !lockeddata)
1186         return InvalidParameter;
1187     if(!image_lock(&bitmap->image, &unlock))
1188         return ObjectBusy;
1189 
1190     if(!bitmap->lockmode)
1191     {
1192         image_unlock(&bitmap->image, unlock);
1193         return WrongState;
1194     }
1195 
1196     if(!(lockeddata->Reserved & ImageLockModeWrite)){
1197         bitmap->lockmode = 0;
1198         heap_free(bitmap->bitmapbits);
1199         bitmap->bitmapbits = NULL;
1200         image_unlock(&bitmap->image, unlock);
1201         return Ok;
1202     }
1203 
1204     if (!bitmap->bitmapbits && !(lockeddata->Reserved & ImageLockModeUserInputBuf))
1205     {
1206         /* we passed a direct reference; no need to do anything */
1207         bitmap->lockmode = 0;
1208         image_unlock(&bitmap->image, unlock);
1209         return Ok;
1210     }
1211 
1212     if (!fixme && (PIXELFORMATBPP(bitmap->format) * bitmap->lockx) % 8 != 0)
1213     {
1214         FIXME("Cannot copy rows that don't start at a whole byte.\n");
1215         fixme = TRUE;
1216     }
1217 
1218     stat = convert_pixels(lockeddata->Width, lockeddata->Height,
1219         bitmap->stride,
1220         bitmap->bits + bitmap->stride * bitmap->locky + PIXELFORMATBPP(bitmap->format) * bitmap->lockx / 8,
1221         bitmap->format,
1222         lockeddata->Stride, lockeddata->Scan0, lockeddata->PixelFormat, NULL);
1223 
1224     if (stat != Ok)
1225     {
1226         ERR("failed to convert pixels; this should never happen\n");
1227     }
1228 
1229     heap_free(bitmap->bitmapbits);
1230     bitmap->bitmapbits = NULL;
1231     bitmap->lockmode = 0;
1232 
1233     image_unlock(&bitmap->image, unlock);
1234     return stat;
1235 }
1236 
1237 GpStatus WINGDIPAPI GdipCloneBitmapArea(REAL x, REAL y, REAL width, REAL height,
1238     PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
1239 {
1240     Rect area;
1241     GpStatus stat;
1242 
1243     TRACE("(%f,%f,%f,%f,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
1244 
1245     if (!srcBitmap || !dstBitmap || srcBitmap->image.type != ImageTypeBitmap ||
1246         x < 0 || y < 0 ||
1247         x + width > srcBitmap->width || y + height > srcBitmap->height)
1248     {
1249         TRACE("<-- InvalidParameter\n");
1250         return InvalidParameter;
1251     }
1252 
1253     if (format == PixelFormatDontCare)
1254         format = srcBitmap->format;
1255 
1256     area.X = gdip_round(x);
1257     area.Y = gdip_round(y);
1258     area.Width = gdip_round(width);
1259     area.Height = gdip_round(height);
1260 
1261     stat = GdipCreateBitmapFromScan0(area.Width, area.Height, 0, format, NULL, dstBitmap);
1262     if (stat == Ok)
1263     {
1264         stat = convert_pixels(area.Width, area.Height, (*dstBitmap)->stride, (*dstBitmap)->bits, (*dstBitmap)->format,
1265                               srcBitmap->stride,
1266                               srcBitmap->bits + srcBitmap->stride * area.Y + PIXELFORMATBPP(srcBitmap->format) * area.X / 8,
1267                               srcBitmap->format, srcBitmap->image.palette);
1268 
1269         if (stat == Ok && srcBitmap->image.palette)
1270         {
1271             ColorPalette *src_palette, *dst_palette;
1272 
1273             src_palette = srcBitmap->image.palette;
1274 
1275             dst_palette = heap_alloc_zero(sizeof(UINT) * 2 + sizeof(ARGB) * src_palette->Count);
1276 
1277             if (dst_palette)
1278             {
1279                 dst_palette->Flags = src_palette->Flags;
1280                 dst_palette->Count = src_palette->Count;
1281                 memcpy(dst_palette->Entries, src_palette->Entries, sizeof(ARGB) * src_palette->Count);
1282 
1283                 heap_free((*dstBitmap)->image.palette);
1284                 (*dstBitmap)->image.palette = dst_palette;
1285             }
1286             else
1287                 stat = OutOfMemory;
1288         }
1289 
1290         if (stat != Ok)
1291             GdipDisposeImage(&(*dstBitmap)->image);
1292     }
1293 
1294     if (stat != Ok)
1295         *dstBitmap = NULL;
1296 
1297     return stat;
1298 }
1299 
1300 GpStatus WINGDIPAPI GdipCloneBitmapAreaI(INT x, INT y, INT width, INT height,
1301     PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
1302 {
1303     TRACE("(%i,%i,%i,%i,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
1304 
1305     return GdipCloneBitmapArea(x, y, width, height, format, srcBitmap, dstBitmap);
1306 }
1307 
1308 GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
1309 {
1310     TRACE("%p, %p\n", image, cloneImage);
1311 
1312     if (!image || !cloneImage)
1313         return InvalidParameter;
1314 
1315     if (image->type == ImageTypeBitmap)
1316     {
1317         GpBitmap *bitmap = (GpBitmap *)image;
1318 
1319         return GdipCloneBitmapAreaI(0, 0, bitmap->width, bitmap->height,
1320                                     bitmap->format, bitmap, (GpBitmap **)cloneImage);
1321     }
1322     else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
1323     {
1324         GpMetafile *result, *metafile;
1325 
1326         metafile = (GpMetafile*)image;
1327 
1328         result = heap_alloc_zero(sizeof(*result));
1329         if (!result)
1330             return OutOfMemory;
1331 
1332         result->image.type = ImageTypeMetafile;
1333         result->image.format = image->format;
1334         result->image.flags = image->flags;
1335         result->image.frame_count = 1;
1336         result->image.xres = image->xres;
1337         result->image.yres = image->yres;
1338         result->bounds = metafile->bounds;
1339         result->unit = metafile->unit;
1340         result->metafile_type = metafile->metafile_type;
1341         result->hemf = CopyEnhMetaFileW(metafile->hemf, NULL);
1342         list_init(&result->containers);
1343 
1344         if (!result->hemf)
1345         {
1346             heap_free(result);
1347             return OutOfMemory;
1348         }
1349 
1350         *cloneImage = &result->image;
1351         return Ok;
1352     }
1353     else
1354     {
1355         WARN("GpImage with no image data (metafile in wrong state?)\n");
1356         return InvalidParameter;
1357     }
1358 }
1359 
1360 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
1361     GpBitmap **bitmap)
1362 {
1363     GpStatus stat;
1364     IStream *stream;
1365 
1366     TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
1367 
1368     if(!filename || !bitmap)
1369         return InvalidParameter;
1370 
1371     *bitmap = NULL;
1372 
1373     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
1374 
1375     if(stat != Ok)
1376         return stat;
1377 
1378     stat = GdipCreateBitmapFromStream(stream, bitmap);
1379 
1380     IStream_Release(stream);
1381 
1382     return stat;
1383 }
1384 
1385 GpStatus WINGDIPAPI GdipCreateBitmapFromGdiDib(GDIPCONST BITMAPINFO* info,
1386                                                VOID *bits, GpBitmap **bitmap)
1387 {
1388     DWORD height, stride;
1389     PixelFormat format;
1390 
1391     FIXME("(%p, %p, %p) - partially implemented\n", info, bits, bitmap);
1392 
1393     if (!info || !bits || !bitmap)
1394         return InvalidParameter;
1395 
1396     height = abs(info->bmiHeader.biHeight);
1397     stride = ((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) >> 3) & ~3;
1398 
1399     if(info->bmiHeader.biHeight > 0) /* bottom-up */
1400     {
1401         bits = (BYTE*)bits + (height - 1) * stride;
1402         stride = -stride;
1403     }
1404 
1405     switch(info->bmiHeader.biBitCount) {
1406     case 1:
1407         format = PixelFormat1bppIndexed;
1408         break;
1409     case 4:
1410         format = PixelFormat4bppIndexed;
1411         break;
1412     case 8:
1413         format = PixelFormat8bppIndexed;
1414         break;
1415     case 16:
1416         format = PixelFormat16bppRGB555;
1417         break;
1418     case 24:
1419         format = PixelFormat24bppRGB;
1420         break;
1421     case 32:
1422         format = PixelFormat32bppRGB;
1423         break;
1424     default:
1425         FIXME("don't know how to handle %d bpp\n", info->bmiHeader.biBitCount);
1426         *bitmap = NULL;
1427         return InvalidParameter;
1428     }
1429 
1430     return GdipCreateBitmapFromScan0(info->bmiHeader.biWidth, height, stride, format,
1431                                      bits, bitmap);
1432 
1433 }
1434 
1435 /* FIXME: no icm */
1436 GpStatus WINGDIPAPI GdipCreateBitmapFromFileICM(GDIPCONST WCHAR* filename,
1437     GpBitmap **bitmap)
1438 {
1439     TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
1440 
1441     return GdipCreateBitmapFromFile(filename, bitmap);
1442 }
1443 
1444 GpStatus WINGDIPAPI GdipCreateBitmapFromResource(HINSTANCE hInstance,
1445     GDIPCONST WCHAR* lpBitmapName, GpBitmap** bitmap)
1446 {
1447     HBITMAP hbm;
1448     GpStatus stat = InvalidParameter;
1449 
1450     TRACE("%p (%s) %p\n", hInstance, debugstr_w(lpBitmapName), bitmap);
1451 
1452     if(!lpBitmapName || !bitmap)
1453         return InvalidParameter;
1454 
1455     /* load DIB */
1456     hbm = LoadImageW(hInstance, lpBitmapName, IMAGE_BITMAP, 0, 0,
1457                      LR_CREATEDIBSECTION);
1458 
1459     if(hbm){
1460         stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
1461         DeleteObject(hbm);
1462     }
1463 
1464     return stat;
1465 }
1466 
1467 static inline DWORD blend_argb_no_bkgnd_alpha(DWORD src, DWORD bkgnd)
1468 {
1469     BYTE b = (BYTE)src;
1470     BYTE g = (BYTE)(src >> 8);
1471     BYTE r = (BYTE)(src >> 16);
1472     DWORD alpha  = (BYTE)(src >> 24);
1473     return ((b     + ((BYTE)bkgnd         * (255 - alpha) + 127) / 255) |
1474             (g     + ((BYTE)(bkgnd >> 8)  * (255 - alpha) + 127) / 255) << 8 |
1475             (r     + ((BYTE)(bkgnd >> 16) * (255 - alpha) + 127) / 255) << 16 |
1476             (alpha << 24));
1477 }
1478 
1479 GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
1480     HBITMAP* hbmReturn, ARGB background)
1481 {
1482     GpStatus stat;
1483     HBITMAP result;
1484     UINT width, height;
1485     BITMAPINFOHEADER bih;
1486     LPBYTE bits;
1487     BOOL unlock;
1488 
1489     TRACE("(%p,%p,%x)\n", bitmap, hbmReturn, background);
1490 
1491     if (!bitmap || !hbmReturn) return InvalidParameter;
1492     if (!image_lock(&bitmap->image, &unlock)) return ObjectBusy;
1493 
1494     GdipGetImageWidth(&bitmap->image, &width);
1495     GdipGetImageHeight(&bitmap->image, &height);
1496 
1497     bih.biSize = sizeof(bih);
1498     bih.biWidth = width;
1499     bih.biHeight = height;
1500     bih.biPlanes = 1;
1501     bih.biBitCount = 32;
1502     bih.biCompression = BI_RGB;
1503     bih.biSizeImage = 0;
1504     bih.biXPelsPerMeter = 0;
1505     bih.biYPelsPerMeter = 0;
1506     bih.biClrUsed = 0;
1507     bih.biClrImportant = 0;
1508 
1509     result = CreateDIBSection(0, (BITMAPINFO*)&bih, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1510     if (!result)
1511     {
1512         image_unlock(&bitmap->image, unlock);
1513         return GenericError;
1514     }
1515 
1516     stat = convert_pixels(width, height, -width*4,
1517             bits + (width * 4 * (height - 1)), PixelFormat32bppPARGB,
1518             bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette);
1519     if (stat != Ok)
1520     {
1521         DeleteObject(result);
1522         image_unlock(&bitmap->image, unlock);
1523         return stat;
1524     }
1525 
1526     if (background & 0xffffff)
1527     {
1528         DWORD *ptr;
1529         UINT i;
1530         for (ptr = (DWORD*)bits, i = 0; i < width * height; ptr++, i++)
1531         {
1532             if ((*ptr & 0xff000000) == 0xff000000) continue;
1533             *ptr = blend_argb_no_bkgnd_alpha(*ptr, background);
1534         }
1535     }
1536 
1537     *hbmReturn = result;
1538     image_unlock(&bitmap->image, unlock);
1539     return Ok;
1540 }
1541 
1542 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
1543     GpGraphics* target, GpBitmap** bitmap)
1544 {
1545     GpStatus ret;
1546 
1547     TRACE("(%d, %d, %p, %p)\n", width, height, target, bitmap);
1548 
1549     if(!target || !bitmap)
1550         return InvalidParameter;
1551 
1552     ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
1553                                     NULL, bitmap);
1554 
1555     if (ret == Ok)
1556     {
1557         GdipGetDpiX(target, &(*bitmap)->image.xres);
1558         GdipGetDpiY(target, &(*bitmap)->image.yres);
1559     }
1560 
1561     return ret;
1562 }
1563 
1564 GpStatus WINGDIPAPI GdipCreateBitmapFromHICON(HICON hicon, GpBitmap** bitmap)
1565 {
1566     GpStatus stat;
1567     ICONINFO iinfo;
1568     BITMAP bm;
1569     int ret;
1570     UINT width, height, stride;
1571     GpRect rect;
1572     BitmapData lockeddata;
1573     HDC screendc;
1574     BOOL has_alpha;
1575     int x, y;
1576     BITMAPINFOHEADER bih;
1577     DWORD *src;
1578     BYTE *dst_row;
1579     DWORD *dst;
1580 
1581     TRACE("%p, %p\n", hicon, bitmap);
1582 
1583     if(!bitmap || !GetIconInfo(hicon, &iinfo))
1584         return InvalidParameter;
1585 
1586     /* get the size of the icon */
1587     ret = GetObjectA(iinfo.hbmColor ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
1588     if (ret == 0) {
1589         DeleteObject(iinfo.hbmColor);
1590         DeleteObject(iinfo.hbmMask);
1591         return GenericError;
1592     }
1593 
1594     width = bm.bmWidth;
1595     height = iinfo.hbmColor ? abs(bm.bmHeight) : abs(bm.bmHeight) / 2;
1596     stride = width * 4;
1597 
1598     stat = GdipCreateBitmapFromScan0(width, height, stride, PixelFormat32bppARGB, NULL, bitmap);
1599     if (stat != Ok) {
1600         DeleteObject(iinfo.hbmColor);
1601         DeleteObject(iinfo.hbmMask);
1602         return stat;
1603     }
1604 
1605     rect.X = 0;
1606     rect.Y = 0;
1607     rect.Width = width;
1608     rect.Height = height;
1609 
1610     stat = GdipBitmapLockBits(*bitmap, &rect, ImageLockModeWrite, PixelFormat32bppARGB, &lockeddata);
1611     if (stat != Ok) {
1612         DeleteObject(iinfo.hbmColor);
1613         DeleteObject(iinfo.hbmMask);
1614         GdipDisposeImage(&(*bitmap)->image);
1615         return stat;
1616     }
1617 
1618     bih.biSize = sizeof(bih);
1619     bih.biWidth = width;
1620     bih.biHeight = iinfo.hbmColor ? -height: -height * 2;
1621     bih.biPlanes = 1;
1622     bih.biBitCount = 32;
1623     bih.biCompression = BI_RGB;
1624     bih.biSizeImage = 0;
1625     bih.biXPelsPerMeter = 0;
1626     bih.biYPelsPerMeter = 0;
1627     bih.biClrUsed = 0;
1628     bih.biClrImportant = 0;
1629 
1630     screendc = CreateCompatibleDC(0);
1631     if (iinfo.hbmColor)
1632     {
1633         GetDIBits(screendc, iinfo.hbmColor, 0, height, lockeddata.Scan0, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1634 
1635         if (bm.bmBitsPixel == 32)
1636         {
1637             has_alpha = FALSE;
1638 
1639             /* If any pixel has a non-zero alpha, ignore hbmMask */
1640             src = (DWORD*)lockeddata.Scan0;
1641             for (x=0; x<width && !has_alpha; x++)
1642                 for (y=0; y<height && !has_alpha; y++)
1643                     if ((*src++ & 0xff000000) != 0)
1644                         has_alpha = TRUE;
1645         }
1646         else has_alpha = FALSE;
1647     }
1648     else
1649     {
1650         GetDIBits(screendc, iinfo.hbmMask, 0, height, lockeddata.Scan0, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1651         has_alpha = FALSE;
1652     }
1653 
1654     if (!has_alpha)
1655     {
1656         if (iinfo.hbmMask)
1657         {
1658             BYTE *bits = heap_alloc(height * stride);
1659 
1660             /* read alpha data from the mask */
1661             if (iinfo.hbmColor)
1662                 GetDIBits(screendc, iinfo.hbmMask, 0, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1663             else
1664                 GetDIBits(screendc, iinfo.hbmMask, height, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1665 
1666             src = (DWORD*)bits;
1667             dst_row = lockeddata.Scan0;
1668             for (y=0; y<height; y++)
1669             {
1670                 dst = (DWORD*)dst_row;
1671                 for (x=0; x<height; x++)
1672                 {
1673                     DWORD src_value = *src++;
1674                     if (src_value)
1675                         *dst++ = 0;
1676                     else
1677                         *dst++ |= 0xff000000;
1678                 }
1679                 dst_row += lockeddata.Stride;
1680             }
1681 
1682             heap_free(bits);
1683         }
1684         else
1685         {
1686             /* set constant alpha of 255 */
1687             dst_row = lockeddata.Scan0;
1688             for (y=0; y<height; y++)
1689             {
1690                 dst = (DWORD*)dst_row;
1691                 for (x=0; x<height; x++)
1692                     *dst++ |= 0xff000000;
1693                 dst_row += lockeddata.Stride;
1694             }
1695         }
1696     }
1697 
1698     DeleteDC(screendc);
1699 
1700     DeleteObject(iinfo.hbmColor);
1701     DeleteObject(iinfo.hbmMask);
1702 
1703     GdipBitmapUnlockBits(*bitmap, &lockeddata);
1704 
1705     return Ok;
1706 }
1707 
1708 static void generate_halftone_palette(ARGB *entries, UINT count)
1709 {
1710     static const BYTE halftone_values[6]={0x00,0x33,0x66,0x99,0xcc,0xff};
1711     UINT i;
1712 
1713     for (i=0; i<8 && i<count; i++)
1714     {
1715         entries[i] = 0xff000000;
1716         if (i&1) entries[i] |= 0x800000;
1717         if (i&2) entries[i] |= 0x8000;
1718         if (i&4) entries[i] |= 0x80;
1719     }
1720 
1721     if (8 < count)
1722         entries[i] = 0xffc0c0c0;
1723 
1724     for (i=9; i<16 && i<count; i++)
1725     {
1726         entries[i] = 0xff000000;
1727         if (i&1) entries[i] |= 0xff0000;
1728         if (i&2) entries[i] |= 0xff00;
1729         if (i&4) entries[i] |= 0xff;
1730     }
1731 
1732     for (i=16; i<40 && i<count; i++)
1733     {
1734         entries[i] = 0;
1735     }
1736 
1737     for (i=40; i<256 && i<count; i++)
1738     {
1739         entries[i] = 0xff000000;
1740         entries[i] |= halftone_values[(i-40)%6];
1741         entries[i] |= halftone_values[((i-40)/6)%6] << 8;
1742         entries[i] |= halftone_values[((i-40)/36)%6] << 16;
1743     }
1744 }
1745 
1746 static GpStatus get_screen_resolution(REAL *xres, REAL *yres)
1747 {
1748     HDC screendc = CreateCompatibleDC(0);
1749 
1750     if (!screendc) return GenericError;
1751 
1752     *xres = (REAL)GetDeviceCaps(screendc, LOGPIXELSX);
1753     *yres = (REAL)GetDeviceCaps(screendc, LOGPIXELSY);
1754 
1755     DeleteDC(screendc);
1756 
1757     return Ok;
1758 }
1759 
1760 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
1761     PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
1762 {
1763     HBITMAP hbitmap=NULL;
1764     INT row_size, dib_stride;
1765     BYTE *bits=NULL, *own_bits=NULL;
1766     REAL xres, yres;
1767     GpStatus stat;
1768 
1769     TRACE("%d %d %d 0x%x %p %p\n", width, height, stride, format, scan0, bitmap);
1770 
1771     if (!bitmap) return InvalidParameter;
1772 
1773     if(width <= 0 || height <= 0 || (scan0 && (stride % 4))){
1774         *bitmap = NULL;
1775         return InvalidParameter;
1776     }
1777 
1778     if(scan0 && !stride)
1779         return InvalidParameter;
1780 
1781     stat = get_screen_resolution(&xres, &yres);
1782     if (stat != Ok) return stat;
1783 
1784     row_size = (width * PIXELFORMATBPP(format)+7) / 8;
1785     dib_stride = (row_size + 3) & ~3;
1786 
1787     if(stride == 0)
1788         stride = dib_stride;
1789 
1790     if (format & PixelFormatGDI && !(format & (PixelFormatAlpha|PixelFormatIndexed)) && !scan0)
1791     {
1792         char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)];
1793         BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
1794 
1795         pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1796         pbmi->bmiHeader.biWidth = width;
1797         pbmi->bmiHeader.biHeight = -height;
1798         pbmi->bmiHeader.biPlanes = 1;
1799         /* FIXME: use the rest of the data from format */
1800         pbmi->bmiHeader.biBitCount = PIXELFORMATBPP(format);
1801         pbmi->bmiHeader.biCompression = BI_RGB;
1802         pbmi->bmiHeader.biSizeImage = 0;
1803         pbmi->bmiHeader.biXPelsPerMeter = 0;
1804         pbmi->bmiHeader.biYPelsPerMeter = 0;
1805         pbmi->bmiHeader.biClrUsed = 0;
1806         pbmi->bmiHeader.biClrImportant = 0;
1807 
1808         hbitmap = CreateDIBSection(0, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1809 
1810         if (!hbitmap) return GenericError;
1811 
1812         stride = dib_stride;
1813     }
1814     else
1815     {
1816         /* Not a GDI format; don't try to make an HBITMAP. */
1817         if (scan0)
1818             bits = scan0;
1819         else
1820         {
1821             INT size = abs(stride) * height;
1822 
1823             own_bits = bits = heap_alloc_zero(size);
1824             if (!own_bits) return OutOfMemory;
1825 
1826             if (stride < 0)
1827                 bits += stride * (1 - height);
1828         }
1829     }
1830 
1831     *bitmap = heap_alloc_zero(sizeof(GpBitmap));
1832     if(!*bitmap)
1833     {
1834         DeleteObject(hbitmap);
1835         heap_free(own_bits);
1836         return OutOfMemory;
1837     }
1838 
1839     (*bitmap)->image.type = ImageTypeBitmap;
1840     memcpy(&(*bitmap)->image.format, &ImageFormatMemoryBMP, sizeof(GUID));
1841     (*bitmap)->image.flags = ImageFlagsNone;
1842     (*bitmap)->image.frame_count = 1;
1843     (*bitmap)->image.current_frame = 0;
1844     (*bitmap)->image.palette = NULL;
1845     (*bitmap)->image.xres = xres;
1846     (*bitmap)->image.yres = yres;
1847     (*bitmap)->width = width;
1848     (*bitmap)->height = height;
1849     (*bitmap)->format = format;
1850     (*bitmap)->image.decoder = NULL;
1851     (*bitmap)->hbitmap = hbitmap;
1852     (*bitmap)->hdc = NULL;
1853     (*bitmap)->bits = bits;
1854     (*bitmap)->stride = stride;
1855     (*bitmap)->own_bits = own_bits;
1856     (*bitmap)->metadata_reader = NULL;
1857     (*bitmap)->prop_count = 0;
1858     (*bitmap)->prop_item = NULL;
1859 
1860     /* set format-related flags */
1861     if (format & (PixelFormatAlpha|PixelFormatPAlpha|PixelFormatIndexed))
1862         (*bitmap)->image.flags |= ImageFlagsHasAlpha;
1863 
1864     if (format == PixelFormat1bppIndexed ||
1865         format == PixelFormat4bppIndexed ||
1866         format == PixelFormat8bppIndexed)
1867     {
1868         (*bitmap)->image.palette = heap_alloc_zero(sizeof(UINT) * 2 + sizeof(ARGB) * (1 << PIXELFORMATBPP(format)));
1869 
1870         if (!(*bitmap)->image.palette)
1871         {
1872             GdipDisposeImage(&(*bitmap)->image);
1873             *bitmap = NULL;
1874             return OutOfMemory;
1875         }
1876 
1877         (*bitmap)->image.palette->Count = 1 << PIXELFORMATBPP(format);
1878 
1879         if (format == PixelFormat1bppIndexed)
1880         {
1881             (*bitmap)->image.palette->Flags = PaletteFlagsGrayScale;
1882             (*bitmap)->image.palette->Entries[0] = 0xff000000;
1883             (*bitmap)->image.palette->Entries[1] = 0xffffffff;
1884         }
1885         else
1886         {
1887             if (format == PixelFormat8bppIndexed)
1888                 (*bitmap)->image.palette->Flags = PaletteFlagsHalftone;
1889 
1890             generate_halftone_palette((*bitmap)->image.palette->Entries,
1891                 (*bitmap)->image.palette->Count);
1892         }
1893     }
1894 
1895     TRACE("<-- %p\n", *bitmap);
1896 
1897     return Ok;
1898 }
1899 
1900 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
1901     GpBitmap **bitmap)
1902 {
1903     GpStatus stat;
1904 
1905     TRACE("%p %p\n", stream, bitmap);
1906 
1907     stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
1908 
1909     if(stat != Ok)
1910         return stat;
1911 
1912     if((*bitmap)->image.type != ImageTypeBitmap){
1913         GdipDisposeImage(&(*bitmap)->image);
1914         *bitmap = NULL;
1915         return GenericError; /* FIXME: what error to return? */
1916     }
1917 
1918     return Ok;
1919 }
1920 
1921 /* FIXME: no icm */
1922 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
1923     GpBitmap **bitmap)
1924 {
1925     TRACE("%p %p\n", stream, bitmap);
1926 
1927     return GdipCreateBitmapFromStream(stream, bitmap);
1928 }
1929 
1930 GpStatus WINGDIPAPI GdipCreateCachedBitmap(GpBitmap *bitmap, GpGraphics *graphics,
1931     GpCachedBitmap **cachedbmp)
1932 {
1933     GpStatus stat;
1934 
1935     TRACE("%p %p %p\n", bitmap, graphics, cachedbmp);
1936 
1937     if(!bitmap || !graphics || !cachedbmp)
1938         return InvalidParameter;
1939 
1940     *cachedbmp = heap_alloc_zero(sizeof(GpCachedBitmap));
1941     if(!*cachedbmp)
1942         return OutOfMemory;
1943 
1944     stat = GdipCloneImage(&(bitmap->image), &(*cachedbmp)->image);
1945     if(stat != Ok){
1946         heap_free(*cachedbmp);
1947         return stat;
1948     }
1949 
1950     return Ok;
1951 }
1952 
1953 GpStatus WINGDIPAPI GdipCreateHICONFromBitmap(GpBitmap *bitmap, HICON *hicon)
1954 {
1955     GpStatus stat;
1956     BitmapData lockeddata;
1957     ULONG andstride, xorstride, bitssize;
1958     LPBYTE andbits, xorbits, androw, xorrow, srcrow;
1959     UINT x, y;
1960 
1961     TRACE("(%p, %p)\n", bitmap, hicon);
1962 
1963     if (!bitmap || !hicon)
1964         return InvalidParameter;
1965 
1966     stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead,
1967         PixelFormat32bppPARGB, &lockeddata);
1968     if (stat == Ok)
1969     {
1970         andstride = ((lockeddata.Width+31)/32)*4;
1971         xorstride = lockeddata.Width*4;
1972         bitssize = (andstride + xorstride) * lockeddata.Height;
1973 
1974         andbits = heap_alloc_zero(bitssize);
1975 
1976         if (andbits)
1977         {
1978             xorbits = andbits + andstride * lockeddata.Height;
1979 
1980             for (y=0; y<lockeddata.Height; y++)
1981             {
1982                 srcrow = ((LPBYTE)lockeddata.Scan0) + lockeddata.Stride * y;
1983 
1984                 androw = andbits + andstride * y;
1985                 for (x=0; x<lockeddata.Width; x++)
1986                     if (srcrow[3+4*x] >= 128)
1987                         androw[x/8] |= 1 << (7-x%8);
1988 
1989                 xorrow = xorbits + xorstride * y;
1990                 memcpy(xorrow, srcrow, xorstride);
1991             }
1992 
1993             *hicon = CreateIcon(NULL, lockeddata.Width, lockeddata.Height, 1, 32,
1994                 andbits, xorbits);
1995 
1996             heap_free(andbits);
1997         }
1998         else
1999             stat = OutOfMemory;
2000 
2001         GdipBitmapUnlockBits(bitmap, &lockeddata);
2002     }
2003 
2004     return stat;
2005 }
2006 
2007 GpStatus WINGDIPAPI GdipDeleteCachedBitmap(GpCachedBitmap *cachedbmp)
2008 {
2009     TRACE("%p\n", cachedbmp);
2010 
2011     if(!cachedbmp)
2012         return InvalidParameter;
2013 
2014     GdipDisposeImage(cachedbmp->image);
2015     heap_free(cachedbmp);
2016 
2017     return Ok;
2018 }
2019 
2020 GpStatus WINGDIPAPI GdipDrawCachedBitmap(GpGraphics *graphics,
2021     GpCachedBitmap *cachedbmp, INT x, INT y)
2022 {
2023     TRACE("%p %p %d %d\n", graphics, cachedbmp, x, y);
2024 
2025     if(!graphics || !cachedbmp)
2026         return InvalidParameter;
2027 
2028     return GdipDrawImage(graphics, cachedbmp->image, (REAL)x, (REAL)y);
2029 }
2030 
2031 /* Internal utility function: Replace the image data of dst with that of src,
2032  * and free src. */
2033 static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
2034 {
2035     assert(src->image.type == ImageTypeBitmap);
2036     assert(dst->image.type == ImageTypeBitmap);
2037 
2038     heap_free(dst->bitmapbits);
2039     heap_free(dst->own_bits);
2040     DeleteDC(dst->hdc);
2041     DeleteObject(dst->hbitmap);
2042 
2043     if (clobber_palette)
2044     {
2045         heap_free(dst->image.palette);
2046         dst->image.palette = src->image.palette;
2047     }
2048     else
2049         heap_free(src->image.palette);
2050 
2051     dst->image.xres = src->image.xres;
2052     dst->image.yres = src->image.yres;
2053     dst->width = src->width;
2054     dst->height = src->height;
2055     dst->format = src->format;
2056     dst->hbitmap = src->hbitmap;
2057     dst->hdc = src->hdc;
2058     dst->bits = src->bits;
2059     dst->stride = src->stride;
2060     dst->own_bits = src->own_bits;
2061     if (dst->metadata_reader)
2062         IWICMetadataReader_Release(dst->metadata_reader);
2063     dst->metadata_reader = src->metadata_reader;
2064     heap_free(dst->prop_item);
2065     dst->prop_item = src->prop_item;
2066     dst->prop_count = src->prop_count;
2067     if (dst->image.decoder)
2068         IWICBitmapDecoder_Release(dst->image.decoder);
2069     dst->image.decoder = src->image.decoder;
2070     dst->image.frame_count = src->image.frame_count;
2071     dst->image.current_frame = src->image.current_frame;
2072     dst->image.format = src->image.format;
2073 
2074     src->image.type = ~0;
2075     heap_free(src);
2076 }
2077 
2078 static GpStatus free_image_data(GpImage *image)
2079 {
2080     if(!image)
2081         return InvalidParameter;
2082 
2083     if (image->type == ImageTypeBitmap)
2084     {
2085         heap_free(((GpBitmap*)image)->bitmapbits);
2086         heap_free(((GpBitmap*)image)->own_bits);
2087         DeleteDC(((GpBitmap*)image)->hdc);
2088         DeleteObject(((GpBitmap*)image)->hbitmap);
2089         if (((GpBitmap*)image)->metadata_reader)
2090             IWICMetadataReader_Release(((GpBitmap*)image)->metadata_reader);
2091         heap_free(((GpBitmap*)image)->prop_item);
2092     }
2093     else if (image->type == ImageTypeMetafile)
2094         METAFILE_Free((GpMetafile *)image);
2095     else
2096     {
2097         WARN("invalid image: %p\n", image);
2098         return ObjectBusy;
2099     }
2100     if (image->decoder)
2101         IWICBitmapDecoder_Release(image->decoder);
2102     heap_free(image->palette);
2103 
2104     return Ok;
2105 }
2106 
2107 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
2108 {
2109     GpStatus status;
2110 
2111     TRACE("%p\n", image);
2112 
2113     status = free_image_data(image);
2114     if (status != Ok) return status;
2115     image->type = ~0;
2116     heap_free(image);
2117 
2118     return Ok;
2119 }
2120 
2121 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
2122 {
2123     static int calls;
2124 
2125     TRACE("(%p,%p)\n", image, item);
2126 
2127     if(!image || !item)
2128         return InvalidParameter;
2129 
2130     if (!(calls++))
2131         FIXME("not implemented\n");
2132 
2133     return NotImplemented;
2134 }
2135 
2136 GpStatus WINGDIPAPI GdipGetImageItemData(GpImage *image, ImageItemData *item)
2137 {
2138     static int calls;
2139 
2140     TRACE("(%p,%p)\n", image, item);
2141 
2142     if (!(calls++))
2143         FIXME("not implemented\n");
2144 
2145     return NotImplemented;
2146 }
2147 
2148 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
2149     GpUnit *srcUnit)
2150 {
2151     TRACE("%p %p %p\n", image, srcRect, srcUnit);
2152 
2153     if(!image || !srcRect || !srcUnit)
2154         return InvalidParameter;
2155     if(image->type == ImageTypeMetafile){
2156         *srcRect = ((GpMetafile*)image)->bounds;
2157         *srcUnit = ((GpMetafile*)image)->unit;
2158     }
2159     else if(image->type == ImageTypeBitmap){
2160         srcRect->X = srcRect->Y = 0.0;
2161         srcRect->Width = (REAL) ((GpBitmap*)image)->width;
2162         srcRect->Height = (REAL) ((GpBitmap*)image)->height;
2163         *srcUnit = UnitPixel;
2164     }
2165     else{
2166         WARN("GpImage with no image data\n");
2167         return InvalidParameter;
2168     }
2169 
2170     TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
2171           srcRect->Width, srcRect->Height, *srcUnit);
2172 
2173     return Ok;
2174 }
2175 
2176 GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
2177     REAL *height)
2178 {
2179     TRACE("%p %p %p\n", image, width, height);
2180 
2181     if(!image || !height || !width)
2182         return InvalidParameter;
2183 
2184     if(image->type == ImageTypeMetafile){
2185         *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
2186         *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
2187     }
2188     else if(image->type == ImageTypeBitmap){
2189         *height = ((GpBitmap*)image)->height;
2190         *width = ((GpBitmap*)image)->width;
2191     }
2192     else{
2193         WARN("GpImage with no image data\n");
2194         return InvalidParameter;
2195     }
2196 
2197     TRACE("returning (%f, %f)\n", *height, *width);
2198     return Ok;
2199 }
2200 
2201 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
2202     GpGraphics **graphics)
2203 {
2204     HDC hdc;
2205     GpStatus stat;
2206 
2207     TRACE("%p %p\n", image, graphics);
2208 
2209     if(!image || !graphics)
2210         return InvalidParameter;
2211 
2212     if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
2213     {
2214         hdc = ((GpBitmap*)image)->hdc;
2215 
2216         if(!hdc){
2217             hdc = CreateCompatibleDC(0);
2218             SelectObject(hdc, ((GpBitmap*)image)->hbitmap);
2219             ((GpBitmap*)image)->hdc = hdc;
2220         }
2221 
2222         stat = GdipCreateFromHDC(hdc, graphics);
2223 
2224         if (stat == Ok)
2225         {
2226             (*graphics)->image = image;
2227             (*graphics)->xres = image->xres;
2228             (*graphics)->yres = image->yres;
2229         }
2230     }
2231     else if (image->type == ImageTypeMetafile)
2232         stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics);
2233     else
2234         stat = graphics_from_image(image, graphics);
2235 
2236     return stat;
2237 }
2238 
2239 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
2240 {
2241     TRACE("%p %p\n", image, height);
2242 
2243     if(!image || !height)
2244         return InvalidParameter;
2245 
2246     if(image->type == ImageTypeMetafile)
2247         *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
2248     else if(image->type == ImageTypeBitmap)
2249         *height = ((GpBitmap*)image)->height;
2250     else
2251     {
2252         WARN("GpImage with no image data\n");
2253         return InvalidParameter;
2254     }
2255 
2256     TRACE("returning %d\n", *height);
2257 
2258     return Ok;
2259 }
2260 
2261 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
2262 {
2263     if(!image || !res)
2264         return InvalidParameter;
2265 
2266     *res = image->xres;
2267 
2268     TRACE("(%p) <-- %0.2f\n", image, *res);
2269 
2270     return Ok;
2271 }
2272 
2273 GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size)
2274 {
2275     TRACE("%p %p\n", image, size);
2276 
2277     if(!image || !size)
2278         return InvalidParameter;
2279 
2280     if (!image->palette || image->palette->Count == 0)
2281         *size = sizeof(ColorPalette);
2282     else
2283         *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette->Count;
2284 
2285     TRACE("<-- %u\n", *size);
2286 
2287     return Ok;
2288 }
2289 
2290 /* FIXME: test this function for non-bitmap types */
2291 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
2292 {
2293     TRACE("%p %p\n", image, format);
2294 
2295     if(!image || !format)
2296         return InvalidParameter;
2297 
2298     if(image->type != ImageTypeBitmap)
2299         *format = PixelFormat24bppRGB;
2300     else
2301         *format = ((GpBitmap*) image)->format;
2302 
2303     return Ok;
2304 }
2305 
2306 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
2307 {
2308     TRACE("(%p, %p)\n", image, format);
2309 
2310     if(!image || !format)
2311         return InvalidParameter;
2312 
2313     memcpy(format, &image->format, sizeof(GUID));
2314 
2315     return Ok;
2316 }
2317 
2318 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
2319 {
2320     TRACE("%p %p\n", image, type);
2321 
2322     if(!image || !type)
2323         return InvalidParameter;
2324 
2325     *type = image->type;
2326 
2327     return Ok;
2328 }
2329 
2330 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
2331 {
2332     if(!image || !res)
2333         return InvalidParameter;
2334 
2335     *res = image->yres;
2336 
2337     TRACE("(%p) <-- %0.2f\n", image, *res);
2338 
2339     return Ok;
2340 }
2341 
2342 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
2343 {
2344     TRACE("%p %p\n", image, width);
2345 
2346     if(!image || !width)
2347         return InvalidParameter;
2348 
2349     if(image->type == ImageTypeMetafile)
2350         *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
2351     else if(image->type == ImageTypeBitmap)
2352         *width = ((GpBitmap*)image)->width;
2353     else
2354     {
2355         WARN("GpImage with no image data\n");
2356         return InvalidParameter;
2357     }
2358 
2359     TRACE("returning %d\n", *width);
2360 
2361     return Ok;
2362 }
2363 
2364 GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT *num)
2365 {
2366     TRACE("(%p, %p)\n", image, num);
2367 
2368     if (!image || !num) return InvalidParameter;
2369 
2370     *num = 0;
2371 
2372     if (image->type == ImageTypeBitmap)
2373     {
2374         if (((GpBitmap *)image)->prop_item)
2375         {
2376             *num = ((GpBitmap *)image)->prop_count;
2377             return Ok;
2378         }
2379 
2380         if (((GpBitmap *)image)->metadata_reader)
2381             IWICMetadataReader_GetCount(((GpBitmap *)image)->metadata_reader, num);
2382     }
2383 
2384     return Ok;
2385 }
2386 
2387 GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID *list)
2388 {
2389     HRESULT hr;
2390     IWICMetadataReader *reader;
2391     IWICEnumMetadataItem *enumerator;
2392     UINT prop_count, i, items_returned;
2393 
2394     TRACE("(%p, %u, %p)\n", image, num, list);
2395 
2396     if (!image || !list) return InvalidParameter;
2397 
2398     if (image->type != ImageTypeBitmap)
2399     {
2400         FIXME("Not implemented for type %d\n", image->type);
2401         return NotImplemented;
2402     }
2403 
2404     if (((GpBitmap *)image)->prop_item)
2405     {
2406         if (num != ((GpBitmap *)image)->prop_count) return InvalidParameter;
2407 
2408         for (i = 0; i < num; i++)
2409         {
2410             list[i] = ((GpBitmap *)image)->prop_item[i].id;
2411         }
2412 
2413         return Ok;
2414     }
2415 
2416     reader = ((GpBitmap *)image)->metadata_reader;
2417     if (!reader)
2418     {
2419         if (num != 0) return InvalidParameter;
2420         return Ok;
2421     }
2422 
2423     hr = IWICMetadataReader_GetCount(reader, &prop_count);
2424     if (FAILED(hr)) return hresult_to_status(hr);
2425 
2426     if (num != prop_count) return InvalidParameter;
2427 
2428     hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2429     if (FAILED(hr)) return hresult_to_status(hr);
2430 
2431     IWICEnumMetadataItem_Reset(enumerator);
2432 
2433     for (i = 0; i < num; i++)
2434     {
2435         PROPVARIANT id;
2436 
2437         hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, NULL, &items_returned);
2438         if (hr != S_OK) break;
2439 
2440         if (id.vt != VT_UI2)
2441         {
2442             FIXME("not supported propvariant type for id: %u\n", id.vt);
2443             list[i] = 0;
2444             continue;
2445         }
2446         list[i] = id.u.uiVal;
2447     }
2448 
2449     IWICEnumMetadataItem_Release(enumerator);
2450 
2451     return hr == S_OK ? Ok : hresult_to_status(hr);
2452 }
2453 
2454 static UINT propvariant_size(PROPVARIANT *value)
2455 {
2456     switch (value->vt & ~VT_VECTOR)
2457     {
2458     case VT_EMPTY:
2459         return 0;
2460     case VT_I1:
2461     case VT_UI1:
2462         if (!(value->vt & VT_VECTOR)) return 1;
2463         return value->u.caub.cElems;
2464     case VT_I2:
2465     case VT_UI2:
2466         if (!(value->vt & VT_VECTOR)) return 2;
2467         return value->u.caui.cElems * 2;
2468     case VT_I4:
2469     case VT_UI4:
2470     case VT_R4:
2471         if (!(value->vt & VT_VECTOR)) return 4;
2472         return value->u.caul.cElems * 4;
2473     case VT_I8:
2474     case VT_UI8:
2475     case VT_R8:
2476         if (!(value->vt & VT_VECTOR)) return 8;
2477         return value->u.cauh.cElems * 8;
2478     case VT_LPSTR:
2479         return value->u.pszVal ? strlen(value->u.pszVal) + 1 : 0;
2480     case VT_BLOB:
2481         return value->u.blob.cbSize;
2482     default:
2483         FIXME("not supported variant type %d\n", value->vt);
2484         return 0;
2485     }
2486 }
2487 
2488 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID propid, UINT *size)
2489 {
2490     HRESULT hr;
2491     IWICMetadataReader *reader;
2492     PROPVARIANT id, value;
2493 
2494     TRACE("(%p,%#x,%p)\n", image, propid, size);
2495 
2496     if (!size || !image) return InvalidParameter;
2497 
2498     if (image->type != ImageTypeBitmap)
2499     {
2500         FIXME("Not implemented for type %d\n", image->type);
2501         return NotImplemented;
2502     }
2503 
2504     if (((GpBitmap *)image)->prop_item)
2505     {
2506         UINT i;
2507 
2508         for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2509         {
2510             if (propid == ((GpBitmap *)image)->prop_item[i].id)
2511             {
2512                 *size = sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2513                 return Ok;
2514             }
2515         }
2516 
2517         return PropertyNotFound;
2518     }
2519 
2520     reader = ((GpBitmap *)image)->metadata_reader;
2521     if (!reader) return PropertyNotFound;
2522 
2523     id.vt = VT_UI2;
2524     id.u.uiVal = propid;
2525     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2526     if (FAILED(hr)) return PropertyNotFound;
2527 
2528     *size = propvariant_size(&value);
2529     if (*size) *size += sizeof(PropertyItem);
2530     PropVariantClear(&value);
2531 
2532     return Ok;
2533 }
2534 
2535 #ifndef PropertyTagTypeSByte
2536 #define PropertyTagTypeSByte  6
2537 #define PropertyTagTypeSShort 8
2538 #define PropertyTagTypeFloat  11
2539 #define PropertyTagTypeDouble 12
2540 #endif
2541 
2542 static UINT vt_to_itemtype(UINT vt)
2543 {
2544     static const struct
2545     {
2546         UINT vt, type;
2547     } vt2type[] =
2548     {
2549         { VT_I1, PropertyTagTypeSByte },
2550         { VT_UI1, PropertyTagTypeByte },
2551         { VT_I2, PropertyTagTypeSShort },
2552         { VT_UI2, PropertyTagTypeShort },
2553         { VT_I4, PropertyTagTypeSLONG },
2554         { VT_UI4, PropertyTagTypeLong },
2555         { VT_I8, PropertyTagTypeSRational },
2556         { VT_UI8, PropertyTagTypeRational },
2557         { VT_R4, PropertyTagTypeFloat },
2558         { VT_R8, PropertyTagTypeDouble },
2559         { VT_LPSTR, PropertyTagTypeASCII },
2560         { VT_BLOB, PropertyTagTypeUndefined }
2561     };
2562     UINT i;
2563     for (i = 0; i < ARRAY_SIZE(vt2type); i++)
2564     {
2565         if (vt2type[i].vt == vt) return vt2type[i].type;
2566     }
2567     FIXME("not supported variant type %u\n", vt);
2568     return 0;
2569 }
2570 
2571 static GpStatus propvariant_to_item(PROPVARIANT *value, PropertyItem *item,
2572                                     UINT size, PROPID id)
2573 {
2574     UINT item_size, item_type;
2575 
2576     item_size = propvariant_size(value);
2577     if (size != item_size + sizeof(PropertyItem)) return InvalidParameter;
2578 
2579     item_type = vt_to_itemtype(value->vt & ~VT_VECTOR);
2580     if (!item_type) return InvalidParameter;
2581 
2582     item->value = item + 1;
2583 
2584     switch (value->vt & ~VT_VECTOR)
2585     {
2586     case VT_I1:
2587     case VT_UI1:
2588         if (!(value->vt & VT_VECTOR))
2589             *(BYTE *)item->value = value->u.bVal;
2590         else
2591             memcpy(item->value, value->u.caub.pElems, item_size);
2592         break;
2593     case VT_I2:
2594     case VT_UI2:
2595         if (!(value->vt & VT_VECTOR))
2596             *(USHORT *)item->value = value->u.uiVal;
2597         else
2598             memcpy(item->value, value->u.caui.pElems, item_size);
2599         break;
2600     case VT_I4:
2601     case VT_UI4:
2602     case VT_R4:
2603         if (!(value->vt & VT_VECTOR))
2604             *(ULONG *)item->value = value->u.ulVal;
2605         else
2606             memcpy(item->value, value->u.caul.pElems, item_size);
2607         break;
2608     case VT_I8:
2609     case VT_UI8:
2610     case VT_R8:
2611         if (!(value->vt & VT_VECTOR))
2612             *(ULONGLONG *)item->value = value->u.uhVal.QuadPart;
2613         else
2614             memcpy(item->value, value->u.cauh.pElems, item_size);
2615         break;
2616     case VT_LPSTR:
2617         memcpy(item->value, value->u.pszVal, item_size);
2618         break;
2619     case VT_BLOB:
2620         memcpy(item->value, value->u.blob.pBlobData, item_size);
2621         break;
2622     default:
2623         FIXME("not supported variant type %d\n", value->vt);
2624         return InvalidParameter;
2625     }
2626 
2627     item->length = item_size;
2628     item->type = item_type;
2629     item->id = id;
2630 
2631     return Ok;
2632 }
2633 
2634 GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID propid, UINT size,
2635                                         PropertyItem *buffer)
2636 {
2637     GpStatus stat;
2638     HRESULT hr;
2639     IWICMetadataReader *reader;
2640     PROPVARIANT id, value;
2641 
2642     TRACE("(%p,%#x,%u,%p)\n", image, propid, size, buffer);
2643 
2644     if (!image || !buffer) return InvalidParameter;
2645 
2646     if (image->type != ImageTypeBitmap)
2647     {
2648         FIXME("Not implemented for type %d\n", image->type);
2649         return NotImplemented;
2650     }
2651 
2652     if (((GpBitmap *)image)->prop_item)
2653     {
2654         UINT i;
2655 
2656         for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2657         {
2658             if (propid == ((GpBitmap *)image)->prop_item[i].id)
2659             {
2660                 if (size != sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length)
2661                     return InvalidParameter;
2662 
2663                 *buffer = ((GpBitmap *)image)->prop_item[i];
2664                 buffer->value = buffer + 1;
2665                 memcpy(buffer->value, ((GpBitmap *)image)->prop_item[i].value, buffer->length);
2666                 return Ok;
2667             }
2668         }
2669 
2670         return PropertyNotFound;
2671     }
2672 
2673     reader = ((GpBitmap *)image)->metadata_reader;
2674     if (!reader) return PropertyNotFound;
2675 
2676     id.vt = VT_UI2;
2677     id.u.uiVal = propid;
2678     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2679     if (FAILED(hr)) return PropertyNotFound;
2680 
2681     stat = propvariant_to_item(&value, buffer, size, propid);
2682     PropVariantClear(&value);
2683 
2684     return stat;
2685 }
2686 
2687 GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT *size, UINT *count)
2688 {
2689     HRESULT hr;
2690     IWICMetadataReader *reader;
2691     IWICEnumMetadataItem *enumerator;
2692     UINT prop_count, prop_size, i;
2693     PROPVARIANT id, value;
2694 
2695     TRACE("(%p,%p,%p)\n", image, size, count);
2696 
2697     if (!image || !size || !count) return InvalidParameter;
2698 
2699     if (image->type != ImageTypeBitmap)
2700     {
2701         FIXME("Not implemented for type %d\n", image->type);
2702         return NotImplemented;
2703     }
2704 
2705     if (((GpBitmap *)image)->prop_item)
2706     {
2707         *count = ((GpBitmap *)image)->prop_count;
2708         *size = 0;
2709 
2710         for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2711         {
2712             *size += sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2713         }
2714 
2715         return Ok;
2716     }
2717 
2718     reader = ((GpBitmap *)image)->metadata_reader;
2719     if (!reader) return PropertyNotFound;
2720 
2721     hr = IWICMetadataReader_GetCount(reader, &prop_count);
2722     if (FAILED(hr)) return hresult_to_status(hr);
2723 
2724     hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2725     if (FAILED(hr)) return hresult_to_status(hr);
2726 
2727     IWICEnumMetadataItem_Reset(enumerator);
2728 
2729     prop_size = 0;
2730 
2731     PropVariantInit(&id);
2732     PropVariantInit(&value);
2733 
2734     for (i = 0; i < prop_count; i++)
2735     {
2736         UINT items_returned, item_size;
2737 
2738         hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2739         if (hr != S_OK) break;
2740 
2741         item_size = propvariant_size(&value);
2742         if (item_size) prop_size += sizeof(PropertyItem) + item_size;
2743 
2744         PropVariantClear(&id);
2745         PropVariantClear(&value);
2746     }
2747 
2748     IWICEnumMetadataItem_Release(enumerator);
2749 
2750     if (hr != S_OK) return PropertyNotFound;
2751 
2752     *count = prop_count;
2753     *size = prop_size;
2754     return Ok;
2755 }
2756 
2757 GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
2758                                             UINT count, PropertyItem *buf)
2759 {
2760     GpStatus status;
2761     HRESULT hr;
2762     IWICMetadataReader *reader;
2763     IWICEnumMetadataItem *enumerator;
2764     UINT prop_count, prop_size, i;
2765     PROPVARIANT id, value;
2766     char *item_value;
2767 
2768     TRACE("(%p,%u,%u,%p)\n", image, size, count, buf);
2769 
2770     if (!image || !buf) return InvalidParameter;
2771 
2772     if (image->type != ImageTypeBitmap)
2773     {
2774         FIXME("Not implemented for type %d\n", image->type);
2775         return NotImplemented;
2776     }
2777 
2778     status = GdipGetPropertySize(image, &prop_size, &prop_count);
2779     if (status != Ok) return status;
2780 
2781     if (prop_count != count || prop_size != size) return InvalidParameter;
2782 
2783     if (((GpBitmap *)image)->prop_item)
2784     {
2785         memcpy(buf, ((GpBitmap *)image)->prop_item, prop_size);
2786 
2787         item_value = (char *)(buf + prop_count);
2788 
2789         for (i = 0; i < prop_count; i++)
2790         {
2791             buf[i].value = item_value;
2792             item_value += buf[i].length;
2793         }
2794 
2795         return Ok;
2796     }
2797 
2798     reader = ((GpBitmap *)image)->metadata_reader;
2799     if (!reader) return PropertyNotFound;
2800 
2801     hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2802     if (FAILED(hr)) return hresult_to_status(hr);
2803 
2804     IWICEnumMetadataItem_Reset(enumerator);
2805 
2806     item_value = (char *)(buf + prop_count);
2807 
2808     PropVariantInit(&id);
2809     PropVariantInit(&value);
2810 
2811     for (i = 0; i < prop_count; i++)
2812     {
2813         PropertyItem *item;
2814         UINT items_returned, item_size;
2815 
2816         hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2817         if (hr != S_OK) break;
2818 
2819         if (id.vt != VT_UI2)
2820         {
2821             FIXME("not supported propvariant type for id: %u\n", id.vt);
2822             continue;
2823         }
2824 
2825         item_size = propvariant_size(&value);
2826         if (item_size)
2827         {
2828             item = heap_alloc(item_size + sizeof(*item));
2829 
2830             propvariant_to_item(&value, item, item_size + sizeof(*item), id.u.uiVal);
2831             buf[i].id = item->id;
2832             buf[i].type = item->type;
2833             buf[i].length = item_size;
2834             buf[i].value = item_value;
2835             memcpy(item_value, item->value, item_size);
2836             item_value += item_size;
2837 
2838             heap_free(item);
2839         }
2840 
2841         PropVariantClear(&id);
2842         PropVariantClear(&value);
2843     }
2844 
2845     IWICEnumMetadataItem_Release(enumerator);
2846 
2847     if (hr != S_OK) return PropertyNotFound;
2848 
2849     return Ok;
2850 }
2851 
2852 struct image_format_dimension
2853 {
2854     const GUID *format;
2855     const GUID *dimension;
2856 };
2857 
2858 static const struct image_format_dimension image_format_dimensions[] =
2859 {
2860     {&ImageFormatGIF, &FrameDimensionTime},
2861     {&ImageFormatIcon, &FrameDimensionResolution},
2862     {NULL}
2863 };
2864 
2865 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
2866     GDIPCONST GUID* dimensionID, UINT* count)
2867 {
2868     TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
2869 
2870     if(!image || !count)
2871         return InvalidParameter;
2872 
2873     if (!dimensionID ||
2874         IsEqualGUID(dimensionID, &image->format) ||
2875         IsEqualGUID(dimensionID, &FrameDimensionPage) ||
2876         IsEqualGUID(dimensionID, &FrameDimensionTime))
2877     {
2878         *count = image->frame_count;
2879         return Ok;
2880     }
2881 
2882     return InvalidParameter;
2883 }
2884 
2885 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
2886     UINT* count)
2887 {
2888     TRACE("(%p, %p)\n", image, count);
2889 
2890     /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
2891 
2892     if(!image || !count)
2893         return InvalidParameter;
2894 
2895     *count = 1;
2896 
2897     return Ok;
2898 }
2899 
2900 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
2901     GUID* dimensionIDs, UINT count)
2902 {
2903     int i;
2904     const GUID *result=NULL;
2905 
2906     TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
2907 
2908     if(!image || !dimensionIDs || count != 1)
2909         return InvalidParameter;
2910 
2911     for (i=0; image_format_dimensions[i].format; i++)
2912     {
2913         if (IsEqualGUID(&image->format, image_format_dimensions[i].format))
2914         {
2915             result = image_format_dimensions[i].dimension;
2916             break;
2917         }
2918     }
2919 
2920     if (!result)
2921         result = &FrameDimensionPage;
2922 
2923     memcpy(dimensionIDs, result, sizeof(GUID));
2924 
2925     return Ok;
2926 }
2927 
2928 GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename,
2929                                           GpImage **image)
2930 {
2931     GpStatus stat;
2932     IStream *stream;
2933 
2934     TRACE("(%s) %p\n", debugstr_w(filename), image);
2935 
2936     if (!filename || !image)
2937         return InvalidParameter;
2938 
2939     *image = NULL;
2940 
2941     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
2942 
2943     if (stat != Ok)
2944         return stat;
2945 
2946     stat = GdipLoadImageFromStream(stream, image);
2947 
2948     IStream_Release(stream);
2949 
2950     return stat;
2951 }
2952 
2953 /* FIXME: no icm handling */
2954 GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage **image)
2955 {
2956     TRACE("(%s) %p\n", debugstr_w(filename), image);
2957 
2958     return GdipLoadImageFromFile(filename, image);
2959 }
2960 
2961 static void add_property(GpBitmap *bitmap, PropertyItem *item)
2962 {
2963     UINT prop_size, prop_count;
2964     PropertyItem *prop_item;
2965 
2966     if (bitmap->prop_item == NULL)
2967     {
2968         prop_size = prop_count = 0;
2969         prop_item = heap_alloc_zero(item->length + sizeof(PropertyItem));
2970         if (!prop_item) return;
2971     }
2972     else
2973     {
2974         UINT i;
2975         char *item_value;
2976 
2977         GdipGetPropertySize(&bitmap->image, &prop_size, &prop_count);
2978 
2979         prop_item = heap_alloc_zero(prop_size + item->length + sizeof(PropertyItem));
2980         if (!prop_item) return;
2981         memcpy(prop_item, bitmap->prop_item, sizeof(PropertyItem) * bitmap->prop_count);
2982         prop_size -= sizeof(PropertyItem) * bitmap->prop_count;
2983         memcpy(prop_item + prop_count + 1, bitmap->prop_item + prop_count, prop_size);
2984 
2985         item_value = (char *)(prop_item + prop_count + 1);
2986 
2987         for (i = 0; i < prop_count; i++)
2988         {
2989             prop_item[i].value = item_value;
2990             item_value += prop_item[i].length;
2991         }
2992     }
2993 
2994     prop_item[prop_count].id = item->id;
2995     prop_item[prop_count].type = item->type;
2996     prop_item[prop_count].length = item->length;
2997     prop_item[prop_count].value = (char *)(prop_item + prop_count + 1) + prop_size;
2998     memcpy(prop_item[prop_count].value, item->value, item->length);
2999 
3000     heap_free(bitmap->prop_item);
3001     bitmap->prop_item = prop_item;
3002     bitmap->prop_count++;
3003 }
3004 
3005 static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3006 {
3007     HRESULT hr;
3008     GUID format;
3009     PROPVARIANT id, value;
3010     BOOL ret = FALSE;
3011 
3012     hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3013     if (FAILED(hr) || !IsEqualGUID(&format, guid)) return FALSE;
3014 
3015     PropVariantInit(&id);
3016     PropVariantInit(&value);
3017 
3018     id.vt = VT_LPWSTR;
3019     id.u.pwszVal = CoTaskMemAlloc((lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3020     if (!id.u.pwszVal) return FALSE;
3021     lstrcpyW(id.u.pwszVal, prop_name);
3022     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3023     if (hr == S_OK && value.vt == VT_BOOL)
3024         ret = value.u.boolVal;
3025 
3026     PropVariantClear(&id);
3027     PropVariantClear(&value);
3028 
3029     return ret;
3030 }
3031 
3032 static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3033 {
3034     HRESULT hr;
3035     GUID format;
3036     PROPVARIANT id, value;
3037     PropertyItem *item = NULL;
3038 
3039     hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3040     if (FAILED(hr) || !IsEqualGUID(&format, guid)) return NULL;
3041 
3042     PropVariantInit(&id);
3043     PropVariantInit(&value);
3044 
3045     id.vt = VT_LPWSTR;
3046     id.u.pwszVal = CoTaskMemAlloc((lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3047     if (!id.u.pwszVal) return NULL;
3048     lstrcpyW(id.u.pwszVal, prop_name);
3049     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3050     if (hr == S_OK)
3051     {
3052         UINT item_size = propvariant_size(&value);
3053         if (item_size)
3054         {
3055             item_size += sizeof(*item);
3056             item = heap_alloc_zero(item_size);
3057             if (propvariant_to_item(&value, item, item_size, 0) != Ok)
3058             {
3059                 heap_free(item);
3060                 item = NULL;
3061             }
3062         }
3063     }
3064 
3065     PropVariantClear(&id);
3066     PropVariantClear(&value);
3067 
3068     return item;
3069 }
3070 
3071 static PropertyItem *get_gif_comment(IWICMetadataReader *reader)
3072 {
3073     static const WCHAR textentryW[] = { 'T','e','x','t','E','n','t','r','y',0 };
3074     PropertyItem *comment;
3075 
3076     comment = get_property(reader, &GUID_MetadataFormatGifComment, textentryW);
3077     if (comment)
3078         comment->id = PropertyTagExifUserComment;
3079 
3080     return comment;
3081 }
3082 
3083 static PropertyItem *get_gif_loopcount(IWICMetadataReader *reader)
3084 {
3085     static const WCHAR applicationW[] = { 'A','p','p','l','i','c','a','t','i','o','n',0 };
3086     static const WCHAR dataW[] = { 'D','a','t','a',0 };
3087     PropertyItem *appext = NULL, *appdata = NULL, *loop = NULL;
3088 
3089     appext = get_property(reader, &GUID_MetadataFormatAPE, applicationW);
3090     if (appext)
3091     {
3092         if (appext->type == PropertyTagTypeByte && appext->length == 11 &&
3093             (!memcmp(appext->value, "NETSCAPE2.0", 11) || !memcmp(appext->value, "ANIMEXTS1.0", 11)))
3094         {
3095             appdata = get_property(reader, &GUID_MetadataFormatAPE, dataW);
3096             if (appdata)
3097             {
3098                 if (appdata->type == PropertyTagTypeByte && appdata->length == 4)
3099                 {
3100                     BYTE *data = appdata->value;
3101                     if (data[0] == 3 && data[1] == 1)
3102                     {
3103                         loop = heap_alloc_zero(sizeof(*loop) + sizeof(SHORT));
3104                         if (loop)
3105                         {
3106                             loop->type = PropertyTagTypeShort;
3107                             loop->id = PropertyTagLoopCount;
3108                             loop->length = sizeof(SHORT);
3109                             loop->value = loop + 1;
3110                             *(SHORT *)loop->value = data[2] | (data[3] << 8);
3111                         }
3112                     }
3113                 }
3114             }
3115         }
3116     }
3117 
3118     heap_free(appext);
3119     heap_free(appdata);
3120 
3121     return loop;
3122 }
3123 
3124 static PropertyItem *get_gif_background(IWICMetadataReader *reader)
3125 {
3126     static const WCHAR backgroundW[] = { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 };
3127     PropertyItem *background;
3128 
3129     background = get_property(reader, &GUID_MetadataFormatLSD, backgroundW);
3130     if (background)
3131         background->id = PropertyTagIndexBackground;
3132 
3133     return background;
3134 }
3135 
3136 static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataReader *reader)
3137 {
3138     static const WCHAR global_flagW[] = { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 };
3139     HRESULT hr;
3140     IWICImagingFactory *factory;
3141     IWICPalette *palette;
3142     UINT count = 0;
3143     WICColor colors[256];
3144 
3145     if (!get_bool_property(reader, &GUID_MetadataFormatLSD, global_flagW))
3146         return NULL;
3147 
3148     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
3149     if (hr != S_OK) return NULL;
3150 
3151     hr = IWICImagingFactory_CreatePalette(factory, &palette);
3152     if (hr == S_OK)
3153     {
3154         hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
3155         if (hr == S_OK)
3156             IWICPalette_GetColors(palette, 256, colors, &count);
3157 
3158         IWICPalette_Release(palette);
3159     }
3160 
3161     IWICImagingFactory_Release(factory);
3162 
3163     if (count)
3164     {
3165         PropertyItem *pal;
3166         UINT i;
3167         BYTE *rgb;
3168 
3169         pal = heap_alloc_zero(sizeof(*pal) + count * 3);
3170         if (!pal) return NULL;
3171         pal->type = PropertyTagTypeByte;
3172         pal->id = PropertyTagGlobalPalette;
3173         pal->value = pal + 1;
3174         pal->length = count * 3;
3175 
3176         rgb = pal->value;
3177 
3178         for (i = 0; i < count; i++)
3179         {
3180             rgb[i*3] = (colors[i] >> 16) & 0xff;
3181             rgb[i*3 + 1] = (colors[i] >> 8) & 0xff;
3182             rgb[i*3 + 2] = colors[i] & 0xff;
3183         }
3184 
3185         return pal;
3186     }
3187 
3188     return NULL;
3189 }
3190 
3191 static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader)
3192 {
3193     static const WCHAR transparency_flagW[] = { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 };
3194     static const WCHAR colorW[] = { 'T','r','a','n','s','p','a','r','e','n','t','C','o','l','o','r','I','n','d','e','x',0 };
3195     PropertyItem *index = NULL;
3196 
3197     if (get_bool_property(reader, &GUID_MetadataFormatGCE, transparency_flagW))
3198     {
3199         index = get_property(reader, &GUID_MetadataFormatGCE, colorW);
3200         if (index)
3201             index->id = PropertyTagIndexTransparent;
3202     }
3203     return index;
3204 }
3205 
3206 static LONG get_gif_frame_property(IWICBitmapFrameDecode *frame, const GUID *format, const WCHAR *property)
3207 {
3208     HRESULT hr;
3209     IWICMetadataBlockReader *block_reader;
3210     IWICMetadataReader *reader;
3211     UINT block_count, i;
3212     PropertyItem *prop;
3213     LONG value = 0;
3214 
3215     hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3216     if (hr == S_OK)
3217     {
3218         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3219         if (hr == S_OK)
3220         {
3221             for (i = 0; i < block_count; i++)
3222             {
3223                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3224                 if (hr == S_OK)
3225                 {
3226                     prop = get_property(reader, format, property);
3227                     if (prop)
3228                     {
3229                         if (prop->type == PropertyTagTypeByte && prop->length == 1)
3230                             value = *(BYTE *)prop->value;
3231                         else if (prop->type == PropertyTagTypeShort && prop->length == 2)
3232                             value = *(SHORT *)prop->value;
3233 
3234                         heap_free(prop);
3235                     }
3236                     IWICMetadataReader_Release(reader);
3237                 }
3238             }
3239         }
3240         IWICMetadataBlockReader_Release(block_reader);
3241     }
3242 
3243     return value;
3244 }
3245 
3246 static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3247 {
3248     static const WCHAR delayW[] = { 'D','e','l','a','y',0 };
3249     HRESULT hr;
3250     IWICBitmapFrameDecode *frame;
3251     IWICMetadataBlockReader *block_reader;
3252     IWICMetadataReader *reader;
3253     UINT frame_count, block_count, i;
3254     PropertyItem *delay = NULL, *comment = NULL, *background = NULL;
3255     PropertyItem *transparent_idx = NULL, *loop = NULL, *palette = NULL;
3256 
3257     IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3258     if (frame_count > 1)
3259     {
3260         delay = heap_alloc_zero(sizeof(*delay) + frame_count * sizeof(LONG));
3261         if (delay)
3262         {
3263             LONG *value;
3264 
3265             delay->type = PropertyTagTypeLong;
3266             delay->id = PropertyTagFrameDelay;
3267             delay->length = frame_count * sizeof(LONG);
3268             delay->value = delay + 1;
3269 
3270             value = delay->value;
3271 
3272             for (i = 0; i < frame_count; i++)
3273             {
3274                 hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
3275                 if (hr == S_OK)
3276                 {
3277                     value[i] = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, delayW);
3278                     IWICBitmapFrameDecode_Release(frame);
3279                 }
3280                 else value[i] = 0;
3281             }
3282         }
3283     }
3284 
3285     hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3286     if (hr == S_OK)
3287     {
3288         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3289         if (hr == S_OK)
3290         {
3291             for (i = 0; i < block_count; i++)
3292             {
3293                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3294                 if (hr == S_OK)
3295                 {
3296                     if (!comment)
3297                         comment = get_gif_comment(reader);
3298 
3299                     if (frame_count > 1 && !loop)
3300                         loop = get_gif_loopcount(reader);
3301 
3302                     if (!background)
3303                         background = get_gif_background(reader);
3304 
3305                     if (!palette)
3306                         palette = get_gif_palette(decoder, reader);
3307 
3308                     IWICMetadataReader_Release(reader);
3309                 }
3310             }
3311         }
3312         IWICMetadataBlockReader_Release(block_reader);
3313     }
3314 
3315     if (frame_count > 1 && !loop)
3316     {
3317         loop = heap_alloc_zero(sizeof(*loop) + sizeof(SHORT));
3318         if (loop)
3319         {
3320             loop->type = PropertyTagTypeShort;
3321             loop->id = PropertyTagLoopCount;
3322             loop->length = sizeof(SHORT);
3323             loop->value = loop + 1;
3324             *(SHORT *)loop->value = 1;
3325         }
3326     }
3327 
3328     if (delay) add_property(bitmap, delay);
3329     if (comment) add_property(bitmap, comment);
3330     if (loop) add_property(bitmap, loop);
3331     if (palette) add_property(bitmap, palette);
3332     if (background) add_property(bitmap, background);
3333 
3334     heap_free(delay);
3335     heap_free(comment);
3336     heap_free(loop);
3337     heap_free(palette);
3338     heap_free(background);
3339 
3340     /* Win7 gdiplus always returns transparent color index from frame 0 */
3341     hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
3342     if (hr != S_OK) return;
3343 
3344     hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3345     if (hr == S_OK)
3346     {
3347         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3348         if (hr == S_OK)
3349         {
3350             for (i = 0; i < block_count; i++)
3351             {
3352                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3353                 if (hr == S_OK)
3354                 {
3355                     if (!transparent_idx)
3356                         transparent_idx = get_gif_transparent_idx(reader);
3357 
3358                     IWICMetadataReader_Release(reader);
3359                 }
3360             }
3361         }
3362         IWICMetadataBlockReader_Release(block_reader);
3363     }
3364 
3365     if (transparent_idx) add_property(bitmap, transparent_idx);
3366     heap_free(transparent_idx);
3367 
3368     IWICBitmapFrameDecode_Release(frame);
3369 }
3370 
3371 static PropertyItem* create_prop(PROPID propid, PROPVARIANT* value)
3372 {
3373     PropertyItem *item = NULL;
3374     UINT item_size = propvariant_size(value);
3375 
3376     if (item_size)
3377     {
3378         item_size += sizeof(*item);
3379         item = heap_alloc_zero(item_size);
3380         if (propvariant_to_item(value, item, item_size, propid) != Ok)
3381         {
3382             heap_free(item);
3383             item = NULL;
3384         }
3385     }
3386 
3387     return item;
3388 }
3389 
3390 static ULONG get_ulong_by_index(IWICMetadataReader* reader, ULONG index)
3391 {
3392     PROPVARIANT value;
3393     HRESULT hr;
3394     ULONG result=0;
3395 
3396     hr = IWICMetadataReader_GetValueByIndex(reader, index, NULL, NULL, &value);
3397     if (SUCCEEDED(hr))
3398     {
3399         switch (value.vt)
3400         {
3401         case VT_UI4:
3402             result = value.u.ulVal;
3403             break;
3404         default:
3405             ERR("unhandled case %u\n", value.vt);
3406             break;
3407         }
3408         PropVariantClear(&value);
3409     }
3410     return result;
3411 }
3412 
3413 static void png_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3414 {
3415     HRESULT hr;
3416     IWICBitmapFrameDecode *frame;
3417     IWICMetadataBlockReader *block_reader;
3418     IWICMetadataReader *reader;
3419     UINT block_count, i, j;
3420     struct keyword_info {
3421         const char* name;
3422         PROPID propid;
3423         BOOL seen;
3424     } keywords[] = {
3425         { "Title", PropertyTagImageTitle },
3426         { "Author", PropertyTagArtist },
3427         { "Description", PropertyTagImageDescription },
3428         { "Copyright", PropertyTagCopyright },
3429         { "Software", PropertyTagSoftwareUsed },
3430         { "Source", PropertyTagEquipModel },
3431         { "Comment", PropertyTagExifUserComment },
3432     };
3433     BOOL seen_gamma=FALSE, seen_whitepoint=FALSE, seen_chrm=FALSE;
3434 
3435     hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3436     if (hr != S_OK) return;
3437 
3438     hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3439     if (hr == S_OK)
3440     {
3441         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3442         if (hr == S_OK)
3443         {
3444             for (i = 0; i < block_count; i++)
3445             {
3446                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3447                 if (hr == S_OK)
3448                 {
3449                     GUID format;
3450 
3451                     hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3452                     if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunktEXt, &format))
3453                     {
3454                         PROPVARIANT name, value;
3455                         PropertyItem* item;
3456 
3457                         hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &name, &value);
3458 
3459                         if (SUCCEEDED(hr))
3460                         {
3461                             if (name.vt == VT_LPSTR)
3462                             {
3463                                 for (j = 0; j < ARRAY_SIZE(keywords); j++)
3464                                     if (!strcmp(keywords[j].name, name.u.pszVal))
3465                                         break;
3466                                 if (j < ARRAY_SIZE(keywords) && !keywords[j].seen)
3467                                 {
3468                                     keywords[j].seen = TRUE;
3469                                     item = create_prop(keywords[j].propid, &value);
3470                                     if (item)
3471                                         add_property(bitmap, item);
3472                                     heap_free(item);
3473                                 }
3474                             }
3475 
3476                             PropVariantClear(&name);
3477                             PropVariantClear(&value);
3478                         }
3479                     }
3480                     else if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunkgAMA, &format))
3481                     {
3482                         PropertyItem* item;
3483 
3484                         if (!seen_gamma)
3485                         {
3486                             item = heap_alloc_zero(sizeof(PropertyItem) + sizeof(ULONG) * 2);
3487                             if (item)
3488                             {
3489                                 ULONG *rational;
3490                                 item->length = sizeof(ULONG) * 2;
3491                                 item->type = PropertyTagTypeRational;
3492                                 item->id = PropertyTagGamma;
3493                                 rational = item->value = item + 1;
3494                                 rational[0] = 100000;
3495                                 rational[1] = get_ulong_by_index(reader, 0);
3496                                 add_property(bitmap, item);
3497                                 seen_gamma = TRUE;
3498                                 heap_free(item);
3499                             }
3500                         }
3501                     }
3502                     else if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunkcHRM, &format))
3503                     {
3504                         PropertyItem* item;
3505 
3506                         if (!seen_whitepoint)
3507                         {
3508                             item = GdipAlloc(sizeof(PropertyItem) + sizeof(ULONG) * 4);
3509                             if (item)
3510                             {
3511                                 ULONG *rational;
3512                                 item->length = sizeof(ULONG) * 4;
3513                                 item->type = PropertyTagTypeRational;
3514                                 item->id = PropertyTagWhitePoint;
3515                                 rational = item->value = item + 1;
3516                                 rational[0] = get_ulong_by_index(reader, 0);
3517                                 rational[1] = 100000;
3518                                 rational[2] = get_ulong_by_index(reader, 1);
3519                                 rational[3] = 100000;
3520                                 add_property(bitmap, item);
3521                                 seen_whitepoint = TRUE;
3522                                 GdipFree(item);
3523                             }
3524                         }
3525                         if (!seen_chrm)
3526                         {
3527                             item = GdipAlloc(sizeof(PropertyItem) + sizeof(ULONG) * 12);
3528                             if (item)
3529                             {
3530                                 ULONG *rational;
3531                                 item->length = sizeof(ULONG) * 12;
3532                                 item->type = PropertyTagTypeRational;
3533                                 item->id = PropertyTagPrimaryChromaticities;
3534                                 rational = item->value = item + 1;
3535                                 rational[0] = get_ulong_by_index(reader, 2);
3536                                 rational[1] = 100000;
3537                                 rational[2] = get_ulong_by_index(reader, 3);
3538                                 rational[3] = 100000;
3539                                 rational[4] = get_ulong_by_index(reader, 4);
3540                                 rational[5] = 100000;
3541                                 rational[6] = get_ulong_by_index(reader, 5);
3542                                 rational[7] = 100000;
3543                                 rational[8] = get_ulong_by_index(reader, 6);
3544                                 rational[9] = 100000;
3545                                 rational[10] = get_ulong_by_index(reader, 7);
3546                                 rational[11] = 100000;
3547                                 add_property(bitmap, item);
3548                                 seen_chrm = TRUE;
3549                                 GdipFree(item);
3550                             }
3551                         }
3552                     }
3553 
3554                     IWICMetadataReader_Release(reader);
3555                 }
3556             }
3557         }
3558         IWICMetadataBlockReader_Release(block_reader);
3559     }
3560 
3561     IWICBitmapFrameDecode_Release(frame);
3562 }
3563 
3564 static GpStatus initialize_decoder_wic(IStream *stream, REFGUID container, IWICBitmapDecoder **decoder)
3565 {
3566     IWICImagingFactory *factory;
3567     HRESULT hr;
3568 
3569     TRACE("%p,%s\n", stream, wine_dbgstr_guid(container));
3570 
3571     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
3572     if (FAILED(hr)) return hresult_to_status(hr);
3573     hr = IWICImagingFactory_CreateDecoder(factory, container, NULL, decoder);
3574     IWICImagingFactory_Release(factory);
3575     if (FAILED(hr)) return hresult_to_status(hr);
3576 
3577     hr = IWICBitmapDecoder_Initialize(*decoder, stream, WICDecodeMetadataCacheOnLoad);
3578     if (FAILED(hr)) return hresult_to_status(hr);
3579     return Ok;
3580 }
3581 
3582 typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame);
3583 
3584 static GpStatus decode_frame_wic(IWICBitmapDecoder *decoder, BOOL force_conversion,
3585     UINT active_frame, metadata_reader_func metadata_reader, GpImage **image)
3586 {
3587     GpStatus status=Ok;
3588     GpBitmap *bitmap;
3589     HRESULT hr;
3590     IWICBitmapFrameDecode *frame;
3591     IWICBitmapSource *source=NULL;
3592     IWICMetadataBlockReader *block_reader;
3593     WICPixelFormatGUID wic_format;
3594     PixelFormat gdip_format=0;
3595     ColorPalette *palette = NULL;
3596     WICBitmapPaletteType palette_type = WICBitmapPaletteTypeFixedHalftone256;
3597     int i;
3598     UINT width, height, frame_count;
3599     BitmapData lockeddata;
3600     WICRect wrc;
3601 
3602     TRACE("%p,%u,%p\n", decoder, active_frame, image);
3603 
3604     IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3605     hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3606     if (SUCCEEDED(hr)) /* got frame */
3607     {
3608         hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &wic_format);
3609 
3610         if (SUCCEEDED(hr))
3611         {
3612             if (!force_conversion)
3613             {
3614                 for (i=0; pixel_formats[i].wic_format; i++)
3615                 {
3616                     if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format))
3617                     {
3618                         source = (IWICBitmapSource*)frame;
3619                         IWICBitmapSource_AddRef(source);
3620                         gdip_format = pixel_formats[i].gdip_format;
3621                         palette_type = pixel_formats[i].palette_type;
3622                         break;
3623                     }
3624                 }
3625             }
3626             if (!source)
3627             {
3628                 /* unknown format; fall back on 32bppARGB */
3629                 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
3630                 gdip_format = PixelFormat32bppARGB;
3631             }
3632             TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format);
3633         }
3634 
3635         if (SUCCEEDED(hr)) /* got source */
3636         {
3637             hr = IWICBitmapSource_GetSize(source, &width, &height);
3638 
3639             if (SUCCEEDED(hr))
3640                 status = GdipCreateBitmapFromScan0(width, height, 0, gdip_format,
3641                     NULL, &bitmap);
3642 
3643             if (SUCCEEDED(hr) && status == Ok) /* created bitmap */
3644             {
3645                 status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeWrite,
3646                     gdip_format, &lockeddata);
3647                 if (status == Ok) /* locked bitmap */
3648                 {
3649                     wrc.X = 0;
3650                     wrc.Width = width;
3651                     wrc.Height = 1;
3652                     for (i=0; i<height; i++)
3653                     {
3654                         wrc.Y = i;
3655                         hr = IWICBitmapSource_CopyPixels(source, &wrc, abs(lockeddata.Stride),
3656                             abs(lockeddata.Stride), (BYTE*)lockeddata.Scan0+lockeddata.Stride*i);
3657                         if (FAILED(hr)) break;
3658                     }
3659 
3660                     GdipBitmapUnlockBits(bitmap, &lockeddata);
3661                 }
3662 
3663                 if (SUCCEEDED(hr) && status == Ok)
3664                     *image = &bitmap->image;
3665                 else
3666                 {
3667                     *image = NULL;
3668                     GdipDisposeImage(&bitmap->image);
3669                 }
3670 
3671                 if (SUCCEEDED(hr) && status == Ok)
3672                 {
3673                     double dpix, dpiy;
3674                     hr = IWICBitmapSource_GetResolution(source, &dpix, &dpiy);
3675                     if (SUCCEEDED(hr))
3676                     {
3677                         bitmap->image.xres = dpix;
3678                         bitmap->image.yres = dpiy;
3679                     }
3680                     hr = S_OK;
3681                 }
3682             }
3683 
3684             IWICBitmapSource_Release(source);
3685         }
3686 
3687         if (SUCCEEDED(hr)) {
3688             bitmap->metadata_reader = NULL;
3689 
3690             if (metadata_reader)
3691                 metadata_reader(bitmap, decoder, active_frame);
3692             else if (IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader) == S_OK)
3693             {
3694                 UINT block_count = 0;
3695                 if (IWICMetadataBlockReader_GetCount(block_reader, &block_count) == S_OK && block_count)
3696                     IWICMetadataBlockReader_GetReaderByIndex(block_reader, 0, &bitmap->metadata_reader);
3697                 IWICMetadataBlockReader_Release(block_reader);
3698             }
3699 
3700             palette = get_palette(frame, palette_type);
3701             IWICBitmapFrameDecode_Release(frame);
3702         }
3703     }
3704 
3705     if (FAILED(hr) && status == Ok) status = hresult_to_status(hr);
3706 
3707     if (status == Ok)
3708     {
3709         /* Native GDI+ used to be smarter, but since Win7 it just sets these flags. */
3710         bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI;
3711         if (IsEqualGUID(&wic_format, &GUID_WICPixelFormat2bppGray) ||
3712             IsEqualGUID(&wic_format, &GUID_WICPixelFormat4bppGray) ||
3713             IsEqualGUID(&wic_format, &GUID_WICPixelFormat8bppGray) ||
3714             IsEqualGUID(&wic_format, &GUID_WICPixelFormat16bppGray))
3715             bitmap->image.flags |= ImageFlagsColorSpaceGRAY;
3716         else
3717             bitmap->image.flags |= ImageFlagsColorSpaceRGB;
3718         bitmap->image.frame_count = frame_count;
3719         bitmap->image.current_frame = active_frame;
3720         bitmap->image.decoder = decoder;
3721         IWICBitmapDecoder_AddRef(decoder);
3722         if (palette)
3723         {
3724             heap_free(bitmap->image.palette);
3725             bitmap->image.palette = palette;
3726         }
3727         else
3728         {
3729             if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite))
3730                 bitmap->image.palette->Flags = 0;
3731         }
3732         TRACE("=> %p\n", *image);
3733     }
3734 
3735     return status;
3736 }
3737 
3738 static GpStatus decode_image_wic(IStream *stream, REFGUID container,
3739         metadata_reader_func metadata_reader, GpImage **image)
3740 {
3741     IWICBitmapDecoder *decoder;
3742     GpStatus status;
3743 
3744     status = initialize_decoder_wic(stream, container, &decoder);
3745     if(status != Ok)
3746         return status;
3747 
3748     status = decode_frame_wic(decoder, FALSE, 0, metadata_reader, image);
3749     IWICBitmapDecoder_Release(decoder);
3750     return status;
3751 }
3752 
3753 static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
3754 {
3755     GpImage *new_image;
3756     GpStatus status;
3757 
3758     status = decode_frame_wic(image->decoder, FALSE, active_frame, NULL, &new_image);
3759     if(status != Ok)
3760         return status;
3761 
3762     new_image->busy = image->busy;
3763     memcpy(&new_image->format, &image->format, sizeof(GUID));
3764     free_image_data(image);
3765     if (image->type == ImageTypeBitmap)
3766         *(GpBitmap *)image = *(GpBitmap *)new_image;
3767     else if (image->type == ImageTypeMetafile)
3768         *(GpMetafile *)image = *(GpMetafile *)new_image;
3769     new_image->type = ~0;
3770     heap_free(new_image);
3771     return Ok;
3772 }
3773 
3774 static HRESULT get_gif_frame_rect(IWICBitmapFrameDecode *frame,
3775         UINT *left, UINT *top, UINT *width, UINT *height)
3776 {
3777     static const WCHAR leftW[] = {'L','e','f','t',0};
3778     static const WCHAR topW[] = {'T','o','p',0};
3779 
3780     *left = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, leftW);
3781     *top = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, topW);
3782 
3783     return IWICBitmapFrameDecode_GetSize(frame, width, height);
3784 }
3785 
3786 static HRESULT blit_gif_frame(GpBitmap *bitmap, IWICBitmapFrameDecode *frame, BOOL first_frame)
3787 {
3788     UINT i, j, left, top, width, height;
3789     IWICBitmapSource *source;
3790     BYTE *new_bits;
3791     HRESULT hr;
3792 
3793     hr = get_gif_frame_rect(frame, &left, &top, &width, &height);
3794     if(FAILED(hr))
3795         return hr;
3796 
3797     hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
3798     if(FAILED(hr))
3799         return hr;
3800 
3801     new_bits = heap_alloc_zero(width*height*4);
3802     if(!new_bits)
3803         return E_OUTOFMEMORY;
3804 
3805     hr = IWICBitmapSource_CopyPixels(source, NULL, width*4, width*height*4, new_bits);
3806     IWICBitmapSource_Release(source);
3807     if(FAILED(hr)) {
3808         heap_free(new_bits);
3809         return hr;
3810     }
3811 
3812     for(i=0; i<height && i+top<bitmap->height; i++) {
3813         for(j=0; j<width && j+left<bitmap->width; j++) {
3814             DWORD *src = (DWORD*)(new_bits+i*width*4+j*4);
3815             DWORD *dst = (DWORD*)(bitmap->bits+(i+top)*bitmap->stride+(j+left)*4);
3816 
3817             if(first_frame || *src>>24 != 0)
3818                 *dst = *src;
3819         }
3820     }
3821     heap_free(new_bits);
3822     return hr;
3823 }
3824 
3825 static DWORD get_gif_background_color(GpBitmap *bitmap)
3826 {
3827     BYTE bgcolor_idx = 0;
3828     UINT i;
3829 
3830     for(i=0; i<bitmap->prop_count; i++) {
3831         if(bitmap->prop_item[i].id == PropertyTagIndexBackground) {
3832             bgcolor_idx = *(BYTE*)bitmap->prop_item[i].value;
3833             break;
3834         }
3835     }
3836 
3837     for(i=0; i<bitmap->prop_count; i++) {
3838         if(bitmap->prop_item[i].id == PropertyTagIndexTransparent) {
3839             BYTE transparent_idx;
3840             transparent_idx = *(BYTE*)bitmap->prop_item[i].value;
3841 
3842             if(transparent_idx == bgcolor_idx)
3843                 return 0;
3844         }
3845     }
3846 
3847     for(i=0; i<bitmap->prop_count; i++) {
3848         if(bitmap->prop_item[i].id == PropertyTagGlobalPalette) {
3849             if(bitmap->prop_item[i].length/3 > bgcolor_idx) {
3850                 BYTE *color = ((BYTE*)bitmap->prop_item[i].value)+bgcolor_idx*3;
3851                 return color[2] + (color[1]<<8) + (color[0]<<16) + (0xffu<<24);
3852             }
3853             break;
3854         }
3855     }
3856 
3857     FIXME("can't get gif background color\n");
3858     return 0xffffffff;
3859 }
3860 
3861 static GpStatus select_frame_gif(GpImage* image, UINT active_frame)
3862 {
3863     static const WCHAR disposalW[] = {'D','i','s','p','o','s','a','l',0};
3864 
3865     GpBitmap *bitmap = (GpBitmap*)image;
3866     IWICBitmapFrameDecode *frame;
3867     int cur_frame=0, disposal;
3868     BOOL bgcolor_set = FALSE;
3869     DWORD bgcolor = 0;
3870     HRESULT hr;
3871 
3872     if(active_frame > image->current_frame) {
3873         hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, image->current_frame, &frame);
3874         if(FAILED(hr))
3875             return hresult_to_status(hr);
3876         disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW);
3877         IWICBitmapFrameDecode_Release(frame);
3878 
3879         if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND)
3880             cur_frame = image->current_frame;
3881         else if(disposal != GIF_DISPOSE_RESTORE_TO_PREV)
3882             cur_frame = image->current_frame+1;
3883     }
3884 
3885     while(cur_frame != active_frame) {
3886         hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, cur_frame, &frame);
3887         if(FAILED(hr))
3888             return hresult_to_status(hr);
3889         disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW);
3890 
3891         if(disposal==GIF_DISPOSE_UNSPECIFIED || disposal==GIF_DISPOSE_DO_NOT_DISPOSE) {
3892             hr = blit_gif_frame(bitmap, frame, cur_frame==0);
3893             if(FAILED(hr))
3894                 return hresult_to_status(hr);
3895         }else if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND) {
3896             UINT left, top, width, height, i, j;
3897 
3898             if(!bgcolor_set) {
3899                 bgcolor = get_gif_background_color(bitmap);
3900                 bgcolor_set = TRUE;
3901             }
3902 
3903             hr = get_gif_frame_rect(frame, &left, &top, &width, &height);
3904             if(FAILED(hr))
3905                 return hresult_to_status(hr);
3906             for(i=top; i<top+height && i<bitmap->height; i++) {
3907                 DWORD *bits = (DWORD*)(bitmap->bits+i*bitmap->stride);
3908                 for(j=left; j<left+width && j<bitmap->width; j++)
3909                     bits[j] = bgcolor;
3910             }
3911         }
3912 
3913         IWICBitmapFrameDecode_Release(frame);
3914         cur_frame++;
3915     }
3916 
3917     hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, active_frame, &frame);
3918     if(FAILED(hr))
3919         return hresult_to_status(hr);
3920 
3921     hr = blit_gif_frame(bitmap, frame, cur_frame==0);
3922     IWICBitmapFrameDecode_Release(frame);
3923     if(FAILED(hr))
3924         return hresult_to_status(hr);
3925 
3926     image->current_frame = active_frame;
3927     return Ok;
3928 }
3929 
3930 static GpStatus decode_image_icon(IStream* stream, GpImage **image)
3931 {
3932     return decode_image_wic(stream, &GUID_ContainerFormatIco, NULL, image);
3933 }
3934 
3935 static GpStatus decode_image_bmp(IStream* stream, GpImage **image)
3936 {
3937     GpStatus status;
3938     GpBitmap* bitmap;
3939 
3940     status = decode_image_wic(stream, &GUID_ContainerFormatBmp, NULL, image);
3941 
3942     bitmap = (GpBitmap*)*image;
3943 
3944     if (status == Ok && bitmap->format == PixelFormat32bppARGB)
3945     {
3946         /* WIC supports bmp files with alpha, but gdiplus does not */
3947         bitmap->format = PixelFormat32bppRGB;
3948     }
3949 
3950     return status;
3951 }
3952 
3953 static GpStatus decode_image_jpeg(IStream* stream, GpImage **image)
3954 {
3955     return decode_image_wic(stream, &GUID_ContainerFormatJpeg, NULL, image);
3956 }
3957 
3958 static BOOL has_png_transparency_chunk(IStream *pIStream)
3959 {
3960     LARGE_INTEGER seek;
3961     BOOL has_tRNS = FALSE;
3962     HRESULT hr;
3963     BYTE header[8];
3964 
3965     seek.QuadPart = 8;
3966     do
3967     {
3968         ULARGE_INTEGER chunk_start;
3969         ULONG bytesread, chunk_size;
3970 
3971         hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
3972         if (FAILED(hr)) break;
3973 
3974         hr = IStream_Read(pIStream, header, 8, &bytesread);
3975         if (FAILED(hr) || bytesread < 8) break;
3976 
3977         chunk_size = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3];
3978         if (!memcmp(&header[4], "tRNS", 4))
3979         {
3980             has_tRNS = TRUE;
3981             break;
3982         }
3983 
3984         seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
3985     } while (memcmp(&header[4], "IDAT", 4) && memcmp(&header[4], "IEND", 4));
3986 
3987     TRACE("has_tRNS = %d\n", has_tRNS);
3988     return has_tRNS;
3989 }
3990 
3991 static GpStatus decode_image_png(IStream* stream, GpImage **image)
3992 {
3993     IWICBitmapDecoder *decoder;
3994     IWICBitmapFrameDecode *frame;
3995     GpStatus status;
3996     HRESULT hr;
3997     GUID format;
3998     BOOL force_conversion = FALSE;
3999 
4000     status = initialize_decoder_wic(stream, &GUID_ContainerFormatPng, &decoder);
4001     if (status != Ok)
4002         return status;
4003 
4004     hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
4005     if (hr == S_OK)
4006     {
4007         hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
4008         if (hr == S_OK)
4009         {
4010             if (IsEqualGUID(&format, &GUID_WICPixelFormat8bppGray))
4011                 force_conversion = TRUE;
4012             else if ((IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed) ||
4013                       IsEqualGUID(&format, &GUID_WICPixelFormat4bppIndexed) ||
4014                       IsEqualGUID(&format, &GUID_WICPixelFormat2bppIndexed) ||
4015                       IsEqualGUID(&format, &GUID_WICPixelFormat1bppIndexed) ||
4016                       IsEqualGUID(&format, &GUID_WICPixelFormat24bppBGR)) &&
4017                      has_png_transparency_chunk(stream))
4018                 force_conversion = TRUE;
4019 
4020             status = decode_frame_wic(decoder, force_conversion, 0, png_metadata_reader, image);
4021         }
4022         else
4023             status = hresult_to_status(hr);
4024 
4025         IWICBitmapFrameDecode_Release(frame);
4026     }
4027     else
4028         status = hresult_to_status(hr);
4029 
4030     IWICBitmapDecoder_Release(decoder);
4031     return status;
4032 }
4033 
4034 static GpStatus decode_image_gif(IStream* stream, GpImage **image)
4035 {
4036     IWICBitmapDecoder *decoder;
4037     UINT frame_count;
4038     GpStatus status;
4039     HRESULT hr;
4040 
4041     status = initialize_decoder_wic(stream, &GUID_ContainerFormatGif, &decoder);
4042     if(status != Ok)
4043         return status;
4044 
4045     hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
4046     if(FAILED(hr))
4047         return hresult_to_status(hr);
4048 
4049     status = decode_frame_wic(decoder, frame_count > 1, 0, gif_metadata_reader, image);
4050     IWICBitmapDecoder_Release(decoder);
4051     if(status != Ok)
4052         return status;
4053 
4054     if(frame_count > 1) {
4055         heap_free((*image)->palette);
4056         (*image)->palette = NULL;
4057     }
4058     return Ok;
4059 }
4060 
4061 static GpStatus decode_image_tiff(IStream* stream, GpImage **image)
4062 {
4063     return decode_image_wic(stream, &GUID_ContainerFormatTiff, NULL, image);
4064 }
4065 
4066 static GpStatus load_wmf(IStream *stream, GpMetafile **metafile)
4067 {
4068     WmfPlaceableFileHeader pfh;
4069     BOOL is_placeable = FALSE;
4070     LARGE_INTEGER seek;
4071     GpStatus status;
4072     METAHEADER mh;
4073     HMETAFILE hmf;
4074     HRESULT hr;
4075     UINT size;
4076     void *buf;
4077 
4078     hr = IStream_Read(stream, &mh, sizeof(mh), &size);
4079     if (hr != S_OK || size != sizeof(mh))
4080         return GenericError;
4081 
4082     if (((WmfPlaceableFileHeader *)&mh)->Key == WMF_PLACEABLE_KEY)
4083     {
4084         seek.QuadPart = 0;
4085         hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4086         if (FAILED(hr)) return hresult_to_status(hr);
4087 
4088         hr = IStream_Read(stream, &pfh, sizeof(pfh), &size);
4089         if (hr != S_OK || size != sizeof(pfh))
4090             return GenericError;
4091 
4092         hr = IStream_Read(stream, &mh, sizeof(mh), &size);
4093         if (hr != S_OK || size != sizeof(mh))
4094             return GenericError;
4095 
4096         is_placeable = TRUE;
4097     }
4098 
4099     seek.QuadPart = is_placeable ? sizeof(pfh) : 0;
4100     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4101     if (FAILED(hr)) return hresult_to_status(hr);
4102 
4103     buf = heap_alloc(mh.mtSize * 2);
4104     if (!buf) return OutOfMemory;
4105 
4106     hr = IStream_Read(stream, buf, mh.mtSize * 2, &size);
4107     if (hr != S_OK || size != mh.mtSize * 2)
4108     {
4109         heap_free(buf);
4110         return GenericError;
4111     }
4112 
4113     hmf = SetMetaFileBitsEx(mh.mtSize * 2, buf);
4114     heap_free(buf);
4115     if (!hmf)
4116         return GenericError;
4117 
4118     status = GdipCreateMetafileFromWmf(hmf, TRUE, is_placeable ? &pfh : NULL, metafile);
4119     if (status != Ok)
4120         DeleteMetaFile(hmf);
4121     return status;
4122 }
4123 
4124 static GpStatus decode_image_wmf(IStream *stream, GpImage **image)
4125 {
4126     GpMetafile *metafile;
4127     GpStatus status;
4128 
4129     TRACE("%p %p\n", stream, image);
4130 
4131     if (!stream || !image)
4132         return InvalidParameter;
4133 
4134     status = load_wmf(stream, &metafile);
4135     if (status != Ok)
4136     {
4137         TRACE("Could not load metafile\n");
4138         return status;
4139     }
4140 
4141     *image = (GpImage *)metafile;
4142     TRACE("<-- %p\n", *image);
4143 
4144     return Ok;
4145 }
4146 
4147 static GpStatus load_emf(IStream *stream, GpMetafile **metafile)
4148 {
4149     LARGE_INTEGER seek;
4150     ENHMETAHEADER emh;
4151     HENHMETAFILE hemf;
4152     GpStatus status;
4153     HRESULT hr;
4154     UINT size;
4155     void *buf;
4156 
4157     hr = IStream_Read(stream, &emh, sizeof(emh), &size);
4158     if (hr != S_OK || size != sizeof(emh) || emh.dSignature != ENHMETA_SIGNATURE)
4159         return GenericError;
4160 
4161     seek.QuadPart = 0;
4162     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4163     if (FAILED(hr)) return hresult_to_status(hr);
4164 
4165     buf = heap_alloc(emh.nBytes);
4166     if (!buf) return OutOfMemory;
4167 
4168     hr = IStream_Read(stream, buf, emh.nBytes, &size);
4169     if (hr != S_OK || size != emh.nBytes)
4170     {
4171         heap_free(buf);
4172         return GenericError;
4173     }
4174 
4175     hemf = SetEnhMetaFileBits(emh.nBytes, buf);
4176     heap_free(buf);
4177     if (!hemf)
4178         return GenericError;
4179 
4180     status = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4181     if (status != Ok)
4182         DeleteEnhMetaFile(hemf);
4183     return status;
4184 }
4185 
4186 static GpStatus decode_image_emf(IStream *stream, GpImage **image)
4187 {
4188     GpMetafile *metafile;
4189     GpStatus status;
4190 
4191     TRACE("%p %p\n", stream, image);
4192 
4193     if (!stream || !image)
4194         return InvalidParameter;
4195 
4196     status = load_emf(stream, &metafile);
4197     if (status != Ok)
4198     {
4199         TRACE("Could not load metafile\n");
4200         return status;
4201     }
4202 
4203     *image = (GpImage *)metafile;
4204     TRACE("<-- %p\n", *image);
4205 
4206     return Ok;
4207 }
4208 
4209 typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
4210     GDIPCONST EncoderParameters* params);
4211 
4212 typedef GpStatus (*decode_image_func)(IStream *stream, GpImage **image);
4213 
4214 typedef GpStatus (*select_image_func)(GpImage *image, UINT active_frame);
4215 
4216 typedef struct image_codec {
4217     ImageCodecInfo info;
4218     encode_image_func encode_func;
4219     decode_image_func decode_func;
4220     select_image_func select_func;
4221 } image_codec;
4222 
4223 typedef enum {
4224     BMP,
4225     JPEG,
4226     GIF,
4227     TIFF,
4228     EMF,
4229     WMF,
4230     PNG,
4231     ICO,
4232     NUM_CODECS
4233 } ImageFormat;
4234 
4235 static const struct image_codec codecs[NUM_CODECS];
4236 
4237 static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result)
4238 {
4239     BYTE signature[8];
4240     const BYTE *pattern, *mask;
4241     LARGE_INTEGER seek;
4242     HRESULT hr;
4243     UINT bytesread;
4244     int i;
4245     DWORD j, sig;
4246 
4247     /* seek to the start of the stream */
4248     seek.QuadPart = 0;
4249     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4250     if (FAILED(hr)) return hresult_to_status(hr);
4251 
4252     /* read the first 8 bytes */
4253     /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */
4254     hr = IStream_Read(stream, signature, 8, &bytesread);
4255     if (FAILED(hr)) return hresult_to_status(hr);
4256     if (hr == S_FALSE || bytesread == 0) return GenericError;
4257 
4258     for (i = 0; i < NUM_CODECS; i++) {
4259         if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
4260             bytesread >= codecs[i].info.SigSize)
4261         {
4262             for (sig=0; sig<codecs[i].info.SigCount; sig++)
4263             {
4264                 pattern = &codecs[i].info.SigPattern[codecs[i].info.SigSize*sig];
4265                 mask = &codecs[i].info.SigMask[codecs[i].info.SigSize*sig];
4266                 for (j=0; j<codecs[i].info.SigSize; j++)
4267                     if ((signature[j] & mask[j]) != pattern[j])
4268                         break;
4269                 if (j == codecs[i].info.SigSize)
4270                 {
4271                     *result = &codecs[i];
4272                     return Ok;
4273                 }
4274             }
4275         }
4276     }
4277 
4278     TRACE("no match for %i byte signature %x %x %x %x %x %x %x %x\n", bytesread,
4279         signature[0],signature[1],signature[2],signature[3],
4280         signature[4],signature[5],signature[6],signature[7]);
4281 
4282     return GenericError;
4283 }
4284 
4285 static GpStatus get_decoder_info_from_image(GpImage *image, const struct image_codec **result)
4286 {
4287     int i;
4288 
4289     for (i = 0; i < NUM_CODECS; i++) {
4290         if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
4291                 IsEqualIID(&codecs[i].info.FormatID, &image->format))
4292         {
4293             *result = &codecs[i];
4294             return Ok;
4295         }
4296     }
4297 
4298     TRACE("no match for format: %s\n", wine_dbgstr_guid(&image->format));
4299     return GenericError;
4300 }
4301 
4302 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID,
4303                                                UINT frame)
4304 {
4305     GpStatus stat;
4306     const struct image_codec *codec = NULL;
4307     BOOL unlock;
4308 
4309     TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
4310 
4311     if (!image || !dimensionID)
4312         return InvalidParameter;
4313     if(!image_lock(image, &unlock))
4314         return ObjectBusy;
4315 
4316     if (frame >= image->frame_count)
4317     {
4318         WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
4319         image_unlock(image, unlock);
4320         return InvalidParameter;
4321     }
4322 
4323     if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
4324     {
4325         WARN("invalid image type %d\n", image->type);
4326         image_unlock(image, unlock);
4327         return InvalidParameter;
4328     }
4329 
4330     if (image->current_frame == frame)
4331     {
4332         image_unlock(image, unlock);
4333         return Ok;
4334     }
4335 
4336     if (!image->decoder)
4337     {
4338         TRACE("image doesn't have an associated decoder\n");
4339         image_unlock(image, unlock);
4340         return Ok;
4341     }
4342 
4343     /* choose an appropriate image decoder */
4344     stat = get_decoder_info_from_image(image, &codec);
4345     if (stat != Ok)
4346     {
4347         WARN("can't find decoder info\n");
4348         image_unlock(image, unlock);
4349         return stat;
4350     }
4351 
4352     stat = codec->select_func(image, frame);
4353     image_unlock(image, unlock);
4354     return stat;
4355 }
4356 
4357 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image)
4358 {
4359     GpStatus stat;
4360     LARGE_INTEGER seek;
4361     HRESULT hr;
4362     const struct image_codec *codec=NULL;
4363 
4364     TRACE("%p %p\n", stream, image);
4365 
4366     if (!stream || !image)
4367         return InvalidParameter;
4368 
4369     /* choose an appropriate image decoder */
4370     stat = get_decoder_info(stream, &codec);
4371     if (stat != Ok) return stat;
4372 
4373     /* seek to the start of the stream */
4374     seek.QuadPart = 0;
4375     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4376     if (FAILED(hr)) return hresult_to_status(hr);
4377 
4378     /* call on the image decoder to do the real work */
4379     stat = codec->decode_func(stream, image);
4380 
4381     /* take note of the original data format */
4382     if (stat == Ok)
4383     {
4384         memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID));
4385         return Ok;
4386     }
4387 
4388     return stat;
4389 }
4390 
4391 /* FIXME: no ICM */
4392 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
4393 {
4394     TRACE("%p %p\n", stream, image);
4395 
4396     return GdipLoadImageFromStream(stream, image);
4397 }
4398 
4399 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
4400 {
4401     static int calls;
4402 
4403     TRACE("(%p,%u)\n", image, propId);
4404 
4405     if(!image)
4406         return InvalidParameter;
4407 
4408     if(!(calls++))
4409         FIXME("not implemented\n");
4410 
4411     return NotImplemented;
4412 }
4413 
4414 GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* item)
4415 {
4416     static int calls;
4417 
4418     if (!image || !item) return InvalidParameter;
4419 
4420     TRACE("(%p,%p:%#x,%u,%u,%p)\n", image, item, item->id, item->type, item->length, item->value);
4421 
4422     if(!(calls++))
4423         FIXME("not implemented\n");
4424 
4425     return Ok;
4426 }
4427 
4428 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
4429                                         GDIPCONST CLSID *clsidEncoder,
4430                                         GDIPCONST EncoderParameters *encoderParams)
4431 {
4432     GpStatus stat;
4433     IStream *stream;
4434 
4435     TRACE("%p (%s) %p %p\n", image, debugstr_w(filename), clsidEncoder, encoderParams);
4436 
4437     if (!image || !filename|| !clsidEncoder)
4438         return InvalidParameter;
4439 
4440     stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
4441     if (stat != Ok)
4442         return GenericError;
4443 
4444     stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
4445 
4446     IStream_Release(stream);
4447     return stat;
4448 }
4449 
4450 /*************************************************************************
4451  * Encoding functions -
4452  *   These functions encode an image in different image file formats.
4453  */
4454 
4455 static GpStatus encode_image_wic(GpImage *image, IStream* stream,
4456     REFGUID container, GDIPCONST EncoderParameters* params)
4457 {
4458     GpStatus stat;
4459     GpBitmap *bitmap;
4460     IWICImagingFactory *factory;
4461     IWICBitmapEncoder *encoder;
4462     IWICBitmapFrameEncode *frameencode;
4463     IPropertyBag2 *encoderoptions;
4464     HRESULT hr;
4465     UINT width, height;
4466     PixelFormat gdipformat=0;
4467     const WICPixelFormatGUID *desired_wicformat;
4468     WICPixelFormatGUID wicformat;
4469     GpRect rc;
4470     BitmapData lockeddata;
4471     UINT i;
4472 
4473     if (image->type != ImageTypeBitmap)
4474         return GenericError;
4475 
4476     bitmap = (GpBitmap*)image;
4477 
4478     GdipGetImageWidth(image, &width);
4479     GdipGetImageHeight(image, &height);
4480 
4481     rc.X = 0;
4482     rc.Y = 0;
4483     rc.Width = width;
4484     rc.Height = height;
4485 
4486     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
4487     if (FAILED(hr))
4488         return hresult_to_status(hr);
4489     hr = IWICImagingFactory_CreateEncoder(factory, container, NULL, &encoder);
4490     IWICImagingFactory_Release(factory);
4491     if (FAILED(hr))
4492         return hresult_to_status(hr);
4493 
4494     hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
4495 
4496     if (SUCCEEDED(hr))
4497     {
4498         hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &encoderoptions);
4499     }
4500 
4501     if (SUCCEEDED(hr)) /* created frame */
4502     {
4503         hr = IWICBitmapFrameEncode_Initialize(frameencode, encoderoptions);
4504 
4505         if (SUCCEEDED(hr))
4506             hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height);
4507 
4508         if (SUCCEEDED(hr))
4509             hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres);
4510 
4511         if (SUCCEEDED(hr))
4512         {
4513             for (i=0; pixel_formats[i].wic_format; i++)
4514             {
4515                 if (pixel_formats[i].gdip_format == bitmap->format)
4516                 {
4517                     desired_wicformat = pixel_formats[i].wic_format;
4518                     gdipformat = bitmap->format;
4519                     break;
4520                 }
4521             }
4522             if (!gdipformat)
4523             {
4524                 desired_wicformat = &GUID_WICPixelFormat32bppBGRA;
4525                 gdipformat = PixelFormat32bppARGB;
4526             }
4527 
4528             memcpy(&wicformat, desired_wicformat, sizeof(GUID));
4529             hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat);
4530         }
4531 
4532         if (SUCCEEDED(hr) && !IsEqualGUID(desired_wicformat, &wicformat))
4533         {
4534             /* Encoder doesn't support this bitmap's format. */
4535             gdipformat = 0;
4536             for (i=0; pixel_formats[i].wic_format; i++)
4537             {
4538                 if (IsEqualGUID(&wicformat, pixel_formats[i].wic_format))
4539                 {
4540                     gdipformat = pixel_formats[i].gdip_format;
4541                     break;
4542                 }
4543             }
4544             if (!gdipformat)
4545             {
4546                 ERR("Cannot support encoder format %s\n", debugstr_guid(&wicformat));
4547                 hr = E_FAIL;
4548             }
4549         }
4550 
4551         if (SUCCEEDED(hr))
4552         {
4553             stat = GdipBitmapLockBits(bitmap, &rc, ImageLockModeRead, gdipformat,
4554                 &lockeddata);
4555 
4556             if (stat == Ok)
4557             {
4558                 UINT row_size = (lockeddata.Width * PIXELFORMATBPP(gdipformat) + 7)/8;
4559                 BYTE *row;
4560 
4561                 /* write one row at a time in case stride is negative */
4562                 row = lockeddata.Scan0;
4563                 for (i=0; i<lockeddata.Height; i++)
4564                 {
4565                     hr = IWICBitmapFrameEncode_WritePixels(frameencode, 1, row_size, row_size, row);
4566                     if (FAILED(hr)) break;
4567                     row += lockeddata.Stride;
4568                 }
4569 
4570                 GdipBitmapUnlockBits(bitmap, &lockeddata);
4571             }
4572             else
4573                 hr = E_FAIL;
4574         }
4575 
4576         if (SUCCEEDED(hr))
4577             hr = IWICBitmapFrameEncode_Commit(frameencode);
4578 
4579         IWICBitmapFrameEncode_Release(frameencode);
4580         IPropertyBag2_Release(encoderoptions);
4581     }
4582 
4583     if (SUCCEEDED(hr))
4584         hr = IWICBitmapEncoder_Commit(encoder);
4585 
4586     IWICBitmapEncoder_Release(encoder);
4587     return hresult_to_status(hr);
4588 }
4589 
4590 static GpStatus encode_image_BMP(GpImage *image, IStream* stream,
4591     GDIPCONST EncoderParameters* params)
4592 {
4593     return encode_image_wic(image, stream, &GUID_ContainerFormatBmp, params);
4594 }
4595 
4596 static GpStatus encode_image_tiff(GpImage *image, IStream* stream,
4597     GDIPCONST EncoderParameters* params)
4598 {
4599     return encode_image_wic(image, stream, &GUID_ContainerFormatTiff, params);
4600 }
4601 
4602 GpStatus encode_image_png(GpImage *image, IStream* stream,
4603     GDIPCONST EncoderParameters* params)
4604 {
4605     return encode_image_wic(image, stream, &GUID_ContainerFormatPng, params);
4606 }
4607 
4608 static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
4609     GDIPCONST EncoderParameters* params)
4610 {
4611     return encode_image_wic(image, stream, &GUID_ContainerFormatJpeg, params);
4612 }
4613 
4614 static GpStatus encode_image_gif(GpImage *image, IStream* stream,
4615     GDIPCONST EncoderParameters* params)
4616 {
4617     return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params);
4618 }
4619 
4620 /*****************************************************************************
4621  * GdipSaveImageToStream [GDIPLUS.@]
4622  */
4623 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
4624     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4625 {
4626     GpStatus stat;
4627     encode_image_func encode_image;
4628     int i;
4629 
4630     TRACE("%p, %p, %s, %p\n", image, stream, wine_dbgstr_guid(clsid), params);
4631 
4632     if(!image || !stream)
4633         return InvalidParameter;
4634 
4635     /* select correct encoder */
4636     encode_image = NULL;
4637     for (i = 0; i < NUM_CODECS; i++) {
4638         if ((codecs[i].info.Flags & ImageCodecFlagsEncoder) &&
4639             IsEqualCLSID(clsid, &codecs[i].info.Clsid))
4640             encode_image = codecs[i].encode_func;
4641     }
4642     if (encode_image == NULL)
4643         return UnknownImageFormat;
4644 
4645     stat = encode_image(image, stream, params);
4646 
4647     return stat;
4648 }
4649 
4650 /*****************************************************************************
4651  * GdipSaveAdd [GDIPLUS.@]
4652  */
4653 GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params)
4654 {
4655     FIXME("(%p,%p): stub\n", image, params);
4656     return Ok;
4657 }
4658 
4659 /*****************************************************************************
4660  * GdipGetImagePalette [GDIPLUS.@]
4661  */
4662 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
4663 {
4664     INT count;
4665 
4666     TRACE("(%p,%p,%i)\n", image, palette, size);
4667 
4668     if (!image || !palette)
4669         return InvalidParameter;
4670 
4671     count = image->palette ? image->palette->Count : 0;
4672 
4673     if (size < (sizeof(UINT)*2+sizeof(ARGB)*count))
4674     {
4675         TRACE("<-- InsufficientBuffer\n");
4676         return InsufficientBuffer;
4677     }
4678 
4679     if (image->palette)
4680     {
4681         palette->Flags = image->palette->Flags;
4682         palette->Count = image->palette->Count;
4683         memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count);
4684     }
4685     else
4686     {
4687         palette->Flags = 0;
4688         palette->Count = 0;
4689     }
4690     return Ok;
4691 }
4692 
4693 /*****************************************************************************
4694  * GdipSetImagePalette [GDIPLUS.@]
4695  */
4696 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
4697     GDIPCONST ColorPalette *palette)
4698 {
4699     ColorPalette *new_palette;
4700 
4701     TRACE("(%p,%p)\n", image, palette);
4702 
4703     if(!image || !palette || palette->Count > 256)
4704         return InvalidParameter;
4705 
4706     new_palette = heap_alloc_zero(2 * sizeof(UINT) + palette->Count * sizeof(ARGB));
4707     if (!new_palette) return OutOfMemory;
4708 
4709     heap_free(image->palette);
4710     image->palette = new_palette;
4711     image->palette->Flags = palette->Flags;
4712     image->palette->Count = palette->Count;
4713     memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count);
4714 
4715     return Ok;
4716 }
4717 
4718 /*************************************************************************
4719  * Encoders -
4720  *   Structures that represent which formats we support for encoding.
4721  */
4722 
4723 /* ImageCodecInfo creation routines taken from libgdiplus */
4724 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */
4725 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
4726 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
4727 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
4728 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
4729 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
4730 
4731 static const WCHAR jpeg_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'J','P','E','G', 0};
4732 static const WCHAR jpeg_extension[] = {'*','.','J','P','G',';', '*','.','J','P','E','G',';', '*','.','J','P','E',';', '*','.','J','F','I','F',0};
4733 static const WCHAR jpeg_mimetype[] = {'i','m','a','g','e','/','j','p','e','g', 0};
4734 static const WCHAR jpeg_format[] = {'J','P','E','G',0};
4735 static const BYTE jpeg_sig_pattern[] = { 0xFF, 0xD8 };
4736 static const BYTE jpeg_sig_mask[] = { 0xFF, 0xFF };
4737 
4738 static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'G','I','F', 0};
4739 static const WCHAR gif_extension[] = {'*','.','G','I','F',0};
4740 static const WCHAR gif_mimetype[] = {'i','m','a','g','e','/','g','i','f', 0};
4741 static const WCHAR gif_format[] = {'G','I','F',0};
4742 static const BYTE gif_sig_pattern[12] = "GIF87aGIF89a";
4743 static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4744 
4745 static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0};
4746 static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0};
4747 static const WCHAR tiff_mimetype[] = {'i','m','a','g','e','/','t','i','f','f', 0};
4748 static const WCHAR tiff_format[] = {'T','I','F','F',0};
4749 static const BYTE tiff_sig_pattern[] = {0x49,0x49,42,0,0x4d,0x4d,0,42};
4750 static const BYTE tiff_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4751 
4752 static const WCHAR emf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'E','M','F', 0};
4753 static const WCHAR emf_extension[] = {'*','.','E','M','F',0};
4754 static const WCHAR emf_mimetype[] = {'i','m','a','g','e','/','x','-','e','m','f', 0};
4755 static const WCHAR emf_format[] = {'E','M','F',0};
4756 static const BYTE emf_sig_pattern[] = { 0x01, 0x00, 0x00, 0x00 };
4757 static const BYTE emf_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4758 
4759 static const WCHAR wmf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'W','M','F', 0};
4760 static const WCHAR wmf_extension[] = {'*','.','W','M','F',0};
4761 static const WCHAR wmf_mimetype[] = {'i','m','a','g','e','/','x','-','w','m','f', 0};
4762 static const WCHAR wmf_format[] = {'W','M','F',0};
4763 static const BYTE wmf_sig_pattern[] = { 0xd7, 0xcd };
4764 static const BYTE wmf_sig_mask[] = { 0xFF, 0xFF };
4765 
4766 static const WCHAR png_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'P','N','G', 0};
4767 static const WCHAR png_extension[] = {'*','.','P','N','G',0};
4768 static const WCHAR png_mimetype[] = {'i','m','a','g','e','/','p','n','g', 0};
4769 static const WCHAR png_format[] = {'P','N','G',0};
4770 static const BYTE png_sig_pattern[] = { 137, 80, 78, 71, 13, 10, 26, 10, };
4771 static const BYTE png_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4772 
4773 static const WCHAR ico_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'I','C','O', 0};
4774 static const WCHAR ico_extension[] = {'*','.','I','C','O',0};
4775 static const WCHAR ico_mimetype[] = {'i','m','a','g','e','/','x','-','i','c','o','n', 0};
4776 static const WCHAR ico_format[] = {'I','C','O',0};
4777 static const BYTE ico_sig_pattern[] = { 0x00, 0x00, 0x01, 0x00 };
4778 static const BYTE ico_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4779 
4780 static const struct image_codec codecs[NUM_CODECS] = {
4781     {
4782         { /* BMP */
4783             /* Clsid */              { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4784             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4785             /* CodecName */          bmp_codecname,
4786             /* DllName */            NULL,
4787             /* FormatDescription */  bmp_format,
4788             /* FilenameExtension */  bmp_extension,
4789             /* MimeType */           bmp_mimetype,
4790             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4791             /* Version */            1,
4792             /* SigCount */           1,
4793             /* SigSize */            2,
4794             /* SigPattern */         bmp_sig_pattern,
4795             /* SigMask */            bmp_sig_mask,
4796         },
4797         encode_image_BMP,
4798         decode_image_bmp,
4799         select_frame_wic
4800     },
4801     {
4802         { /* JPEG */
4803             /* Clsid */              { 0x557cf401, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4804             /* FormatID */           { 0xb96b3caeU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4805             /* CodecName */          jpeg_codecname,
4806             /* DllName */            NULL,
4807             /* FormatDescription */  jpeg_format,
4808             /* FilenameExtension */  jpeg_extension,
4809             /* MimeType */           jpeg_mimetype,
4810             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4811             /* Version */            1,
4812             /* SigCount */           1,
4813             /* SigSize */            2,
4814             /* SigPattern */         jpeg_sig_pattern,
4815             /* SigMask */            jpeg_sig_mask,
4816         },
4817         encode_image_jpeg,
4818         decode_image_jpeg,
4819         select_frame_wic
4820     },
4821     {
4822         { /* GIF */
4823             /* Clsid */              { 0x557cf402, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4824             /* FormatID */           { 0xb96b3cb0U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4825             /* CodecName */          gif_codecname,
4826             /* DllName */            NULL,
4827             /* FormatDescription */  gif_format,
4828             /* FilenameExtension */  gif_extension,
4829             /* MimeType */           gif_mimetype,
4830             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4831             /* Version */            1,
4832             /* SigCount */           2,
4833             /* SigSize */            6,
4834             /* SigPattern */         gif_sig_pattern,
4835             /* SigMask */            gif_sig_mask,
4836         },
4837         encode_image_gif,
4838         decode_image_gif,
4839         select_frame_gif
4840     },
4841     {
4842         { /* TIFF */
4843             /* Clsid */              { 0x557cf405, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4844             /* FormatID */           { 0xb96b3cb1U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4845             /* CodecName */          tiff_codecname,
4846             /* DllName */            NULL,
4847             /* FormatDescription */  tiff_format,
4848             /* FilenameExtension */  tiff_extension,
4849             /* MimeType */           tiff_mimetype,
4850             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4851             /* Version */            1,
4852             /* SigCount */           2,
4853             /* SigSize */            4,
4854             /* SigPattern */         tiff_sig_pattern,
4855             /* SigMask */            tiff_sig_mask,
4856         },
4857         encode_image_tiff,
4858         decode_image_tiff,
4859         select_frame_wic
4860     },
4861     {
4862         { /* EMF */
4863             /* Clsid */              { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4864             /* FormatID */           { 0xb96b3cacU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4865             /* CodecName */          emf_codecname,
4866             /* DllName */            NULL,
4867             /* FormatDescription */  emf_format,
4868             /* FilenameExtension */  emf_extension,
4869             /* MimeType */           emf_mimetype,
4870             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
4871             /* Version */            1,
4872             /* SigCount */           1,
4873             /* SigSize */            4,
4874             /* SigPattern */         emf_sig_pattern,
4875             /* SigMask */            emf_sig_mask,
4876         },
4877         NULL,
4878         decode_image_emf,
4879         NULL
4880     },
4881     {
4882         { /* WMF */
4883             /* Clsid */              { 0x557cf404, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4884             /* FormatID */           { 0xb96b3cadU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4885             /* CodecName */          wmf_codecname,
4886             /* DllName */            NULL,
4887             /* FormatDescription */  wmf_format,
4888             /* FilenameExtension */  wmf_extension,
4889             /* MimeType */           wmf_mimetype,
4890             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
4891             /* Version */            1,
4892             /* SigCount */           1,
4893             /* SigSize */            2,
4894             /* SigPattern */         wmf_sig_pattern,
4895             /* SigMask */            wmf_sig_mask,
4896         },
4897         NULL,
4898         decode_image_wmf,
4899         NULL
4900     },
4901     {
4902         { /* PNG */
4903             /* Clsid */              { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4904             /* FormatID */           { 0xb96b3cafU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4905             /* CodecName */          png_codecname,
4906             /* DllName */            NULL,
4907             /* FormatDescription */  png_format,
4908             /* FilenameExtension */  png_extension,
4909             /* MimeType */           png_mimetype,
4910             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4911             /* Version */            1,
4912             /* SigCount */           1,
4913             /* SigSize */            8,
4914             /* SigPattern */         png_sig_pattern,
4915             /* SigMask */            png_sig_mask,
4916         },
4917         encode_image_png,
4918         decode_image_png,
4919         select_frame_wic
4920     },
4921     {
4922         { /* ICO */
4923             /* Clsid */              { 0x557cf407, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4924             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4925             /* CodecName */          ico_codecname,
4926             /* DllName */            NULL,
4927             /* FormatDescription */  ico_format,
4928             /* FilenameExtension */  ico_extension,
4929             /* MimeType */           ico_mimetype,
4930             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4931             /* Version */            1,
4932             /* SigCount */           1,
4933             /* SigSize */            4,
4934             /* SigPattern */         ico_sig_pattern,
4935             /* SigMask */            ico_sig_mask,
4936         },
4937         NULL,
4938         decode_image_icon,
4939         select_frame_wic
4940     },
4941 };
4942 
4943 /*****************************************************************************
4944  * GdipGetImageDecodersSize [GDIPLUS.@]
4945  */
4946 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
4947 {
4948     int decoder_count=0;
4949     int i;
4950     TRACE("%p %p\n", numDecoders, size);
4951 
4952     if (!numDecoders || !size)
4953         return InvalidParameter;
4954 
4955     for (i=0; i<NUM_CODECS; i++)
4956     {
4957         if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
4958             decoder_count++;
4959     }
4960 
4961     *numDecoders = decoder_count;
4962     *size = decoder_count * sizeof(ImageCodecInfo);
4963 
4964     return Ok;
4965 }
4966 
4967 /*****************************************************************************
4968  * GdipGetImageDecoders [GDIPLUS.@]
4969  */
4970 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
4971 {
4972     int i, decoder_count=0;
4973     TRACE("%u %u %p\n", numDecoders, size, decoders);
4974 
4975     if (!decoders ||
4976         size != numDecoders * sizeof(ImageCodecInfo))
4977         return GenericError;
4978 
4979     for (i=0; i<NUM_CODECS; i++)
4980     {
4981         if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
4982         {
4983             if (decoder_count == numDecoders) return GenericError;
4984             memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
4985             decoder_count++;
4986         }
4987     }
4988 
4989     if (decoder_count < numDecoders) return GenericError;
4990 
4991     return Ok;
4992 }
4993 
4994 /*****************************************************************************
4995  * GdipGetImageEncodersSize [GDIPLUS.@]
4996  */
4997 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
4998 {
4999     int encoder_count=0;
5000     int i;
5001     TRACE("%p %p\n", numEncoders, size);
5002 
5003     if (!numEncoders || !size)
5004         return InvalidParameter;
5005 
5006     for (i=0; i<NUM_CODECS; i++)
5007     {
5008         if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
5009             encoder_count++;
5010     }
5011 
5012     *numEncoders = encoder_count;
5013     *size = encoder_count * sizeof(ImageCodecInfo);
5014 
5015     return Ok;
5016 }
5017 
5018 /*****************************************************************************
5019  * GdipGetImageEncoders [GDIPLUS.@]
5020  */
5021 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
5022 {
5023     int i, encoder_count=0;
5024     TRACE("%u %u %p\n", numEncoders, size, encoders);
5025 
5026     if (!encoders ||
5027         size != numEncoders * sizeof(ImageCodecInfo))
5028         return GenericError;
5029 
5030     for (i=0; i<NUM_CODECS; i++)
5031     {
5032         if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
5033         {
5034             if (encoder_count == numEncoders) return GenericError;
5035             memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
5036             encoder_count++;
5037         }
5038     }
5039 
5040     if (encoder_count < numEncoders) return GenericError;
5041 
5042     return Ok;
5043 }
5044 
5045 GpStatus WINGDIPAPI GdipGetEncoderParameterListSize(GpImage *image,
5046     GDIPCONST CLSID* clsidEncoder, UINT *size)
5047 {
5048     static int calls;
5049 
5050     TRACE("(%p,%s,%p)\n", image, debugstr_guid(clsidEncoder), size);
5051 
5052     if(!(calls++))
5053         FIXME("not implemented\n");
5054 
5055     *size = 0;
5056 
5057     return NotImplemented;
5058 }
5059 
5060 static PixelFormat get_16bpp_format(HBITMAP hbm)
5061 {
5062     BITMAPV4HEADER bmh;
5063     HDC hdc;
5064     PixelFormat result;
5065 
5066     hdc = CreateCompatibleDC(NULL);
5067 
5068     memset(&bmh, 0, sizeof(bmh));
5069     bmh.bV4Size = sizeof(bmh);
5070     bmh.bV4Width = 1;
5071     bmh.bV4Height = 1;
5072     bmh.bV4V4Compression = BI_BITFIELDS;
5073     bmh.bV4BitCount = 16;
5074 
5075     GetDIBits(hdc, hbm, 0, 0, NULL, (BITMAPINFO*)&bmh, DIB_RGB_COLORS);
5076 
5077     if (bmh.bV4RedMask == 0x7c00 &&
5078         bmh.bV4GreenMask == 0x3e0 &&
5079         bmh.bV4BlueMask == 0x1f)
5080     {
5081         result = PixelFormat16bppRGB555;
5082     }
5083     else if (bmh.bV4RedMask == 0xf800 &&
5084         bmh.bV4GreenMask == 0x7e0 &&
5085         bmh.bV4BlueMask == 0x1f)
5086     {
5087         result = PixelFormat16bppRGB565;
5088     }
5089     else
5090     {
5091         FIXME("unrecognized bitfields %x,%x,%x\n", bmh.bV4RedMask,
5092             bmh.bV4GreenMask, bmh.bV4BlueMask);
5093         result = PixelFormatUndefined;
5094     }
5095 
5096     DeleteDC(hdc);
5097 
5098     return result;
5099 }
5100 
5101 /*****************************************************************************
5102  * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
5103  */
5104 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
5105 {
5106     BITMAP bm;
5107     GpStatus retval;
5108     PixelFormat format;
5109     BitmapData lockeddata;
5110 
5111     TRACE("%p %p %p\n", hbm, hpal, bitmap);
5112 
5113     if(!hbm || !bitmap)
5114         return InvalidParameter;
5115 
5116     if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
5117             return InvalidParameter;
5118 
5119     /* TODO: Figure out the correct format for 16, 32, 64 bpp */
5120     switch(bm.bmBitsPixel) {
5121         case 1:
5122             format = PixelFormat1bppIndexed;
5123             break;
5124         case 4:
5125             format = PixelFormat4bppIndexed;
5126             break;
5127         case 8:
5128             format = PixelFormat8bppIndexed;
5129             break;
5130         case 16:
5131             format = get_16bpp_format(hbm);
5132             if (format == PixelFormatUndefined)
5133                 return InvalidParameter;
5134             break;
5135         case 24:
5136             format = PixelFormat24bppRGB;
5137             break;
5138         case 32:
5139             format = PixelFormat32bppRGB;
5140             break;
5141         case 48:
5142             format = PixelFormat48bppRGB;
5143             break;
5144         default:
5145             FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
5146             return InvalidParameter;
5147     }
5148 
5149     retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, 0,
5150         format, NULL, bitmap);
5151 
5152     if (retval == Ok)
5153     {
5154         retval = GdipBitmapLockBits(*bitmap, NULL, ImageLockModeWrite,
5155             format, &lockeddata);
5156         if (retval == Ok)
5157         {
5158             HDC hdc;
5159             char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)];
5160             BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
5161             INT src_height;
5162 
5163             hdc = CreateCompatibleDC(NULL);
5164 
5165             pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
5166             pbmi->bmiHeader.biBitCount = 0;
5167 
5168             GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
5169 
5170             src_height = abs(pbmi->bmiHeader.biHeight);
5171             pbmi->bmiHeader.biHeight = -src_height;
5172 
5173             GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS);
5174 
5175             DeleteDC(hdc);
5176 
5177             GdipBitmapUnlockBits(*bitmap, &lockeddata);
5178         }
5179 
5180         if (retval == Ok && hpal)
5181         {
5182             PALETTEENTRY entry[256];
5183             ColorPalette *palette=NULL;
5184             int i, num_palette_entries;
5185 
5186             num_palette_entries = GetPaletteEntries(hpal, 0, 256, entry);
5187             if (!num_palette_entries)
5188                 retval = GenericError;
5189 
5190             palette = heap_alloc_zero(sizeof(ColorPalette) + sizeof(ARGB) * (num_palette_entries-1));
5191             if (!palette)
5192                 retval = OutOfMemory;
5193 
5194             if (retval == Ok)
5195             {
5196                 palette->Flags = 0;
5197                 palette->Count = num_palette_entries;
5198 
5199                 for (i=0; i<num_palette_entries; i++)
5200                 {
5201                     palette->Entries[i] = 0xff000000 | entry[i].peRed << 16 |
5202                                           entry[i].peGreen << 8 | entry[i].peBlue;
5203                 }
5204 
5205                 retval = GdipSetImagePalette(&(*bitmap)->image, palette);
5206             }
5207 
5208             heap_free(palette);
5209         }
5210 
5211         if (retval != Ok)
5212         {
5213             GdipDisposeImage(&(*bitmap)->image);
5214             *bitmap = NULL;
5215         }
5216     }
5217 
5218     return retval;
5219 }
5220 
5221 /*****************************************************************************
5222  * GdipCreateEffect [GDIPLUS.@]
5223  */
5224 GpStatus WINGDIPAPI GdipCreateEffect(const GUID guid, CGpEffect **effect)
5225 {
5226     FIXME("(%s, %p): stub\n", debugstr_guid(&guid), effect);
5227 
5228     if(!effect)
5229         return InvalidParameter;
5230 
5231     *effect = NULL;
5232 
5233     return NotImplemented;
5234 }
5235 
5236 /*****************************************************************************
5237  * GdipDeleteEffect [GDIPLUS.@]
5238  */
5239 GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect)
5240 {
5241     FIXME("(%p): stub\n", effect);
5242     /* note: According to Jose Roca's GDI+ Docs, this is not implemented
5243      * in Windows's gdiplus */
5244     return NotImplemented;
5245 }
5246 
5247 /*****************************************************************************
5248  * GdipSetEffectParameters [GDIPLUS.@]
5249  */
5250 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
5251     const VOID *params, const UINT size)
5252 {
5253     static int calls;
5254 
5255     TRACE("(%p,%p,%u)\n", effect, params, size);
5256 
5257     if(!(calls++))
5258         FIXME("not implemented\n");
5259 
5260     return NotImplemented;
5261 }
5262 
5263 /*****************************************************************************
5264  * GdipGetImageFlags [GDIPLUS.@]
5265  */
5266 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
5267 {
5268     TRACE("%p %p\n", image, flags);
5269 
5270     if(!image || !flags)
5271         return InvalidParameter;
5272 
5273     *flags = image->flags;
5274 
5275     return Ok;
5276 }
5277 
5278 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param)
5279 {
5280     TRACE("(%d, %p)\n", control, param);
5281 
5282     switch(control){
5283         case TestControlForceBilinear:
5284             if(param)
5285                 FIXME("TestControlForceBilinear not handled\n");
5286             break;
5287         case TestControlNoICM:
5288             if(param)
5289                 FIXME("TestControlNoICM not handled\n");
5290             break;
5291         case TestControlGetBuildNumber:
5292             *((DWORD*)param) = 3102;
5293             break;
5294     }
5295 
5296     return Ok;
5297 }
5298 
5299 GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image)
5300 {
5301     TRACE("%p\n", image);
5302 
5303     return Ok;
5304 }
5305 
5306 /*****************************************************************************
5307  * GdipGetImageThumbnail [GDIPLUS.@]
5308  */
5309 GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height,
5310                             GpImage **ret_image, GetThumbnailImageAbort cb,
5311                             VOID * cb_data)
5312 {
5313     GpStatus stat;
5314     GpGraphics *graphics;
5315     UINT srcwidth, srcheight;
5316 
5317     TRACE("(%p %u %u %p %p %p)\n",
5318         image, width, height, ret_image, cb, cb_data);
5319 
5320     if (!image || !ret_image)
5321         return InvalidParameter;
5322 
5323     if (!width) width = 120;
5324     if (!height) height = 120;
5325 
5326     GdipGetImageWidth(image, &srcwidth);
5327     GdipGetImageHeight(image, &srcheight);
5328 
5329     stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
5330         NULL, (GpBitmap**)ret_image);
5331 
5332     if (stat == Ok)
5333     {
5334         stat = GdipGetImageGraphicsContext(*ret_image, &graphics);
5335 
5336         if (stat == Ok)
5337         {
5338             stat = GdipDrawImageRectRectI(graphics, image,
5339                 0, 0, width, height, 0, 0, srcwidth, srcheight, UnitPixel,
5340                 NULL, NULL, NULL);
5341 
5342             GdipDeleteGraphics(graphics);
5343         }
5344 
5345         if (stat != Ok)
5346         {
5347             GdipDisposeImage(*ret_image);
5348             *ret_image = NULL;
5349         }
5350     }
5351 
5352     return stat;
5353 }
5354 
5355 /*****************************************************************************
5356  * GdipImageRotateFlip [GDIPLUS.@]
5357  */
5358 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
5359 {
5360     GpBitmap *new_bitmap;
5361     GpBitmap *bitmap;
5362     int bpp, bytesperpixel;
5363     BOOL rotate_90, flip_x, flip_y;
5364     int src_x_offset, src_y_offset;
5365     LPBYTE src_origin;
5366     UINT x, y, width, height;
5367     BitmapData src_lock, dst_lock;
5368     GpStatus stat;
5369     BOOL unlock;
5370 
5371     TRACE("(%p, %u)\n", image, type);
5372 
5373     if (!image)
5374         return InvalidParameter;
5375     if (!image_lock(image, &unlock))
5376         return ObjectBusy;
5377 
5378     rotate_90 = type&1;
5379     flip_x = (type&6) == 2 || (type&6) == 4;
5380     flip_y = (type&3) == 1 || (type&3) == 2;
5381 
5382     if (image->type != ImageTypeBitmap)
5383     {
5384         FIXME("Not implemented for type %i\n", image->type);
5385         image_unlock(image, unlock);
5386         return NotImplemented;
5387     }
5388 
5389     bitmap = (GpBitmap*)image;
5390     bpp = PIXELFORMATBPP(bitmap->format);
5391 
5392     if (bpp < 8)
5393     {
5394         FIXME("Not implemented for %i bit images\n", bpp);
5395         image_unlock(image, unlock);
5396         return NotImplemented;
5397     }
5398 
5399     if (rotate_90)
5400     {
5401         width = bitmap->height;
5402         height = bitmap->width;
5403     }
5404     else
5405     {
5406         width = bitmap->width;
5407         height = bitmap->height;
5408     }
5409 
5410     bytesperpixel = bpp/8;
5411 
5412     stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap);
5413 
5414     if (stat != Ok)
5415     {
5416         image_unlock(image, unlock);
5417         return stat;
5418     }
5419 
5420     stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format, &src_lock);
5421 
5422     if (stat == Ok)
5423     {
5424         stat = GdipBitmapLockBits(new_bitmap, NULL, ImageLockModeWrite, bitmap->format, &dst_lock);
5425 
5426         if (stat == Ok)
5427         {
5428             LPBYTE src_row, src_pixel;
5429             LPBYTE dst_row, dst_pixel;
5430 
5431             src_origin = src_lock.Scan0;
5432             if (flip_x) src_origin += bytesperpixel * (bitmap->width - 1);
5433             if (flip_y) src_origin += src_lock.Stride * (bitmap->height - 1);
5434 
5435             if (rotate_90)
5436             {
5437                 if (flip_y) src_x_offset = -src_lock.Stride;
5438                 else src_x_offset = src_lock.Stride;
5439                 if (flip_x) src_y_offset = -bytesperpixel;
5440                 else src_y_offset = bytesperpixel;
5441             }
5442             else
5443             {
5444                 if (flip_x) src_x_offset = -bytesperpixel;
5445                 else src_x_offset = bytesperpixel;
5446                 if (flip_y) src_y_offset = -src_lock.Stride;
5447                 else src_y_offset = src_lock.Stride;
5448             }
5449 
5450             src_row = src_origin;
5451             dst_row = dst_lock.Scan0;
5452             for (y=0; y<height; y++)
5453             {
5454                 src_pixel = src_row;
5455                 dst_pixel = dst_row;
5456                 for (x=0; x<width; x++)
5457                 {
5458                     /* FIXME: This could probably be faster without memcpy. */
5459                     memcpy(dst_pixel, src_pixel, bytesperpixel);
5460                     dst_pixel += bytesperpixel;
5461                     src_pixel += src_x_offset;
5462                 }
5463                 src_row += src_y_offset;
5464                 dst_row += dst_lock.Stride;
5465             }
5466 
5467             GdipBitmapUnlockBits(new_bitmap, &dst_lock);
5468         }
5469 
5470         GdipBitmapUnlockBits(bitmap, &src_lock);
5471     }
5472 
5473     if (stat == Ok)
5474         move_bitmap(bitmap, new_bitmap, FALSE);
5475     else
5476         GdipDisposeImage(&new_bitmap->image);
5477 
5478     image_unlock(image, unlock);
5479     return stat;
5480 }
5481 
5482 /*****************************************************************************
5483  * GdipImageSetAbort [GDIPLUS.@]
5484  */
5485 GpStatus WINGDIPAPI GdipImageSetAbort(GpImage *image, GdiplusAbort *pabort)
5486 {
5487     TRACE("(%p, %p)\n", image, pabort);
5488 
5489     if (!image)
5490         return InvalidParameter;
5491 
5492     if (pabort)
5493         FIXME("Abort callback is not supported.\n");
5494 
5495     return Ok;
5496 }
5497 
5498 /*****************************************************************************
5499  * GdipBitmapConvertFormat [GDIPLUS.@]
5500  */
5501 GpStatus WINGDIPAPI GdipBitmapConvertFormat(GpBitmap *bitmap, PixelFormat format, DitherType dithertype,
5502     PaletteType palettetype, ColorPalette *palette, REAL alphathreshold)
5503 {
5504     FIXME("(%p, 0x%08x, %d, %d, %p, %f): stub\n", bitmap, format, dithertype, palettetype, palette, alphathreshold);
5505     return NotImplemented;
5506 }
5507 
5508 static void set_histogram_point_argb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5509 {
5510     ch0[ color >> 24        ]++;
5511     ch1[(color >> 16) & 0xff]++;
5512     ch2[(color >>  8) & 0xff]++;
5513     ch3[ color        & 0xff]++;
5514 }
5515 
5516 static void set_histogram_point_pargb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5517 {
5518     BYTE alpha = color >> 24;
5519 
5520     ch0[alpha]++;
5521     ch1[(((color >> 16) & 0xff) * alpha) / 0xff]++;
5522     ch2[(((color >>  8) & 0xff) * alpha) / 0xff]++;
5523     ch3[(( color        & 0xff) * alpha) / 0xff]++;
5524 }
5525 
5526 static void set_histogram_point_rgb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5527 {
5528     ch0[(color >> 16) & 0xff]++;
5529     ch1[(color >>  8) & 0xff]++;
5530     ch2[ color        & 0xff]++;
5531 }
5532 
5533 static void set_histogram_point_gray(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5534 {
5535     ch0[(76 * ((color >> 16) & 0xff) + 150 * ((color >> 8) & 0xff) + 29 * (color & 0xff)) / 0xff]++;
5536 }
5537 
5538 static void set_histogram_point_b(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5539 {
5540     ch0[color & 0xff]++;
5541 }
5542 
5543 static void set_histogram_point_g(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5544 {
5545     ch0[(color >> 8) & 0xff]++;
5546 }
5547 
5548 static void set_histogram_point_r(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5549 {
5550     ch0[(color >> 16) & 0xff]++;
5551 }
5552 
5553 static void set_histogram_point_a(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5554 {
5555     ch0[(color >> 24) & 0xff]++;
5556 }
5557 
5558 /*****************************************************************************
5559  * GdipBitmapGetHistogram [GDIPLUS.@]
5560  */
5561 GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap *bitmap, HistogramFormat format, UINT num_of_entries,
5562     UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5563 {
5564     static void (* const set_histogram_point[])(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) =
5565     {
5566         set_histogram_point_argb,
5567         set_histogram_point_pargb,
5568         set_histogram_point_rgb,
5569         set_histogram_point_gray,
5570         set_histogram_point_b,
5571         set_histogram_point_g,
5572         set_histogram_point_r,
5573         set_histogram_point_a,
5574     };
5575     UINT width, height, x, y;
5576 
5577     TRACE("(%p, %d, %u, %p, %p, %p, %p)\n", bitmap, format, num_of_entries,
5578         ch0, ch1, ch2, ch3);
5579 
5580     if (!bitmap || num_of_entries != 256)
5581         return InvalidParameter;
5582 
5583     /* Make sure passed channel pointers match requested format */
5584     switch (format)
5585     {
5586     case HistogramFormatARGB:
5587     case HistogramFormatPARGB:
5588         if (!ch0 || !ch1 || !ch2 || !ch3)
5589             return InvalidParameter;
5590         memset(ch0, 0, num_of_entries * sizeof(UINT));
5591         memset(ch1, 0, num_of_entries * sizeof(UINT));
5592         memset(ch2, 0, num_of_entries * sizeof(UINT));
5593         memset(ch3, 0, num_of_entries * sizeof(UINT));
5594         break;
5595     case HistogramFormatRGB:
5596         if (!ch0 || !ch1 || !ch2 || ch3)
5597             return InvalidParameter;
5598         memset(ch0, 0, num_of_entries * sizeof(UINT));
5599         memset(ch1, 0, num_of_entries * sizeof(UINT));
5600         memset(ch2, 0, num_of_entries * sizeof(UINT));
5601         break;
5602     case HistogramFormatGray:
5603     case HistogramFormatB:
5604     case HistogramFormatG:
5605     case HistogramFormatR:
5606     case HistogramFormatA:
5607         if (!ch0 || ch1 || ch2 || ch3)
5608             return InvalidParameter;
5609         memset(ch0, 0, num_of_entries * sizeof(UINT));
5610         break;
5611     default:
5612         WARN("Invalid histogram format requested, %d\n", format);
5613         return InvalidParameter;
5614     }
5615 
5616     GdipGetImageWidth(&bitmap->image, &width);
5617     GdipGetImageHeight(&bitmap->image, &height);
5618 
5619     for (y = 0; y < height; y++)
5620         for (x = 0; x < width; x++)
5621         {
5622             ARGB color;
5623 
5624             GdipBitmapGetPixel(bitmap, x, y, &color);
5625             set_histogram_point[format](color, ch0, ch1, ch2, ch3);
5626         }
5627 
5628     return Ok;
5629 }
5630 
5631 /*****************************************************************************
5632  * GdipBitmapGetHistogramSize [GDIPLUS.@]
5633  */
5634 GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat format, UINT *num_of_entries)
5635 {
5636     TRACE("(%d, %p)\n", format, num_of_entries);
5637 
5638     if (!num_of_entries)
5639         return InvalidParameter;
5640 
5641     *num_of_entries = 256;
5642     return Ok;
5643 }
5644 
5645 static GpStatus create_optimal_palette(ColorPalette *palette, INT desired,
5646     BOOL transparent, GpBitmap *bitmap)
5647 {
5648     GpStatus status;
5649     BitmapData data;
5650     HRESULT hr;
5651     IWICImagingFactory *factory;
5652     IWICPalette *wic_palette;
5653 
5654     if (!bitmap) return InvalidParameter;
5655     if (palette->Count < desired) return GenericError;
5656 
5657     status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &data);
5658     if (status != Ok) return status;
5659 
5660     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
5661     if (hr != S_OK)
5662     {
5663         GdipBitmapUnlockBits(bitmap, &data);
5664         return hresult_to_status(hr);
5665     }
5666 
5667     hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
5668     if (hr == S_OK)
5669     {
5670         IWICBitmap *bitmap;
5671 
5672         /* PixelFormat24bppRGB actually stores the bitmap bits as BGR. */
5673         hr = IWICImagingFactory_CreateBitmapFromMemory(factory, data.Width, data.Height,
5674                 &GUID_WICPixelFormat24bppBGR, data.Stride, data.Stride * data.Width, data.Scan0, &bitmap);
5675         if (hr == S_OK)
5676         {
5677             hr = IWICPalette_InitializeFromBitmap(wic_palette, (IWICBitmapSource *)bitmap, desired, transparent);
5678             if (hr == S_OK)
5679             {
5680                 palette->Flags = 0;
5681                 IWICPalette_GetColorCount(wic_palette, &palette->Count);
5682                 IWICPalette_GetColors(wic_palette, palette->Count, palette->Entries, &palette->Count);
5683             }
5684 
5685             IWICBitmap_Release(bitmap);
5686         }
5687 
5688         IWICPalette_Release(wic_palette);
5689     }
5690 
5691     IWICImagingFactory_Release(factory);
5692     GdipBitmapUnlockBits(bitmap, &data);
5693 
5694     return hresult_to_status(hr);
5695 }
5696 
5697 /*****************************************************************************
5698  * GdipInitializePalette [GDIPLUS.@]
5699  */
5700 GpStatus WINGDIPAPI GdipInitializePalette(ColorPalette *palette,
5701     PaletteType type, INT desired, BOOL transparent, GpBitmap *bitmap)
5702 {
5703     TRACE("(%p,%d,%d,%d,%p)\n", palette, type, desired, transparent, bitmap);
5704 
5705     if (!palette) return InvalidParameter;
5706 
5707     switch (type)
5708     {
5709     case PaletteTypeCustom:
5710         return Ok;
5711 
5712     case PaletteTypeOptimal:
5713         return create_optimal_palette(palette, desired, transparent, bitmap);
5714 
5715     /* WIC palette type enumeration matches these gdiplus enums */
5716     case PaletteTypeFixedBW:
5717     case PaletteTypeFixedHalftone8:
5718     case PaletteTypeFixedHalftone27:
5719     case PaletteTypeFixedHalftone64:
5720     case PaletteTypeFixedHalftone125:
5721     case PaletteTypeFixedHalftone216:
5722     case PaletteTypeFixedHalftone252:
5723     case PaletteTypeFixedHalftone256:
5724     {
5725         ColorPalette *wic_palette;
5726         GpStatus status = Ok;
5727 
5728         wic_palette = get_palette(NULL, type);
5729         if (!wic_palette) return OutOfMemory;
5730 
5731         if (palette->Count >= wic_palette->Count)
5732         {
5733             palette->Flags = wic_palette->Flags;
5734             palette->Count = wic_palette->Count;
5735             memcpy(palette->Entries, wic_palette->Entries, wic_palette->Count * sizeof(wic_palette->Entries[0]));
5736         }
5737         else
5738             status = GenericError;
5739 
5740         heap_free(wic_palette);
5741 
5742         return status;
5743     }
5744 
5745     default:
5746         FIXME("unknown palette type %d\n", type);
5747         break;
5748     }
5749 
5750     return InvalidParameter;
5751 }
5752