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