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