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