gdiplus: Implemented GdipPathIterNextMarkerPath with tests.
[wine] / dlls / gdiplus / image.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "wingdi.h"
25
26 #define COBJMACROS
27 #include "objbase.h"
28 #include "olectl.h"
29 #include "ole2.h"
30
31 #include "gdiplus.h"
32 #include "gdiplus_private.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
36
37 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
38
39 static INT ipicture_pixel_height(IPicture *pic)
40 {
41     HDC hdcref;
42     OLE_YSIZE_HIMETRIC y;
43
44     IPicture_get_Height(pic, &y);
45
46     hdcref = GetDC(0);
47
48     y = MulDiv(y, GetDeviceCaps(hdcref, LOGPIXELSY), INCH_HIMETRIC);
49     ReleaseDC(0, hdcref);
50
51     return y;
52 }
53
54 static INT ipicture_pixel_width(IPicture *pic)
55 {
56     HDC hdcref;
57     OLE_XSIZE_HIMETRIC x;
58
59     IPicture_get_Width(pic, &x);
60
61     hdcref = GetDC(0);
62
63     x = MulDiv(x, GetDeviceCaps(hdcref, LOGPIXELSX), INCH_HIMETRIC);
64
65     ReleaseDC(0, hdcref);
66
67     return x;
68 }
69
70 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
71     ARGB *color)
72 {
73     static int calls;
74     TRACE("%p %d %d %p\n", bitmap, x, y, color);
75
76     if(!bitmap || !color)
77         return InvalidParameter;
78
79     if(!(calls++))
80         FIXME("not implemented\n");
81
82     *color = 0xdeadbeef;
83
84     return NotImplemented;
85 }
86
87 /* This function returns a pointer to an array of pixels that represents the
88  * bitmap. The *entire* bitmap is locked according to the lock mode specified by
89  * flags.  It is correct behavior that a user who calls this function with write
90  * privileges can write to the whole bitmap (not just the area in rect).
91  *
92  * FIXME: only used portion of format is bits per pixel. */
93 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
94     UINT flags, PixelFormat format, BitmapData* lockeddata)
95 {
96     BOOL bm_is_selected;
97     INT stride, bitspp = PIXELFORMATBPP(format);
98     OLE_HANDLE hbm;
99     HDC hdc;
100     HBITMAP old = NULL;
101     BITMAPINFO bmi;
102     BYTE *buff = NULL;
103     UINT abs_height;
104     GpRect act_rect; /* actual rect to be used */
105
106     TRACE("%p %p %d %d %p\n", bitmap, rect, flags, format, lockeddata);
107
108     if(!lockeddata || !bitmap)
109         return InvalidParameter;
110
111     if(rect){
112         if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
113           (rect->Y + rect->Height > bitmap->height) || !flags)
114             return InvalidParameter;
115
116         act_rect = *rect;
117     }
118     else{
119         act_rect.X = act_rect.Y = 0;
120         act_rect.Width  = bitmap->width;
121         act_rect.Height = bitmap->height;
122     }
123
124     if(flags & ImageLockModeUserInputBuf)
125         return NotImplemented;
126
127     if(bitmap->lockmode)
128         return WrongState;
129
130     IPicture_get_Handle(bitmap->image.picture, &hbm);
131     IPicture_get_CurDC(bitmap->image.picture, &hdc);
132     bm_is_selected = (hdc != 0);
133
134     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
135     bmi.bmiHeader.biBitCount = 0;
136
137     if(!bm_is_selected){
138         hdc = CreateCompatibleDC(0);
139         old = SelectObject(hdc, (HBITMAP)hbm);
140     }
141
142     /* fill out bmi */
143     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
144
145     abs_height = abs(bmi.bmiHeader.biHeight);
146     stride = bmi.bmiHeader.biWidth * bitspp / 8;
147     stride = (stride + 3) & ~3;
148
149     buff = GdipAlloc(stride * abs_height);
150
151     bmi.bmiHeader.biBitCount = bitspp;
152
153     if(buff)
154         GetDIBits(hdc, (HBITMAP)hbm, 0, abs_height, buff, &bmi, DIB_RGB_COLORS);
155
156     if(!bm_is_selected){
157         SelectObject(hdc, old);
158         DeleteDC(hdc);
159     }
160
161     if(!buff)
162         return OutOfMemory;
163
164     lockeddata->Width  = act_rect.Width;
165     lockeddata->Height = act_rect.Height;
166     lockeddata->PixelFormat = format;
167     lockeddata->Reserved = flags;
168
169     if(bmi.bmiHeader.biHeight > 0){
170         lockeddata->Stride = -stride;
171         lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X +
172                              stride * (abs_height - 1 - act_rect.Y);
173     }
174     else{
175         lockeddata->Stride = stride;
176         lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X + stride * act_rect.Y;
177     }
178
179     bitmap->lockmode = flags;
180     bitmap->numlocks++;
181
182     bitmap->bitmapbits = buff;
183
184     return Ok;
185 }
186
187 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
188     BitmapData* lockeddata)
189 {
190     OLE_HANDLE hbm;
191     HDC hdc;
192     HBITMAP old = NULL;
193     BOOL bm_is_selected;
194     BITMAPINFO bmi;
195
196     if(!bitmap || !lockeddata)
197         return InvalidParameter;
198
199     if(!bitmap->lockmode)
200         return WrongState;
201
202     if(lockeddata->Reserved & ImageLockModeUserInputBuf)
203         return NotImplemented;
204
205     if(lockeddata->Reserved & ImageLockModeRead){
206         if(!(--bitmap->numlocks))
207             bitmap->lockmode = 0;
208
209         GdipFree(bitmap->bitmapbits);
210         bitmap->bitmapbits = NULL;
211         return Ok;
212     }
213
214     IPicture_get_Handle(bitmap->image.picture, &hbm);
215     IPicture_get_CurDC(bitmap->image.picture, &hdc);
216     bm_is_selected = (hdc != 0);
217
218     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
219     bmi.bmiHeader.biBitCount = 0;
220
221     if(!bm_is_selected){
222         hdc = CreateCompatibleDC(0);
223         old = SelectObject(hdc, (HBITMAP)hbm);
224     }
225
226     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
227     bmi.bmiHeader.biBitCount = PIXELFORMATBPP(lockeddata->PixelFormat);
228     SetDIBits(hdc, (HBITMAP)hbm, 0, abs(bmi.bmiHeader.biHeight),
229               bitmap->bitmapbits, &bmi, DIB_RGB_COLORS);
230
231     if(!bm_is_selected){
232         SelectObject(hdc, old);
233         DeleteDC(hdc);
234     }
235
236     GdipFree(bitmap->bitmapbits);
237     bitmap->bitmapbits = NULL;
238     bitmap->lockmode = 0;
239
240     return Ok;
241 }
242
243 GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
244 {
245     if (!(image && cloneImage)) return InvalidParameter;
246
247     FIXME("stub: %p, %p\n", image, cloneImage);
248
249     return NotImplemented;
250 }
251
252 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
253     GpBitmap **bitmap)
254 {
255     GpStatus stat;
256     IStream *stream;
257
258     if(!filename || !bitmap)
259         return InvalidParameter;
260
261     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
262
263     if(stat != Ok)
264         return stat;
265
266     stat = GdipCreateBitmapFromStream(stream, bitmap);
267
268     IStream_Release(stream);
269
270     return stat;
271 }
272
273 GpStatus WINGDIPAPI GdipCreateBitmapFromGdiDib(GDIPCONST BITMAPINFO* info,
274                                                VOID *bits, GpBitmap **bitmap)
275 {
276     DWORD height, stride;
277     PixelFormat format;
278
279     FIXME("(%p, %p, %p) - partially implemented\n", info, bits, bitmap);
280
281     height = abs(info->bmiHeader.biHeight);
282     stride = ((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) >> 3) & ~3;
283
284     if(info->bmiHeader.biHeight > 0) /* bottom-up */
285     {
286         bits = (BYTE*)bits + (height - 1) * stride;
287         stride = -stride;
288     }
289
290     switch(info->bmiHeader.biBitCount) {
291     case 1:
292         format = PixelFormat1bppIndexed;
293         break;
294     case 4:
295         format = PixelFormat4bppIndexed;
296         break;
297     case 8:
298         format = PixelFormat8bppIndexed;
299         break;
300     case 24:
301         format = PixelFormat24bppRGB;
302         break;
303     default:
304         FIXME("don't know how to handle %d bpp\n", info->bmiHeader.biBitCount);
305         *bitmap = NULL;
306         return InvalidParameter;
307     }
308
309     return GdipCreateBitmapFromScan0(info->bmiHeader.biWidth, height, stride, format,
310                                      bits, bitmap);
311
312 }
313
314 /* FIXME: no icm */
315 GpStatus WINGDIPAPI GdipCreateBitmapFromFileICM(GDIPCONST WCHAR* filename,
316     GpBitmap **bitmap)
317 {
318     return GdipCreateBitmapFromFile(filename, bitmap);
319 }
320
321 GpStatus WINGDIPAPI GdipCreateBitmapFromResource(HINSTANCE hInstance,
322     GDIPCONST WCHAR* lpBitmapName, GpBitmap** bitmap)
323 {
324     HBITMAP hbm;
325     GpStatus stat = InvalidParameter;
326
327     if(!lpBitmapName || !bitmap)
328         return InvalidParameter;
329
330     /* load DIB */
331     hbm = (HBITMAP)LoadImageW(hInstance,lpBitmapName,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
332
333     if(hbm){
334         stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
335         DeleteObject(hbm);
336     }
337
338     return stat;
339 }
340
341 GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
342     HBITMAP* hbmReturn, ARGB background)
343 {
344     FIXME("stub\n");
345
346     hbmReturn = NULL;
347
348     return NotImplemented;
349 }
350
351 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
352     GpMetafile* metafile, BOOL* succ, EmfType emfType,
353     const WCHAR* description, GpMetafile** out_metafile)
354 {
355     static int calls;
356
357     if(!ref || !metafile || !out_metafile)
358         return InvalidParameter;
359
360     *succ = FALSE;
361     *out_metafile = NULL;
362
363     if(!(calls++))
364         FIXME("not implemented\n");
365
366     return NotImplemented;
367 }
368
369 /* FIXME: this should create a bitmap in the given size with the attributes
370  * (resolution etc.) of the graphics object */
371 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
372     GpGraphics* target, GpBitmap** bitmap)
373 {
374     static int calls;
375     GpStatus ret;
376
377     if(!target || !bitmap)
378         return InvalidParameter;
379
380     if(!(calls++))
381         FIXME("hacked stub\n");
382
383     ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat24bppRGB,
384                                     NULL, bitmap);
385
386     return ret;
387 }
388
389 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
390     PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
391 {
392     BITMAPFILEHEADER *bmfh;
393     BITMAPINFOHEADER *bmih;
394     BYTE *buff;
395     INT datalen, size;
396     IStream *stream;
397
398     TRACE("%d %d %d %d %p %p\n", width, height, stride, format, scan0, bitmap);
399
400     if(!bitmap || width <= 0 || height <= 0 || (scan0 && (stride % 4))){
401         *bitmap = NULL;
402         return InvalidParameter;
403     }
404
405     if(scan0 && !stride)
406         return InvalidParameter;
407
408     /* FIXME: windows allows negative stride (reads backwards from scan0) */
409     if(stride < 0){
410         FIXME("negative stride\n");
411         return InvalidParameter;
412     }
413
414     *bitmap = GdipAlloc(sizeof(GpBitmap));
415     if(!*bitmap)    return OutOfMemory;
416
417     if(stride == 0){
418         stride = width * (PIXELFORMATBPP(format) / 8);
419         stride = (stride + 3) & ~3;
420     }
421
422     datalen = abs(stride * height);
423     size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + datalen;
424     buff = GdipAlloc(size);
425     if(!buff){
426         GdipFree(*bitmap);
427         return OutOfMemory;
428     }
429
430     bmfh = (BITMAPFILEHEADER*) buff;
431     bmih = (BITMAPINFOHEADER*) (bmfh + 1);
432
433     bmfh->bfType    = (((WORD)'M') << 8) + (WORD)'B';
434     bmfh->bfSize    = size;
435     bmfh->bfOffBits = size - datalen;
436
437     bmih->biSize            = sizeof(BITMAPINFOHEADER);
438     bmih->biWidth           = width;
439     bmih->biHeight          = -height;
440     /* FIXME: use the rest of the data from format */
441     bmih->biBitCount        = PIXELFORMATBPP(format);
442     bmih->biCompression     = BI_RGB;
443     bmih->biSizeImage       = datalen;
444
445     if(scan0)
446         memcpy(bmih + 1, scan0, datalen);
447     else
448         memset(bmih + 1, 0, datalen);
449
450     if(CreateStreamOnHGlobal(buff, TRUE, &stream) != S_OK){
451         ERR("could not make stream\n");
452         GdipFree(*bitmap);
453         GdipFree(buff);
454         return GenericError;
455     }
456
457     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
458         (LPVOID*) &((*bitmap)->image.picture)) != S_OK){
459         TRACE("Could not load picture\n");
460         IStream_Release(stream);
461         GdipFree(*bitmap);
462         GdipFree(buff);
463         return GenericError;
464     }
465
466     (*bitmap)->image.type = ImageTypeBitmap;
467     (*bitmap)->image.flags = ImageFlagsNone;
468     (*bitmap)->width = width;
469     (*bitmap)->height = height;
470     (*bitmap)->format = format;
471
472     return Ok;
473 }
474
475 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
476     GpBitmap **bitmap)
477 {
478     GpStatus stat;
479
480     stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
481
482     if(stat != Ok)
483         return stat;
484
485     if((*bitmap)->image.type != ImageTypeBitmap){
486         IPicture_Release((*bitmap)->image.picture);
487         GdipFree(bitmap);
488         return GenericError; /* FIXME: what error to return? */
489     }
490
491     return Ok;
492 }
493
494 /* FIXME: no icm */
495 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
496     GpBitmap **bitmap)
497 {
498     return GdipCreateBitmapFromStream(stream, bitmap);
499 }
500
501 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
502 {
503     HDC hdc;
504
505     if(!image)
506         return InvalidParameter;
507
508     IPicture_get_CurDC(image->picture, &hdc);
509     DeleteDC(hdc);
510     IPicture_Release(image->picture);
511     if (image->type == ImageTypeBitmap)
512         GdipFree(((GpBitmap*)image)->bitmapbits);
513     GdipFree(image);
514
515     return Ok;
516 }
517
518 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
519 {
520     if(!image || !item)
521         return InvalidParameter;
522
523     return NotImplemented;
524 }
525
526 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
527     GpUnit *srcUnit)
528 {
529     if(!image || !srcRect || !srcUnit)
530         return InvalidParameter;
531     if(image->type == ImageTypeMetafile){
532         *srcRect = ((GpMetafile*)image)->bounds;
533         *srcUnit = ((GpMetafile*)image)->unit;
534     }
535     else if(image->type == ImageTypeBitmap){
536         srcRect->X = srcRect->Y = 0.0;
537         srcRect->Width = (REAL) ((GpBitmap*)image)->width;
538         srcRect->Height = (REAL) ((GpBitmap*)image)->height;
539         *srcUnit = UnitPixel;
540     }
541     else{
542         srcRect->X = srcRect->Y = 0.0;
543         srcRect->Width = ipicture_pixel_width(image->picture);
544         srcRect->Height = ipicture_pixel_height(image->picture);
545         *srcUnit = UnitPixel;
546     }
547
548     TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
549           srcRect->Width, srcRect->Height, *srcUnit);
550
551     return Ok;
552 }
553
554 GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
555     REAL *height)
556 {
557     if(!image || !height || !width)
558         return InvalidParameter;
559
560     if(image->type == ImageTypeMetafile){
561         HDC hdc = GetDC(0);
562
563         *height = convert_unit(hdc, ((GpMetafile*)image)->unit) *
564                         ((GpMetafile*)image)->bounds.Height;
565
566         *width = convert_unit(hdc, ((GpMetafile*)image)->unit) *
567                         ((GpMetafile*)image)->bounds.Width;
568
569         ReleaseDC(0, hdc);
570     }
571
572     else if(image->type == ImageTypeBitmap){
573         *height = ((GpBitmap*)image)->height;
574         *width = ((GpBitmap*)image)->width;
575     }
576     else{
577         *height = ipicture_pixel_height(image->picture);
578         *width = ipicture_pixel_width(image->picture);
579     }
580
581     TRACE("returning (%f, %f)\n", *height, *width);
582     return Ok;
583 }
584
585 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
586     GpGraphics **graphics)
587 {
588     HDC hdc;
589
590     if(!image || !graphics)
591         return InvalidParameter;
592
593     if(image->type != ImageTypeBitmap){
594         FIXME("not implemented for image type %d\n", image->type);
595         return NotImplemented;
596     }
597
598     IPicture_get_CurDC(image->picture, &hdc);
599
600     if(!hdc){
601         hdc = CreateCompatibleDC(0);
602         IPicture_SelectPicture(image->picture, hdc, NULL, NULL);
603     }
604
605     return GdipCreateFromHDC(hdc, graphics);
606 }
607
608 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
609 {
610     if(!image || !height)
611         return InvalidParameter;
612
613     if(image->type == ImageTypeMetafile){
614         HDC hdc = GetDC(0);
615
616         *height = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
617                         ((GpMetafile*)image)->bounds.Height);
618
619         ReleaseDC(0, hdc);
620     }
621     else if(image->type == ImageTypeBitmap)
622         *height = ((GpBitmap*)image)->height;
623     else
624         *height = ipicture_pixel_height(image->picture);
625
626     TRACE("returning %d\n", *height);
627
628     return Ok;
629 }
630
631 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
632 {
633     static int calls;
634
635     if(!image || !res)
636         return InvalidParameter;
637
638     if(!(calls++))
639         FIXME("not implemented\n");
640
641     return NotImplemented;
642 }
643
644 /* FIXME: test this function for non-bitmap types */
645 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
646 {
647     if(!image || !format)
648         return InvalidParameter;
649
650     if(image->type != ImageTypeBitmap)
651         *format = PixelFormat24bppRGB;
652     else
653         *format = ((GpBitmap*) image)->format;
654
655     return Ok;
656 }
657
658 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
659 {
660     static int calls;
661
662     if(!image || !format)
663         return InvalidParameter;
664
665     if(!(calls++))
666         FIXME("not implemented\n");
667
668     return NotImplemented;
669 }
670
671 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
672 {
673     if(!image || !type)
674         return InvalidParameter;
675
676     *type = image->type;
677
678     return Ok;
679 }
680
681 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
682 {
683     static int calls;
684
685     if(!image || !res)
686         return InvalidParameter;
687
688     if(!(calls++))
689         FIXME("not implemented\n");
690
691     return NotImplemented;
692 }
693
694 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
695 {
696     if(!image || !width)
697         return InvalidParameter;
698
699     if(image->type == ImageTypeMetafile){
700         HDC hdc = GetDC(0);
701
702         *width = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
703                         ((GpMetafile*)image)->bounds.Width);
704
705         ReleaseDC(0, hdc);
706     }
707     else if(image->type == ImageTypeBitmap)
708         *width = ((GpBitmap*)image)->width;
709     else
710         *width = ipicture_pixel_width(image->picture);
711
712     TRACE("returning %d\n", *width);
713
714     return Ok;
715 }
716
717 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
718     MetafileHeader * header)
719 {
720     static int calls;
721
722     if(!metafile || !header)
723         return InvalidParameter;
724
725     if(!(calls++))
726         FIXME("not implemented\n");
727
728     return Ok;
729 }
730
731 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID pid,
732     UINT* size)
733 {
734     static int calls;
735
736     TRACE("%p %x %p\n", image, pid, size);
737
738     if(!size || !image)
739         return InvalidParameter;
740
741     if(!(calls++))
742         FIXME("not implemented\n");
743
744     return NotImplemented;
745 }
746
747 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
748     GDIPCONST GUID* dimensionID, UINT* count)
749 {
750     static int calls;
751
752     if(!image || !dimensionID || !count)
753         return InvalidParameter;
754
755     if(!(calls++))
756         FIXME("not implemented\n");
757
758     return NotImplemented;
759 }
760
761 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
762     UINT* count)
763 {
764     if(!image || !count)
765         return InvalidParameter;
766
767     *count = 1;
768
769     FIXME("stub\n");
770
771     return Ok;
772 }
773
774 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
775     GUID* dimensionIDs, UINT count)
776 {
777     static int calls;
778
779     if(!image || !dimensionIDs)
780         return InvalidParameter;
781
782     if(!(calls++))
783         FIXME("not implemented\n");
784
785     return Ok;
786 }
787
788 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image,
789     GDIPCONST GUID* dimensionID, UINT frameidx)
790 {
791     static int calls;
792
793     if(!image || !dimensionID)
794         return InvalidParameter;
795
796     if(!(calls++))
797         FIXME("not implemented\n");
798
799     return Ok;
800 }
801
802 GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename,
803                                           GpImage **image)
804 {
805     GpStatus stat;
806     IStream *stream;
807
808     if (!filename || !image)
809         return InvalidParameter;
810
811     stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
812
813     if (stat != Ok)
814         return stat;
815
816     stat = GdipLoadImageFromStream(stream, image);
817
818     IStream_Release(stream);
819
820     return stat;
821 }
822
823 /* FIXME: no icm handling */
824 GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage **image)
825 {
826     return GdipLoadImageFromFile(filename, image);
827 }
828
829 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
830 {
831     IPicture *pic;
832     short type;
833
834     if(!stream || !image)
835         return InvalidParameter;
836
837     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
838         (LPVOID*) &pic) != S_OK){
839         TRACE("Could not load picture\n");
840         return GenericError;
841     }
842
843     IPicture_get_Type(pic, &type);
844
845     if(type == PICTYPE_BITMAP){
846         BITMAPINFO bmi;
847         BITMAPCOREHEADER* bmch;
848         OLE_HANDLE hbm;
849         HDC hdc;
850
851         *image = GdipAlloc(sizeof(GpBitmap));
852         if(!*image) return OutOfMemory;
853         (*image)->type = ImageTypeBitmap;
854
855         (*((GpBitmap**) image))->width = ipicture_pixel_width(pic);
856         (*((GpBitmap**) image))->height = ipicture_pixel_height(pic);
857
858         /* get the pixel format */
859         IPicture_get_Handle(pic, &hbm);
860         IPicture_get_CurDC(pic, &hdc);
861
862         ZeroMemory(&bmi, sizeof(bmi));
863         bmch = (BITMAPCOREHEADER*) (&bmi.bmiHeader);
864         bmch->bcSize = sizeof(BITMAPCOREHEADER);
865
866         if(!hdc){
867             HBITMAP old;
868             hdc = CreateCompatibleDC(0);
869             old = SelectObject(hdc, (HBITMAP)hbm);
870             GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
871             SelectObject(hdc, old);
872             DeleteDC(hdc);
873         }
874         else
875             GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
876
877         (*((GpBitmap**) image))->format = (bmch->bcBitCount << 8) | PixelFormatGDI;
878     }
879     else if(type == PICTYPE_METAFILE || type == PICTYPE_ENHMETAFILE){
880         /* FIXME: missing initialization code */
881         *image = GdipAlloc(sizeof(GpMetafile));
882         if(!*image) return OutOfMemory;
883         (*image)->type = ImageTypeMetafile;
884     }
885     else{
886         *image = GdipAlloc(sizeof(GpImage));
887         if(!*image) return OutOfMemory;
888         (*image)->type = ImageTypeUnknown;
889     }
890
891     (*image)->picture = pic;
892     (*image)->flags   = ImageFlagsNone;
893
894     return Ok;
895 }
896
897 /* FIXME: no ICM */
898 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
899 {
900     return GdipLoadImageFromStream(stream, image);
901 }
902
903 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
904 {
905     static int calls;
906
907     if(!image)
908         return InvalidParameter;
909
910     if(!(calls++))
911         FIXME("not implemented\n");
912
913     return NotImplemented;
914 }
915
916 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
917                                         GDIPCONST CLSID *clsidEncoder,
918                                         GDIPCONST EncoderParameters *encoderParams)
919 {
920     GpStatus stat;
921     IStream *stream;
922
923     if (!image || !filename|| !clsidEncoder)
924         return InvalidParameter;
925
926     if (!(image->picture))
927         return InvalidParameter;
928
929     stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
930     if (stat != Ok)
931         return GenericError;
932
933     stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
934
935     IStream_Release(stream);
936     return stat;
937 }
938
939 /*************************************************************************
940  * Encoding functions -
941  *   These functions encode an image in different image file formats.
942  */
943 #define BITMAP_FORMAT_BMP   0x4d42 /* "BM" */
944 #define BITMAP_FORMAT_JPEG  0xd8ff
945 #define BITMAP_FORMAT_GIF   0x4947
946 #define BITMAP_FORMAT_PNG   0x5089
947 #define BITMAP_FORMAT_APM   0xcdd7
948
949 static GpStatus encode_image_BMP(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
950                                  void **output, unsigned int *output_size)
951 {
952     int num_palette_entries;
953     BITMAPFILEHEADER *bmp_file_hdr;
954     BITMAPINFO *bmp_info_hdr;
955
956     if (bitmap_info->bmiHeader.biClrUsed) {
957         num_palette_entries = bitmap_info->bmiHeader.biClrUsed;
958         if (num_palette_entries > 256) num_palette_entries = 256;
959     } else {
960         if (bitmap_info->bmiHeader.biBitCount <= 8)
961             num_palette_entries = 1 << bitmap_info->bmiHeader.biBitCount;
962         else
963             num_palette_entries = 0;
964     }
965
966     *output_size =
967         sizeof(BITMAPFILEHEADER) +
968         sizeof(BITMAPINFOHEADER) +
969         num_palette_entries * sizeof(RGBQUAD) +
970         bitmap_info->bmiHeader.biSizeImage;
971
972     *output = GdipAlloc(*output_size);
973
974     bmp_file_hdr = (BITMAPFILEHEADER*) *output;
975     bmp_file_hdr->bfType = BITMAP_FORMAT_BMP;
976     bmp_file_hdr->bfSize = *output_size;
977     bmp_file_hdr->bfOffBits =
978         sizeof(BITMAPFILEHEADER) +
979         sizeof(BITMAPINFOHEADER) +
980         num_palette_entries * sizeof (RGBQUAD);
981
982     bmp_info_hdr = (BITMAPINFO*) ((unsigned char*)(*output) + sizeof(BITMAPFILEHEADER));
983     memcpy(bmp_info_hdr, bitmap_info, sizeof(BITMAPINFOHEADER) + num_palette_entries * sizeof(RGBQUAD));
984     memcpy((unsigned char *)(*output) +
985            sizeof(BITMAPFILEHEADER) +
986            sizeof(BITMAPINFOHEADER) +
987            num_palette_entries * sizeof(RGBQUAD),
988            bitmap_bits, bitmap_info->bmiHeader.biSizeImage);
989
990     return Ok;
991 }
992
993 typedef GpStatus encode_image_func(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
994                                    void **output, unsigned int *output_size);
995
996 typedef enum {
997     BMP,
998     NUM_ENCODERS_SUPPORTED
999 } ImageFormat;
1000
1001 static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED];
1002 static encode_image_func *const encode_image_funcs[NUM_ENCODERS_SUPPORTED] = {
1003     encode_image_BMP,
1004 };
1005
1006 /*****************************************************************************
1007  * GdipSaveImageToStream [GDIPLUS.@]
1008  */
1009 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
1010     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
1011 {
1012     GpStatus stat;
1013     HRESULT hr;
1014     short type;
1015     HBITMAP hbmp;
1016     HBITMAP old_hbmp;
1017     HDC hdc;
1018     int bm_is_selected;
1019     BITMAPINFO bmp_info;
1020     LPVOID bmp_bits;
1021     encode_image_func* encode_image;
1022     LPVOID output;
1023     unsigned int output_size;
1024     unsigned int dummy;
1025     int i;
1026
1027     old_hbmp = 0;
1028     output = NULL;
1029     output_size = 0;
1030
1031     if(!image || !stream)
1032         return InvalidParameter;
1033
1034     if (!image->picture)
1035         return GenericError;
1036
1037     hr = IPicture_get_Type(image->picture, &type);
1038     if (FAILED(hr) || type != PICTYPE_BITMAP)
1039         return GenericError;
1040
1041     /* select correct encoder */
1042     encode_image = NULL;
1043     for (i = 0; i < NUM_ENCODERS_SUPPORTED; i++) {
1044         if (IsEqualCLSID(clsid, &codecs[i].Clsid))
1045             encode_image = encode_image_funcs[i];
1046     }
1047     if (encode_image == NULL)
1048         return UnknownImageFormat;
1049
1050     /* extract underlying hbitmap representation from the IPicture */
1051     hr = IPicture_get_Handle(image->picture, (OLE_HANDLE*)&hbmp);
1052     if (FAILED(hr) || !hbmp)
1053         return GenericError;
1054     hr = IPicture_get_CurDC(image->picture, &hdc);
1055     if (FAILED(hr))
1056         return GenericError;
1057     bm_is_selected = (hdc != 0);
1058     if (!bm_is_selected) {
1059         hdc = CreateCompatibleDC(0);
1060         old_hbmp = SelectObject(hdc, hbmp);
1061     }
1062
1063     /* get bits from HBITMAP */
1064     bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
1065     bmp_info.bmiHeader.biBitCount = 0;
1066     GetDIBits(hdc, hbmp, 0, 0, NULL, &bmp_info, DIB_RGB_COLORS);
1067
1068     bmp_bits = GdipAlloc(bmp_info.bmiHeader.biSizeImage);
1069
1070     if (bmp_bits)
1071         GetDIBits(hdc, hbmp, 0, abs(bmp_info.bmiHeader.biHeight), bmp_bits, &bmp_info, DIB_RGB_COLORS);
1072
1073     if (!bm_is_selected) {
1074         SelectObject(hdc, old_hbmp);
1075         DeleteDC(hdc);
1076     }
1077
1078     if (!bmp_bits)
1079         return OutOfMemory;
1080
1081     stat = encode_image(bmp_bits, &bmp_info, &output, &output_size);
1082     if (stat == Ok)
1083         IStream_Write(stream, output, output_size, &dummy);
1084
1085     GdipFree(output);
1086     GdipFree(bmp_bits);
1087
1088     return stat;
1089 }
1090
1091 /*****************************************************************************
1092  * GdipSetImagePalette [GDIPLUS.@]
1093  */
1094 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
1095     GDIPCONST ColorPalette *palette)
1096 {
1097     static int calls;
1098
1099     if(!image || !palette)
1100         return InvalidParameter;
1101
1102     if(!(calls++))
1103         FIXME("not implemented\n");
1104
1105     return NotImplemented;
1106 }
1107
1108 /*************************************************************************
1109  * Encoders -
1110  *   Structures that represent which formats we support for encoding.
1111  */
1112
1113 /* ImageCodecInfo creation routines taken from libgdiplus */
1114 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */
1115 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
1116 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
1117 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
1118 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
1119 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
1120
1121 static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED] =
1122     {
1123         { /* BMP */
1124             /* Clsid */              { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
1125             /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
1126             /* CodecName */          bmp_codecname,
1127             /* DllName */            NULL,
1128             /* FormatDescription */  bmp_format,
1129             /* FilenameExtension */  bmp_extension,
1130             /* MimeType */           bmp_mimetype,
1131             /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
1132             /* Version */            1,
1133             /* SigCount */           1,
1134             /* SigSize */            2,
1135             /* SigPattern */         bmp_sig_pattern,
1136             /* SigMask */            bmp_sig_mask,
1137         },
1138     };
1139
1140 /*****************************************************************************
1141  * GdipGetImageEncodersSize [GDIPLUS.@]
1142  */
1143 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
1144 {
1145     if (!numEncoders || !size)
1146         return InvalidParameter;
1147
1148     *numEncoders = NUM_ENCODERS_SUPPORTED;
1149     *size = sizeof (codecs);
1150
1151     return Ok;
1152 }
1153
1154 /*****************************************************************************
1155  * GdipGetImageEncoders [GDIPLUS.@]
1156  */
1157 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
1158 {
1159     if (!encoders ||
1160         (numEncoders != NUM_ENCODERS_SUPPORTED) ||
1161         (size != sizeof (codecs)))
1162         return GenericError;
1163
1164     memcpy(encoders, codecs, sizeof (codecs));
1165
1166     return Ok;
1167 }
1168
1169 /*****************************************************************************
1170  * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
1171  */
1172 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
1173 {
1174     BITMAP bm;
1175     GpStatus retval;
1176     PixelFormat format;
1177
1178     if(!hbm || !bitmap)
1179         return InvalidParameter;
1180
1181     /* TODO: Support for device-dependent bitmaps */
1182     if(hpal){
1183         FIXME("no support for device-dependent bitmaps\n");
1184         return NotImplemented;
1185     }
1186
1187     if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
1188             return InvalidParameter;
1189
1190     /* TODO: Figure out the correct format for 16, 32, 64 bpp */
1191     switch(bm.bmBitsPixel) {
1192         case 1:
1193             format = PixelFormat1bppIndexed;
1194             break;
1195         case 4:
1196             format = PixelFormat4bppIndexed;
1197             break;
1198         case 8:
1199             format = PixelFormat8bppIndexed;
1200             break;
1201         case 24:
1202             format = PixelFormat24bppRGB;
1203             break;
1204         case 48:
1205             format = PixelFormat48bppRGB;
1206             break;
1207         default:
1208             FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
1209             return InvalidParameter;
1210     }
1211
1212     retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, bm.bmWidthBytes,
1213         format, bm.bmBits, bitmap);
1214
1215     return retval;
1216 }
1217
1218 /*****************************************************************************
1219  * GdipSetEffectParameters [GDIPLUS.@]
1220  */
1221 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
1222     const VOID *params, const UINT size)
1223 {
1224     static int calls;
1225
1226     if(!(calls++))
1227         FIXME("not implemented\n");
1228
1229     return NotImplemented;
1230 }
1231
1232 /*****************************************************************************
1233  * GdipGetImageFlags [GDIPLUS.@]
1234  */
1235 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
1236 {
1237     if(!image || !flags)
1238         return InvalidParameter;
1239
1240     *flags = image->flags;
1241
1242     return Ok;
1243 }