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