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