gdiplus: Implement GdipGetImageDecoders[Size].
[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     if (hbmReturn) *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     stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
1294     if (stat != Ok)
1295         return GenericError;
1296
1297     stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
1298
1299     IStream_Release(stream);
1300     return stat;
1301 }
1302
1303 /*************************************************************************
1304  * Encoding functions -
1305  *   These functions encode an image in different image file formats.
1306  */
1307 #define BITMAP_FORMAT_BMP   0x4d42 /* "BM" */
1308 #define BITMAP_FORMAT_JPEG  0xd8ff
1309 #define BITMAP_FORMAT_GIF   0x4947
1310 #define BITMAP_FORMAT_PNG   0x5089
1311 #define BITMAP_FORMAT_APM   0xcdd7
1312
1313 static GpStatus encode_image_BMP(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
1314                                  void **output, unsigned int *output_size)
1315 {
1316     int num_palette_entries;
1317     BITMAPFILEHEADER *bmp_file_hdr;
1318     BITMAPINFO *bmp_info_hdr;
1319
1320     if (bitmap_info->bmiHeader.biClrUsed) {
1321         num_palette_entries = bitmap_info->bmiHeader.biClrUsed;
1322         if (num_palette_entries > 256) num_palette_entries = 256;
1323     } else {
1324         if (bitmap_info->bmiHeader.biBitCount <= 8)
1325             num_palette_entries = 1 << bitmap_info->bmiHeader.biBitCount;
1326         else
1327             num_palette_entries = 0;
1328     }
1329
1330     *output_size =
1331         sizeof(BITMAPFILEHEADER) +
1332         sizeof(BITMAPINFOHEADER) +
1333         num_palette_entries * sizeof(RGBQUAD) +
1334         bitmap_info->bmiHeader.biSizeImage;
1335
1336     *output = GdipAlloc(*output_size);
1337
1338     bmp_file_hdr = *output;
1339     bmp_file_hdr->bfType = BITMAP_FORMAT_BMP;
1340     bmp_file_hdr->bfSize = *output_size;
1341     bmp_file_hdr->bfOffBits =
1342         sizeof(BITMAPFILEHEADER) +
1343         sizeof(BITMAPINFOHEADER) +
1344         num_palette_entries * sizeof (RGBQUAD);
1345
1346     bmp_info_hdr = (BITMAPINFO*) ((unsigned char*)(*output) + sizeof(BITMAPFILEHEADER));
1347     memcpy(bmp_info_hdr, bitmap_info, sizeof(BITMAPINFOHEADER) + num_palette_entries * sizeof(RGBQUAD));
1348     memcpy((unsigned char *)(*output) +
1349            sizeof(BITMAPFILEHEADER) +
1350            sizeof(BITMAPINFOHEADER) +
1351            num_palette_entries * sizeof(RGBQUAD),
1352            bitmap_bits, bitmap_info->bmiHeader.biSizeImage);
1353
1354     return Ok;
1355 }
1356
1357 typedef GpStatus (*encode_image_func)(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
1358                                    void **output, unsigned int *output_size);
1359
1360 typedef struct image_codec {
1361     ImageCodecInfo info;
1362     encode_image_func encode_func;
1363 } image_codec;
1364
1365 typedef enum {
1366     BMP,
1367     NUM_CODECS
1368 } ImageFormat;
1369
1370 static const struct image_codec codecs[NUM_CODECS];
1371
1372 /*****************************************************************************
1373  * GdipSaveImageToStream [GDIPLUS.@]
1374  */
1375 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
1376     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
1377 {
1378     GpStatus stat;
1379     HRESULT hr;
1380     short type;
1381     HBITMAP hbmp;
1382     HBITMAP old_hbmp;
1383     HDC hdc;
1384     int bm_is_selected;
1385     BITMAPINFO bmp_info;
1386     LPVOID bmp_bits;
1387     encode_image_func encode_image;
1388     LPVOID output;
1389     unsigned int output_size;
1390     unsigned int dummy;
1391     int i;
1392
1393     old_hbmp = 0;
1394     output = NULL;
1395     output_size = 0;
1396
1397     TRACE("%p %p %p %p\n", image, stream, clsid, params);
1398
1399     if(!image || !stream)
1400         return InvalidParameter;
1401
1402     if (!image->picture)
1403         return GenericError;
1404
1405     hr = IPicture_get_Type(image->picture, &type);
1406     if (FAILED(hr) || type != PICTYPE_BITMAP)
1407         return GenericError;
1408
1409     /* select correct encoder */
1410     encode_image = NULL;
1411     for (i = 0; i < NUM_CODECS; i++) {
1412         if (IsEqualCLSID(clsid, &codecs[i].info.Clsid))
1413             encode_image = codecs[i].encode_func;
1414     }
1415     if (encode_image == NULL)
1416         return UnknownImageFormat;
1417
1418     /* extract underlying hbitmap representation from the IPicture */
1419     hr = IPicture_get_Handle(image->picture, (OLE_HANDLE*)&hbmp);
1420     if (FAILED(hr) || !hbmp)
1421         return GenericError;
1422     hr = IPicture_get_CurDC(image->picture, &hdc);
1423     if (FAILED(hr))
1424         return GenericError;
1425     bm_is_selected = (hdc != 0);
1426     if (!bm_is_selected) {
1427         hdc = CreateCompatibleDC(0);
1428         old_hbmp = SelectObject(hdc, hbmp);
1429     }
1430
1431     /* get bits from HBITMAP */
1432     bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
1433     bmp_info.bmiHeader.biBitCount = 0;
1434     GetDIBits(hdc, hbmp, 0, 0, NULL, &bmp_info, DIB_RGB_COLORS);
1435
1436     bmp_bits = GdipAlloc(bmp_info.bmiHeader.biSizeImage);
1437
1438     if (bmp_bits)
1439         GetDIBits(hdc, hbmp, 0, abs(bmp_info.bmiHeader.biHeight), bmp_bits, &bmp_info, DIB_RGB_COLORS);
1440
1441     if (!bm_is_selected) {
1442         SelectObject(hdc, old_hbmp);
1443         DeleteDC(hdc);
1444     }
1445
1446     if (!bmp_bits)
1447         return OutOfMemory;
1448
1449     stat = encode_image(bmp_bits, &bmp_info, &output, &output_size);
1450     if (stat == Ok)
1451         IStream_Write(stream, output, output_size, &dummy);
1452
1453     GdipFree(output);
1454     GdipFree(bmp_bits);
1455
1456     return stat;
1457 }
1458
1459 /*****************************************************************************
1460  * GdipGetImagePalette [GDIPLUS.@]
1461  */
1462 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
1463 {
1464     static int calls = 0;
1465
1466     if(!image)
1467         return InvalidParameter;
1468
1469     if(!(calls++))
1470         FIXME("not implemented\n");
1471
1472     return NotImplemented;
1473 }
1474
1475 /*****************************************************************************
1476  * GdipSetImagePalette [GDIPLUS.@]
1477  */
1478 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
1479     GDIPCONST ColorPalette *palette)
1480 {
1481     static int calls;
1482
1483     if(!image || !palette)
1484         return InvalidParameter;
1485
1486     if(!(calls++))
1487         FIXME("not implemented\n");
1488
1489     return NotImplemented;
1490 }
1491
1492 /*************************************************************************
1493  * Encoders -
1494  *   Structures that represent which formats we support for encoding.
1495  */
1496
1497 /* ImageCodecInfo creation routines taken from libgdiplus */
1498 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */
1499 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
1500 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
1501 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
1502 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
1503 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
1504
1505 static const struct image_codec codecs[NUM_CODECS] = {
1506     {
1507         { /* BMP */
1508             /* Clsid */              { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
1509             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
1510             /* CodecName */          bmp_codecname,
1511             /* DllName */            NULL,
1512             /* FormatDescription */  bmp_format,
1513             /* FilenameExtension */  bmp_extension,
1514             /* MimeType */           bmp_mimetype,
1515             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
1516             /* Version */            1,
1517             /* SigCount */           1,
1518             /* SigSize */            2,
1519             /* SigPattern */         bmp_sig_pattern,
1520             /* SigMask */            bmp_sig_mask,
1521         },
1522         encode_image_BMP
1523     }
1524 };
1525
1526 /*****************************************************************************
1527  * GdipGetImageDecodersSize [GDIPLUS.@]
1528  */
1529 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
1530 {
1531     int decoder_count=0;
1532     int i;
1533     TRACE("%p %p\n", numDecoders, size);
1534
1535     if (!numDecoders || !size)
1536         return InvalidParameter;
1537
1538     for (i=0; i<NUM_CODECS; i++)
1539     {
1540         if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
1541             decoder_count++;
1542     }
1543
1544     *numDecoders = decoder_count;
1545     *size = decoder_count * sizeof(ImageCodecInfo);
1546
1547     return Ok;
1548 }
1549
1550 /*****************************************************************************
1551  * GdipGetImageDecoders [GDIPLUS.@]
1552  */
1553 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
1554 {
1555     int i, decoder_count=0;
1556     TRACE("%u %u %p\n", numDecoders, size, decoders);
1557
1558     if (!decoders ||
1559         size != numDecoders * sizeof(ImageCodecInfo))
1560         return GenericError;
1561
1562     for (i=0; i<NUM_CODECS; i++)
1563     {
1564         if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
1565         {
1566             if (decoder_count == numDecoders) return GenericError;
1567             memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
1568             decoder_count++;
1569         }
1570     }
1571
1572     if (decoder_count < numDecoders) return GenericError;
1573
1574     return Ok;
1575 }
1576
1577 /*****************************************************************************
1578  * GdipGetImageEncodersSize [GDIPLUS.@]
1579  */
1580 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
1581 {
1582     int encoder_count=0;
1583     int i;
1584     TRACE("%p %p\n", numEncoders, size);
1585
1586     if (!numEncoders || !size)
1587         return InvalidParameter;
1588
1589     for (i=0; i<NUM_CODECS; i++)
1590     {
1591         if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
1592             encoder_count++;
1593     }
1594
1595     *numEncoders = encoder_count;
1596     *size = encoder_count * sizeof(ImageCodecInfo);
1597
1598     return Ok;
1599 }
1600
1601 /*****************************************************************************
1602  * GdipGetImageEncoders [GDIPLUS.@]
1603  */
1604 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
1605 {
1606     int i, encoder_count=0;
1607     TRACE("%u %u %p\n", numEncoders, size, encoders);
1608
1609     if (!encoders ||
1610         size != numEncoders * sizeof(ImageCodecInfo))
1611         return GenericError;
1612
1613     for (i=0; i<NUM_CODECS; i++)
1614     {
1615         if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
1616         {
1617             if (encoder_count == numEncoders) return GenericError;
1618             memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
1619             encoder_count++;
1620         }
1621     }
1622
1623     if (encoder_count < numEncoders) return GenericError;
1624
1625     return Ok;
1626 }
1627
1628 /*****************************************************************************
1629  * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
1630  */
1631 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
1632 {
1633     BITMAP bm;
1634     GpStatus retval;
1635     PixelFormat format;
1636     BYTE* bits;
1637
1638     TRACE("%p %p %p\n", hbm, hpal, bitmap);
1639
1640     if(!hbm || !bitmap)
1641         return InvalidParameter;
1642
1643     /* TODO: Support for device-dependent bitmaps */
1644     if(hpal){
1645         FIXME("no support for device-dependent bitmaps\n");
1646         return NotImplemented;
1647     }
1648
1649     if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
1650             return InvalidParameter;
1651
1652     /* TODO: Figure out the correct format for 16, 32, 64 bpp */
1653     switch(bm.bmBitsPixel) {
1654         case 1:
1655             format = PixelFormat1bppIndexed;
1656             break;
1657         case 4:
1658             format = PixelFormat4bppIndexed;
1659             break;
1660         case 8:
1661             format = PixelFormat8bppIndexed;
1662             break;
1663         case 24:
1664             format = PixelFormat24bppRGB;
1665             break;
1666         case 32:
1667             format = PixelFormat32bppRGB;
1668             break;
1669         case 48:
1670             format = PixelFormat48bppRGB;
1671             break;
1672         default:
1673             FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
1674             return InvalidParameter;
1675     }
1676
1677     if (bm.bmBits)
1678         bits = (BYTE*)bm.bmBits + (bm.bmHeight - 1) * bm.bmWidthBytes;
1679     else
1680     {
1681         FIXME("can only get image data from DIB sections\n");
1682         bits = NULL;
1683     }
1684
1685     retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, -bm.bmWidthBytes,
1686         format, bits, bitmap);
1687
1688     return retval;
1689 }
1690
1691 GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect)
1692 {
1693     FIXME("(%p): stub\n", effect);
1694     /* note: According to Jose Roca's GDI+ Docs, this is not implemented
1695      * in Windows's gdiplus */
1696     return NotImplemented;
1697 }
1698
1699 /*****************************************************************************
1700  * GdipSetEffectParameters [GDIPLUS.@]
1701  */
1702 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
1703     const VOID *params, const UINT size)
1704 {
1705     static int calls;
1706
1707     if(!(calls++))
1708         FIXME("not implemented\n");
1709
1710     return NotImplemented;
1711 }
1712
1713 /*****************************************************************************
1714  * GdipGetImageFlags [GDIPLUS.@]
1715  */
1716 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
1717 {
1718     TRACE("%p %p\n", image, flags);
1719
1720     if(!image || !flags)
1721         return InvalidParameter;
1722
1723     *flags = image->flags;
1724
1725     return Ok;
1726 }
1727
1728 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param)
1729 {
1730     TRACE("(%d, %p)\n", control, param);
1731
1732     switch(control){
1733         case TestControlForceBilinear:
1734             if(param)
1735                 FIXME("TestControlForceBilinear not handled\n");
1736             break;
1737         case TestControlNoICM:
1738             if(param)
1739                 FIXME("TestControlNoICM not handled\n");
1740             break;
1741         case TestControlGetBuildNumber:
1742             *((DWORD*)param) = 3102;
1743             break;
1744     }
1745
1746     return Ok;
1747 }
1748
1749 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
1750                             HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
1751                             MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
1752                             GpMetafile **metafile)
1753 {
1754     FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1755                                  frameUnit, debugstr_w(desc), metafile);
1756
1757     return NotImplemented;
1758 }
1759
1760 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
1761                             GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
1762                             GDIPCONST WCHAR *desc, GpMetafile **metafile)
1763 {
1764     FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1765                                  frameUnit, debugstr_w(desc), metafile);
1766
1767     return NotImplemented;
1768 }
1769
1770 GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image)
1771 {
1772     FIXME("%p\n", image);
1773
1774     return Ok;
1775 }
1776
1777 /*****************************************************************************
1778  * GdipGetImageThumbnail [GDIPLUS.@]
1779  */
1780 GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height,
1781                             GpImage **ret_image, GetThumbnailImageAbort cb,
1782                             VOID * cb_data)
1783 {
1784     FIXME("(%p %u %u %p %p %p) stub\n",
1785         image, width, height, ret_image, cb, cb_data);
1786     return NotImplemented;
1787 }
1788
1789 /*****************************************************************************
1790  * GdipImageRotateFlip [GDIPLUS.@]
1791  */
1792 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
1793 {
1794     FIXME("(%p %u) stub\n", image, type);
1795     return NotImplemented;
1796 }