gdiplus: Added GdipAddPathBezierI.
[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 typedef void ImageItemData;
38
39 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
40
41 static INT ipicture_pixel_height(IPicture *pic)
42 {
43     HDC hdcref;
44     OLE_YSIZE_HIMETRIC y;
45
46     IPicture_get_Height(pic, &y);
47
48     hdcref = GetDC(0);
49
50     y = (UINT)(((REAL)y) * ((REAL)GetDeviceCaps(hdcref, LOGPIXELSY)) /
51               ((REAL)INCH_HIMETRIC));
52     ReleaseDC(0, hdcref);
53
54     return y;
55 }
56
57 static INT ipicture_pixel_width(IPicture *pic)
58 {
59     HDC hdcref;
60     OLE_XSIZE_HIMETRIC x;
61
62     IPicture_get_Width(pic, &x);
63
64     hdcref = GetDC(0);
65
66     x = (UINT)(((REAL)x) * ((REAL)GetDeviceCaps(hdcref, LOGPIXELSX)) /
67               ((REAL)INCH_HIMETRIC));
68
69     ReleaseDC(0, hdcref);
70
71     return x;
72 }
73
74 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
75     ARGB *color)
76 {
77     static int calls;
78     TRACE("%p %d %d %p\n", bitmap, x, y, color);
79
80     if(!bitmap || !color)
81         return InvalidParameter;
82
83     if(!(calls++))
84         FIXME("not implemented\n");
85
86     *color = 0xdeadbeef;
87
88     return NotImplemented;
89 }
90
91 /* This function returns a pointer to an array of pixels that represents the
92  * bitmap. The *entire* bitmap is locked according to the lock mode specified by
93  * flags.  It is correct behavior that a user who calls this function with write
94  * privileges can write to the whole bitmap (not just the area in rect).
95  *
96  * FIXME: only used portion of format is bits per pixel. */
97 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
98     UINT flags, PixelFormat format, BitmapData* lockeddata)
99 {
100     BOOL bm_is_selected;
101     INT stride, bitspp = PIXELFORMATBPP(format);
102     OLE_HANDLE hbm;
103     HDC hdc;
104     HBITMAP old = NULL;
105     BITMAPINFO bmi;
106     BYTE *buff = NULL;
107     UINT abs_height;
108
109     TRACE("%p %p %d %d %p\n", bitmap, rect, flags, format, lockeddata);
110
111     if(!lockeddata || !bitmap || !rect)
112         return InvalidParameter;
113
114     if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
115        (rect->Y + rect->Height > bitmap->height) || !flags)
116         return InvalidParameter;
117
118     if(flags & ImageLockModeUserInputBuf)
119         return NotImplemented;
120
121     if((bitmap->lockmode & ImageLockModeWrite) || (bitmap->lockmode &&
122         (flags & ImageLockModeWrite)))
123         return WrongState;
124
125     IPicture_get_Handle(bitmap->image.picture, &hbm);
126     IPicture_get_CurDC(bitmap->image.picture, &hdc);
127     bm_is_selected = (hdc != 0);
128
129     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
130     bmi.bmiHeader.biBitCount = 0;
131
132     if(!bm_is_selected){
133         hdc = CreateCompatibleDC(0);
134         old = SelectObject(hdc, (HBITMAP)hbm);
135     }
136
137     /* fill out bmi */
138     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
139
140     abs_height = abs(bmi.bmiHeader.biHeight);
141     stride = bmi.bmiHeader.biWidth * bitspp / 8;
142     stride = (stride + 3) & ~3;
143
144     buff = GdipAlloc(stride * abs_height);
145
146     bmi.bmiHeader.biBitCount = bitspp;
147
148     if(buff)
149         GetDIBits(hdc, (HBITMAP)hbm, 0, abs_height, buff, &bmi, DIB_RGB_COLORS);
150
151     if(!bm_is_selected){
152         SelectObject(hdc, old);
153         DeleteDC(hdc);
154     }
155
156     if(!buff)
157         return OutOfMemory;
158
159     lockeddata->Width = rect->Width;
160     lockeddata->Height = rect->Height;
161     lockeddata->PixelFormat = format;
162     lockeddata->Reserved = flags;
163
164     if(bmi.bmiHeader.biHeight > 0){
165         lockeddata->Stride = -stride;
166         lockeddata->Scan0 = buff + (bitspp / 8) * rect->X +
167                             stride * (abs_height - 1 - rect->Y);
168     }
169     else{
170         lockeddata->Stride = stride;
171         lockeddata->Scan0 = buff + (bitspp / 8) * rect->X + stride * rect->Y;
172     }
173
174     bitmap->lockmode = flags;
175     bitmap->numlocks++;
176
177     if(flags & ImageLockModeWrite)
178         bitmap->bitmapbits = buff;
179
180     return Ok;
181 }
182
183 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
184     BitmapData* lockeddata)
185 {
186     OLE_HANDLE hbm;
187     HDC hdc;
188     HBITMAP old = NULL;
189     BOOL bm_is_selected;
190     BITMAPINFO bmi;
191
192     if(!bitmap || !lockeddata)
193         return InvalidParameter;
194
195     if(!bitmap->lockmode)
196         return WrongState;
197
198     if(lockeddata->Reserved & ImageLockModeUserInputBuf)
199         return NotImplemented;
200
201     if(lockeddata->Reserved & ImageLockModeRead){
202         if(!(--bitmap->numlocks))
203             bitmap->lockmode = 0;
204
205         GdipFree(lockeddata->Scan0);
206         return Ok;
207     }
208
209     IPicture_get_Handle(bitmap->image.picture, &hbm);
210     IPicture_get_CurDC(bitmap->image.picture, &hdc);
211     bm_is_selected = (hdc != 0);
212
213     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
214     bmi.bmiHeader.biBitCount = 0;
215
216     if(!bm_is_selected){
217         hdc = CreateCompatibleDC(0);
218         old = SelectObject(hdc, (HBITMAP)hbm);
219     }
220
221     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
222     bmi.bmiHeader.biBitCount = PIXELFORMATBPP(lockeddata->PixelFormat);
223     SetDIBits(hdc, (HBITMAP)hbm, 0, abs(bmi.bmiHeader.biHeight),
224               bitmap->bitmapbits, &bmi, DIB_RGB_COLORS);
225
226     if(!bm_is_selected){
227         SelectObject(hdc, old);
228         DeleteDC(hdc);
229     }
230
231     GdipFree(bitmap->bitmapbits);
232
233     return Ok;
234 }
235
236 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
237     GpBitmap **bitmap)
238 {
239     GpStatus stat;
240     IStream *stream;
241
242     if(!filename || !bitmap)
243         return InvalidParameter;
244
245     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
246
247     if(stat != Ok)
248         return stat;
249
250     stat = GdipCreateBitmapFromStream(stream, bitmap);
251
252     if(!stat)
253         IStream_Release(stream);
254
255     return stat;
256 }
257
258 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
259     GpMetafile* metafile, BOOL* succ, EmfType emfType,
260     const WCHAR* description, GpMetafile** out_metafile)
261 {
262     static int calls;
263
264     if(!ref || !metafile || !out_metafile)
265         return InvalidParameter;
266
267     *succ = FALSE;
268     *out_metafile = NULL;
269
270     if(!(calls++))
271         FIXME("not implemented\n");
272
273     return NotImplemented;
274 }
275
276 /* FIXME: this should create a bitmap in the given size with the attributes
277  * (resolution etc.) of the graphics object */
278 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
279     GpGraphics* target, GpBitmap** bitmap)
280 {
281     static int calls;
282     GpStatus ret;
283
284     if(!target || !bitmap)
285         return InvalidParameter;
286
287     if(!(calls++))
288         FIXME("hacked stub\n");
289
290     ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat24bppRGB,
291                                     NULL, bitmap);
292
293     return ret;
294 }
295
296 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
297     PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
298 {
299     BITMAPFILEHEADER *bmfh;
300     BITMAPINFOHEADER *bmih;
301     BYTE *buff;
302     INT datalen, size;
303     IStream *stream;
304
305     TRACE("%d %d %d %d %p %p\n", width, height, stride, format, scan0, bitmap);
306
307     if(!bitmap || width <= 0 || height <= 0 || (scan0 && (stride % 4))){
308         *bitmap = NULL;
309         return InvalidParameter;
310     }
311
312     if(scan0 && !stride)
313         return InvalidParameter;
314
315     /* FIXME: windows allows negative stride (reads backwards from scan0) */
316     if(stride < 0){
317         FIXME("negative stride\n");
318         return InvalidParameter;
319     }
320
321     *bitmap = GdipAlloc(sizeof(GpBitmap));
322     if(!*bitmap)    return OutOfMemory;
323
324     if(stride == 0){
325         stride = width * (PIXELFORMATBPP(format) / 8);
326         stride = (stride + 3) & ~3;
327     }
328
329     datalen = abs(stride * height);
330     size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + datalen;
331     buff = GdipAlloc(size);
332     if(!buff){
333         GdipFree(*bitmap);
334         return OutOfMemory;
335     }
336
337     bmfh = (BITMAPFILEHEADER*) buff;
338     bmih = (BITMAPINFOHEADER*) (bmfh + 1);
339
340     bmfh->bfType    = (((WORD)'M') << 8) + (WORD)'B';
341     bmfh->bfSize    = size;
342     bmfh->bfOffBits = size - datalen;
343
344     bmih->biSize            = sizeof(BITMAPINFOHEADER);
345     bmih->biWidth           = width;
346     bmih->biHeight          = -height;
347     /* FIXME: use the rest of the data from format */
348     bmih->biBitCount        = PIXELFORMATBPP(format);
349     bmih->biCompression     = BI_RGB;
350     bmih->biSizeImage       = datalen;
351
352     if(scan0)
353         memcpy(bmih + 1, scan0, datalen);
354     else
355         memset(bmih + 1, 0, datalen);
356
357     if(CreateStreamOnHGlobal(buff, TRUE, &stream) != S_OK){
358         ERR("could not make stream\n");
359         GdipFree(*bitmap);
360         GdipFree(buff);
361         return GenericError;
362     }
363
364     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
365         (LPVOID*) &((*bitmap)->image.picture)) != S_OK){
366         TRACE("Could not load picture\n");
367         IStream_Release(stream);
368         GdipFree(*bitmap);
369         GdipFree(buff);
370         return GenericError;
371     }
372
373     (*bitmap)->image.type = ImageTypeBitmap;
374     (*bitmap)->width = width;
375     (*bitmap)->height = height;
376     (*bitmap)->format = format;
377
378     return Ok;
379 }
380
381 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
382     GpBitmap **bitmap)
383 {
384     GpStatus stat;
385
386     stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
387
388     if(stat != Ok)
389         return stat;
390
391     if((*bitmap)->image.type != ImageTypeBitmap){
392         IPicture_Release((*bitmap)->image.picture);
393         GdipFree(bitmap);
394         return GenericError; /* FIXME: what error to return? */
395     }
396
397     return Ok;
398 }
399
400 /* FIXME: no icm */
401 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
402     GpBitmap **bitmap)
403 {
404     return GdipCreateBitmapFromStream(stream, bitmap);
405 }
406
407 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
408 {
409     HDC hdc;
410
411     if(!image)
412         return InvalidParameter;
413
414     IPicture_get_CurDC(image->picture, &hdc);
415     DeleteDC(hdc);
416     IPicture_Release(image->picture);
417     GdipFree(image);
418
419     return Ok;
420 }
421
422 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
423 {
424     if(!image || !item)
425         return InvalidParameter;
426
427     return NotImplemented;
428 }
429
430 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
431     GpUnit *srcUnit)
432 {
433     if(!image || !srcRect || !srcUnit)
434         return InvalidParameter;
435     if(image->type == ImageTypeMetafile){
436         memcpy(srcRect, &((GpMetafile*)image)->bounds, sizeof(GpRectF));
437         *srcUnit = ((GpMetafile*)image)->unit;
438     }
439     else if(image->type == ImageTypeBitmap){
440         srcRect->X = srcRect->Y = 0.0;
441         srcRect->Width = (REAL) ((GpBitmap*)image)->width;
442         srcRect->Height = (REAL) ((GpBitmap*)image)->height;
443         *srcUnit = UnitPixel;
444     }
445     else{
446         srcRect->X = srcRect->Y = 0.0;
447         srcRect->Width = ipicture_pixel_width(image->picture);
448         srcRect->Height = ipicture_pixel_height(image->picture);
449         *srcUnit = UnitPixel;
450     }
451
452     TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
453           srcRect->Width, srcRect->Height, *srcUnit);
454
455     return Ok;
456 }
457
458 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
459     GpGraphics **graphics)
460 {
461     HDC hdc;
462
463     if(!image || !graphics)
464         return InvalidParameter;
465
466     if(image->type != ImageTypeBitmap){
467         FIXME("not implemented for image type %d\n", image->type);
468         return NotImplemented;
469     }
470
471     IPicture_get_CurDC(image->picture, &hdc);
472
473     if(!hdc){
474         hdc = CreateCompatibleDC(0);
475         IPicture_SelectPicture(image->picture, hdc, NULL, NULL);
476     }
477
478     return GdipCreateFromHDC(hdc, graphics);
479 }
480
481 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
482 {
483     if(!image || !height)
484         return InvalidParameter;
485
486     if(image->type == ImageTypeMetafile){
487         HDC hdc = GetDC(0);
488
489         *height = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
490                         ((GpMetafile*)image)->bounds.Height);
491
492         ReleaseDC(0, hdc);
493     }
494     else if(image->type == ImageTypeBitmap)
495         *height = ((GpBitmap*)image)->height;
496     else
497         *height = ipicture_pixel_height(image->picture);
498
499     TRACE("returning %d\n", *height);
500
501     return Ok;
502 }
503
504 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
505 {
506     static int calls;
507
508     if(!image || !res)
509         return InvalidParameter;
510
511     if(!(calls++))
512         FIXME("not implemented\n");
513
514     return NotImplemented;
515 }
516
517 /* FIXME: test this function for non-bitmap types */
518 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
519 {
520     if(!image || !format)
521         return InvalidParameter;
522
523     if(image->type != ImageTypeBitmap)
524         *format = PixelFormat24bppRGB;
525     else
526         *format = ((GpBitmap*) image)->format;
527
528     return Ok;
529 }
530
531 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
532 {
533     static int calls;
534
535     if(!image || !format)
536         return InvalidParameter;
537
538     if(!(calls++))
539         FIXME("not implemented\n");
540
541     return NotImplemented;
542 }
543
544 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
545 {
546     if(!image || !type)
547         return InvalidParameter;
548
549     *type = image->type;
550
551     return Ok;
552 }
553
554 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
555 {
556     static int calls;
557
558     if(!image || !res)
559         return InvalidParameter;
560
561     if(!(calls++))
562         FIXME("not implemented\n");
563
564     return NotImplemented;
565 }
566
567 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
568 {
569     if(!image || !width)
570         return InvalidParameter;
571
572     if(image->type == ImageTypeMetafile){
573         HDC hdc = GetDC(0);
574
575         *width = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
576                         ((GpMetafile*)image)->bounds.Width);
577
578         ReleaseDC(0, hdc);
579     }
580     else if(image->type == ImageTypeBitmap)
581         *width = ((GpBitmap*)image)->width;
582     else
583         *width = ipicture_pixel_width(image->picture);
584
585     TRACE("returning %d\n", *width);
586
587     return Ok;
588 }
589
590 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
591     MetafileHeader * header)
592 {
593     static int calls;
594
595     if(!metafile || !header)
596         return InvalidParameter;
597
598     if(!(calls++))
599         FIXME("not implemented\n");
600
601     return Ok;
602 }
603
604 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID pid,
605     UINT* size)
606 {
607     static int calls;
608
609     TRACE("%p %x %p\n", image, pid, size);
610
611     if(!size || !image)
612         return InvalidParameter;
613
614     if(!(calls++))
615         FIXME("not implemented\n");
616
617     return NotImplemented;
618 }
619
620 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
621     GDIPCONST GUID* dimensionID, UINT* count)
622 {
623     static int calls;
624
625     if(!image || !dimensionID || !count)
626         return InvalidParameter;
627
628     if(!(calls++))
629         FIXME("not implemented\n");
630
631     return NotImplemented;
632 }
633
634 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
635     GUID* dimensionIDs, UINT count)
636 {
637     static int calls;
638
639     if(!image || !dimensionIDs)
640         return InvalidParameter;
641
642     if(!(calls++))
643         FIXME("not implemented\n");
644
645     return Ok;
646 }
647
648 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image,
649     GDIPCONST GUID* dimensionID, UINT frameidx)
650 {
651     static int calls;
652
653     if(!image || !dimensionID)
654         return InvalidParameter;
655
656     if(!(calls++))
657         FIXME("not implemented\n");
658
659     return Ok;
660 }
661
662 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
663 {
664     IPicture *pic;
665     short type;
666
667     if(!stream || !image)
668         return InvalidParameter;
669
670     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
671         (LPVOID*) &pic) != S_OK){
672         TRACE("Could not load picture\n");
673         return GenericError;
674     }
675
676     IStream_AddRef(stream);
677
678     IPicture_get_Type(pic, &type);
679
680     if(type == PICTYPE_BITMAP){
681         BITMAPINFO bmi;
682         BITMAPCOREHEADER* bmch;
683         OLE_HANDLE hbm;
684         HDC hdc;
685
686         *image = GdipAlloc(sizeof(GpBitmap));
687         if(!*image) return OutOfMemory;
688         (*image)->type = ImageTypeBitmap;
689
690         (*((GpBitmap**) image))->width = ipicture_pixel_width(pic);
691         (*((GpBitmap**) image))->height = ipicture_pixel_height(pic);
692
693         /* get the pixel format */
694         IPicture_get_Handle(pic, &hbm);
695         IPicture_get_CurDC(pic, &hdc);
696
697         bmch = (BITMAPCOREHEADER*) (&bmi.bmiHeader);
698         bmch->bcSize = sizeof(BITMAPCOREHEADER);
699
700         if(!hdc){
701             HBITMAP old;
702             hdc = CreateCompatibleDC(0);
703             old = SelectObject(hdc, (HBITMAP)hbm);
704             GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
705             SelectObject(hdc, old);
706             DeleteDC(hdc);
707         }
708         else
709             GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
710
711         (*((GpBitmap**) image))->format = (bmch->bcBitCount << 8) | PixelFormatGDI;
712     }
713     else if(type == PICTYPE_METAFILE || type == PICTYPE_ENHMETAFILE){
714         /* FIXME: missing initialization code */
715         *image = GdipAlloc(sizeof(GpMetafile));
716         if(!*image) return OutOfMemory;
717         (*image)->type = ImageTypeMetafile;
718     }
719     else{
720         *image = GdipAlloc(sizeof(GpImage));
721         if(!*image) return OutOfMemory;
722         (*image)->type = ImageTypeUnknown;
723     }
724
725     (*image)->picture = pic;
726
727     return Ok;
728 }
729
730 /* FIXME: no ICM */
731 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
732 {
733     return GdipLoadImageFromStream(stream, image);
734 }
735
736 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
737 {
738     static int calls;
739
740     if(!image)
741         return InvalidParameter;
742
743     if(!(calls++))
744         FIXME("not implemented\n");
745
746     return NotImplemented;
747 }
748
749 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
750     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
751 {
752     if(!image || !stream)
753         return InvalidParameter;
754
755     /* FIXME: CLSID, EncoderParameters not used */
756
757     IPicture_SaveAsFile(image->picture, stream, FALSE, NULL);
758
759     return Ok;
760 }
761
762 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
763     GDIPCONST ColorPalette *palette)
764 {
765     static int calls;
766
767     if(!image || !palette)
768         return InvalidParameter;
769
770     if(!(calls++))
771         FIXME("not implemented\n");
772
773     return NotImplemented;
774 }