wined3d: Rename CreateAdditionalSwapChain to CreateSwapChain.
[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
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "wingdi.h"
25
26 #define COBJMACROS
27 #include "objbase.h"
28 #include "olectl.h"
29 #include "ole2.h"
30
31 #include "gdiplus.h"
32 #include "gdiplus_private.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
36
37 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
38
39 static INT ipicture_pixel_height(IPicture *pic)
40 {
41     HDC hdcref;
42     OLE_YSIZE_HIMETRIC y;
43
44     IPicture_get_Height(pic, &y);
45
46     hdcref = GetDC(0);
47
48     y = MulDiv(y, GetDeviceCaps(hdcref, LOGPIXELSY), INCH_HIMETRIC);
49     ReleaseDC(0, hdcref);
50
51     return y;
52 }
53
54 static INT ipicture_pixel_width(IPicture *pic)
55 {
56     HDC hdcref;
57     OLE_XSIZE_HIMETRIC x;
58
59     IPicture_get_Width(pic, &x);
60
61     hdcref = GetDC(0);
62
63     x = MulDiv(x, GetDeviceCaps(hdcref, LOGPIXELSX), INCH_HIMETRIC);
64
65     ReleaseDC(0, hdcref);
66
67     return x;
68 }
69
70 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
71     ARGB *color)
72 {
73     static int calls;
74     TRACE("%p %d %d %p\n", bitmap, x, y, color);
75
76     if(!bitmap || !color)
77         return InvalidParameter;
78
79     if(!(calls++))
80         FIXME("not implemented\n");
81
82     *color = 0xdeadbeef;
83
84     return NotImplemented;
85 }
86
87 /* This function returns a pointer to an array of pixels that represents the
88  * bitmap. The *entire* bitmap is locked according to the lock mode specified by
89  * flags.  It is correct behavior that a user who calls this function with write
90  * privileges can write to the whole bitmap (not just the area in rect).
91  *
92  * FIXME: only used portion of format is bits per pixel. */
93 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
94     UINT flags, PixelFormat format, BitmapData* lockeddata)
95 {
96     BOOL bm_is_selected;
97     INT stride, bitspp = PIXELFORMATBPP(format);
98     OLE_HANDLE hbm;
99     HDC hdc;
100     HBITMAP old = NULL;
101     BITMAPINFO *pbmi;
102     BYTE *buff = NULL;
103     UINT abs_height;
104     GpRect act_rect; /* actual rect to be used */
105
106     TRACE("%p %p %d %d %p\n", bitmap, rect, flags, format, lockeddata);
107
108     if(!lockeddata || !bitmap)
109         return InvalidParameter;
110
111     if(rect){
112         if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
113           (rect->Y + rect->Height > bitmap->height) || !flags)
114             return InvalidParameter;
115
116         act_rect = *rect;
117     }
118     else{
119         act_rect.X = act_rect.Y = 0;
120         act_rect.Width  = bitmap->width;
121         act_rect.Height = bitmap->height;
122     }
123
124     if(flags & ImageLockModeUserInputBuf)
125         return NotImplemented;
126
127     if(bitmap->lockmode)
128         return WrongState;
129
130     IPicture_get_Handle(bitmap->image.picture, &hbm);
131     IPicture_get_CurDC(bitmap->image.picture, &hdc);
132     bm_is_selected = (hdc != 0);
133
134     pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
135     if (!pbmi)
136         return OutOfMemory;
137     pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
138     pbmi->bmiHeader.biBitCount = 0;
139
140     if(!bm_is_selected){
141         hdc = CreateCompatibleDC(0);
142         old = SelectObject(hdc, (HBITMAP)hbm);
143     }
144
145     /* fill out bmi */
146     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
147
148     abs_height = abs(pbmi->bmiHeader.biHeight);
149     stride = pbmi->bmiHeader.biWidth * bitspp / 8;
150     stride = (stride + 3) & ~3;
151
152     buff = GdipAlloc(stride * abs_height);
153
154     pbmi->bmiHeader.biBitCount = bitspp;
155
156     if(buff)
157         GetDIBits(hdc, (HBITMAP)hbm, 0, abs_height, buff, pbmi, DIB_RGB_COLORS);
158
159     if(!bm_is_selected){
160         SelectObject(hdc, old);
161         DeleteDC(hdc);
162     }
163
164     if(!buff){
165         GdipFree(pbmi);
166         return OutOfMemory;
167     }
168
169     lockeddata->Width  = act_rect.Width;
170     lockeddata->Height = act_rect.Height;
171     lockeddata->PixelFormat = format;
172     lockeddata->Reserved = flags;
173
174     if(pbmi->bmiHeader.biHeight > 0){
175         lockeddata->Stride = -stride;
176         lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X +
177                              stride * (abs_height - 1 - act_rect.Y);
178     }
179     else{
180         lockeddata->Stride = stride;
181         lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X + stride * act_rect.Y;
182     }
183
184     bitmap->lockmode = flags;
185     bitmap->numlocks++;
186
187     bitmap->bitmapbits = buff;
188
189     GdipFree(pbmi);
190     return Ok;
191 }
192
193 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
194     BitmapData* lockeddata)
195 {
196     OLE_HANDLE hbm;
197     HDC hdc;
198     HBITMAP old = NULL;
199     BOOL bm_is_selected;
200     BITMAPINFO *pbmi;
201
202     if(!bitmap || !lockeddata)
203         return InvalidParameter;
204
205     if(!bitmap->lockmode)
206         return WrongState;
207
208     if(lockeddata->Reserved & ImageLockModeUserInputBuf)
209         return NotImplemented;
210
211     if(lockeddata->Reserved & ImageLockModeRead){
212         if(!(--bitmap->numlocks))
213             bitmap->lockmode = 0;
214
215         GdipFree(bitmap->bitmapbits);
216         bitmap->bitmapbits = NULL;
217         return Ok;
218     }
219
220     IPicture_get_Handle(bitmap->image.picture, &hbm);
221     IPicture_get_CurDC(bitmap->image.picture, &hdc);
222     bm_is_selected = (hdc != 0);
223
224     pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
225     pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
226     pbmi->bmiHeader.biBitCount = 0;
227
228     if(!bm_is_selected){
229         hdc = CreateCompatibleDC(0);
230         old = SelectObject(hdc, (HBITMAP)hbm);
231     }
232
233     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
234     pbmi->bmiHeader.biBitCount = PIXELFORMATBPP(lockeddata->PixelFormat);
235     SetDIBits(hdc, (HBITMAP)hbm, 0, abs(pbmi->bmiHeader.biHeight),
236               bitmap->bitmapbits, pbmi, DIB_RGB_COLORS);
237
238     if(!bm_is_selected){
239         SelectObject(hdc, old);
240         DeleteDC(hdc);
241     }
242
243     GdipFree(pbmi);
244     GdipFree(bitmap->bitmapbits);
245     bitmap->bitmapbits = NULL;
246     bitmap->lockmode = 0;
247
248     return Ok;
249 }
250
251 GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
252 {
253     IStream* stream;
254     HRESULT hr;
255     INT size;
256     LARGE_INTEGER move;
257     GpStatus stat = GenericError;
258
259     TRACE("%p, %p\n", image, cloneImage);
260
261     if (!image || !cloneImage)
262         return InvalidParameter;
263
264     hr = CreateStreamOnHGlobal(0, TRUE, &stream);
265     if (FAILED(hr))
266         return GenericError;
267
268     hr = IPicture_SaveAsFile(image->picture, stream, FALSE, &size);
269     if(FAILED(hr))
270     {
271         WARN("Failed to save image on stream\n");
272         goto out;
273     }
274
275     /* Set seek pointer back to the beginning of the picture */
276     move.QuadPart = 0;
277     hr = IStream_Seek(stream, move, STREAM_SEEK_SET, NULL);
278     if (FAILED(hr))
279         goto out;
280
281     stat = GdipLoadImageFromStream(stream, cloneImage);
282     if (stat != Ok) WARN("Failed to load image from stream\n");
283
284 out:
285     IStream_Release(stream);
286     return stat;
287 }
288
289 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
290     GpBitmap **bitmap)
291 {
292     GpStatus stat;
293     IStream *stream;
294
295     TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
296
297     if(!filename || !bitmap)
298         return InvalidParameter;
299
300     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
301
302     if(stat != Ok)
303         return stat;
304
305     stat = GdipCreateBitmapFromStream(stream, bitmap);
306
307     IStream_Release(stream);
308
309     return stat;
310 }
311
312 GpStatus WINGDIPAPI GdipCreateBitmapFromGdiDib(GDIPCONST BITMAPINFO* info,
313                                                VOID *bits, GpBitmap **bitmap)
314 {
315     DWORD height, stride;
316     PixelFormat format;
317
318     FIXME("(%p, %p, %p) - partially implemented\n", info, bits, bitmap);
319
320     height = abs(info->bmiHeader.biHeight);
321     stride = ((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) >> 3) & ~3;
322
323     if(info->bmiHeader.biHeight > 0) /* bottom-up */
324     {
325         bits = (BYTE*)bits + (height - 1) * stride;
326         stride = -stride;
327     }
328
329     switch(info->bmiHeader.biBitCount) {
330     case 1:
331         format = PixelFormat1bppIndexed;
332         break;
333     case 4:
334         format = PixelFormat4bppIndexed;
335         break;
336     case 8:
337         format = PixelFormat8bppIndexed;
338         break;
339     case 24:
340         format = PixelFormat24bppRGB;
341         break;
342     default:
343         FIXME("don't know how to handle %d bpp\n", info->bmiHeader.biBitCount);
344         *bitmap = NULL;
345         return InvalidParameter;
346     }
347
348     return GdipCreateBitmapFromScan0(info->bmiHeader.biWidth, height, stride, format,
349                                      bits, bitmap);
350
351 }
352
353 /* FIXME: no icm */
354 GpStatus WINGDIPAPI GdipCreateBitmapFromFileICM(GDIPCONST WCHAR* filename,
355     GpBitmap **bitmap)
356 {
357     TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
358
359     return GdipCreateBitmapFromFile(filename, bitmap);
360 }
361
362 GpStatus WINGDIPAPI GdipCreateBitmapFromResource(HINSTANCE hInstance,
363     GDIPCONST WCHAR* lpBitmapName, GpBitmap** bitmap)
364 {
365     HBITMAP hbm;
366     GpStatus stat = InvalidParameter;
367
368     TRACE("%p (%s) %p\n", hInstance, debugstr_w(lpBitmapName), bitmap);
369
370     if(!lpBitmapName || !bitmap)
371         return InvalidParameter;
372
373     /* load DIB */
374     hbm = (HBITMAP)LoadImageW(hInstance,lpBitmapName,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
375
376     if(hbm){
377         stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
378         DeleteObject(hbm);
379     }
380
381     return stat;
382 }
383
384 GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
385     HBITMAP* hbmReturn, ARGB background)
386 {
387     FIXME("stub\n");
388
389     hbmReturn = NULL;
390
391     return NotImplemented;
392 }
393
394 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
395     GpMetafile* metafile, BOOL* succ, EmfType emfType,
396     const WCHAR* description, GpMetafile** out_metafile)
397 {
398     static int calls;
399
400     if(!ref || !metafile || !out_metafile)
401         return InvalidParameter;
402
403     *succ = FALSE;
404     *out_metafile = NULL;
405
406     if(!(calls++))
407         FIXME("not implemented\n");
408
409     return NotImplemented;
410 }
411
412 /* FIXME: this should create a bitmap in the given size with the attributes
413  * (resolution etc.) of the graphics object */
414 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
415     GpGraphics* target, GpBitmap** bitmap)
416 {
417     static int calls;
418     GpStatus ret;
419
420     if(!target || !bitmap)
421         return InvalidParameter;
422
423     if(!(calls++))
424         FIXME("hacked stub\n");
425
426     ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat24bppRGB,
427                                     NULL, bitmap);
428
429     return ret;
430 }
431
432 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
433     PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
434 {
435     BITMAPFILEHEADER *bmfh;
436     BITMAPINFOHEADER *bmih;
437     BYTE *buff;
438     INT datalen, size;
439     IStream *stream;
440
441     TRACE("%d %d %d %d %p %p\n", width, height, stride, format, scan0, bitmap);
442
443     if (!bitmap) return InvalidParameter;
444
445     if(width <= 0 || height <= 0 || (scan0 && (stride % 4))){
446         *bitmap = NULL;
447         return InvalidParameter;
448     }
449
450     if(scan0 && !stride)
451         return InvalidParameter;
452
453     /* FIXME: windows allows negative stride (reads backwards from scan0) */
454     if(stride < 0){
455         FIXME("negative stride\n");
456         return InvalidParameter;
457     }
458
459     *bitmap = GdipAlloc(sizeof(GpBitmap));
460     if(!*bitmap)    return OutOfMemory;
461
462     if(stride == 0){
463         stride = width * (PIXELFORMATBPP(format) / 8);
464         stride = (stride + 3) & ~3;
465     }
466
467     datalen = abs(stride * height);
468     size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + datalen;
469     buff = GdipAlloc(size);
470     if(!buff){
471         GdipFree(*bitmap);
472         return OutOfMemory;
473     }
474
475     bmfh = (BITMAPFILEHEADER*) buff;
476     bmih = (BITMAPINFOHEADER*) (bmfh + 1);
477
478     bmfh->bfType    = (((WORD)'M') << 8) + (WORD)'B';
479     bmfh->bfSize    = size;
480     bmfh->bfOffBits = size - datalen;
481
482     bmih->biSize            = sizeof(BITMAPINFOHEADER);
483     bmih->biWidth           = width;
484     bmih->biHeight          = -height;
485     /* FIXME: use the rest of the data from format */
486     bmih->biBitCount        = PIXELFORMATBPP(format);
487     bmih->biCompression     = BI_RGB;
488     bmih->biSizeImage       = datalen;
489
490     if(scan0)
491         memcpy(bmih + 1, scan0, datalen);
492     else
493         memset(bmih + 1, 0, datalen);
494
495     if(CreateStreamOnHGlobal(buff, TRUE, &stream) != S_OK){
496         ERR("could not make stream\n");
497         GdipFree(*bitmap);
498         GdipFree(buff);
499         return GenericError;
500     }
501
502     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
503         (LPVOID*) &((*bitmap)->image.picture)) != S_OK){
504         TRACE("Could not load picture\n");
505         IStream_Release(stream);
506         GdipFree(*bitmap);
507         GdipFree(buff);
508         return GenericError;
509     }
510
511     (*bitmap)->image.type = ImageTypeBitmap;
512     (*bitmap)->image.flags = ImageFlagsNone;
513     (*bitmap)->width = width;
514     (*bitmap)->height = height;
515     (*bitmap)->format = format;
516
517     return Ok;
518 }
519
520 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
521     GpBitmap **bitmap)
522 {
523     GpStatus stat;
524
525     TRACE("%p %p\n", stream, bitmap);
526
527     stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
528
529     if(stat != Ok)
530         return stat;
531
532     if((*bitmap)->image.type != ImageTypeBitmap){
533         IPicture_Release((*bitmap)->image.picture);
534         GdipFree(bitmap);
535         return GenericError; /* FIXME: what error to return? */
536     }
537
538     return Ok;
539 }
540
541 /* FIXME: no icm */
542 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
543     GpBitmap **bitmap)
544 {
545     TRACE("%p %p\n", stream, bitmap);
546
547     return GdipCreateBitmapFromStream(stream, bitmap);
548 }
549
550 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
551 {
552     HDC hdc;
553
554     TRACE("%p\n", image);
555
556     if(!image)
557         return InvalidParameter;
558
559     IPicture_get_CurDC(image->picture, &hdc);
560     DeleteDC(hdc);
561     IPicture_Release(image->picture);
562     if (image->type == ImageTypeBitmap)
563         GdipFree(((GpBitmap*)image)->bitmapbits);
564     GdipFree(image);
565
566     return Ok;
567 }
568
569 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
570 {
571     if(!image || !item)
572         return InvalidParameter;
573
574     return NotImplemented;
575 }
576
577 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
578     GpUnit *srcUnit)
579 {
580     TRACE("%p %p %p\n", image, srcRect, srcUnit);
581
582     if(!image || !srcRect || !srcUnit)
583         return InvalidParameter;
584     if(image->type == ImageTypeMetafile){
585         *srcRect = ((GpMetafile*)image)->bounds;
586         *srcUnit = ((GpMetafile*)image)->unit;
587     }
588     else if(image->type == ImageTypeBitmap){
589         srcRect->X = srcRect->Y = 0.0;
590         srcRect->Width = (REAL) ((GpBitmap*)image)->width;
591         srcRect->Height = (REAL) ((GpBitmap*)image)->height;
592         *srcUnit = UnitPixel;
593     }
594     else{
595         srcRect->X = srcRect->Y = 0.0;
596         srcRect->Width = ipicture_pixel_width(image->picture);
597         srcRect->Height = ipicture_pixel_height(image->picture);
598         *srcUnit = UnitPixel;
599     }
600
601     TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
602           srcRect->Width, srcRect->Height, *srcUnit);
603
604     return Ok;
605 }
606
607 GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
608     REAL *height)
609 {
610     TRACE("%p %p %p\n", image, width, height);
611
612     if(!image || !height || !width)
613         return InvalidParameter;
614
615     if(image->type == ImageTypeMetafile){
616         HDC hdc = GetDC(0);
617
618         *height = convert_unit(hdc, ((GpMetafile*)image)->unit) *
619                         ((GpMetafile*)image)->bounds.Height;
620
621         *width = convert_unit(hdc, ((GpMetafile*)image)->unit) *
622                         ((GpMetafile*)image)->bounds.Width;
623
624         ReleaseDC(0, hdc);
625     }
626
627     else if(image->type == ImageTypeBitmap){
628         *height = ((GpBitmap*)image)->height;
629         *width = ((GpBitmap*)image)->width;
630     }
631     else{
632         *height = ipicture_pixel_height(image->picture);
633         *width = ipicture_pixel_width(image->picture);
634     }
635
636     TRACE("returning (%f, %f)\n", *height, *width);
637     return Ok;
638 }
639
640 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
641     GpGraphics **graphics)
642 {
643     HDC hdc;
644
645     TRACE("%p %p\n", image, graphics);
646
647     if(!image || !graphics)
648         return InvalidParameter;
649
650     if(image->type != ImageTypeBitmap){
651         FIXME("not implemented for image type %d\n", image->type);
652         return NotImplemented;
653     }
654
655     IPicture_get_CurDC(image->picture, &hdc);
656
657     if(!hdc){
658         hdc = CreateCompatibleDC(0);
659         IPicture_SelectPicture(image->picture, hdc, NULL, NULL);
660     }
661
662     return GdipCreateFromHDC(hdc, graphics);
663 }
664
665 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
666 {
667     TRACE("%p %p\n", image, height);
668
669     if(!image || !height)
670         return InvalidParameter;
671
672     if(image->type == ImageTypeMetafile){
673         HDC hdc = GetDC(0);
674
675         *height = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
676                         ((GpMetafile*)image)->bounds.Height);
677
678         ReleaseDC(0, hdc);
679     }
680     else if(image->type == ImageTypeBitmap)
681         *height = ((GpBitmap*)image)->height;
682     else
683         *height = ipicture_pixel_height(image->picture);
684
685     TRACE("returning %d\n", *height);
686
687     return Ok;
688 }
689
690 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
691 {
692     static int calls;
693
694     if(!image || !res)
695         return InvalidParameter;
696
697     if(!(calls++))
698         FIXME("not implemented\n");
699
700     return NotImplemented;
701 }
702
703 /* FIXME: test this function for non-bitmap types */
704 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
705 {
706     TRACE("%p %p\n", image, format);
707
708     if(!image || !format)
709         return InvalidParameter;
710
711     if(image->type != ImageTypeBitmap)
712         *format = PixelFormat24bppRGB;
713     else
714         *format = ((GpBitmap*) image)->format;
715
716     return Ok;
717 }
718
719 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
720 {
721     static int calls;
722
723     if(!image || !format)
724         return InvalidParameter;
725
726     if(!(calls++))
727         FIXME("not implemented\n");
728
729     return NotImplemented;
730 }
731
732 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
733 {
734     TRACE("%p %p\n", image, type);
735
736     if(!image || !type)
737         return InvalidParameter;
738
739     *type = image->type;
740
741     return Ok;
742 }
743
744 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
745 {
746     static int calls;
747
748     if(!image || !res)
749         return InvalidParameter;
750
751     if(!(calls++))
752         FIXME("not implemented\n");
753
754     return NotImplemented;
755 }
756
757 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
758 {
759     TRACE("%p %p\n", image, width);
760
761     if(!image || !width)
762         return InvalidParameter;
763
764     if(image->type == ImageTypeMetafile){
765         HDC hdc = GetDC(0);
766
767         *width = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
768                         ((GpMetafile*)image)->bounds.Width);
769
770         ReleaseDC(0, hdc);
771     }
772     else if(image->type == ImageTypeBitmap)
773         *width = ((GpBitmap*)image)->width;
774     else
775         *width = ipicture_pixel_width(image->picture);
776
777     TRACE("returning %d\n", *width);
778
779     return Ok;
780 }
781
782 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
783     MetafileHeader * header)
784 {
785     static int calls;
786
787     if(!metafile || !header)
788         return InvalidParameter;
789
790     if(!(calls++))
791         FIXME("not implemented\n");
792
793     return Ok;
794 }
795
796 GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
797     UINT num, PropertyItem* items)
798 {
799     static int calls;
800
801     if(!(calls++))
802         FIXME("not implemented\n");
803
804     return InvalidParameter;
805 }
806
807 GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT* num)
808 {
809     static int calls;
810
811     if(!(calls++))
812         FIXME("not implemented\n");
813
814     return InvalidParameter;
815 }
816
817 GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID* list)
818 {
819     static int calls;
820
821     if(!(calls++))
822         FIXME("not implemented\n");
823
824     return InvalidParameter;
825 }
826
827 GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID id, UINT size,
828     PropertyItem* buffer)
829 {
830     static int calls;
831
832     if(!(calls++))
833         FIXME("not implemented\n");
834
835     return InvalidParameter;
836 }
837
838 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID pid,
839     UINT* size)
840 {
841     static int calls;
842
843     TRACE("%p %x %p\n", image, pid, size);
844
845     if(!size || !image)
846         return InvalidParameter;
847
848     if(!(calls++))
849         FIXME("not implemented\n");
850
851     return NotImplemented;
852 }
853
854 GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT* size, UINT* num)
855 {
856     static int calls;
857
858     if(!(calls++))
859         FIXME("not implemented\n");
860
861     return InvalidParameter;
862 }
863
864 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
865     GDIPCONST GUID* dimensionID, UINT* count)
866 {
867     static int calls;
868
869     if(!image || !dimensionID || !count)
870         return InvalidParameter;
871
872     if(!(calls++))
873         FIXME("not implemented\n");
874
875     return NotImplemented;
876 }
877
878 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
879     UINT* count)
880 {
881     if(!image || !count)
882         return InvalidParameter;
883
884     *count = 1;
885
886     FIXME("stub\n");
887
888     return Ok;
889 }
890
891 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
892     GUID* dimensionIDs, UINT count)
893 {
894     static int calls;
895
896     if(!image || !dimensionIDs)
897         return InvalidParameter;
898
899     if(!(calls++))
900         FIXME("not implemented\n");
901
902     return Ok;
903 }
904
905 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image,
906     GDIPCONST GUID* dimensionID, UINT frameidx)
907 {
908     static int calls;
909
910     if(!image || !dimensionID)
911         return InvalidParameter;
912
913     if(!(calls++))
914         FIXME("not implemented\n");
915
916     return Ok;
917 }
918
919 GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename,
920                                           GpImage **image)
921 {
922     GpStatus stat;
923     IStream *stream;
924
925     TRACE("(%s) %p\n", debugstr_w(filename), image);
926
927     if (!filename || !image)
928         return InvalidParameter;
929
930     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
931
932     if (stat != Ok)
933         return stat;
934
935     stat = GdipLoadImageFromStream(stream, image);
936
937     IStream_Release(stream);
938
939     return stat;
940 }
941
942 /* FIXME: no icm handling */
943 GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage **image)
944 {
945     TRACE("(%s) %p\n", debugstr_w(filename), image);
946
947     return GdipLoadImageFromFile(filename, image);
948 }
949
950 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
951 {
952     IPicture *pic;
953     short type;
954
955     TRACE("%p %p\n", stream, image);
956
957     if(!stream || !image)
958         return InvalidParameter;
959
960     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
961         (LPVOID*) &pic) != S_OK){
962         TRACE("Could not load picture\n");
963         return GenericError;
964     }
965
966     IPicture_get_Type(pic, &type);
967
968     if(type == PICTYPE_BITMAP){
969         BITMAPINFO *pbmi;
970         BITMAPCOREHEADER* bmch;
971         OLE_HANDLE hbm;
972         HDC hdc;
973
974         pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
975         if (!pbmi)
976             return OutOfMemory;
977         *image = GdipAlloc(sizeof(GpBitmap));
978         if(!*image){
979             GdipFree(pbmi);
980             return OutOfMemory;
981         }
982         (*image)->type = ImageTypeBitmap;
983
984         (*((GpBitmap**) image))->width = ipicture_pixel_width(pic);
985         (*((GpBitmap**) image))->height = ipicture_pixel_height(pic);
986
987         /* get the pixel format */
988         IPicture_get_Handle(pic, &hbm);
989         IPicture_get_CurDC(pic, &hdc);
990
991         bmch = (BITMAPCOREHEADER*) (&pbmi->bmiHeader);
992         bmch->bcSize = sizeof(BITMAPCOREHEADER);
993
994         if(!hdc){
995             HBITMAP old;
996             hdc = CreateCompatibleDC(0);
997             old = SelectObject(hdc, (HBITMAP)hbm);
998             GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
999             SelectObject(hdc, old);
1000             DeleteDC(hdc);
1001         }
1002         else
1003             GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
1004
1005         (*((GpBitmap**) image))->format = (bmch->bcBitCount << 8) | PixelFormatGDI;
1006         GdipFree(pbmi);
1007     }
1008     else if(type == PICTYPE_METAFILE || type == PICTYPE_ENHMETAFILE){
1009         /* FIXME: missing initialization code */
1010         *image = GdipAlloc(sizeof(GpMetafile));
1011         if(!*image) return OutOfMemory;
1012         (*image)->type = ImageTypeMetafile;
1013     }
1014     else{
1015         *image = GdipAlloc(sizeof(GpImage));
1016         if(!*image) return OutOfMemory;
1017         (*image)->type = ImageTypeUnknown;
1018     }
1019
1020     (*image)->picture = pic;
1021     (*image)->flags   = ImageFlagsNone;
1022
1023     return Ok;
1024 }
1025
1026 /* FIXME: no ICM */
1027 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
1028 {
1029     TRACE("%p %p\n", stream, image);
1030
1031     return GdipLoadImageFromStream(stream, image);
1032 }
1033
1034 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
1035 {
1036     static int calls;
1037
1038     if(!image)
1039         return InvalidParameter;
1040
1041     if(!(calls++))
1042         FIXME("not implemented\n");
1043
1044     return NotImplemented;
1045 }
1046
1047 GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* item)
1048 {
1049     static int calls;
1050
1051     if(!(calls++))
1052         FIXME("not implemented\n");
1053
1054     return NotImplemented;
1055 }
1056
1057 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
1058                                         GDIPCONST CLSID *clsidEncoder,
1059                                         GDIPCONST EncoderParameters *encoderParams)
1060 {
1061     GpStatus stat;
1062     IStream *stream;
1063
1064     TRACE("%p (%s) %p %p\n", image, debugstr_w(filename), clsidEncoder, encoderParams);
1065
1066     if (!image || !filename|| !clsidEncoder)
1067         return InvalidParameter;
1068
1069     if (!(image->picture))
1070         return InvalidParameter;
1071
1072     stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
1073     if (stat != Ok)
1074         return GenericError;
1075
1076     stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
1077
1078     IStream_Release(stream);
1079     return stat;
1080 }
1081
1082 /*************************************************************************
1083  * Encoding functions -
1084  *   These functions encode an image in different image file formats.
1085  */
1086 #define BITMAP_FORMAT_BMP   0x4d42 /* "BM" */
1087 #define BITMAP_FORMAT_JPEG  0xd8ff
1088 #define BITMAP_FORMAT_GIF   0x4947
1089 #define BITMAP_FORMAT_PNG   0x5089
1090 #define BITMAP_FORMAT_APM   0xcdd7
1091
1092 static GpStatus encode_image_BMP(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
1093                                  void **output, unsigned int *output_size)
1094 {
1095     int num_palette_entries;
1096     BITMAPFILEHEADER *bmp_file_hdr;
1097     BITMAPINFO *bmp_info_hdr;
1098
1099     if (bitmap_info->bmiHeader.biClrUsed) {
1100         num_palette_entries = bitmap_info->bmiHeader.biClrUsed;
1101         if (num_palette_entries > 256) num_palette_entries = 256;
1102     } else {
1103         if (bitmap_info->bmiHeader.biBitCount <= 8)
1104             num_palette_entries = 1 << bitmap_info->bmiHeader.biBitCount;
1105         else
1106             num_palette_entries = 0;
1107     }
1108
1109     *output_size =
1110         sizeof(BITMAPFILEHEADER) +
1111         sizeof(BITMAPINFOHEADER) +
1112         num_palette_entries * sizeof(RGBQUAD) +
1113         bitmap_info->bmiHeader.biSizeImage;
1114
1115     *output = GdipAlloc(*output_size);
1116
1117     bmp_file_hdr = (BITMAPFILEHEADER*) *output;
1118     bmp_file_hdr->bfType = BITMAP_FORMAT_BMP;
1119     bmp_file_hdr->bfSize = *output_size;
1120     bmp_file_hdr->bfOffBits =
1121         sizeof(BITMAPFILEHEADER) +
1122         sizeof(BITMAPINFOHEADER) +
1123         num_palette_entries * sizeof (RGBQUAD);
1124
1125     bmp_info_hdr = (BITMAPINFO*) ((unsigned char*)(*output) + sizeof(BITMAPFILEHEADER));
1126     memcpy(bmp_info_hdr, bitmap_info, sizeof(BITMAPINFOHEADER) + num_palette_entries * sizeof(RGBQUAD));
1127     memcpy((unsigned char *)(*output) +
1128            sizeof(BITMAPFILEHEADER) +
1129            sizeof(BITMAPINFOHEADER) +
1130            num_palette_entries * sizeof(RGBQUAD),
1131            bitmap_bits, bitmap_info->bmiHeader.biSizeImage);
1132
1133     return Ok;
1134 }
1135
1136 typedef GpStatus encode_image_func(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
1137                                    void **output, unsigned int *output_size);
1138
1139 typedef enum {
1140     BMP,
1141     NUM_ENCODERS_SUPPORTED
1142 } ImageFormat;
1143
1144 static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED];
1145 static encode_image_func *const encode_image_funcs[NUM_ENCODERS_SUPPORTED] = {
1146     encode_image_BMP,
1147 };
1148
1149 /*****************************************************************************
1150  * GdipSaveImageToStream [GDIPLUS.@]
1151  */
1152 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
1153     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
1154 {
1155     GpStatus stat;
1156     HRESULT hr;
1157     short type;
1158     HBITMAP hbmp;
1159     HBITMAP old_hbmp;
1160     HDC hdc;
1161     int bm_is_selected;
1162     BITMAPINFO bmp_info;
1163     LPVOID bmp_bits;
1164     encode_image_func* encode_image;
1165     LPVOID output;
1166     unsigned int output_size;
1167     unsigned int dummy;
1168     int i;
1169
1170     old_hbmp = 0;
1171     output = NULL;
1172     output_size = 0;
1173
1174     TRACE("%p %p %p %p\n", image, stream, clsid, params);
1175
1176     if(!image || !stream)
1177         return InvalidParameter;
1178
1179     if (!image->picture)
1180         return GenericError;
1181
1182     hr = IPicture_get_Type(image->picture, &type);
1183     if (FAILED(hr) || type != PICTYPE_BITMAP)
1184         return GenericError;
1185
1186     /* select correct encoder */
1187     encode_image = NULL;
1188     for (i = 0; i < NUM_ENCODERS_SUPPORTED; i++) {
1189         if (IsEqualCLSID(clsid, &codecs[i].Clsid))
1190             encode_image = encode_image_funcs[i];
1191     }
1192     if (encode_image == NULL)
1193         return UnknownImageFormat;
1194
1195     /* extract underlying hbitmap representation from the IPicture */
1196     hr = IPicture_get_Handle(image->picture, (OLE_HANDLE*)&hbmp);
1197     if (FAILED(hr) || !hbmp)
1198         return GenericError;
1199     hr = IPicture_get_CurDC(image->picture, &hdc);
1200     if (FAILED(hr))
1201         return GenericError;
1202     bm_is_selected = (hdc != 0);
1203     if (!bm_is_selected) {
1204         hdc = CreateCompatibleDC(0);
1205         old_hbmp = SelectObject(hdc, hbmp);
1206     }
1207
1208     /* get bits from HBITMAP */
1209     bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
1210     bmp_info.bmiHeader.biBitCount = 0;
1211     GetDIBits(hdc, hbmp, 0, 0, NULL, &bmp_info, DIB_RGB_COLORS);
1212
1213     bmp_bits = GdipAlloc(bmp_info.bmiHeader.biSizeImage);
1214
1215     if (bmp_bits)
1216         GetDIBits(hdc, hbmp, 0, abs(bmp_info.bmiHeader.biHeight), bmp_bits, &bmp_info, DIB_RGB_COLORS);
1217
1218     if (!bm_is_selected) {
1219         SelectObject(hdc, old_hbmp);
1220         DeleteDC(hdc);
1221     }
1222
1223     if (!bmp_bits)
1224         return OutOfMemory;
1225
1226     stat = encode_image(bmp_bits, &bmp_info, &output, &output_size);
1227     if (stat == Ok)
1228         IStream_Write(stream, output, output_size, &dummy);
1229
1230     GdipFree(output);
1231     GdipFree(bmp_bits);
1232
1233     return stat;
1234 }
1235
1236 /*****************************************************************************
1237  * GdipSetImagePalette [GDIPLUS.@]
1238  */
1239 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
1240     GDIPCONST ColorPalette *palette)
1241 {
1242     static int calls;
1243
1244     if(!image || !palette)
1245         return InvalidParameter;
1246
1247     if(!(calls++))
1248         FIXME("not implemented\n");
1249
1250     return NotImplemented;
1251 }
1252
1253 /*************************************************************************
1254  * Encoders -
1255  *   Structures that represent which formats we support for encoding.
1256  */
1257
1258 /* ImageCodecInfo creation routines taken from libgdiplus */
1259 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */
1260 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
1261 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
1262 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
1263 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
1264 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
1265
1266 static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED] =
1267     {
1268         { /* BMP */
1269             /* Clsid */              { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
1270             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
1271             /* CodecName */          bmp_codecname,
1272             /* DllName */            NULL,
1273             /* FormatDescription */  bmp_format,
1274             /* FilenameExtension */  bmp_extension,
1275             /* MimeType */           bmp_mimetype,
1276             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
1277             /* Version */            1,
1278             /* SigCount */           1,
1279             /* SigSize */            2,
1280             /* SigPattern */         bmp_sig_pattern,
1281             /* SigMask */            bmp_sig_mask,
1282         },
1283     };
1284
1285 /*****************************************************************************
1286  * GdipGetImageDecodersSize [GDIPLUS.@]
1287  */
1288 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
1289 {
1290     FIXME("%p %p stub!\n", numDecoders, size);
1291
1292     if (!numDecoders || !size)
1293         return InvalidParameter;
1294
1295     *numDecoders = 0;
1296     *size = 0;
1297
1298     return Ok;
1299 }
1300
1301 /*****************************************************************************
1302  * GdipGetImageDecoders [GDIPLUS.@]
1303  */
1304 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
1305 {
1306     FIXME("%u %u %p stub!\n", numDecoders, size, decoders);
1307
1308     if (!decoders)
1309         return GenericError;
1310
1311     return NotImplemented;
1312 }
1313
1314 /*****************************************************************************
1315  * GdipGetImageEncodersSize [GDIPLUS.@]
1316  */
1317 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
1318 {
1319     TRACE("%p %p\n", numEncoders, size);
1320
1321     if (!numEncoders || !size)
1322         return InvalidParameter;
1323
1324     *numEncoders = NUM_ENCODERS_SUPPORTED;
1325     *size = sizeof (codecs);
1326
1327     return Ok;
1328 }
1329
1330 /*****************************************************************************
1331  * GdipGetImageEncoders [GDIPLUS.@]
1332  */
1333 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
1334 {
1335     TRACE("%u %u %p\n", numEncoders, size, encoders);
1336
1337     if (!encoders ||
1338         (numEncoders != NUM_ENCODERS_SUPPORTED) ||
1339         (size != sizeof (codecs)))
1340         return GenericError;
1341
1342     memcpy(encoders, codecs, sizeof (codecs));
1343
1344     return Ok;
1345 }
1346
1347 /*****************************************************************************
1348  * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
1349  */
1350 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
1351 {
1352     BITMAP bm;
1353     GpStatus retval;
1354     PixelFormat format;
1355
1356     TRACE("%p %p %p\n", hbm, hpal, bitmap);
1357
1358     if(!hbm || !bitmap)
1359         return InvalidParameter;
1360
1361     /* TODO: Support for device-dependent bitmaps */
1362     if(hpal){
1363         FIXME("no support for device-dependent bitmaps\n");
1364         return NotImplemented;
1365     }
1366
1367     if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
1368             return InvalidParameter;
1369
1370     /* TODO: Figure out the correct format for 16, 32, 64 bpp */
1371     switch(bm.bmBitsPixel) {
1372         case 1:
1373             format = PixelFormat1bppIndexed;
1374             break;
1375         case 4:
1376             format = PixelFormat4bppIndexed;
1377             break;
1378         case 8:
1379             format = PixelFormat8bppIndexed;
1380             break;
1381         case 24:
1382             format = PixelFormat24bppRGB;
1383             break;
1384         case 32:
1385             format = PixelFormat32bppRGB;
1386             break;
1387         case 48:
1388             format = PixelFormat48bppRGB;
1389             break;
1390         default:
1391             FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
1392             return InvalidParameter;
1393     }
1394
1395     retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, bm.bmWidthBytes,
1396         format, bm.bmBits, bitmap);
1397
1398     return retval;
1399 }
1400
1401 /*****************************************************************************
1402  * GdipSetEffectParameters [GDIPLUS.@]
1403  */
1404 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
1405     const VOID *params, const UINT size)
1406 {
1407     static int calls;
1408
1409     if(!(calls++))
1410         FIXME("not implemented\n");
1411
1412     return NotImplemented;
1413 }
1414
1415 /*****************************************************************************
1416  * GdipGetImageFlags [GDIPLUS.@]
1417  */
1418 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
1419 {
1420     TRACE("%p %p\n", image, flags);
1421
1422     if(!image || !flags)
1423         return InvalidParameter;
1424
1425     *flags = image->flags;
1426
1427     return Ok;
1428 }
1429
1430 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param)
1431 {
1432     TRACE("(%d, %p)\n", control, param);
1433
1434     switch(control){
1435         case TestControlForceBilinear:
1436             if(param)
1437                 FIXME("TestControlForceBilinear not handled\n");
1438             break;
1439         case TestControlNoICM:
1440             if(param)
1441                 FIXME("TestControlNoICM not handled\n");
1442             break;
1443         case TestControlGetBuildNumber:
1444             *((DWORD*)param) = 3102;
1445             break;
1446     }
1447
1448     return Ok;
1449 }
1450
1451 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
1452                             HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
1453                             MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
1454                             GpMetafile **metafile)
1455 {
1456     FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1457                                  frameUnit, debugstr_w(desc), metafile);
1458
1459     return NotImplemented;
1460 }
1461
1462 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
1463                             GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
1464                             GDIPCONST WCHAR *desc, GpMetafile **metafile)
1465 {
1466     FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1467                                  frameUnit, debugstr_w(desc), metafile);
1468
1469     return NotImplemented;
1470 }