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