xref: /reactos/dll/win32/gdiplus/image.c (revision 09dde2cf)
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 #ifdef __REACTOS__
1901 static HBITMAP hbitmap_from_emf(HENHMETAFILE hemf)
1902 {
1903     BITMAPINFO bmi;
1904     HBITMAP hbm;
1905     SIZE size;
1906     ENHMETAHEADER header;
1907     HGDIOBJ hbmOld;
1908     RECT rc;
1909     HDC hdc;
1910 
1911     GetEnhMetaFileHeader(hemf, sizeof(header), &header);
1912     size.cx = header.rclBounds.right - header.rclBounds.left + 1;
1913     size.cy = header.rclBounds.bottom - header.rclBounds.top + 1;
1914 
1915     ZeroMemory(&bmi, sizeof(bmi));
1916     bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
1917     bmi.bmiHeader.biWidth = size.cx;
1918     bmi.bmiHeader.biHeight = size.cy;
1919     bmi.bmiHeader.biPlanes = 1;
1920     bmi.bmiHeader.biBitCount = 24;
1921 
1922     hdc = CreateCompatibleDC(NULL);
1923     hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
1924 
1925     hbmOld = SelectObject(hdc, hbm);
1926     SetRect(&rc, 0, 0, size.cx, size.cy);
1927     PlayEnhMetaFile(hdc, hemf, &rc);
1928     SelectObject(hdc, hbmOld);
1929 
1930     DeleteDC(hdc);
1931     return hbm;
1932 }
1933 
1934 #endif
1935 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
1936     GpBitmap **bitmap)
1937 {
1938     GpStatus stat;
1939 
1940     TRACE("%p %p\n", stream, bitmap);
1941 
1942     stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
1943 
1944     if(stat != Ok)
1945         return stat;
1946 
1947 #ifdef __REACTOS__
1948     if ((*bitmap)->image.type == ImageTypeMetafile)
1949     {
1950         HBITMAP hbm = hbitmap_from_emf(((GpMetafile*)*bitmap)->hemf);
1951         GdipDisposeImage(&(*bitmap)->image);
1952         if (!hbm)
1953             return GenericError; /* FIXME: what error to return? */
1954 
1955         GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
1956         DeleteObject(hbm);
1957     }
1958     else
1959 #endif
1960     if((*bitmap)->image.type != ImageTypeBitmap){
1961         GdipDisposeImage(&(*bitmap)->image);
1962         *bitmap = NULL;
1963         return GenericError; /* FIXME: what error to return? */
1964     }
1965 
1966     return Ok;
1967 }
1968 
1969 /* FIXME: no icm */
1970 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
1971     GpBitmap **bitmap)
1972 {
1973     TRACE("%p %p\n", stream, bitmap);
1974 
1975     return GdipCreateBitmapFromStream(stream, bitmap);
1976 }
1977 
1978 GpStatus WINGDIPAPI GdipCreateCachedBitmap(GpBitmap *bitmap, GpGraphics *graphics,
1979     GpCachedBitmap **cachedbmp)
1980 {
1981     GpStatus stat;
1982 
1983     TRACE("%p %p %p\n", bitmap, graphics, cachedbmp);
1984 
1985     if(!bitmap || !graphics || !cachedbmp)
1986         return InvalidParameter;
1987 
1988     *cachedbmp = heap_alloc_zero(sizeof(GpCachedBitmap));
1989     if(!*cachedbmp)
1990         return OutOfMemory;
1991 
1992     stat = GdipCloneImage(&(bitmap->image), &(*cachedbmp)->image);
1993     if(stat != Ok){
1994         heap_free(*cachedbmp);
1995         return stat;
1996     }
1997 
1998     return Ok;
1999 }
2000 
2001 GpStatus WINGDIPAPI GdipCreateHICONFromBitmap(GpBitmap *bitmap, HICON *hicon)
2002 {
2003     GpStatus stat;
2004     BitmapData lockeddata;
2005     ULONG andstride, xorstride, bitssize;
2006     LPBYTE andbits, xorbits, androw, xorrow, srcrow;
2007     UINT x, y;
2008 
2009     TRACE("(%p, %p)\n", bitmap, hicon);
2010 
2011     if (!bitmap || !hicon)
2012         return InvalidParameter;
2013 
2014     stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead,
2015         PixelFormat32bppPARGB, &lockeddata);
2016     if (stat == Ok)
2017     {
2018         andstride = ((lockeddata.Width+31)/32)*4;
2019         xorstride = lockeddata.Width*4;
2020         bitssize = (andstride + xorstride) * lockeddata.Height;
2021 
2022         andbits = heap_alloc_zero(bitssize);
2023 
2024         if (andbits)
2025         {
2026             xorbits = andbits + andstride * lockeddata.Height;
2027 
2028             for (y=0; y<lockeddata.Height; y++)
2029             {
2030                 srcrow = ((LPBYTE)lockeddata.Scan0) + lockeddata.Stride * y;
2031 
2032                 androw = andbits + andstride * y;
2033                 for (x=0; x<lockeddata.Width; x++)
2034                     if (srcrow[3+4*x] >= 128)
2035                         androw[x/8] |= 1 << (7-x%8);
2036 
2037                 xorrow = xorbits + xorstride * y;
2038                 memcpy(xorrow, srcrow, xorstride);
2039             }
2040 
2041             *hicon = CreateIcon(NULL, lockeddata.Width, lockeddata.Height, 1, 32,
2042                 andbits, xorbits);
2043 
2044             heap_free(andbits);
2045         }
2046         else
2047             stat = OutOfMemory;
2048 
2049         GdipBitmapUnlockBits(bitmap, &lockeddata);
2050     }
2051 
2052     return stat;
2053 }
2054 
2055 GpStatus WINGDIPAPI GdipDeleteCachedBitmap(GpCachedBitmap *cachedbmp)
2056 {
2057     TRACE("%p\n", cachedbmp);
2058 
2059     if(!cachedbmp)
2060         return InvalidParameter;
2061 
2062     GdipDisposeImage(cachedbmp->image);
2063     heap_free(cachedbmp);
2064 
2065     return Ok;
2066 }
2067 
2068 GpStatus WINGDIPAPI GdipDrawCachedBitmap(GpGraphics *graphics,
2069     GpCachedBitmap *cachedbmp, INT x, INT y)
2070 {
2071     TRACE("%p %p %d %d\n", graphics, cachedbmp, x, y);
2072 
2073     if(!graphics || !cachedbmp)
2074         return InvalidParameter;
2075 
2076     return GdipDrawImage(graphics, cachedbmp->image, (REAL)x, (REAL)y);
2077 }
2078 
2079 /* Internal utility function: Replace the image data of dst with that of src,
2080  * and free src. */
2081 static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
2082 {
2083     assert(src->image.type == ImageTypeBitmap);
2084     assert(dst->image.type == ImageTypeBitmap);
2085 
2086     heap_free(dst->bitmapbits);
2087     heap_free(dst->own_bits);
2088     DeleteDC(dst->hdc);
2089     DeleteObject(dst->hbitmap);
2090 
2091     if (clobber_palette)
2092     {
2093         heap_free(dst->image.palette);
2094         dst->image.palette = src->image.palette;
2095     }
2096     else
2097         heap_free(src->image.palette);
2098 
2099     dst->image.xres = src->image.xres;
2100     dst->image.yres = src->image.yres;
2101     dst->width = src->width;
2102     dst->height = src->height;
2103     dst->format = src->format;
2104     dst->hbitmap = src->hbitmap;
2105     dst->hdc = src->hdc;
2106     dst->bits = src->bits;
2107     dst->stride = src->stride;
2108     dst->own_bits = src->own_bits;
2109     if (dst->metadata_reader)
2110         IWICMetadataReader_Release(dst->metadata_reader);
2111     dst->metadata_reader = src->metadata_reader;
2112     heap_free(dst->prop_item);
2113     dst->prop_item = src->prop_item;
2114     dst->prop_count = src->prop_count;
2115     if (dst->image.decoder)
2116         IWICBitmapDecoder_Release(dst->image.decoder);
2117     dst->image.decoder = src->image.decoder;
2118     dst->image.frame_count = src->image.frame_count;
2119     dst->image.current_frame = src->image.current_frame;
2120     dst->image.format = src->image.format;
2121 
2122     src->image.type = ~0;
2123     heap_free(src);
2124 }
2125 
2126 static GpStatus free_image_data(GpImage *image)
2127 {
2128     if(!image)
2129         return InvalidParameter;
2130 
2131     if (image->type == ImageTypeBitmap)
2132     {
2133         heap_free(((GpBitmap*)image)->bitmapbits);
2134         heap_free(((GpBitmap*)image)->own_bits);
2135         DeleteDC(((GpBitmap*)image)->hdc);
2136         DeleteObject(((GpBitmap*)image)->hbitmap);
2137         if (((GpBitmap*)image)->metadata_reader)
2138             IWICMetadataReader_Release(((GpBitmap*)image)->metadata_reader);
2139         heap_free(((GpBitmap*)image)->prop_item);
2140     }
2141     else if (image->type == ImageTypeMetafile)
2142         METAFILE_Free((GpMetafile *)image);
2143     else
2144     {
2145         WARN("invalid image: %p\n", image);
2146         return ObjectBusy;
2147     }
2148     if (image->decoder)
2149         IWICBitmapDecoder_Release(image->decoder);
2150     heap_free(image->palette);
2151 
2152     return Ok;
2153 }
2154 
2155 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
2156 {
2157     GpStatus status;
2158 
2159     TRACE("%p\n", image);
2160 
2161     status = free_image_data(image);
2162     if (status != Ok) return status;
2163     image->type = ~0;
2164     heap_free(image);
2165 
2166     return Ok;
2167 }
2168 
2169 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
2170 {
2171     static int calls;
2172 
2173     TRACE("(%p,%p)\n", image, item);
2174 
2175     if(!image || !item)
2176         return InvalidParameter;
2177 
2178     if (!(calls++))
2179         FIXME("not implemented\n");
2180 
2181     return NotImplemented;
2182 }
2183 
2184 GpStatus WINGDIPAPI GdipGetImageItemData(GpImage *image, ImageItemData *item)
2185 {
2186     static int calls;
2187 
2188     TRACE("(%p,%p)\n", image, item);
2189 
2190     if (!(calls++))
2191         FIXME("not implemented\n");
2192 
2193     return NotImplemented;
2194 }
2195 
2196 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
2197     GpUnit *srcUnit)
2198 {
2199     TRACE("%p %p %p\n", image, srcRect, srcUnit);
2200 
2201     if(!image || !srcRect || !srcUnit)
2202         return InvalidParameter;
2203     if(image->type == ImageTypeMetafile){
2204         *srcRect = ((GpMetafile*)image)->bounds;
2205         *srcUnit = ((GpMetafile*)image)->unit;
2206     }
2207     else if(image->type == ImageTypeBitmap){
2208         srcRect->X = srcRect->Y = 0.0;
2209         srcRect->Width = (REAL) ((GpBitmap*)image)->width;
2210         srcRect->Height = (REAL) ((GpBitmap*)image)->height;
2211         *srcUnit = UnitPixel;
2212     }
2213     else{
2214         WARN("GpImage with no image data\n");
2215         return InvalidParameter;
2216     }
2217 
2218     TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
2219           srcRect->Width, srcRect->Height, *srcUnit);
2220 
2221     return Ok;
2222 }
2223 
2224 GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
2225     REAL *height)
2226 {
2227     TRACE("%p %p %p\n", image, width, height);
2228 
2229     if(!image || !height || !width)
2230         return InvalidParameter;
2231 
2232     if(image->type == ImageTypeMetafile){
2233         *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
2234         *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
2235     }
2236     else if(image->type == ImageTypeBitmap){
2237         *height = ((GpBitmap*)image)->height;
2238         *width = ((GpBitmap*)image)->width;
2239     }
2240     else{
2241         WARN("GpImage with no image data\n");
2242         return InvalidParameter;
2243     }
2244 
2245     TRACE("returning (%f, %f)\n", *height, *width);
2246     return Ok;
2247 }
2248 
2249 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
2250     GpGraphics **graphics)
2251 {
2252     HDC hdc;
2253     GpStatus stat;
2254 
2255     TRACE("%p %p\n", image, graphics);
2256 
2257     if(!image || !graphics)
2258         return InvalidParameter;
2259 
2260     if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
2261     {
2262         hdc = ((GpBitmap*)image)->hdc;
2263 
2264         if(!hdc){
2265             hdc = CreateCompatibleDC(0);
2266             SelectObject(hdc, ((GpBitmap*)image)->hbitmap);
2267             ((GpBitmap*)image)->hdc = hdc;
2268         }
2269 
2270         stat = GdipCreateFromHDC(hdc, graphics);
2271 
2272         if (stat == Ok)
2273         {
2274             (*graphics)->image = image;
2275             (*graphics)->xres = image->xres;
2276             (*graphics)->yres = image->yres;
2277         }
2278     }
2279     else if (image->type == ImageTypeMetafile)
2280         stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics);
2281     else
2282         stat = graphics_from_image(image, graphics);
2283 
2284     return stat;
2285 }
2286 
2287 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
2288 {
2289     TRACE("%p %p\n", image, height);
2290 
2291     if(!image || !height)
2292         return InvalidParameter;
2293 
2294     if(image->type == ImageTypeMetafile)
2295         *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
2296     else if(image->type == ImageTypeBitmap)
2297         *height = ((GpBitmap*)image)->height;
2298     else
2299     {
2300         WARN("GpImage with no image data\n");
2301         return InvalidParameter;
2302     }
2303 
2304     TRACE("returning %d\n", *height);
2305 
2306     return Ok;
2307 }
2308 
2309 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
2310 {
2311     if(!image || !res)
2312         return InvalidParameter;
2313 
2314     *res = image->xres;
2315 
2316     TRACE("(%p) <-- %0.2f\n", image, *res);
2317 
2318     return Ok;
2319 }
2320 
2321 GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size)
2322 {
2323     TRACE("%p %p\n", image, size);
2324 
2325     if(!image || !size)
2326         return InvalidParameter;
2327 
2328     if (!image->palette || image->palette->Count == 0)
2329         *size = sizeof(ColorPalette);
2330     else
2331         *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette->Count;
2332 
2333     TRACE("<-- %u\n", *size);
2334 
2335     return Ok;
2336 }
2337 
2338 /* FIXME: test this function for non-bitmap types */
2339 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
2340 {
2341     TRACE("%p %p\n", image, format);
2342 
2343     if(!image || !format)
2344         return InvalidParameter;
2345 
2346     if(image->type != ImageTypeBitmap)
2347         *format = PixelFormat24bppRGB;
2348     else
2349         *format = ((GpBitmap*) image)->format;
2350 
2351     return Ok;
2352 }
2353 
2354 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
2355 {
2356     TRACE("(%p, %p)\n", image, format);
2357 
2358     if(!image || !format)
2359         return InvalidParameter;
2360 
2361     memcpy(format, &image->format, sizeof(GUID));
2362 
2363     return Ok;
2364 }
2365 
2366 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
2367 {
2368     TRACE("%p %p\n", image, type);
2369 
2370     if(!image || !type)
2371         return InvalidParameter;
2372 
2373     *type = image->type;
2374 
2375     return Ok;
2376 }
2377 
2378 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
2379 {
2380     if(!image || !res)
2381         return InvalidParameter;
2382 
2383     *res = image->yres;
2384 
2385     TRACE("(%p) <-- %0.2f\n", image, *res);
2386 
2387     return Ok;
2388 }
2389 
2390 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
2391 {
2392     TRACE("%p %p\n", image, width);
2393 
2394     if(!image || !width)
2395         return InvalidParameter;
2396 
2397     if(image->type == ImageTypeMetafile)
2398         *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
2399     else if(image->type == ImageTypeBitmap)
2400         *width = ((GpBitmap*)image)->width;
2401     else
2402     {
2403         WARN("GpImage with no image data\n");
2404         return InvalidParameter;
2405     }
2406 
2407     TRACE("returning %d\n", *width);
2408 
2409     return Ok;
2410 }
2411 
2412 GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT *num)
2413 {
2414     TRACE("(%p, %p)\n", image, num);
2415 
2416     if (!image || !num) return InvalidParameter;
2417 
2418     *num = 0;
2419 
2420     if (image->type == ImageTypeBitmap)
2421     {
2422         if (((GpBitmap *)image)->prop_item)
2423         {
2424             *num = ((GpBitmap *)image)->prop_count;
2425             return Ok;
2426         }
2427 
2428         if (((GpBitmap *)image)->metadata_reader)
2429             IWICMetadataReader_GetCount(((GpBitmap *)image)->metadata_reader, num);
2430     }
2431 
2432     return Ok;
2433 }
2434 
2435 GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID *list)
2436 {
2437     HRESULT hr;
2438     IWICMetadataReader *reader;
2439     IWICEnumMetadataItem *enumerator;
2440     UINT prop_count, i, items_returned;
2441 
2442     TRACE("(%p, %u, %p)\n", image, num, list);
2443 
2444     if (!image || !list) return InvalidParameter;
2445 
2446     if (image->type != ImageTypeBitmap)
2447     {
2448         FIXME("Not implemented for type %d\n", image->type);
2449         return NotImplemented;
2450     }
2451 
2452     if (((GpBitmap *)image)->prop_item)
2453     {
2454         if (num != ((GpBitmap *)image)->prop_count) return InvalidParameter;
2455 
2456         for (i = 0; i < num; i++)
2457         {
2458             list[i] = ((GpBitmap *)image)->prop_item[i].id;
2459         }
2460 
2461         return Ok;
2462     }
2463 
2464     reader = ((GpBitmap *)image)->metadata_reader;
2465     if (!reader)
2466     {
2467         if (num != 0) return InvalidParameter;
2468         return Ok;
2469     }
2470 
2471     hr = IWICMetadataReader_GetCount(reader, &prop_count);
2472     if (FAILED(hr)) return hresult_to_status(hr);
2473 
2474     if (num != prop_count) return InvalidParameter;
2475 
2476     hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2477     if (FAILED(hr)) return hresult_to_status(hr);
2478 
2479     IWICEnumMetadataItem_Reset(enumerator);
2480 
2481     for (i = 0; i < num; i++)
2482     {
2483         PROPVARIANT id;
2484 
2485         hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, NULL, &items_returned);
2486         if (hr != S_OK) break;
2487 
2488         if (id.vt != VT_UI2)
2489         {
2490             FIXME("not supported propvariant type for id: %u\n", id.vt);
2491             list[i] = 0;
2492             continue;
2493         }
2494         list[i] = id.u.uiVal;
2495     }
2496 
2497     IWICEnumMetadataItem_Release(enumerator);
2498 
2499     return hr == S_OK ? Ok : hresult_to_status(hr);
2500 }
2501 
2502 static UINT propvariant_size(PROPVARIANT *value)
2503 {
2504     switch (value->vt & ~VT_VECTOR)
2505     {
2506     case VT_EMPTY:
2507         return 0;
2508     case VT_I1:
2509     case VT_UI1:
2510         if (!(value->vt & VT_VECTOR)) return 1;
2511         return value->u.caub.cElems;
2512     case VT_I2:
2513     case VT_UI2:
2514         if (!(value->vt & VT_VECTOR)) return 2;
2515         return value->u.caui.cElems * 2;
2516     case VT_I4:
2517     case VT_UI4:
2518     case VT_R4:
2519         if (!(value->vt & VT_VECTOR)) return 4;
2520         return value->u.caul.cElems * 4;
2521     case VT_I8:
2522     case VT_UI8:
2523     case VT_R8:
2524         if (!(value->vt & VT_VECTOR)) return 8;
2525         return value->u.cauh.cElems * 8;
2526     case VT_LPSTR:
2527         return value->u.pszVal ? strlen(value->u.pszVal) + 1 : 0;
2528     case VT_BLOB:
2529         return value->u.blob.cbSize;
2530     default:
2531         FIXME("not supported variant type %d\n", value->vt);
2532         return 0;
2533     }
2534 }
2535 
2536 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID propid, UINT *size)
2537 {
2538     HRESULT hr;
2539     IWICMetadataReader *reader;
2540     PROPVARIANT id, value;
2541 
2542     TRACE("(%p,%#x,%p)\n", image, propid, size);
2543 
2544     if (!size || !image) return InvalidParameter;
2545 
2546     if (image->type != ImageTypeBitmap)
2547     {
2548         FIXME("Not implemented for type %d\n", image->type);
2549         return NotImplemented;
2550     }
2551 
2552     if (((GpBitmap *)image)->prop_item)
2553     {
2554         UINT i;
2555 
2556         for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2557         {
2558             if (propid == ((GpBitmap *)image)->prop_item[i].id)
2559             {
2560                 *size = sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2561                 return Ok;
2562             }
2563         }
2564 
2565         return PropertyNotFound;
2566     }
2567 
2568     reader = ((GpBitmap *)image)->metadata_reader;
2569     if (!reader) return PropertyNotFound;
2570 
2571     id.vt = VT_UI2;
2572     id.u.uiVal = propid;
2573     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2574     if (FAILED(hr)) return PropertyNotFound;
2575 
2576     *size = propvariant_size(&value);
2577     if (*size) *size += sizeof(PropertyItem);
2578     PropVariantClear(&value);
2579 
2580     return Ok;
2581 }
2582 
2583 #ifndef PropertyTagTypeSByte
2584 #define PropertyTagTypeSByte  6
2585 #define PropertyTagTypeSShort 8
2586 #define PropertyTagTypeFloat  11
2587 #define PropertyTagTypeDouble 12
2588 #endif
2589 
2590 static UINT vt_to_itemtype(UINT vt)
2591 {
2592     static const struct
2593     {
2594         UINT vt, type;
2595     } vt2type[] =
2596     {
2597         { VT_I1, PropertyTagTypeSByte },
2598         { VT_UI1, PropertyTagTypeByte },
2599         { VT_I2, PropertyTagTypeSShort },
2600         { VT_UI2, PropertyTagTypeShort },
2601         { VT_I4, PropertyTagTypeSLONG },
2602         { VT_UI4, PropertyTagTypeLong },
2603         { VT_I8, PropertyTagTypeSRational },
2604         { VT_UI8, PropertyTagTypeRational },
2605         { VT_R4, PropertyTagTypeFloat },
2606         { VT_R8, PropertyTagTypeDouble },
2607         { VT_LPSTR, PropertyTagTypeASCII },
2608         { VT_BLOB, PropertyTagTypeUndefined }
2609     };
2610     UINT i;
2611     for (i = 0; i < ARRAY_SIZE(vt2type); i++)
2612     {
2613         if (vt2type[i].vt == vt) return vt2type[i].type;
2614     }
2615     FIXME("not supported variant type %u\n", vt);
2616     return 0;
2617 }
2618 
2619 static GpStatus propvariant_to_item(PROPVARIANT *value, PropertyItem *item,
2620                                     UINT size, PROPID id)
2621 {
2622     UINT item_size, item_type;
2623 
2624     item_size = propvariant_size(value);
2625     if (size != item_size + sizeof(PropertyItem)) return InvalidParameter;
2626 
2627     item_type = vt_to_itemtype(value->vt & ~VT_VECTOR);
2628     if (!item_type) return InvalidParameter;
2629 
2630     item->value = item + 1;
2631 
2632     switch (value->vt & ~VT_VECTOR)
2633     {
2634     case VT_I1:
2635     case VT_UI1:
2636         if (!(value->vt & VT_VECTOR))
2637             *(BYTE *)item->value = value->u.bVal;
2638         else
2639             memcpy(item->value, value->u.caub.pElems, item_size);
2640         break;
2641     case VT_I2:
2642     case VT_UI2:
2643         if (!(value->vt & VT_VECTOR))
2644             *(USHORT *)item->value = value->u.uiVal;
2645         else
2646             memcpy(item->value, value->u.caui.pElems, item_size);
2647         break;
2648     case VT_I4:
2649     case VT_UI4:
2650     case VT_R4:
2651         if (!(value->vt & VT_VECTOR))
2652             *(ULONG *)item->value = value->u.ulVal;
2653         else
2654             memcpy(item->value, value->u.caul.pElems, item_size);
2655         break;
2656     case VT_I8:
2657     case VT_UI8:
2658     case VT_R8:
2659         if (!(value->vt & VT_VECTOR))
2660             *(ULONGLONG *)item->value = value->u.uhVal.QuadPart;
2661         else
2662             memcpy(item->value, value->u.cauh.pElems, item_size);
2663         break;
2664     case VT_LPSTR:
2665         memcpy(item->value, value->u.pszVal, item_size);
2666         break;
2667     case VT_BLOB:
2668         memcpy(item->value, value->u.blob.pBlobData, item_size);
2669         break;
2670     default:
2671         FIXME("not supported variant type %d\n", value->vt);
2672         return InvalidParameter;
2673     }
2674 
2675     item->length = item_size;
2676     item->type = item_type;
2677     item->id = id;
2678 
2679     return Ok;
2680 }
2681 
2682 GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID propid, UINT size,
2683                                         PropertyItem *buffer)
2684 {
2685     GpStatus stat;
2686     HRESULT hr;
2687     IWICMetadataReader *reader;
2688     PROPVARIANT id, value;
2689 
2690     TRACE("(%p,%#x,%u,%p)\n", image, propid, size, buffer);
2691 
2692     if (!image || !buffer) return InvalidParameter;
2693 
2694     if (image->type != ImageTypeBitmap)
2695     {
2696         FIXME("Not implemented for type %d\n", image->type);
2697         return NotImplemented;
2698     }
2699 
2700     if (((GpBitmap *)image)->prop_item)
2701     {
2702         UINT i;
2703 
2704         for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2705         {
2706             if (propid == ((GpBitmap *)image)->prop_item[i].id)
2707             {
2708                 if (size != sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length)
2709                     return InvalidParameter;
2710 
2711                 *buffer = ((GpBitmap *)image)->prop_item[i];
2712                 buffer->value = buffer + 1;
2713                 memcpy(buffer->value, ((GpBitmap *)image)->prop_item[i].value, buffer->length);
2714                 return Ok;
2715             }
2716         }
2717 
2718         return PropertyNotFound;
2719     }
2720 
2721     reader = ((GpBitmap *)image)->metadata_reader;
2722     if (!reader) return PropertyNotFound;
2723 
2724     id.vt = VT_UI2;
2725     id.u.uiVal = propid;
2726     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2727     if (FAILED(hr)) return PropertyNotFound;
2728 
2729     stat = propvariant_to_item(&value, buffer, size, propid);
2730     PropVariantClear(&value);
2731 
2732     return stat;
2733 }
2734 
2735 GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT *size, UINT *count)
2736 {
2737     HRESULT hr;
2738     IWICMetadataReader *reader;
2739     IWICEnumMetadataItem *enumerator;
2740     UINT prop_count, prop_size, i;
2741     PROPVARIANT id, value;
2742 
2743     TRACE("(%p,%p,%p)\n", image, size, count);
2744 
2745     if (!image || !size || !count) return InvalidParameter;
2746 
2747     if (image->type != ImageTypeBitmap)
2748     {
2749         FIXME("Not implemented for type %d\n", image->type);
2750         return NotImplemented;
2751     }
2752 
2753     if (((GpBitmap *)image)->prop_item)
2754     {
2755         *count = ((GpBitmap *)image)->prop_count;
2756         *size = 0;
2757 
2758         for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2759         {
2760             *size += sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2761         }
2762 
2763         return Ok;
2764     }
2765 
2766     reader = ((GpBitmap *)image)->metadata_reader;
2767     if (!reader) return PropertyNotFound;
2768 
2769     hr = IWICMetadataReader_GetCount(reader, &prop_count);
2770     if (FAILED(hr)) return hresult_to_status(hr);
2771 
2772     hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2773     if (FAILED(hr)) return hresult_to_status(hr);
2774 
2775     IWICEnumMetadataItem_Reset(enumerator);
2776 
2777     prop_size = 0;
2778 
2779     PropVariantInit(&id);
2780     PropVariantInit(&value);
2781 
2782     for (i = 0; i < prop_count; i++)
2783     {
2784         UINT items_returned, item_size;
2785 
2786         hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2787         if (hr != S_OK) break;
2788 
2789         item_size = propvariant_size(&value);
2790         if (item_size) prop_size += sizeof(PropertyItem) + item_size;
2791 
2792         PropVariantClear(&id);
2793         PropVariantClear(&value);
2794     }
2795 
2796     IWICEnumMetadataItem_Release(enumerator);
2797 
2798     if (hr != S_OK) return PropertyNotFound;
2799 
2800     *count = prop_count;
2801     *size = prop_size;
2802     return Ok;
2803 }
2804 
2805 GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
2806                                             UINT count, PropertyItem *buf)
2807 {
2808     GpStatus status;
2809     HRESULT hr;
2810     IWICMetadataReader *reader;
2811     IWICEnumMetadataItem *enumerator;
2812     UINT prop_count, prop_size, i;
2813     PROPVARIANT id, value;
2814     char *item_value;
2815 
2816     TRACE("(%p,%u,%u,%p)\n", image, size, count, buf);
2817 
2818     if (!image || !buf) return InvalidParameter;
2819 
2820     if (image->type != ImageTypeBitmap)
2821     {
2822         FIXME("Not implemented for type %d\n", image->type);
2823         return NotImplemented;
2824     }
2825 
2826     status = GdipGetPropertySize(image, &prop_size, &prop_count);
2827     if (status != Ok) return status;
2828 
2829     if (prop_count != count || prop_size != size) return InvalidParameter;
2830 
2831     if (((GpBitmap *)image)->prop_item)
2832     {
2833         memcpy(buf, ((GpBitmap *)image)->prop_item, prop_size);
2834 
2835         item_value = (char *)(buf + prop_count);
2836 
2837         for (i = 0; i < prop_count; i++)
2838         {
2839             buf[i].value = item_value;
2840             item_value += buf[i].length;
2841         }
2842 
2843         return Ok;
2844     }
2845 
2846     reader = ((GpBitmap *)image)->metadata_reader;
2847     if (!reader) return PropertyNotFound;
2848 
2849     hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2850     if (FAILED(hr)) return hresult_to_status(hr);
2851 
2852     IWICEnumMetadataItem_Reset(enumerator);
2853 
2854     item_value = (char *)(buf + prop_count);
2855 
2856     PropVariantInit(&id);
2857     PropVariantInit(&value);
2858 
2859     for (i = 0; i < prop_count; i++)
2860     {
2861         PropertyItem *item;
2862         UINT items_returned, item_size;
2863 
2864         hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2865         if (hr != S_OK) break;
2866 
2867         if (id.vt != VT_UI2)
2868         {
2869             FIXME("not supported propvariant type for id: %u\n", id.vt);
2870             continue;
2871         }
2872 
2873         item_size = propvariant_size(&value);
2874         if (item_size)
2875         {
2876             item = heap_alloc(item_size + sizeof(*item));
2877 
2878             propvariant_to_item(&value, item, item_size + sizeof(*item), id.u.uiVal);
2879             buf[i].id = item->id;
2880             buf[i].type = item->type;
2881             buf[i].length = item_size;
2882             buf[i].value = item_value;
2883             memcpy(item_value, item->value, item_size);
2884             item_value += item_size;
2885 
2886             heap_free(item);
2887         }
2888 
2889         PropVariantClear(&id);
2890         PropVariantClear(&value);
2891     }
2892 
2893     IWICEnumMetadataItem_Release(enumerator);
2894 
2895     if (hr != S_OK) return PropertyNotFound;
2896 
2897     return Ok;
2898 }
2899 
2900 struct image_format_dimension
2901 {
2902     const GUID *format;
2903     const GUID *dimension;
2904 };
2905 
2906 static const struct image_format_dimension image_format_dimensions[] =
2907 {
2908     {&ImageFormatGIF, &FrameDimensionTime},
2909     {&ImageFormatIcon, &FrameDimensionResolution},
2910     {NULL}
2911 };
2912 
2913 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
2914     GDIPCONST GUID* dimensionID, UINT* count)
2915 {
2916     TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
2917 
2918     if(!image || !count)
2919         return InvalidParameter;
2920 
2921     if (!dimensionID ||
2922         IsEqualGUID(dimensionID, &image->format) ||
2923         IsEqualGUID(dimensionID, &FrameDimensionPage) ||
2924         IsEqualGUID(dimensionID, &FrameDimensionTime))
2925     {
2926         *count = image->frame_count;
2927         return Ok;
2928     }
2929 
2930     return InvalidParameter;
2931 }
2932 
2933 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
2934     UINT* count)
2935 {
2936     TRACE("(%p, %p)\n", image, count);
2937 
2938     /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
2939 
2940     if(!image || !count)
2941         return InvalidParameter;
2942 
2943     *count = 1;
2944 
2945     return Ok;
2946 }
2947 
2948 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
2949     GUID* dimensionIDs, UINT count)
2950 {
2951     int i;
2952     const GUID *result=NULL;
2953 
2954     TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
2955 
2956     if(!image || !dimensionIDs || count != 1)
2957         return InvalidParameter;
2958 
2959     for (i=0; image_format_dimensions[i].format; i++)
2960     {
2961         if (IsEqualGUID(&image->format, image_format_dimensions[i].format))
2962         {
2963             result = image_format_dimensions[i].dimension;
2964             break;
2965         }
2966     }
2967 
2968     if (!result)
2969         result = &FrameDimensionPage;
2970 
2971     memcpy(dimensionIDs, result, sizeof(GUID));
2972 
2973     return Ok;
2974 }
2975 
2976 GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename,
2977                                           GpImage **image)
2978 {
2979     GpStatus stat;
2980     IStream *stream;
2981 
2982     TRACE("(%s) %p\n", debugstr_w(filename), image);
2983 
2984     if (!filename || !image)
2985         return InvalidParameter;
2986 
2987     *image = NULL;
2988 
2989     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
2990 
2991     if (stat != Ok)
2992         return stat;
2993 
2994     stat = GdipLoadImageFromStream(stream, image);
2995 
2996     IStream_Release(stream);
2997 
2998     return stat;
2999 }
3000 
3001 /* FIXME: no icm handling */
3002 GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage **image)
3003 {
3004     TRACE("(%s) %p\n", debugstr_w(filename), image);
3005 
3006     return GdipLoadImageFromFile(filename, image);
3007 }
3008 
3009 static void add_property(GpBitmap *bitmap, PropertyItem *item)
3010 {
3011     UINT prop_size, prop_count;
3012     PropertyItem *prop_item;
3013 
3014     if (bitmap->prop_item == NULL)
3015     {
3016         prop_size = prop_count = 0;
3017         prop_item = heap_alloc_zero(item->length + sizeof(PropertyItem));
3018         if (!prop_item) return;
3019     }
3020     else
3021     {
3022         UINT i;
3023         char *item_value;
3024 
3025         GdipGetPropertySize(&bitmap->image, &prop_size, &prop_count);
3026 
3027         prop_item = heap_alloc_zero(prop_size + item->length + sizeof(PropertyItem));
3028         if (!prop_item) return;
3029         memcpy(prop_item, bitmap->prop_item, sizeof(PropertyItem) * bitmap->prop_count);
3030         prop_size -= sizeof(PropertyItem) * bitmap->prop_count;
3031         memcpy(prop_item + prop_count + 1, bitmap->prop_item + prop_count, prop_size);
3032 
3033         item_value = (char *)(prop_item + prop_count + 1);
3034 
3035         for (i = 0; i < prop_count; i++)
3036         {
3037             prop_item[i].value = item_value;
3038             item_value += prop_item[i].length;
3039         }
3040     }
3041 
3042     prop_item[prop_count].id = item->id;
3043     prop_item[prop_count].type = item->type;
3044     prop_item[prop_count].length = item->length;
3045     prop_item[prop_count].value = (char *)(prop_item + prop_count + 1) + prop_size;
3046     memcpy(prop_item[prop_count].value, item->value, item->length);
3047 
3048     heap_free(bitmap->prop_item);
3049     bitmap->prop_item = prop_item;
3050     bitmap->prop_count++;
3051 }
3052 
3053 static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3054 {
3055     HRESULT hr;
3056     GUID format;
3057     PROPVARIANT id, value;
3058     BOOL ret = FALSE;
3059 
3060     hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3061     if (FAILED(hr) || !IsEqualGUID(&format, guid)) return FALSE;
3062 
3063     PropVariantInit(&id);
3064     PropVariantInit(&value);
3065 
3066     id.vt = VT_LPWSTR;
3067     id.u.pwszVal = CoTaskMemAlloc((lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3068     if (!id.u.pwszVal) return FALSE;
3069     lstrcpyW(id.u.pwszVal, prop_name);
3070     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3071     if (hr == S_OK && value.vt == VT_BOOL)
3072         ret = value.u.boolVal;
3073 
3074     PropVariantClear(&id);
3075     PropVariantClear(&value);
3076 
3077     return ret;
3078 }
3079 
3080 static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3081 {
3082     HRESULT hr;
3083     GUID format;
3084     PROPVARIANT id, value;
3085     PropertyItem *item = NULL;
3086 
3087     hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3088     if (FAILED(hr) || !IsEqualGUID(&format, guid)) return NULL;
3089 
3090     PropVariantInit(&id);
3091     PropVariantInit(&value);
3092 
3093     id.vt = VT_LPWSTR;
3094     id.u.pwszVal = CoTaskMemAlloc((lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3095     if (!id.u.pwszVal) return NULL;
3096     lstrcpyW(id.u.pwszVal, prop_name);
3097     hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3098     if (hr == S_OK)
3099     {
3100         UINT item_size = propvariant_size(&value);
3101         if (item_size)
3102         {
3103             item_size += sizeof(*item);
3104             item = heap_alloc_zero(item_size);
3105             if (propvariant_to_item(&value, item, item_size, 0) != Ok)
3106             {
3107                 heap_free(item);
3108                 item = NULL;
3109             }
3110         }
3111     }
3112 
3113     PropVariantClear(&id);
3114     PropVariantClear(&value);
3115 
3116     return item;
3117 }
3118 
3119 static PropertyItem *get_gif_comment(IWICMetadataReader *reader)
3120 {
3121     static const WCHAR textentryW[] = { 'T','e','x','t','E','n','t','r','y',0 };
3122     PropertyItem *comment;
3123 
3124     comment = get_property(reader, &GUID_MetadataFormatGifComment, textentryW);
3125     if (comment)
3126         comment->id = PropertyTagExifUserComment;
3127 
3128     return comment;
3129 }
3130 
3131 static PropertyItem *get_gif_loopcount(IWICMetadataReader *reader)
3132 {
3133     static const WCHAR applicationW[] = { 'A','p','p','l','i','c','a','t','i','o','n',0 };
3134     static const WCHAR dataW[] = { 'D','a','t','a',0 };
3135     PropertyItem *appext = NULL, *appdata = NULL, *loop = NULL;
3136 
3137     appext = get_property(reader, &GUID_MetadataFormatAPE, applicationW);
3138     if (appext)
3139     {
3140         if (appext->type == PropertyTagTypeByte && appext->length == 11 &&
3141             (!memcmp(appext->value, "NETSCAPE2.0", 11) || !memcmp(appext->value, "ANIMEXTS1.0", 11)))
3142         {
3143             appdata = get_property(reader, &GUID_MetadataFormatAPE, dataW);
3144             if (appdata)
3145             {
3146                 if (appdata->type == PropertyTagTypeByte && appdata->length == 4)
3147                 {
3148                     BYTE *data = appdata->value;
3149                     if (data[0] == 3 && data[1] == 1)
3150                     {
3151                         loop = heap_alloc_zero(sizeof(*loop) + sizeof(SHORT));
3152                         if (loop)
3153                         {
3154                             loop->type = PropertyTagTypeShort;
3155                             loop->id = PropertyTagLoopCount;
3156                             loop->length = sizeof(SHORT);
3157                             loop->value = loop + 1;
3158                             *(SHORT *)loop->value = data[2] | (data[3] << 8);
3159                         }
3160                     }
3161                 }
3162             }
3163         }
3164     }
3165 
3166     heap_free(appext);
3167     heap_free(appdata);
3168 
3169     return loop;
3170 }
3171 
3172 static PropertyItem *get_gif_background(IWICMetadataReader *reader)
3173 {
3174     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 };
3175     PropertyItem *background;
3176 
3177     background = get_property(reader, &GUID_MetadataFormatLSD, backgroundW);
3178     if (background)
3179         background->id = PropertyTagIndexBackground;
3180 
3181     return background;
3182 }
3183 
3184 static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataReader *reader)
3185 {
3186     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 };
3187     HRESULT hr;
3188     IWICImagingFactory *factory;
3189     IWICPalette *palette;
3190     UINT count = 0;
3191     WICColor colors[256];
3192 
3193     if (!get_bool_property(reader, &GUID_MetadataFormatLSD, global_flagW))
3194         return NULL;
3195 
3196     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
3197     if (hr != S_OK) return NULL;
3198 
3199     hr = IWICImagingFactory_CreatePalette(factory, &palette);
3200     if (hr == S_OK)
3201     {
3202         hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
3203         if (hr == S_OK)
3204             IWICPalette_GetColors(palette, 256, colors, &count);
3205 
3206         IWICPalette_Release(palette);
3207     }
3208 
3209     IWICImagingFactory_Release(factory);
3210 
3211     if (count)
3212     {
3213         PropertyItem *pal;
3214         UINT i;
3215         BYTE *rgb;
3216 
3217         pal = heap_alloc_zero(sizeof(*pal) + count * 3);
3218         if (!pal) return NULL;
3219         pal->type = PropertyTagTypeByte;
3220         pal->id = PropertyTagGlobalPalette;
3221         pal->value = pal + 1;
3222         pal->length = count * 3;
3223 
3224         rgb = pal->value;
3225 
3226         for (i = 0; i < count; i++)
3227         {
3228             rgb[i*3] = (colors[i] >> 16) & 0xff;
3229             rgb[i*3 + 1] = (colors[i] >> 8) & 0xff;
3230             rgb[i*3 + 2] = colors[i] & 0xff;
3231         }
3232 
3233         return pal;
3234     }
3235 
3236     return NULL;
3237 }
3238 
3239 static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader)
3240 {
3241     static const WCHAR transparency_flagW[] = { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 };
3242     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 };
3243     PropertyItem *index = NULL;
3244 
3245     if (get_bool_property(reader, &GUID_MetadataFormatGCE, transparency_flagW))
3246     {
3247         index = get_property(reader, &GUID_MetadataFormatGCE, colorW);
3248         if (index)
3249             index->id = PropertyTagIndexTransparent;
3250     }
3251     return index;
3252 }
3253 
3254 static LONG get_gif_frame_property(IWICBitmapFrameDecode *frame, const GUID *format, const WCHAR *property)
3255 {
3256     HRESULT hr;
3257     IWICMetadataBlockReader *block_reader;
3258     IWICMetadataReader *reader;
3259     UINT block_count, i;
3260     PropertyItem *prop;
3261     LONG value = 0;
3262 
3263     hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3264     if (hr == S_OK)
3265     {
3266         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3267         if (hr == S_OK)
3268         {
3269             for (i = 0; i < block_count; i++)
3270             {
3271                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3272                 if (hr == S_OK)
3273                 {
3274                     prop = get_property(reader, format, property);
3275                     if (prop)
3276                     {
3277                         if (prop->type == PropertyTagTypeByte && prop->length == 1)
3278                             value = *(BYTE *)prop->value;
3279                         else if (prop->type == PropertyTagTypeShort && prop->length == 2)
3280                             value = *(SHORT *)prop->value;
3281 
3282                         heap_free(prop);
3283                     }
3284                     IWICMetadataReader_Release(reader);
3285                 }
3286             }
3287         }
3288         IWICMetadataBlockReader_Release(block_reader);
3289     }
3290 
3291     return value;
3292 }
3293 
3294 static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3295 {
3296     static const WCHAR delayW[] = { 'D','e','l','a','y',0 };
3297     HRESULT hr;
3298     IWICBitmapFrameDecode *frame;
3299     IWICMetadataBlockReader *block_reader;
3300     IWICMetadataReader *reader;
3301     UINT frame_count, block_count, i;
3302     PropertyItem *delay = NULL, *comment = NULL, *background = NULL;
3303     PropertyItem *transparent_idx = NULL, *loop = NULL, *palette = NULL;
3304 
3305     IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3306     if (frame_count > 1)
3307     {
3308         delay = heap_alloc_zero(sizeof(*delay) + frame_count * sizeof(LONG));
3309         if (delay)
3310         {
3311             LONG *value;
3312 
3313             delay->type = PropertyTagTypeLong;
3314             delay->id = PropertyTagFrameDelay;
3315             delay->length = frame_count * sizeof(LONG);
3316             delay->value = delay + 1;
3317 
3318             value = delay->value;
3319 
3320             for (i = 0; i < frame_count; i++)
3321             {
3322                 hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
3323                 if (hr == S_OK)
3324                 {
3325                     value[i] = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, delayW);
3326                     IWICBitmapFrameDecode_Release(frame);
3327                 }
3328                 else value[i] = 0;
3329             }
3330         }
3331     }
3332 
3333     hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3334     if (hr == S_OK)
3335     {
3336         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3337         if (hr == S_OK)
3338         {
3339             for (i = 0; i < block_count; i++)
3340             {
3341                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3342                 if (hr == S_OK)
3343                 {
3344                     if (!comment)
3345                         comment = get_gif_comment(reader);
3346 
3347                     if (frame_count > 1 && !loop)
3348                         loop = get_gif_loopcount(reader);
3349 
3350                     if (!background)
3351                         background = get_gif_background(reader);
3352 
3353                     if (!palette)
3354                         palette = get_gif_palette(decoder, reader);
3355 
3356                     IWICMetadataReader_Release(reader);
3357                 }
3358             }
3359         }
3360         IWICMetadataBlockReader_Release(block_reader);
3361     }
3362 
3363     if (frame_count > 1 && !loop)
3364     {
3365         loop = heap_alloc_zero(sizeof(*loop) + sizeof(SHORT));
3366         if (loop)
3367         {
3368             loop->type = PropertyTagTypeShort;
3369             loop->id = PropertyTagLoopCount;
3370             loop->length = sizeof(SHORT);
3371             loop->value = loop + 1;
3372             *(SHORT *)loop->value = 1;
3373         }
3374     }
3375 
3376     if (delay) add_property(bitmap, delay);
3377     if (comment) add_property(bitmap, comment);
3378     if (loop) add_property(bitmap, loop);
3379     if (palette) add_property(bitmap, palette);
3380     if (background) add_property(bitmap, background);
3381 
3382     heap_free(delay);
3383     heap_free(comment);
3384     heap_free(loop);
3385     heap_free(palette);
3386     heap_free(background);
3387 
3388     /* Win7 gdiplus always returns transparent color index from frame 0 */
3389     hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
3390     if (hr != S_OK) return;
3391 
3392     hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3393     if (hr == S_OK)
3394     {
3395         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3396         if (hr == S_OK)
3397         {
3398             for (i = 0; i < block_count; i++)
3399             {
3400                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3401                 if (hr == S_OK)
3402                 {
3403                     if (!transparent_idx)
3404                         transparent_idx = get_gif_transparent_idx(reader);
3405 
3406                     IWICMetadataReader_Release(reader);
3407                 }
3408             }
3409         }
3410         IWICMetadataBlockReader_Release(block_reader);
3411     }
3412 
3413     if (transparent_idx) add_property(bitmap, transparent_idx);
3414     heap_free(transparent_idx);
3415 
3416     IWICBitmapFrameDecode_Release(frame);
3417 }
3418 
3419 static PropertyItem* create_prop(PROPID propid, PROPVARIANT* value)
3420 {
3421     PropertyItem *item = NULL;
3422     UINT item_size = propvariant_size(value);
3423 
3424     if (item_size)
3425     {
3426         item_size += sizeof(*item);
3427         item = heap_alloc_zero(item_size);
3428         if (propvariant_to_item(value, item, item_size, propid) != Ok)
3429         {
3430             heap_free(item);
3431             item = NULL;
3432         }
3433     }
3434 
3435     return item;
3436 }
3437 
3438 static ULONG get_ulong_by_index(IWICMetadataReader* reader, ULONG index)
3439 {
3440     PROPVARIANT value;
3441     HRESULT hr;
3442     ULONG result=0;
3443 
3444     hr = IWICMetadataReader_GetValueByIndex(reader, index, NULL, NULL, &value);
3445     if (SUCCEEDED(hr))
3446     {
3447         switch (value.vt)
3448         {
3449         case VT_UI4:
3450             result = value.u.ulVal;
3451             break;
3452         default:
3453             ERR("unhandled case %u\n", value.vt);
3454             break;
3455         }
3456         PropVariantClear(&value);
3457     }
3458     return result;
3459 }
3460 
3461 static void png_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3462 {
3463     HRESULT hr;
3464     IWICBitmapFrameDecode *frame;
3465     IWICMetadataBlockReader *block_reader;
3466     IWICMetadataReader *reader;
3467     UINT block_count, i, j;
3468     struct keyword_info {
3469         const char* name;
3470         PROPID propid;
3471         BOOL seen;
3472     } keywords[] = {
3473         { "Title", PropertyTagImageTitle },
3474         { "Author", PropertyTagArtist },
3475         { "Description", PropertyTagImageDescription },
3476         { "Copyright", PropertyTagCopyright },
3477         { "Software", PropertyTagSoftwareUsed },
3478         { "Source", PropertyTagEquipModel },
3479         { "Comment", PropertyTagExifUserComment },
3480     };
3481     BOOL seen_gamma=FALSE, seen_whitepoint=FALSE, seen_chrm=FALSE;
3482 
3483     hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3484     if (hr != S_OK) return;
3485 
3486     hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3487     if (hr == S_OK)
3488     {
3489         hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3490         if (hr == S_OK)
3491         {
3492             for (i = 0; i < block_count; i++)
3493             {
3494                 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3495                 if (hr == S_OK)
3496                 {
3497                     GUID format;
3498 
3499                     hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3500                     if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunktEXt, &format))
3501                     {
3502                         PROPVARIANT name, value;
3503                         PropertyItem* item;
3504 
3505                         hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &name, &value);
3506 
3507                         if (SUCCEEDED(hr))
3508                         {
3509                             if (name.vt == VT_LPSTR)
3510                             {
3511                                 for (j = 0; j < ARRAY_SIZE(keywords); j++)
3512                                     if (!strcmp(keywords[j].name, name.u.pszVal))
3513                                         break;
3514                                 if (j < ARRAY_SIZE(keywords) && !keywords[j].seen)
3515                                 {
3516                                     keywords[j].seen = TRUE;
3517                                     item = create_prop(keywords[j].propid, &value);
3518                                     if (item)
3519                                         add_property(bitmap, item);
3520                                     heap_free(item);
3521                                 }
3522                             }
3523 
3524                             PropVariantClear(&name);
3525                             PropVariantClear(&value);
3526                         }
3527                     }
3528                     else if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunkgAMA, &format))
3529                     {
3530                         PropertyItem* item;
3531 
3532                         if (!seen_gamma)
3533                         {
3534                             item = heap_alloc_zero(sizeof(PropertyItem) + sizeof(ULONG) * 2);
3535                             if (item)
3536                             {
3537                                 ULONG *rational;
3538                                 item->length = sizeof(ULONG) * 2;
3539                                 item->type = PropertyTagTypeRational;
3540                                 item->id = PropertyTagGamma;
3541                                 rational = item->value = item + 1;
3542                                 rational[0] = 100000;
3543                                 rational[1] = get_ulong_by_index(reader, 0);
3544                                 add_property(bitmap, item);
3545                                 seen_gamma = TRUE;
3546                                 heap_free(item);
3547                             }
3548                         }
3549                     }
3550                     else if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunkcHRM, &format))
3551                     {
3552                         PropertyItem* item;
3553 
3554                         if (!seen_whitepoint)
3555                         {
3556                             item = GdipAlloc(sizeof(PropertyItem) + sizeof(ULONG) * 4);
3557                             if (item)
3558                             {
3559                                 ULONG *rational;
3560                                 item->length = sizeof(ULONG) * 4;
3561                                 item->type = PropertyTagTypeRational;
3562                                 item->id = PropertyTagWhitePoint;
3563                                 rational = item->value = item + 1;
3564                                 rational[0] = get_ulong_by_index(reader, 0);
3565                                 rational[1] = 100000;
3566                                 rational[2] = get_ulong_by_index(reader, 1);
3567                                 rational[3] = 100000;
3568                                 add_property(bitmap, item);
3569                                 seen_whitepoint = TRUE;
3570                                 GdipFree(item);
3571                             }
3572                         }
3573                         if (!seen_chrm)
3574                         {
3575                             item = GdipAlloc(sizeof(PropertyItem) + sizeof(ULONG) * 12);
3576                             if (item)
3577                             {
3578                                 ULONG *rational;
3579                                 item->length = sizeof(ULONG) * 12;
3580                                 item->type = PropertyTagTypeRational;
3581                                 item->id = PropertyTagPrimaryChromaticities;
3582                                 rational = item->value = item + 1;
3583                                 rational[0] = get_ulong_by_index(reader, 2);
3584                                 rational[1] = 100000;
3585                                 rational[2] = get_ulong_by_index(reader, 3);
3586                                 rational[3] = 100000;
3587                                 rational[4] = get_ulong_by_index(reader, 4);
3588                                 rational[5] = 100000;
3589                                 rational[6] = get_ulong_by_index(reader, 5);
3590                                 rational[7] = 100000;
3591                                 rational[8] = get_ulong_by_index(reader, 6);
3592                                 rational[9] = 100000;
3593                                 rational[10] = get_ulong_by_index(reader, 7);
3594                                 rational[11] = 100000;
3595                                 add_property(bitmap, item);
3596                                 seen_chrm = TRUE;
3597                                 GdipFree(item);
3598                             }
3599                         }
3600                     }
3601 
3602                     IWICMetadataReader_Release(reader);
3603                 }
3604             }
3605         }
3606         IWICMetadataBlockReader_Release(block_reader);
3607     }
3608 
3609     IWICBitmapFrameDecode_Release(frame);
3610 }
3611 
3612 static GpStatus initialize_decoder_wic(IStream *stream, REFGUID container, IWICBitmapDecoder **decoder)
3613 {
3614     IWICImagingFactory *factory;
3615     HRESULT hr;
3616 
3617     TRACE("%p,%s\n", stream, wine_dbgstr_guid(container));
3618 
3619     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
3620     if (FAILED(hr)) return hresult_to_status(hr);
3621     hr = IWICImagingFactory_CreateDecoder(factory, container, NULL, decoder);
3622     IWICImagingFactory_Release(factory);
3623     if (FAILED(hr)) return hresult_to_status(hr);
3624 
3625     hr = IWICBitmapDecoder_Initialize(*decoder, stream, WICDecodeMetadataCacheOnLoad);
3626     if (FAILED(hr)) return hresult_to_status(hr);
3627     return Ok;
3628 }
3629 
3630 typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame);
3631 
3632 static GpStatus decode_frame_wic(IWICBitmapDecoder *decoder, BOOL force_conversion,
3633     UINT active_frame, metadata_reader_func metadata_reader, GpImage **image)
3634 {
3635     GpStatus status=Ok;
3636     GpBitmap *bitmap;
3637     HRESULT hr;
3638     IWICBitmapFrameDecode *frame;
3639     IWICBitmapSource *source=NULL;
3640     IWICMetadataBlockReader *block_reader;
3641     WICPixelFormatGUID wic_format;
3642     PixelFormat gdip_format=0;
3643     ColorPalette *palette = NULL;
3644     WICBitmapPaletteType palette_type = WICBitmapPaletteTypeFixedHalftone256;
3645     int i;
3646     UINT width, height, frame_count;
3647     BitmapData lockeddata;
3648     WICRect wrc;
3649 
3650     TRACE("%p,%u,%p\n", decoder, active_frame, image);
3651 
3652     IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3653     hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3654     if (SUCCEEDED(hr)) /* got frame */
3655     {
3656         hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &wic_format);
3657 
3658         if (SUCCEEDED(hr))
3659         {
3660             if (!force_conversion)
3661             {
3662                 for (i=0; pixel_formats[i].wic_format; i++)
3663                 {
3664                     if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format))
3665                     {
3666                         source = (IWICBitmapSource*)frame;
3667                         IWICBitmapSource_AddRef(source);
3668                         gdip_format = pixel_formats[i].gdip_format;
3669                         palette_type = pixel_formats[i].palette_type;
3670                         break;
3671                     }
3672                 }
3673             }
3674             if (!source)
3675             {
3676                 /* unknown format; fall back on 32bppARGB */
3677                 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
3678                 gdip_format = PixelFormat32bppARGB;
3679             }
3680             TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format);
3681         }
3682 
3683         if (SUCCEEDED(hr)) /* got source */
3684         {
3685             hr = IWICBitmapSource_GetSize(source, &width, &height);
3686 
3687             if (SUCCEEDED(hr))
3688                 status = GdipCreateBitmapFromScan0(width, height, 0, gdip_format,
3689                     NULL, &bitmap);
3690 
3691             if (SUCCEEDED(hr) && status == Ok) /* created bitmap */
3692             {
3693                 status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeWrite,
3694                     gdip_format, &lockeddata);
3695                 if (status == Ok) /* locked bitmap */
3696                 {
3697                     wrc.X = 0;
3698                     wrc.Width = width;
3699                     wrc.Height = 1;
3700                     for (i=0; i<height; i++)
3701                     {
3702                         wrc.Y = i;
3703                         hr = IWICBitmapSource_CopyPixels(source, &wrc, abs(lockeddata.Stride),
3704                             abs(lockeddata.Stride), (BYTE*)lockeddata.Scan0+lockeddata.Stride*i);
3705                         if (FAILED(hr)) break;
3706                     }
3707 
3708                     GdipBitmapUnlockBits(bitmap, &lockeddata);
3709                 }
3710 
3711                 if (SUCCEEDED(hr) && status == Ok)
3712                     *image = &bitmap->image;
3713                 else
3714                 {
3715                     *image = NULL;
3716                     GdipDisposeImage(&bitmap->image);
3717                 }
3718 
3719                 if (SUCCEEDED(hr) && status == Ok)
3720                 {
3721                     double dpix, dpiy;
3722                     hr = IWICBitmapSource_GetResolution(source, &dpix, &dpiy);
3723                     if (SUCCEEDED(hr))
3724                     {
3725                         bitmap->image.xres = dpix;
3726                         bitmap->image.yres = dpiy;
3727                     }
3728                     hr = S_OK;
3729                 }
3730             }
3731 
3732             IWICBitmapSource_Release(source);
3733         }
3734 
3735         if (SUCCEEDED(hr)) {
3736             bitmap->metadata_reader = NULL;
3737 
3738             if (metadata_reader)
3739                 metadata_reader(bitmap, decoder, active_frame);
3740             else if (IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader) == S_OK)
3741             {
3742                 UINT block_count = 0;
3743                 if (IWICMetadataBlockReader_GetCount(block_reader, &block_count) == S_OK && block_count)
3744                     IWICMetadataBlockReader_GetReaderByIndex(block_reader, 0, &bitmap->metadata_reader);
3745                 IWICMetadataBlockReader_Release(block_reader);
3746             }
3747 
3748             palette = get_palette(frame, palette_type);
3749             IWICBitmapFrameDecode_Release(frame);
3750         }
3751     }
3752 
3753     if (FAILED(hr) && status == Ok) status = hresult_to_status(hr);
3754 
3755     if (status == Ok)
3756     {
3757         /* Native GDI+ used to be smarter, but since Win7 it just sets these flags. */
3758         bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI;
3759         if (IsEqualGUID(&wic_format, &GUID_WICPixelFormat2bppGray) ||
3760             IsEqualGUID(&wic_format, &GUID_WICPixelFormat4bppGray) ||
3761             IsEqualGUID(&wic_format, &GUID_WICPixelFormat8bppGray) ||
3762             IsEqualGUID(&wic_format, &GUID_WICPixelFormat16bppGray))
3763             bitmap->image.flags |= ImageFlagsColorSpaceGRAY;
3764         else
3765             bitmap->image.flags |= ImageFlagsColorSpaceRGB;
3766         bitmap->image.frame_count = frame_count;
3767         bitmap->image.current_frame = active_frame;
3768         bitmap->image.decoder = decoder;
3769         IWICBitmapDecoder_AddRef(decoder);
3770         if (palette)
3771         {
3772             heap_free(bitmap->image.palette);
3773             bitmap->image.palette = palette;
3774         }
3775         else
3776         {
3777             if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite))
3778                 bitmap->image.palette->Flags = 0;
3779         }
3780         TRACE("=> %p\n", *image);
3781     }
3782 
3783     return status;
3784 }
3785 
3786 static GpStatus decode_image_wic(IStream *stream, REFGUID container,
3787         metadata_reader_func metadata_reader, GpImage **image)
3788 {
3789     IWICBitmapDecoder *decoder;
3790     GpStatus status;
3791 
3792     status = initialize_decoder_wic(stream, container, &decoder);
3793     if(status != Ok)
3794         return status;
3795 
3796     status = decode_frame_wic(decoder, FALSE, 0, metadata_reader, image);
3797     IWICBitmapDecoder_Release(decoder);
3798     return status;
3799 }
3800 
3801 static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
3802 {
3803     GpImage *new_image;
3804     GpStatus status;
3805 
3806     status = decode_frame_wic(image->decoder, FALSE, active_frame, NULL, &new_image);
3807     if(status != Ok)
3808         return status;
3809 
3810     new_image->busy = image->busy;
3811     memcpy(&new_image->format, &image->format, sizeof(GUID));
3812     free_image_data(image);
3813     if (image->type == ImageTypeBitmap)
3814         *(GpBitmap *)image = *(GpBitmap *)new_image;
3815     else if (image->type == ImageTypeMetafile)
3816         *(GpMetafile *)image = *(GpMetafile *)new_image;
3817     new_image->type = ~0;
3818     heap_free(new_image);
3819     return Ok;
3820 }
3821 
3822 static HRESULT get_gif_frame_rect(IWICBitmapFrameDecode *frame,
3823         UINT *left, UINT *top, UINT *width, UINT *height)
3824 {
3825     static const WCHAR leftW[] = {'L','e','f','t',0};
3826     static const WCHAR topW[] = {'T','o','p',0};
3827 
3828     *left = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, leftW);
3829     *top = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, topW);
3830 
3831     return IWICBitmapFrameDecode_GetSize(frame, width, height);
3832 }
3833 
3834 static HRESULT blit_gif_frame(GpBitmap *bitmap, IWICBitmapFrameDecode *frame, BOOL first_frame)
3835 {
3836     UINT i, j, left, top, width, height;
3837     IWICBitmapSource *source;
3838     BYTE *new_bits;
3839     HRESULT hr;
3840 
3841     hr = get_gif_frame_rect(frame, &left, &top, &width, &height);
3842     if(FAILED(hr))
3843         return hr;
3844 
3845     hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
3846     if(FAILED(hr))
3847         return hr;
3848 
3849     new_bits = heap_alloc_zero(width*height*4);
3850     if(!new_bits)
3851         return E_OUTOFMEMORY;
3852 
3853     hr = IWICBitmapSource_CopyPixels(source, NULL, width*4, width*height*4, new_bits);
3854     IWICBitmapSource_Release(source);
3855     if(FAILED(hr)) {
3856         heap_free(new_bits);
3857         return hr;
3858     }
3859 
3860     for(i=0; i<height && i+top<bitmap->height; i++) {
3861         for(j=0; j<width && j+left<bitmap->width; j++) {
3862             DWORD *src = (DWORD*)(new_bits+i*width*4+j*4);
3863             DWORD *dst = (DWORD*)(bitmap->bits+(i+top)*bitmap->stride+(j+left)*4);
3864 
3865             if(first_frame || *src>>24 != 0)
3866                 *dst = *src;
3867         }
3868     }
3869     heap_free(new_bits);
3870     return hr;
3871 }
3872 
3873 static DWORD get_gif_background_color(GpBitmap *bitmap)
3874 {
3875     BYTE bgcolor_idx = 0;
3876     UINT i;
3877 
3878     for(i=0; i<bitmap->prop_count; i++) {
3879         if(bitmap->prop_item[i].id == PropertyTagIndexBackground) {
3880             bgcolor_idx = *(BYTE*)bitmap->prop_item[i].value;
3881             break;
3882         }
3883     }
3884 
3885     for(i=0; i<bitmap->prop_count; i++) {
3886         if(bitmap->prop_item[i].id == PropertyTagIndexTransparent) {
3887             BYTE transparent_idx;
3888             transparent_idx = *(BYTE*)bitmap->prop_item[i].value;
3889 
3890             if(transparent_idx == bgcolor_idx)
3891                 return 0;
3892         }
3893     }
3894 
3895     for(i=0; i<bitmap->prop_count; i++) {
3896         if(bitmap->prop_item[i].id == PropertyTagGlobalPalette) {
3897             if(bitmap->prop_item[i].length/3 > bgcolor_idx) {
3898                 BYTE *color = ((BYTE*)bitmap->prop_item[i].value)+bgcolor_idx*3;
3899                 return color[2] + (color[1]<<8) + (color[0]<<16) + (0xffu<<24);
3900             }
3901             break;
3902         }
3903     }
3904 
3905     FIXME("can't get gif background color\n");
3906     return 0xffffffff;
3907 }
3908 
3909 static GpStatus select_frame_gif(GpImage* image, UINT active_frame)
3910 {
3911     static const WCHAR disposalW[] = {'D','i','s','p','o','s','a','l',0};
3912 
3913     GpBitmap *bitmap = (GpBitmap*)image;
3914     IWICBitmapFrameDecode *frame;
3915     int cur_frame=0, disposal;
3916     BOOL bgcolor_set = FALSE;
3917     DWORD bgcolor = 0;
3918     HRESULT hr;
3919 
3920     if(active_frame > image->current_frame) {
3921         hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, image->current_frame, &frame);
3922         if(FAILED(hr))
3923             return hresult_to_status(hr);
3924         disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW);
3925         IWICBitmapFrameDecode_Release(frame);
3926 
3927         if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND)
3928             cur_frame = image->current_frame;
3929         else if(disposal != GIF_DISPOSE_RESTORE_TO_PREV)
3930             cur_frame = image->current_frame+1;
3931     }
3932 
3933     while(cur_frame != active_frame) {
3934         hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, cur_frame, &frame);
3935         if(FAILED(hr))
3936             return hresult_to_status(hr);
3937         disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW);
3938 
3939         if(disposal==GIF_DISPOSE_UNSPECIFIED || disposal==GIF_DISPOSE_DO_NOT_DISPOSE) {
3940             hr = blit_gif_frame(bitmap, frame, cur_frame==0);
3941             if(FAILED(hr))
3942                 return hresult_to_status(hr);
3943         }else if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND) {
3944             UINT left, top, width, height, i, j;
3945 
3946             if(!bgcolor_set) {
3947                 bgcolor = get_gif_background_color(bitmap);
3948                 bgcolor_set = TRUE;
3949             }
3950 
3951             hr = get_gif_frame_rect(frame, &left, &top, &width, &height);
3952             if(FAILED(hr))
3953                 return hresult_to_status(hr);
3954             for(i=top; i<top+height && i<bitmap->height; i++) {
3955                 DWORD *bits = (DWORD*)(bitmap->bits+i*bitmap->stride);
3956                 for(j=left; j<left+width && j<bitmap->width; j++)
3957                     bits[j] = bgcolor;
3958             }
3959         }
3960 
3961         IWICBitmapFrameDecode_Release(frame);
3962         cur_frame++;
3963     }
3964 
3965     hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, active_frame, &frame);
3966     if(FAILED(hr))
3967         return hresult_to_status(hr);
3968 
3969     hr = blit_gif_frame(bitmap, frame, cur_frame==0);
3970     IWICBitmapFrameDecode_Release(frame);
3971     if(FAILED(hr))
3972         return hresult_to_status(hr);
3973 
3974     image->current_frame = active_frame;
3975     return Ok;
3976 }
3977 
3978 static GpStatus decode_image_icon(IStream* stream, GpImage **image)
3979 {
3980     return decode_image_wic(stream, &GUID_ContainerFormatIco, NULL, image);
3981 }
3982 
3983 static GpStatus decode_image_bmp(IStream* stream, GpImage **image)
3984 {
3985     GpStatus status;
3986     GpBitmap* bitmap;
3987 
3988     status = decode_image_wic(stream, &GUID_ContainerFormatBmp, NULL, image);
3989 
3990     bitmap = (GpBitmap*)*image;
3991 
3992     if (status == Ok && bitmap->format == PixelFormat32bppARGB)
3993     {
3994         /* WIC supports bmp files with alpha, but gdiplus does not */
3995         bitmap->format = PixelFormat32bppRGB;
3996     }
3997 
3998     return status;
3999 }
4000 
4001 static GpStatus decode_image_jpeg(IStream* stream, GpImage **image)
4002 {
4003     return decode_image_wic(stream, &GUID_ContainerFormatJpeg, NULL, image);
4004 }
4005 
4006 static BOOL has_png_transparency_chunk(IStream *pIStream)
4007 {
4008     LARGE_INTEGER seek;
4009     BOOL has_tRNS = FALSE;
4010     HRESULT hr;
4011     BYTE header[8];
4012 
4013     seek.QuadPart = 8;
4014     do
4015     {
4016         ULARGE_INTEGER chunk_start;
4017         ULONG bytesread, chunk_size;
4018 
4019         hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
4020         if (FAILED(hr)) break;
4021 
4022         hr = IStream_Read(pIStream, header, 8, &bytesread);
4023         if (FAILED(hr) || bytesread < 8) break;
4024 
4025         chunk_size = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3];
4026         if (!memcmp(&header[4], "tRNS", 4))
4027         {
4028             has_tRNS = TRUE;
4029             break;
4030         }
4031 
4032         seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
4033     } while (memcmp(&header[4], "IDAT", 4) && memcmp(&header[4], "IEND", 4));
4034 
4035     TRACE("has_tRNS = %d\n", has_tRNS);
4036     return has_tRNS;
4037 }
4038 
4039 static GpStatus decode_image_png(IStream* stream, GpImage **image)
4040 {
4041     IWICBitmapDecoder *decoder;
4042     IWICBitmapFrameDecode *frame;
4043     GpStatus status;
4044     HRESULT hr;
4045     GUID format;
4046     BOOL force_conversion = FALSE;
4047 
4048     status = initialize_decoder_wic(stream, &GUID_ContainerFormatPng, &decoder);
4049     if (status != Ok)
4050         return status;
4051 
4052     hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
4053     if (hr == S_OK)
4054     {
4055         hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
4056         if (hr == S_OK)
4057         {
4058             if (IsEqualGUID(&format, &GUID_WICPixelFormat8bppGray))
4059                 force_conversion = TRUE;
4060             else if ((IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed) ||
4061                       IsEqualGUID(&format, &GUID_WICPixelFormat4bppIndexed) ||
4062                       IsEqualGUID(&format, &GUID_WICPixelFormat2bppIndexed) ||
4063                       IsEqualGUID(&format, &GUID_WICPixelFormat1bppIndexed) ||
4064                       IsEqualGUID(&format, &GUID_WICPixelFormat24bppBGR)) &&
4065                      has_png_transparency_chunk(stream))
4066                 force_conversion = TRUE;
4067 
4068             status = decode_frame_wic(decoder, force_conversion, 0, png_metadata_reader, image);
4069         }
4070         else
4071             status = hresult_to_status(hr);
4072 
4073         IWICBitmapFrameDecode_Release(frame);
4074     }
4075     else
4076         status = hresult_to_status(hr);
4077 
4078     IWICBitmapDecoder_Release(decoder);
4079     return status;
4080 }
4081 
4082 static GpStatus decode_image_gif(IStream* stream, GpImage **image)
4083 {
4084     IWICBitmapDecoder *decoder;
4085     UINT frame_count;
4086     GpStatus status;
4087     HRESULT hr;
4088 
4089     status = initialize_decoder_wic(stream, &GUID_ContainerFormatGif, &decoder);
4090     if(status != Ok)
4091         return status;
4092 
4093     hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
4094     if(FAILED(hr))
4095         return hresult_to_status(hr);
4096 
4097     status = decode_frame_wic(decoder, frame_count > 1, 0, gif_metadata_reader, image);
4098     IWICBitmapDecoder_Release(decoder);
4099     if(status != Ok)
4100         return status;
4101 
4102     if(frame_count > 1) {
4103         heap_free((*image)->palette);
4104         (*image)->palette = NULL;
4105     }
4106     return Ok;
4107 }
4108 
4109 static GpStatus decode_image_tiff(IStream* stream, GpImage **image)
4110 {
4111     return decode_image_wic(stream, &GUID_ContainerFormatTiff, NULL, image);
4112 }
4113 
4114 static GpStatus load_wmf(IStream *stream, GpMetafile **metafile)
4115 {
4116     WmfPlaceableFileHeader pfh;
4117     BOOL is_placeable = FALSE;
4118     LARGE_INTEGER seek;
4119     GpStatus status;
4120     METAHEADER mh;
4121     HMETAFILE hmf;
4122     HRESULT hr;
4123     UINT size;
4124     void *buf;
4125 
4126     hr = IStream_Read(stream, &mh, sizeof(mh), &size);
4127     if (hr != S_OK || size != sizeof(mh))
4128         return GenericError;
4129 
4130     if (((WmfPlaceableFileHeader *)&mh)->Key == WMF_PLACEABLE_KEY)
4131     {
4132         seek.QuadPart = 0;
4133         hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4134         if (FAILED(hr)) return hresult_to_status(hr);
4135 
4136         hr = IStream_Read(stream, &pfh, sizeof(pfh), &size);
4137         if (hr != S_OK || size != sizeof(pfh))
4138             return GenericError;
4139 
4140         hr = IStream_Read(stream, &mh, sizeof(mh), &size);
4141         if (hr != S_OK || size != sizeof(mh))
4142             return GenericError;
4143 
4144         is_placeable = TRUE;
4145     }
4146 
4147     seek.QuadPart = is_placeable ? sizeof(pfh) : 0;
4148     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4149     if (FAILED(hr)) return hresult_to_status(hr);
4150 
4151     buf = heap_alloc(mh.mtSize * 2);
4152     if (!buf) return OutOfMemory;
4153 
4154     hr = IStream_Read(stream, buf, mh.mtSize * 2, &size);
4155     if (hr != S_OK || size != mh.mtSize * 2)
4156     {
4157         heap_free(buf);
4158         return GenericError;
4159     }
4160 
4161     hmf = SetMetaFileBitsEx(mh.mtSize * 2, buf);
4162     heap_free(buf);
4163     if (!hmf)
4164         return GenericError;
4165 
4166     status = GdipCreateMetafileFromWmf(hmf, TRUE, is_placeable ? &pfh : NULL, metafile);
4167     if (status != Ok)
4168         DeleteMetaFile(hmf);
4169     return status;
4170 }
4171 
4172 static GpStatus decode_image_wmf(IStream *stream, GpImage **image)
4173 {
4174     GpMetafile *metafile;
4175     GpStatus status;
4176 
4177     TRACE("%p %p\n", stream, image);
4178 
4179     if (!stream || !image)
4180         return InvalidParameter;
4181 
4182     status = load_wmf(stream, &metafile);
4183     if (status != Ok)
4184     {
4185         TRACE("Could not load metafile\n");
4186         return status;
4187     }
4188 
4189     *image = (GpImage *)metafile;
4190     TRACE("<-- %p\n", *image);
4191 
4192     return Ok;
4193 }
4194 
4195 static GpStatus load_emf(IStream *stream, GpMetafile **metafile)
4196 {
4197     LARGE_INTEGER seek;
4198     ENHMETAHEADER emh;
4199     HENHMETAFILE hemf;
4200     GpStatus status;
4201     HRESULT hr;
4202     UINT size;
4203     void *buf;
4204 
4205     hr = IStream_Read(stream, &emh, sizeof(emh), &size);
4206     if (hr != S_OK || size != sizeof(emh) || emh.dSignature != ENHMETA_SIGNATURE)
4207         return GenericError;
4208 
4209     seek.QuadPart = 0;
4210     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4211     if (FAILED(hr)) return hresult_to_status(hr);
4212 
4213     buf = heap_alloc(emh.nBytes);
4214     if (!buf) return OutOfMemory;
4215 
4216     hr = IStream_Read(stream, buf, emh.nBytes, &size);
4217     if (hr != S_OK || size != emh.nBytes)
4218     {
4219         heap_free(buf);
4220         return GenericError;
4221     }
4222 
4223     hemf = SetEnhMetaFileBits(emh.nBytes, buf);
4224     heap_free(buf);
4225     if (!hemf)
4226         return GenericError;
4227 
4228     status = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4229     if (status != Ok)
4230         DeleteEnhMetaFile(hemf);
4231     return status;
4232 }
4233 
4234 static GpStatus decode_image_emf(IStream *stream, GpImage **image)
4235 {
4236     GpMetafile *metafile;
4237     GpStatus status;
4238 
4239     TRACE("%p %p\n", stream, image);
4240 
4241     if (!stream || !image)
4242         return InvalidParameter;
4243 
4244     status = load_emf(stream, &metafile);
4245     if (status != Ok)
4246     {
4247         TRACE("Could not load metafile\n");
4248         return status;
4249     }
4250 
4251     *image = (GpImage *)metafile;
4252     TRACE("<-- %p\n", *image);
4253 
4254     return Ok;
4255 }
4256 
4257 typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
4258     GDIPCONST EncoderParameters* params);
4259 
4260 typedef GpStatus (*decode_image_func)(IStream *stream, GpImage **image);
4261 
4262 typedef GpStatus (*select_image_func)(GpImage *image, UINT active_frame);
4263 
4264 typedef struct image_codec {
4265     ImageCodecInfo info;
4266     encode_image_func encode_func;
4267     decode_image_func decode_func;
4268     select_image_func select_func;
4269 } image_codec;
4270 
4271 typedef enum {
4272     BMP,
4273     JPEG,
4274     GIF,
4275     TIFF,
4276     EMF,
4277     WMF,
4278     PNG,
4279     ICO,
4280     NUM_CODECS
4281 } ImageFormat;
4282 
4283 static const struct image_codec codecs[NUM_CODECS];
4284 
4285 static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result)
4286 {
4287     BYTE signature[8];
4288     const BYTE *pattern, *mask;
4289     LARGE_INTEGER seek;
4290     HRESULT hr;
4291     UINT bytesread;
4292     int i;
4293     DWORD j, sig;
4294 
4295     /* seek to the start of the stream */
4296     seek.QuadPart = 0;
4297     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4298     if (FAILED(hr)) return hresult_to_status(hr);
4299 
4300     /* read the first 8 bytes */
4301     /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */
4302     hr = IStream_Read(stream, signature, 8, &bytesread);
4303     if (FAILED(hr)) return hresult_to_status(hr);
4304     if (hr == S_FALSE || bytesread == 0) return GenericError;
4305 
4306     for (i = 0; i < NUM_CODECS; i++) {
4307         if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
4308             bytesread >= codecs[i].info.SigSize)
4309         {
4310             for (sig=0; sig<codecs[i].info.SigCount; sig++)
4311             {
4312                 pattern = &codecs[i].info.SigPattern[codecs[i].info.SigSize*sig];
4313                 mask = &codecs[i].info.SigMask[codecs[i].info.SigSize*sig];
4314                 for (j=0; j<codecs[i].info.SigSize; j++)
4315                     if ((signature[j] & mask[j]) != pattern[j])
4316                         break;
4317                 if (j == codecs[i].info.SigSize)
4318                 {
4319                     *result = &codecs[i];
4320                     return Ok;
4321                 }
4322             }
4323         }
4324     }
4325 
4326     TRACE("no match for %i byte signature %x %x %x %x %x %x %x %x\n", bytesread,
4327         signature[0],signature[1],signature[2],signature[3],
4328         signature[4],signature[5],signature[6],signature[7]);
4329 
4330     return GenericError;
4331 }
4332 
4333 static GpStatus get_decoder_info_from_image(GpImage *image, const struct image_codec **result)
4334 {
4335     int i;
4336 
4337     for (i = 0; i < NUM_CODECS; i++) {
4338         if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
4339                 IsEqualIID(&codecs[i].info.FormatID, &image->format))
4340         {
4341             *result = &codecs[i];
4342             return Ok;
4343         }
4344     }
4345 
4346     TRACE("no match for format: %s\n", wine_dbgstr_guid(&image->format));
4347     return GenericError;
4348 }
4349 
4350 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID,
4351                                                UINT frame)
4352 {
4353     GpStatus stat;
4354     const struct image_codec *codec = NULL;
4355     BOOL unlock;
4356 
4357     TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
4358 
4359     if (!image || !dimensionID)
4360         return InvalidParameter;
4361     if(!image_lock(image, &unlock))
4362         return ObjectBusy;
4363 
4364     if (frame >= image->frame_count)
4365     {
4366         WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
4367         image_unlock(image, unlock);
4368         return InvalidParameter;
4369     }
4370 
4371     if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
4372     {
4373         WARN("invalid image type %d\n", image->type);
4374         image_unlock(image, unlock);
4375         return InvalidParameter;
4376     }
4377 
4378     if (image->current_frame == frame)
4379     {
4380         image_unlock(image, unlock);
4381         return Ok;
4382     }
4383 
4384     if (!image->decoder)
4385     {
4386         TRACE("image doesn't have an associated decoder\n");
4387         image_unlock(image, unlock);
4388         return Ok;
4389     }
4390 
4391     /* choose an appropriate image decoder */
4392     stat = get_decoder_info_from_image(image, &codec);
4393     if (stat != Ok)
4394     {
4395         WARN("can't find decoder info\n");
4396         image_unlock(image, unlock);
4397         return stat;
4398     }
4399 
4400     stat = codec->select_func(image, frame);
4401     image_unlock(image, unlock);
4402     return stat;
4403 }
4404 
4405 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image)
4406 {
4407     GpStatus stat;
4408     LARGE_INTEGER seek;
4409     HRESULT hr;
4410     const struct image_codec *codec=NULL;
4411 
4412     TRACE("%p %p\n", stream, image);
4413 
4414     if (!stream || !image)
4415         return InvalidParameter;
4416 
4417     /* choose an appropriate image decoder */
4418     stat = get_decoder_info(stream, &codec);
4419     if (stat != Ok) return stat;
4420 
4421     /* seek to the start of the stream */
4422     seek.QuadPart = 0;
4423     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4424     if (FAILED(hr)) return hresult_to_status(hr);
4425 
4426     /* call on the image decoder to do the real work */
4427     stat = codec->decode_func(stream, image);
4428 
4429     /* take note of the original data format */
4430     if (stat == Ok)
4431     {
4432         memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID));
4433         return Ok;
4434     }
4435 
4436     return stat;
4437 }
4438 
4439 /* FIXME: no ICM */
4440 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
4441 {
4442     TRACE("%p %p\n", stream, image);
4443 
4444     return GdipLoadImageFromStream(stream, image);
4445 }
4446 
4447 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
4448 {
4449     static int calls;
4450 
4451     TRACE("(%p,%u)\n", image, propId);
4452 
4453     if(!image)
4454         return InvalidParameter;
4455 
4456     if(!(calls++))
4457         FIXME("not implemented\n");
4458 
4459     return NotImplemented;
4460 }
4461 
4462 GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* item)
4463 {
4464     static int calls;
4465 
4466     if (!image || !item) return InvalidParameter;
4467 
4468     TRACE("(%p,%p:%#x,%u,%u,%p)\n", image, item, item->id, item->type, item->length, item->value);
4469 
4470     if(!(calls++))
4471         FIXME("not implemented\n");
4472 
4473     return Ok;
4474 }
4475 
4476 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
4477                                         GDIPCONST CLSID *clsidEncoder,
4478                                         GDIPCONST EncoderParameters *encoderParams)
4479 {
4480     GpStatus stat;
4481     IStream *stream;
4482 
4483     TRACE("%p (%s) %p %p\n", image, debugstr_w(filename), clsidEncoder, encoderParams);
4484 
4485     if (!image || !filename|| !clsidEncoder)
4486         return InvalidParameter;
4487 
4488     stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
4489     if (stat != Ok)
4490         return GenericError;
4491 
4492     stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
4493 
4494     IStream_Release(stream);
4495     return stat;
4496 }
4497 
4498 /*************************************************************************
4499  * Encoding functions -
4500  *   These functions encode an image in different image file formats.
4501  */
4502 
4503 static GpStatus encode_image_wic(GpImage *image, IStream* stream,
4504     REFGUID container, GDIPCONST EncoderParameters* params)
4505 {
4506     GpStatus stat;
4507     GpBitmap *bitmap;
4508     IWICImagingFactory *factory;
4509     IWICBitmapEncoder *encoder;
4510     IWICBitmapFrameEncode *frameencode;
4511     IPropertyBag2 *encoderoptions;
4512     HRESULT hr;
4513     UINT width, height;
4514     PixelFormat gdipformat=0;
4515     const WICPixelFormatGUID *desired_wicformat;
4516     WICPixelFormatGUID wicformat;
4517     GpRect rc;
4518     BitmapData lockeddata;
4519     UINT i;
4520 
4521     if (image->type != ImageTypeBitmap)
4522         return GenericError;
4523 
4524     bitmap = (GpBitmap*)image;
4525 
4526     GdipGetImageWidth(image, &width);
4527     GdipGetImageHeight(image, &height);
4528 
4529     rc.X = 0;
4530     rc.Y = 0;
4531     rc.Width = width;
4532     rc.Height = height;
4533 
4534     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
4535     if (FAILED(hr))
4536         return hresult_to_status(hr);
4537     hr = IWICImagingFactory_CreateEncoder(factory, container, NULL, &encoder);
4538     IWICImagingFactory_Release(factory);
4539     if (FAILED(hr))
4540         return hresult_to_status(hr);
4541 
4542     hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
4543 
4544     if (SUCCEEDED(hr))
4545     {
4546         hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &encoderoptions);
4547     }
4548 
4549     if (SUCCEEDED(hr)) /* created frame */
4550     {
4551         hr = IWICBitmapFrameEncode_Initialize(frameencode, encoderoptions);
4552 
4553         if (SUCCEEDED(hr))
4554             hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height);
4555 
4556         if (SUCCEEDED(hr))
4557             hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres);
4558 
4559         if (SUCCEEDED(hr))
4560         {
4561             for (i=0; pixel_formats[i].wic_format; i++)
4562             {
4563                 if (pixel_formats[i].gdip_format == bitmap->format)
4564                 {
4565                     desired_wicformat = pixel_formats[i].wic_format;
4566                     gdipformat = bitmap->format;
4567                     break;
4568                 }
4569             }
4570             if (!gdipformat)
4571             {
4572                 desired_wicformat = &GUID_WICPixelFormat32bppBGRA;
4573                 gdipformat = PixelFormat32bppARGB;
4574             }
4575 
4576             memcpy(&wicformat, desired_wicformat, sizeof(GUID));
4577             hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat);
4578         }
4579 
4580         if (SUCCEEDED(hr) && !IsEqualGUID(desired_wicformat, &wicformat))
4581         {
4582             /* Encoder doesn't support this bitmap's format. */
4583             gdipformat = 0;
4584             for (i=0; pixel_formats[i].wic_format; i++)
4585             {
4586                 if (IsEqualGUID(&wicformat, pixel_formats[i].wic_format))
4587                 {
4588                     gdipformat = pixel_formats[i].gdip_format;
4589                     break;
4590                 }
4591             }
4592             if (!gdipformat)
4593             {
4594                 ERR("Cannot support encoder format %s\n", debugstr_guid(&wicformat));
4595                 hr = E_FAIL;
4596             }
4597         }
4598 
4599         if (SUCCEEDED(hr))
4600         {
4601             stat = GdipBitmapLockBits(bitmap, &rc, ImageLockModeRead, gdipformat,
4602                 &lockeddata);
4603 
4604             if (stat == Ok)
4605             {
4606                 UINT row_size = (lockeddata.Width * PIXELFORMATBPP(gdipformat) + 7)/8;
4607                 BYTE *row;
4608 
4609                 /* write one row at a time in case stride is negative */
4610                 row = lockeddata.Scan0;
4611                 for (i=0; i<lockeddata.Height; i++)
4612                 {
4613                     hr = IWICBitmapFrameEncode_WritePixels(frameencode, 1, row_size, row_size, row);
4614                     if (FAILED(hr)) break;
4615                     row += lockeddata.Stride;
4616                 }
4617 
4618                 GdipBitmapUnlockBits(bitmap, &lockeddata);
4619             }
4620             else
4621                 hr = E_FAIL;
4622         }
4623 
4624         if (SUCCEEDED(hr))
4625             hr = IWICBitmapFrameEncode_Commit(frameencode);
4626 
4627         IWICBitmapFrameEncode_Release(frameencode);
4628         IPropertyBag2_Release(encoderoptions);
4629     }
4630 
4631     if (SUCCEEDED(hr))
4632         hr = IWICBitmapEncoder_Commit(encoder);
4633 
4634     IWICBitmapEncoder_Release(encoder);
4635     return hresult_to_status(hr);
4636 }
4637 
4638 static GpStatus encode_image_BMP(GpImage *image, IStream* stream,
4639     GDIPCONST EncoderParameters* params)
4640 {
4641     return encode_image_wic(image, stream, &GUID_ContainerFormatBmp, params);
4642 }
4643 
4644 static GpStatus encode_image_tiff(GpImage *image, IStream* stream,
4645     GDIPCONST EncoderParameters* params)
4646 {
4647     return encode_image_wic(image, stream, &GUID_ContainerFormatTiff, params);
4648 }
4649 
4650 GpStatus encode_image_png(GpImage *image, IStream* stream,
4651     GDIPCONST EncoderParameters* params)
4652 {
4653     return encode_image_wic(image, stream, &GUID_ContainerFormatPng, params);
4654 }
4655 
4656 static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
4657     GDIPCONST EncoderParameters* params)
4658 {
4659     return encode_image_wic(image, stream, &GUID_ContainerFormatJpeg, params);
4660 }
4661 
4662 static GpStatus encode_image_gif(GpImage *image, IStream* stream,
4663     GDIPCONST EncoderParameters* params)
4664 {
4665     return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params);
4666 }
4667 
4668 /*****************************************************************************
4669  * GdipSaveImageToStream [GDIPLUS.@]
4670  */
4671 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
4672     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4673 {
4674     GpStatus stat;
4675     encode_image_func encode_image;
4676     int i;
4677 
4678     TRACE("%p, %p, %s, %p\n", image, stream, wine_dbgstr_guid(clsid), params);
4679 
4680     if(!image || !stream)
4681         return InvalidParameter;
4682 
4683     /* select correct encoder */
4684     encode_image = NULL;
4685     for (i = 0; i < NUM_CODECS; i++) {
4686         if ((codecs[i].info.Flags & ImageCodecFlagsEncoder) &&
4687             IsEqualCLSID(clsid, &codecs[i].info.Clsid))
4688             encode_image = codecs[i].encode_func;
4689     }
4690     if (encode_image == NULL)
4691         return UnknownImageFormat;
4692 
4693     stat = encode_image(image, stream, params);
4694 
4695     return stat;
4696 }
4697 
4698 /*****************************************************************************
4699  * GdipSaveAdd [GDIPLUS.@]
4700  */
4701 GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params)
4702 {
4703     FIXME("(%p,%p): stub\n", image, params);
4704     return Ok;
4705 }
4706 
4707 /*****************************************************************************
4708  * GdipGetImagePalette [GDIPLUS.@]
4709  */
4710 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
4711 {
4712     INT count;
4713 
4714     TRACE("(%p,%p,%i)\n", image, palette, size);
4715 
4716     if (!image || !palette)
4717         return InvalidParameter;
4718 
4719     count = image->palette ? image->palette->Count : 0;
4720 
4721     if (size < (sizeof(UINT)*2+sizeof(ARGB)*count))
4722     {
4723         TRACE("<-- InsufficientBuffer\n");
4724         return InsufficientBuffer;
4725     }
4726 
4727     if (image->palette)
4728     {
4729         palette->Flags = image->palette->Flags;
4730         palette->Count = image->palette->Count;
4731         memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count);
4732     }
4733     else
4734     {
4735         palette->Flags = 0;
4736         palette->Count = 0;
4737     }
4738     return Ok;
4739 }
4740 
4741 /*****************************************************************************
4742  * GdipSetImagePalette [GDIPLUS.@]
4743  */
4744 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
4745     GDIPCONST ColorPalette *palette)
4746 {
4747     ColorPalette *new_palette;
4748 
4749     TRACE("(%p,%p)\n", image, palette);
4750 
4751     if(!image || !palette || palette->Count > 256)
4752         return InvalidParameter;
4753 
4754     new_palette = heap_alloc_zero(2 * sizeof(UINT) + palette->Count * sizeof(ARGB));
4755     if (!new_palette) return OutOfMemory;
4756 
4757     heap_free(image->palette);
4758     image->palette = new_palette;
4759     image->palette->Flags = palette->Flags;
4760     image->palette->Count = palette->Count;
4761     memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count);
4762 
4763     return Ok;
4764 }
4765 
4766 /*************************************************************************
4767  * Encoders -
4768  *   Structures that represent which formats we support for encoding.
4769  */
4770 
4771 /* ImageCodecInfo creation routines taken from libgdiplus */
4772 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */
4773 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
4774 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
4775 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
4776 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
4777 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
4778 
4779 static const WCHAR jpeg_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'J','P','E','G', 0};
4780 static const WCHAR jpeg_extension[] = {'*','.','J','P','G',';', '*','.','J','P','E','G',';', '*','.','J','P','E',';', '*','.','J','F','I','F',0};
4781 static const WCHAR jpeg_mimetype[] = {'i','m','a','g','e','/','j','p','e','g', 0};
4782 static const WCHAR jpeg_format[] = {'J','P','E','G',0};
4783 static const BYTE jpeg_sig_pattern[] = { 0xFF, 0xD8 };
4784 static const BYTE jpeg_sig_mask[] = { 0xFF, 0xFF };
4785 
4786 static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'G','I','F', 0};
4787 static const WCHAR gif_extension[] = {'*','.','G','I','F',0};
4788 static const WCHAR gif_mimetype[] = {'i','m','a','g','e','/','g','i','f', 0};
4789 static const WCHAR gif_format[] = {'G','I','F',0};
4790 static const BYTE gif_sig_pattern[12] = "GIF87aGIF89a";
4791 static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4792 
4793 static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0};
4794 static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0};
4795 static const WCHAR tiff_mimetype[] = {'i','m','a','g','e','/','t','i','f','f', 0};
4796 static const WCHAR tiff_format[] = {'T','I','F','F',0};
4797 static const BYTE tiff_sig_pattern[] = {0x49,0x49,42,0,0x4d,0x4d,0,42};
4798 static const BYTE tiff_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4799 
4800 static const WCHAR emf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'E','M','F', 0};
4801 static const WCHAR emf_extension[] = {'*','.','E','M','F',0};
4802 static const WCHAR emf_mimetype[] = {'i','m','a','g','e','/','x','-','e','m','f', 0};
4803 static const WCHAR emf_format[] = {'E','M','F',0};
4804 static const BYTE emf_sig_pattern[] = { 0x01, 0x00, 0x00, 0x00 };
4805 static const BYTE emf_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4806 
4807 static const WCHAR wmf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'W','M','F', 0};
4808 static const WCHAR wmf_extension[] = {'*','.','W','M','F',0};
4809 static const WCHAR wmf_mimetype[] = {'i','m','a','g','e','/','x','-','w','m','f', 0};
4810 static const WCHAR wmf_format[] = {'W','M','F',0};
4811 static const BYTE wmf_sig_pattern[] = { 0xd7, 0xcd };
4812 static const BYTE wmf_sig_mask[] = { 0xFF, 0xFF };
4813 
4814 static const WCHAR png_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'P','N','G', 0};
4815 static const WCHAR png_extension[] = {'*','.','P','N','G',0};
4816 static const WCHAR png_mimetype[] = {'i','m','a','g','e','/','p','n','g', 0};
4817 static const WCHAR png_format[] = {'P','N','G',0};
4818 static const BYTE png_sig_pattern[] = { 137, 80, 78, 71, 13, 10, 26, 10, };
4819 static const BYTE png_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4820 
4821 static const WCHAR ico_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'I','C','O', 0};
4822 static const WCHAR ico_extension[] = {'*','.','I','C','O',0};
4823 static const WCHAR ico_mimetype[] = {'i','m','a','g','e','/','x','-','i','c','o','n', 0};
4824 static const WCHAR ico_format[] = {'I','C','O',0};
4825 static const BYTE ico_sig_pattern[] = { 0x00, 0x00, 0x01, 0x00 };
4826 static const BYTE ico_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4827 
4828 static const struct image_codec codecs[NUM_CODECS] = {
4829     {
4830         { /* BMP */
4831             /* Clsid */              { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4832             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4833             /* CodecName */          bmp_codecname,
4834             /* DllName */            NULL,
4835             /* FormatDescription */  bmp_format,
4836             /* FilenameExtension */  bmp_extension,
4837             /* MimeType */           bmp_mimetype,
4838             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4839             /* Version */            1,
4840             /* SigCount */           1,
4841             /* SigSize */            2,
4842             /* SigPattern */         bmp_sig_pattern,
4843             /* SigMask */            bmp_sig_mask,
4844         },
4845         encode_image_BMP,
4846         decode_image_bmp,
4847         select_frame_wic
4848     },
4849     {
4850         { /* JPEG */
4851             /* Clsid */              { 0x557cf401, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4852             /* FormatID */           { 0xb96b3caeU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4853             /* CodecName */          jpeg_codecname,
4854             /* DllName */            NULL,
4855             /* FormatDescription */  jpeg_format,
4856             /* FilenameExtension */  jpeg_extension,
4857             /* MimeType */           jpeg_mimetype,
4858             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4859             /* Version */            1,
4860             /* SigCount */           1,
4861             /* SigSize */            2,
4862             /* SigPattern */         jpeg_sig_pattern,
4863             /* SigMask */            jpeg_sig_mask,
4864         },
4865         encode_image_jpeg,
4866         decode_image_jpeg,
4867         select_frame_wic
4868     },
4869     {
4870         { /* GIF */
4871             /* Clsid */              { 0x557cf402, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4872             /* FormatID */           { 0xb96b3cb0U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4873             /* CodecName */          gif_codecname,
4874             /* DllName */            NULL,
4875             /* FormatDescription */  gif_format,
4876             /* FilenameExtension */  gif_extension,
4877             /* MimeType */           gif_mimetype,
4878             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4879             /* Version */            1,
4880             /* SigCount */           2,
4881             /* SigSize */            6,
4882             /* SigPattern */         gif_sig_pattern,
4883             /* SigMask */            gif_sig_mask,
4884         },
4885         encode_image_gif,
4886         decode_image_gif,
4887         select_frame_gif
4888     },
4889     {
4890         { /* TIFF */
4891             /* Clsid */              { 0x557cf405, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4892             /* FormatID */           { 0xb96b3cb1U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4893             /* CodecName */          tiff_codecname,
4894             /* DllName */            NULL,
4895             /* FormatDescription */  tiff_format,
4896             /* FilenameExtension */  tiff_extension,
4897             /* MimeType */           tiff_mimetype,
4898             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4899             /* Version */            1,
4900             /* SigCount */           2,
4901             /* SigSize */            4,
4902             /* SigPattern */         tiff_sig_pattern,
4903             /* SigMask */            tiff_sig_mask,
4904         },
4905         encode_image_tiff,
4906         decode_image_tiff,
4907         select_frame_wic
4908     },
4909     {
4910         { /* EMF */
4911             /* Clsid */              { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4912             /* FormatID */           { 0xb96b3cacU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4913             /* CodecName */          emf_codecname,
4914             /* DllName */            NULL,
4915             /* FormatDescription */  emf_format,
4916             /* FilenameExtension */  emf_extension,
4917             /* MimeType */           emf_mimetype,
4918             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
4919             /* Version */            1,
4920             /* SigCount */           1,
4921             /* SigSize */            4,
4922             /* SigPattern */         emf_sig_pattern,
4923             /* SigMask */            emf_sig_mask,
4924         },
4925         NULL,
4926         decode_image_emf,
4927         NULL
4928     },
4929     {
4930         { /* WMF */
4931             /* Clsid */              { 0x557cf404, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4932             /* FormatID */           { 0xb96b3cadU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4933             /* CodecName */          wmf_codecname,
4934             /* DllName */            NULL,
4935             /* FormatDescription */  wmf_format,
4936             /* FilenameExtension */  wmf_extension,
4937             /* MimeType */           wmf_mimetype,
4938             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
4939             /* Version */            1,
4940             /* SigCount */           1,
4941             /* SigSize */            2,
4942             /* SigPattern */         wmf_sig_pattern,
4943             /* SigMask */            wmf_sig_mask,
4944         },
4945         NULL,
4946         decode_image_wmf,
4947         NULL
4948     },
4949     {
4950         { /* PNG */
4951             /* Clsid */              { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4952             /* FormatID */           { 0xb96b3cafU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4953             /* CodecName */          png_codecname,
4954             /* DllName */            NULL,
4955             /* FormatDescription */  png_format,
4956             /* FilenameExtension */  png_extension,
4957             /* MimeType */           png_mimetype,
4958             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4959             /* Version */            1,
4960             /* SigCount */           1,
4961             /* SigSize */            8,
4962             /* SigPattern */         png_sig_pattern,
4963             /* SigMask */            png_sig_mask,
4964         },
4965         encode_image_png,
4966         decode_image_png,
4967         select_frame_wic
4968     },
4969     {
4970         { /* ICO */
4971             /* Clsid */              { 0x557cf407, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4972             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4973             /* CodecName */          ico_codecname,
4974             /* DllName */            NULL,
4975             /* FormatDescription */  ico_format,
4976             /* FilenameExtension */  ico_extension,
4977             /* MimeType */           ico_mimetype,
4978             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4979             /* Version */            1,
4980             /* SigCount */           1,
4981             /* SigSize */            4,
4982             /* SigPattern */         ico_sig_pattern,
4983             /* SigMask */            ico_sig_mask,
4984         },
4985         NULL,
4986         decode_image_icon,
4987         select_frame_wic
4988     },
4989 };
4990 
4991 /*****************************************************************************
4992  * GdipGetImageDecodersSize [GDIPLUS.@]
4993  */
4994 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
4995 {
4996     int decoder_count=0;
4997     int i;
4998     TRACE("%p %p\n", numDecoders, size);
4999 
5000     if (!numDecoders || !size)
5001         return InvalidParameter;
5002 
5003     for (i=0; i<NUM_CODECS; i++)
5004     {
5005         if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
5006             decoder_count++;
5007     }
5008 
5009     *numDecoders = decoder_count;
5010     *size = decoder_count * sizeof(ImageCodecInfo);
5011 
5012     return Ok;
5013 }
5014 
5015 /*****************************************************************************
5016  * GdipGetImageDecoders [GDIPLUS.@]
5017  */
5018 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
5019 {
5020     int i, decoder_count=0;
5021     TRACE("%u %u %p\n", numDecoders, size, decoders);
5022 
5023     if (!decoders ||
5024         size != numDecoders * sizeof(ImageCodecInfo))
5025         return GenericError;
5026 
5027     for (i=0; i<NUM_CODECS; i++)
5028     {
5029         if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
5030         {
5031             if (decoder_count == numDecoders) return GenericError;
5032             memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
5033             decoder_count++;
5034         }
5035     }
5036 
5037     if (decoder_count < numDecoders) return GenericError;
5038 
5039     return Ok;
5040 }
5041 
5042 /*****************************************************************************
5043  * GdipGetImageEncodersSize [GDIPLUS.@]
5044  */
5045 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
5046 {
5047     int encoder_count=0;
5048     int i;
5049     TRACE("%p %p\n", numEncoders, size);
5050 
5051     if (!numEncoders || !size)
5052         return InvalidParameter;
5053 
5054     for (i=0; i<NUM_CODECS; i++)
5055     {
5056         if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
5057             encoder_count++;
5058     }
5059 
5060     *numEncoders = encoder_count;
5061     *size = encoder_count * sizeof(ImageCodecInfo);
5062 
5063     return Ok;
5064 }
5065 
5066 /*****************************************************************************
5067  * GdipGetImageEncoders [GDIPLUS.@]
5068  */
5069 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
5070 {
5071     int i, encoder_count=0;
5072     TRACE("%u %u %p\n", numEncoders, size, encoders);
5073 
5074     if (!encoders ||
5075         size != numEncoders * sizeof(ImageCodecInfo))
5076         return GenericError;
5077 
5078     for (i=0; i<NUM_CODECS; i++)
5079     {
5080         if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
5081         {
5082             if (encoder_count == numEncoders) return GenericError;
5083             memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
5084             encoder_count++;
5085         }
5086     }
5087 
5088     if (encoder_count < numEncoders) return GenericError;
5089 
5090     return Ok;
5091 }
5092 
5093 GpStatus WINGDIPAPI GdipGetEncoderParameterListSize(GpImage *image,
5094     GDIPCONST CLSID* clsidEncoder, UINT *size)
5095 {
5096     static int calls;
5097 
5098     TRACE("(%p,%s,%p)\n", image, debugstr_guid(clsidEncoder), size);
5099 
5100     if(!(calls++))
5101         FIXME("not implemented\n");
5102 
5103     *size = 0;
5104 
5105     return NotImplemented;
5106 }
5107 
5108 static PixelFormat get_16bpp_format(HBITMAP hbm)
5109 {
5110     BITMAPV4HEADER bmh;
5111     HDC hdc;
5112     PixelFormat result;
5113 
5114     hdc = CreateCompatibleDC(NULL);
5115 
5116     memset(&bmh, 0, sizeof(bmh));
5117     bmh.bV4Size = sizeof(bmh);
5118     bmh.bV4Width = 1;
5119     bmh.bV4Height = 1;
5120     bmh.bV4V4Compression = BI_BITFIELDS;
5121     bmh.bV4BitCount = 16;
5122 
5123     GetDIBits(hdc, hbm, 0, 0, NULL, (BITMAPINFO*)&bmh, DIB_RGB_COLORS);
5124 
5125     if (bmh.bV4RedMask == 0x7c00 &&
5126         bmh.bV4GreenMask == 0x3e0 &&
5127         bmh.bV4BlueMask == 0x1f)
5128     {
5129         result = PixelFormat16bppRGB555;
5130     }
5131     else if (bmh.bV4RedMask == 0xf800 &&
5132         bmh.bV4GreenMask == 0x7e0 &&
5133         bmh.bV4BlueMask == 0x1f)
5134     {
5135         result = PixelFormat16bppRGB565;
5136     }
5137     else
5138     {
5139         FIXME("unrecognized bitfields %x,%x,%x\n", bmh.bV4RedMask,
5140             bmh.bV4GreenMask, bmh.bV4BlueMask);
5141         result = PixelFormatUndefined;
5142     }
5143 
5144     DeleteDC(hdc);
5145 
5146     return result;
5147 }
5148 
5149 /*****************************************************************************
5150  * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
5151  */
5152 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
5153 {
5154     BITMAP bm;
5155     GpStatus retval;
5156     PixelFormat format;
5157     BitmapData lockeddata;
5158 
5159     TRACE("%p %p %p\n", hbm, hpal, bitmap);
5160 
5161     if(!hbm || !bitmap)
5162         return InvalidParameter;
5163 
5164     if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
5165             return InvalidParameter;
5166 
5167     /* TODO: Figure out the correct format for 16, 32, 64 bpp */
5168     switch(bm.bmBitsPixel) {
5169         case 1:
5170             format = PixelFormat1bppIndexed;
5171             break;
5172         case 4:
5173             format = PixelFormat4bppIndexed;
5174             break;
5175         case 8:
5176             format = PixelFormat8bppIndexed;
5177             break;
5178         case 16:
5179             format = get_16bpp_format(hbm);
5180             if (format == PixelFormatUndefined)
5181                 return InvalidParameter;
5182             break;
5183         case 24:
5184             format = PixelFormat24bppRGB;
5185             break;
5186         case 32:
5187             format = PixelFormat32bppRGB;
5188             break;
5189         case 48:
5190             format = PixelFormat48bppRGB;
5191             break;
5192         default:
5193             FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
5194             return InvalidParameter;
5195     }
5196 
5197     retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, 0,
5198         format, NULL, bitmap);
5199 
5200     if (retval == Ok)
5201     {
5202         retval = GdipBitmapLockBits(*bitmap, NULL, ImageLockModeWrite,
5203             format, &lockeddata);
5204         if (retval == Ok)
5205         {
5206             HDC hdc;
5207             char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)];
5208             BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
5209             INT src_height;
5210 
5211             hdc = CreateCompatibleDC(NULL);
5212 
5213             pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
5214             pbmi->bmiHeader.biBitCount = 0;
5215 
5216             GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
5217 
5218             src_height = abs(pbmi->bmiHeader.biHeight);
5219             pbmi->bmiHeader.biHeight = -src_height;
5220 
5221             GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS);
5222 
5223             DeleteDC(hdc);
5224 
5225             GdipBitmapUnlockBits(*bitmap, &lockeddata);
5226         }
5227 
5228         if (retval == Ok && hpal)
5229         {
5230             PALETTEENTRY entry[256];
5231             ColorPalette *palette=NULL;
5232             int i, num_palette_entries;
5233 
5234             num_palette_entries = GetPaletteEntries(hpal, 0, 256, entry);
5235             if (!num_palette_entries)
5236                 retval = GenericError;
5237 
5238             palette = heap_alloc_zero(sizeof(ColorPalette) + sizeof(ARGB) * (num_palette_entries-1));
5239             if (!palette)
5240                 retval = OutOfMemory;
5241 
5242             if (retval == Ok)
5243             {
5244                 palette->Flags = 0;
5245                 palette->Count = num_palette_entries;
5246 
5247                 for (i=0; i<num_palette_entries; i++)
5248                 {
5249                     palette->Entries[i] = 0xff000000 | entry[i].peRed << 16 |
5250                                           entry[i].peGreen << 8 | entry[i].peBlue;
5251                 }
5252 
5253                 retval = GdipSetImagePalette(&(*bitmap)->image, palette);
5254             }
5255 
5256             heap_free(palette);
5257         }
5258 
5259         if (retval != Ok)
5260         {
5261             GdipDisposeImage(&(*bitmap)->image);
5262             *bitmap = NULL;
5263         }
5264     }
5265 
5266     return retval;
5267 }
5268 
5269 /*****************************************************************************
5270  * GdipCreateEffect [GDIPLUS.@]
5271  */
5272 GpStatus WINGDIPAPI GdipCreateEffect(const GUID guid, CGpEffect **effect)
5273 {
5274     FIXME("(%s, %p): stub\n", debugstr_guid(&guid), effect);
5275 
5276     if(!effect)
5277         return InvalidParameter;
5278 
5279     *effect = NULL;
5280 
5281     return NotImplemented;
5282 }
5283 
5284 /*****************************************************************************
5285  * GdipDeleteEffect [GDIPLUS.@]
5286  */
5287 GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect)
5288 {
5289     FIXME("(%p): stub\n", effect);
5290     /* note: According to Jose Roca's GDI+ Docs, this is not implemented
5291      * in Windows's gdiplus */
5292     return NotImplemented;
5293 }
5294 
5295 /*****************************************************************************
5296  * GdipSetEffectParameters [GDIPLUS.@]
5297  */
5298 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
5299     const VOID *params, const UINT size)
5300 {
5301     static int calls;
5302 
5303     TRACE("(%p,%p,%u)\n", effect, params, size);
5304 
5305     if(!(calls++))
5306         FIXME("not implemented\n");
5307 
5308     return NotImplemented;
5309 }
5310 
5311 /*****************************************************************************
5312  * GdipGetImageFlags [GDIPLUS.@]
5313  */
5314 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
5315 {
5316     TRACE("%p %p\n", image, flags);
5317 
5318     if(!image || !flags)
5319         return InvalidParameter;
5320 
5321     *flags = image->flags;
5322 
5323     return Ok;
5324 }
5325 
5326 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param)
5327 {
5328     TRACE("(%d, %p)\n", control, param);
5329 
5330     switch(control){
5331         case TestControlForceBilinear:
5332             if(param)
5333                 FIXME("TestControlForceBilinear not handled\n");
5334             break;
5335         case TestControlNoICM:
5336             if(param)
5337                 FIXME("TestControlNoICM not handled\n");
5338             break;
5339         case TestControlGetBuildNumber:
5340             *((DWORD*)param) = 3102;
5341             break;
5342     }
5343 
5344     return Ok;
5345 }
5346 
5347 GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image)
5348 {
5349     TRACE("%p\n", image);
5350 
5351     return Ok;
5352 }
5353 
5354 /*****************************************************************************
5355  * GdipGetImageThumbnail [GDIPLUS.@]
5356  */
5357 GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height,
5358                             GpImage **ret_image, GetThumbnailImageAbort cb,
5359                             VOID * cb_data)
5360 {
5361     GpStatus stat;
5362     GpGraphics *graphics;
5363     UINT srcwidth, srcheight;
5364 
5365     TRACE("(%p %u %u %p %p %p)\n",
5366         image, width, height, ret_image, cb, cb_data);
5367 
5368     if (!image || !ret_image)
5369         return InvalidParameter;
5370 
5371     if (!width) width = 120;
5372     if (!height) height = 120;
5373 
5374     GdipGetImageWidth(image, &srcwidth);
5375     GdipGetImageHeight(image, &srcheight);
5376 
5377     stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
5378         NULL, (GpBitmap**)ret_image);
5379 
5380     if (stat == Ok)
5381     {
5382         stat = GdipGetImageGraphicsContext(*ret_image, &graphics);
5383 
5384         if (stat == Ok)
5385         {
5386             stat = GdipDrawImageRectRectI(graphics, image,
5387                 0, 0, width, height, 0, 0, srcwidth, srcheight, UnitPixel,
5388                 NULL, NULL, NULL);
5389 
5390             GdipDeleteGraphics(graphics);
5391         }
5392 
5393         if (stat != Ok)
5394         {
5395             GdipDisposeImage(*ret_image);
5396             *ret_image = NULL;
5397         }
5398     }
5399 
5400     return stat;
5401 }
5402 
5403 /*****************************************************************************
5404  * GdipImageRotateFlip [GDIPLUS.@]
5405  */
5406 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
5407 {
5408     GpBitmap *new_bitmap;
5409     GpBitmap *bitmap;
5410     int bpp, bytesperpixel;
5411     BOOL rotate_90, flip_x, flip_y;
5412     int src_x_offset, src_y_offset;
5413     LPBYTE src_origin;
5414     UINT x, y, width, height;
5415     BitmapData src_lock, dst_lock;
5416     GpStatus stat;
5417     BOOL unlock;
5418 
5419     TRACE("(%p, %u)\n", image, type);
5420 
5421     if (!image)
5422         return InvalidParameter;
5423     if (!image_lock(image, &unlock))
5424         return ObjectBusy;
5425 
5426     rotate_90 = type&1;
5427     flip_x = (type&6) == 2 || (type&6) == 4;
5428     flip_y = (type&3) == 1 || (type&3) == 2;
5429 
5430     if (image->type != ImageTypeBitmap)
5431     {
5432         FIXME("Not implemented for type %i\n", image->type);
5433         image_unlock(image, unlock);
5434         return NotImplemented;
5435     }
5436 
5437     bitmap = (GpBitmap*)image;
5438     bpp = PIXELFORMATBPP(bitmap->format);
5439 
5440     if (bpp < 8)
5441     {
5442         FIXME("Not implemented for %i bit images\n", bpp);
5443         image_unlock(image, unlock);
5444         return NotImplemented;
5445     }
5446 
5447     if (rotate_90)
5448     {
5449         width = bitmap->height;
5450         height = bitmap->width;
5451     }
5452     else
5453     {
5454         width = bitmap->width;
5455         height = bitmap->height;
5456     }
5457 
5458     bytesperpixel = bpp/8;
5459 
5460     stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap);
5461 
5462     if (stat != Ok)
5463     {
5464         image_unlock(image, unlock);
5465         return stat;
5466     }
5467 
5468     stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format, &src_lock);
5469 
5470     if (stat == Ok)
5471     {
5472         stat = GdipBitmapLockBits(new_bitmap, NULL, ImageLockModeWrite, bitmap->format, &dst_lock);
5473 
5474         if (stat == Ok)
5475         {
5476             LPBYTE src_row, src_pixel;
5477             LPBYTE dst_row, dst_pixel;
5478 
5479             src_origin = src_lock.Scan0;
5480             if (flip_x) src_origin += bytesperpixel * (bitmap->width - 1);
5481             if (flip_y) src_origin += src_lock.Stride * (bitmap->height - 1);
5482 
5483             if (rotate_90)
5484             {
5485                 if (flip_y) src_x_offset = -src_lock.Stride;
5486                 else src_x_offset = src_lock.Stride;
5487                 if (flip_x) src_y_offset = -bytesperpixel;
5488                 else src_y_offset = bytesperpixel;
5489             }
5490             else
5491             {
5492                 if (flip_x) src_x_offset = -bytesperpixel;
5493                 else src_x_offset = bytesperpixel;
5494                 if (flip_y) src_y_offset = -src_lock.Stride;
5495                 else src_y_offset = src_lock.Stride;
5496             }
5497 
5498             src_row = src_origin;
5499             dst_row = dst_lock.Scan0;
5500             for (y=0; y<height; y++)
5501             {
5502                 src_pixel = src_row;
5503                 dst_pixel = dst_row;
5504                 for (x=0; x<width; x++)
5505                 {
5506                     /* FIXME: This could probably be faster without memcpy. */
5507                     memcpy(dst_pixel, src_pixel, bytesperpixel);
5508                     dst_pixel += bytesperpixel;
5509                     src_pixel += src_x_offset;
5510                 }
5511                 src_row += src_y_offset;
5512                 dst_row += dst_lock.Stride;
5513             }
5514 
5515             GdipBitmapUnlockBits(new_bitmap, &dst_lock);
5516         }
5517 
5518         GdipBitmapUnlockBits(bitmap, &src_lock);
5519     }
5520 
5521     if (stat == Ok)
5522         move_bitmap(bitmap, new_bitmap, FALSE);
5523     else
5524         GdipDisposeImage(&new_bitmap->image);
5525 
5526     image_unlock(image, unlock);
5527     return stat;
5528 }
5529 
5530 /*****************************************************************************
5531  * GdipImageSetAbort [GDIPLUS.@]
5532  */
5533 GpStatus WINGDIPAPI GdipImageSetAbort(GpImage *image, GdiplusAbort *pabort)
5534 {
5535     TRACE("(%p, %p)\n", image, pabort);
5536 
5537     if (!image)
5538         return InvalidParameter;
5539 
5540     if (pabort)
5541         FIXME("Abort callback is not supported.\n");
5542 
5543     return Ok;
5544 }
5545 
5546 /*****************************************************************************
5547  * GdipBitmapConvertFormat [GDIPLUS.@]
5548  */
5549 GpStatus WINGDIPAPI GdipBitmapConvertFormat(GpBitmap *bitmap, PixelFormat format, DitherType dithertype,
5550     PaletteType palettetype, ColorPalette *palette, REAL alphathreshold)
5551 {
5552     FIXME("(%p, 0x%08x, %d, %d, %p, %f): stub\n", bitmap, format, dithertype, palettetype, palette, alphathreshold);
5553     return NotImplemented;
5554 }
5555 
5556 static void set_histogram_point_argb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5557 {
5558     ch0[ color >> 24        ]++;
5559     ch1[(color >> 16) & 0xff]++;
5560     ch2[(color >>  8) & 0xff]++;
5561     ch3[ color        & 0xff]++;
5562 }
5563 
5564 static void set_histogram_point_pargb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5565 {
5566     BYTE alpha = color >> 24;
5567 
5568     ch0[alpha]++;
5569     ch1[(((color >> 16) & 0xff) * alpha) / 0xff]++;
5570     ch2[(((color >>  8) & 0xff) * alpha) / 0xff]++;
5571     ch3[(( color        & 0xff) * alpha) / 0xff]++;
5572 }
5573 
5574 static void set_histogram_point_rgb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5575 {
5576     ch0[(color >> 16) & 0xff]++;
5577     ch1[(color >>  8) & 0xff]++;
5578     ch2[ color        & 0xff]++;
5579 }
5580 
5581 static void set_histogram_point_gray(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5582 {
5583     ch0[(76 * ((color >> 16) & 0xff) + 150 * ((color >> 8) & 0xff) + 29 * (color & 0xff)) / 0xff]++;
5584 }
5585 
5586 static void set_histogram_point_b(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5587 {
5588     ch0[color & 0xff]++;
5589 }
5590 
5591 static void set_histogram_point_g(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5592 {
5593     ch0[(color >> 8) & 0xff]++;
5594 }
5595 
5596 static void set_histogram_point_r(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5597 {
5598     ch0[(color >> 16) & 0xff]++;
5599 }
5600 
5601 static void set_histogram_point_a(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5602 {
5603     ch0[(color >> 24) & 0xff]++;
5604 }
5605 
5606 /*****************************************************************************
5607  * GdipBitmapGetHistogram [GDIPLUS.@]
5608  */
5609 GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap *bitmap, HistogramFormat format, UINT num_of_entries,
5610     UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5611 {
5612     static void (* const set_histogram_point[])(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) =
5613     {
5614         set_histogram_point_argb,
5615         set_histogram_point_pargb,
5616         set_histogram_point_rgb,
5617         set_histogram_point_gray,
5618         set_histogram_point_b,
5619         set_histogram_point_g,
5620         set_histogram_point_r,
5621         set_histogram_point_a,
5622     };
5623     UINT width, height, x, y;
5624 
5625     TRACE("(%p, %d, %u, %p, %p, %p, %p)\n", bitmap, format, num_of_entries,
5626         ch0, ch1, ch2, ch3);
5627 
5628     if (!bitmap || num_of_entries != 256)
5629         return InvalidParameter;
5630 
5631     /* Make sure passed channel pointers match requested format */
5632     switch (format)
5633     {
5634     case HistogramFormatARGB:
5635     case HistogramFormatPARGB:
5636         if (!ch0 || !ch1 || !ch2 || !ch3)
5637             return InvalidParameter;
5638         memset(ch0, 0, num_of_entries * sizeof(UINT));
5639         memset(ch1, 0, num_of_entries * sizeof(UINT));
5640         memset(ch2, 0, num_of_entries * sizeof(UINT));
5641         memset(ch3, 0, num_of_entries * sizeof(UINT));
5642         break;
5643     case HistogramFormatRGB:
5644         if (!ch0 || !ch1 || !ch2 || ch3)
5645             return InvalidParameter;
5646         memset(ch0, 0, num_of_entries * sizeof(UINT));
5647         memset(ch1, 0, num_of_entries * sizeof(UINT));
5648         memset(ch2, 0, num_of_entries * sizeof(UINT));
5649         break;
5650     case HistogramFormatGray:
5651     case HistogramFormatB:
5652     case HistogramFormatG:
5653     case HistogramFormatR:
5654     case HistogramFormatA:
5655         if (!ch0 || ch1 || ch2 || ch3)
5656             return InvalidParameter;
5657         memset(ch0, 0, num_of_entries * sizeof(UINT));
5658         break;
5659     default:
5660         WARN("Invalid histogram format requested, %d\n", format);
5661         return InvalidParameter;
5662     }
5663 
5664     GdipGetImageWidth(&bitmap->image, &width);
5665     GdipGetImageHeight(&bitmap->image, &height);
5666 
5667     for (y = 0; y < height; y++)
5668         for (x = 0; x < width; x++)
5669         {
5670             ARGB color;
5671 
5672             GdipBitmapGetPixel(bitmap, x, y, &color);
5673             set_histogram_point[format](color, ch0, ch1, ch2, ch3);
5674         }
5675 
5676     return Ok;
5677 }
5678 
5679 /*****************************************************************************
5680  * GdipBitmapGetHistogramSize [GDIPLUS.@]
5681  */
5682 GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat format, UINT *num_of_entries)
5683 {
5684     TRACE("(%d, %p)\n", format, num_of_entries);
5685 
5686     if (!num_of_entries)
5687         return InvalidParameter;
5688 
5689     *num_of_entries = 256;
5690     return Ok;
5691 }
5692 
5693 static GpStatus create_optimal_palette(ColorPalette *palette, INT desired,
5694     BOOL transparent, GpBitmap *bitmap)
5695 {
5696     GpStatus status;
5697     BitmapData data;
5698     HRESULT hr;
5699     IWICImagingFactory *factory;
5700     IWICPalette *wic_palette;
5701 
5702     if (!bitmap) return InvalidParameter;
5703     if (palette->Count < desired) return GenericError;
5704 
5705     status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &data);
5706     if (status != Ok) return status;
5707 
5708     hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
5709     if (hr != S_OK)
5710     {
5711         GdipBitmapUnlockBits(bitmap, &data);
5712         return hresult_to_status(hr);
5713     }
5714 
5715     hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
5716     if (hr == S_OK)
5717     {
5718         IWICBitmap *bitmap;
5719 
5720         /* PixelFormat24bppRGB actually stores the bitmap bits as BGR. */
5721         hr = IWICImagingFactory_CreateBitmapFromMemory(factory, data.Width, data.Height,
5722                 &GUID_WICPixelFormat24bppBGR, data.Stride, data.Stride * data.Width, data.Scan0, &bitmap);
5723         if (hr == S_OK)
5724         {
5725             hr = IWICPalette_InitializeFromBitmap(wic_palette, (IWICBitmapSource *)bitmap, desired, transparent);
5726             if (hr == S_OK)
5727             {
5728                 palette->Flags = 0;
5729                 IWICPalette_GetColorCount(wic_palette, &palette->Count);
5730                 IWICPalette_GetColors(wic_palette, palette->Count, palette->Entries, &palette->Count);
5731             }
5732 
5733             IWICBitmap_Release(bitmap);
5734         }
5735 
5736         IWICPalette_Release(wic_palette);
5737     }
5738 
5739     IWICImagingFactory_Release(factory);
5740     GdipBitmapUnlockBits(bitmap, &data);
5741 
5742     return hresult_to_status(hr);
5743 }
5744 
5745 /*****************************************************************************
5746  * GdipInitializePalette [GDIPLUS.@]
5747  */
5748 GpStatus WINGDIPAPI GdipInitializePalette(ColorPalette *palette,
5749     PaletteType type, INT desired, BOOL transparent, GpBitmap *bitmap)
5750 {
5751     TRACE("(%p,%d,%d,%d,%p)\n", palette, type, desired, transparent, bitmap);
5752 
5753     if (!palette) return InvalidParameter;
5754 
5755     switch (type)
5756     {
5757     case PaletteTypeCustom:
5758         return Ok;
5759 
5760     case PaletteTypeOptimal:
5761         return create_optimal_palette(palette, desired, transparent, bitmap);
5762 
5763     /* WIC palette type enumeration matches these gdiplus enums */
5764     case PaletteTypeFixedBW:
5765     case PaletteTypeFixedHalftone8:
5766     case PaletteTypeFixedHalftone27:
5767     case PaletteTypeFixedHalftone64:
5768     case PaletteTypeFixedHalftone125:
5769     case PaletteTypeFixedHalftone216:
5770     case PaletteTypeFixedHalftone252:
5771     case PaletteTypeFixedHalftone256:
5772     {
5773         ColorPalette *wic_palette;
5774         GpStatus status = Ok;
5775 
5776         wic_palette = get_palette(NULL, type);
5777         if (!wic_palette) return OutOfMemory;
5778 
5779         if (palette->Count >= wic_palette->Count)
5780         {
5781             palette->Flags = wic_palette->Flags;
5782             palette->Count = wic_palette->Count;
5783             memcpy(palette->Entries, wic_palette->Entries, wic_palette->Count * sizeof(wic_palette->Entries[0]));
5784         }
5785         else
5786             status = GenericError;
5787 
5788         heap_free(wic_palette);
5789 
5790         return status;
5791     }
5792 
5793     default:
5794         FIXME("unknown palette type %d\n", type);
5795         break;
5796     }
5797 
5798     return InvalidParameter;
5799 }
5800