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