gdiplus: Better error checking in GdipBitmapLockBits.
[wine.git] / dlls / gdiplus / image.c
blob633b2257cad45ec085081b58955e5b789b694062
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
19 #include <stdarg.h>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "wingdi.h"
26 #define COBJMACROS
27 #include "objbase.h"
28 #include "olectl.h"
29 #include "ole2.h"
31 #include "gdiplus.h"
32 #include "gdiplus_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
37 typedef void ImageItemData;
39 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
41 static INT ipicture_pixel_height(IPicture *pic)
43 HDC hdcref;
44 OLE_YSIZE_HIMETRIC y;
46 IPicture_get_Height(pic, &y);
48 hdcref = GetDC(0);
50 y = (UINT)(((REAL)y) * ((REAL)GetDeviceCaps(hdcref, LOGPIXELSY)) /
51 ((REAL)INCH_HIMETRIC));
52 ReleaseDC(0, hdcref);
54 return y;
57 static INT ipicture_pixel_width(IPicture *pic)
59 HDC hdcref;
60 OLE_XSIZE_HIMETRIC x;
62 IPicture_get_Width(pic, &x);
64 hdcref = GetDC(0);
66 x = (UINT)(((REAL)x) * ((REAL)GetDeviceCaps(hdcref, LOGPIXELSX)) /
67 ((REAL)INCH_HIMETRIC));
69 ReleaseDC(0, hdcref);
71 return x;
74 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
75 ARGB *color)
77 static int calls;
78 TRACE("%p %d %d %p\n", bitmap, x, y, color);
80 if(!bitmap || !color)
81 return InvalidParameter;
83 if(!(calls++))
84 FIXME("not implemented\n");
86 *color = 0xdeadbeef;
88 return NotImplemented;
91 /* This function returns a pointer to an array of pixels that represents the
92 * bitmap. The *entire* bitmap is locked according to the lock mode specified by
93 * flags. It is correct behavior that a user who calls this function with write
94 * privileges can write to the whole bitmap (not just the area in rect).
96 * FIXME: only used portion of format is bits per pixel. */
97 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
98 UINT flags, PixelFormat format, BitmapData* lockeddata)
100 BOOL bm_is_selected;
101 INT stride, bitspp = PIXELFORMATBPP(format);
102 OLE_HANDLE hbm;
103 HDC hdc;
104 HBITMAP old = NULL;
105 BITMAPINFO bmi;
106 BYTE *buff = NULL;
107 UINT abs_height;
109 TRACE("%p %p %d %d %p\n", bitmap, rect, flags, format, lockeddata);
111 if(!lockeddata || !bitmap || !rect)
112 return InvalidParameter;
114 if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
115 (rect->Y + rect->Height > bitmap->height) || !flags)
116 return InvalidParameter;
118 if(flags & ImageLockModeUserInputBuf)
119 return NotImplemented;
121 if((bitmap->lockmode & ImageLockModeWrite) || (bitmap->lockmode &&
122 (flags & ImageLockModeWrite)))
123 return WrongState;
125 IPicture_get_Handle(bitmap->image.picture, &hbm);
126 IPicture_get_CurDC(bitmap->image.picture, &hdc);
127 bm_is_selected = (hdc != 0);
129 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
130 bmi.bmiHeader.biBitCount = 0;
132 if(!bm_is_selected){
133 hdc = CreateCompatibleDC(0);
134 old = SelectObject(hdc, (HBITMAP)hbm);
137 /* fill out bmi */
138 GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
140 abs_height = abs(bmi.bmiHeader.biHeight);
141 stride = bmi.bmiHeader.biWidth * bitspp / 8;
142 stride = (stride + 3) & ~3;
144 buff = GdipAlloc(stride * abs_height);
146 bmi.bmiHeader.biBitCount = bitspp;
148 if(buff)
149 GetDIBits(hdc, (HBITMAP)hbm, 0, abs_height, buff, &bmi, DIB_RGB_COLORS);
151 if(!bm_is_selected){
152 SelectObject(hdc, old);
153 DeleteDC(hdc);
156 if(!buff)
157 return OutOfMemory;
159 lockeddata->Width = rect->Width;
160 lockeddata->Height = rect->Height;
161 lockeddata->PixelFormat = format;
162 lockeddata->Reserved = flags;
164 if(bmi.bmiHeader.biHeight > 0){
165 lockeddata->Stride = -stride;
166 lockeddata->Scan0 = buff + (bitspp / 8) * rect->X +
167 stride * (abs_height - 1 - rect->Y);
169 else{
170 lockeddata->Stride = stride;
171 lockeddata->Scan0 = buff + (bitspp / 8) * rect->X + stride * rect->Y;
174 bitmap->lockmode = flags;
175 bitmap->numlocks++;
177 if(flags & ImageLockModeWrite)
178 bitmap->bitmapbits = buff;
180 return Ok;
183 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
184 BitmapData* lockeddata)
186 OLE_HANDLE hbm;
187 HDC hdc;
188 HBITMAP old = NULL;
189 BOOL bm_is_selected;
190 BITMAPINFO bmi;
192 if(!bitmap || !lockeddata)
193 return InvalidParameter;
195 if(!bitmap->lockmode)
196 return WrongState;
198 if(lockeddata->Reserved & ImageLockModeUserInputBuf)
199 return NotImplemented;
201 if(lockeddata->Reserved & ImageLockModeRead){
202 if(!(--bitmap->numlocks))
203 bitmap->lockmode = 0;
205 GdipFree(lockeddata->Scan0);
206 return Ok;
209 IPicture_get_Handle(bitmap->image.picture, &hbm);
210 IPicture_get_CurDC(bitmap->image.picture, &hdc);
211 bm_is_selected = (hdc != 0);
213 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
214 bmi.bmiHeader.biBitCount = 0;
216 if(!bm_is_selected){
217 hdc = CreateCompatibleDC(0);
218 old = SelectObject(hdc, (HBITMAP)hbm);
221 GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
222 bmi.bmiHeader.biBitCount = PIXELFORMATBPP(lockeddata->PixelFormat);
223 SetDIBits(hdc, (HBITMAP)hbm, 0, abs(bmi.bmiHeader.biHeight),
224 bitmap->bitmapbits, &bmi, DIB_RGB_COLORS);
226 if(!bm_is_selected){
227 SelectObject(hdc, old);
228 DeleteDC(hdc);
231 GdipFree(bitmap->bitmapbits);
233 return Ok;
236 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
237 GpBitmap **bitmap)
239 GpStatus stat;
240 IStream *stream;
242 if(!filename || !bitmap)
243 return InvalidParameter;
245 stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
247 if(stat != Ok)
248 return stat;
250 stat = GdipCreateBitmapFromStream(stream, bitmap);
252 if(!stat)
253 IStream_Release(stream);
255 return stat;
258 /* FIXME: this should create a bitmap in the given size with the attributes
259 * (resolution etc.) of the graphics object */
260 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
261 GpGraphics* target, GpBitmap** bitmap)
263 static int calls;
264 GpStatus ret;
266 if(!target || !bitmap)
267 return InvalidParameter;
269 if(!(calls++))
270 FIXME("hacked stub\n");
272 ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat24bppRGB,
273 NULL, bitmap);
275 return ret;
278 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
279 PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
281 BITMAPFILEHEADER *bmfh;
282 BITMAPINFOHEADER *bmih;
283 BYTE *buff;
284 INT datalen, size;
285 IStream *stream;
287 TRACE("%d %d %d %d %p %p\n", width, height, stride, format, scan0, bitmap);
289 if(!bitmap || width <= 0 || height <= 0 || (scan0 && (stride % 4))){
290 *bitmap = NULL;
291 return InvalidParameter;
294 if(scan0 && !stride)
295 return InvalidParameter;
297 /* FIXME: windows allows negative stride (reads backwards from scan0) */
298 if(stride < 0){
299 FIXME("negative stride\n");
300 return InvalidParameter;
303 *bitmap = GdipAlloc(sizeof(GpBitmap));
304 if(!*bitmap) return OutOfMemory;
306 if(stride == 0){
307 stride = width * (PIXELFORMATBPP(format) / 8);
308 stride = (stride + 3) & ~3;
311 datalen = abs(stride * height);
312 size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + datalen;
313 buff = GdipAlloc(size);
314 if(!buff){
315 GdipFree(*bitmap);
316 return OutOfMemory;
319 bmfh = (BITMAPFILEHEADER*) buff;
320 bmih = (BITMAPINFOHEADER*) (bmfh + 1);
322 bmfh->bfType = (((WORD)'M') << 8) + (WORD)'B';
323 bmfh->bfSize = size;
324 bmfh->bfOffBits = size - datalen;
326 bmih->biSize = sizeof(BITMAPINFOHEADER);
327 bmih->biWidth = width;
328 bmih->biHeight = -height;
329 /* FIXME: use the rest of the data from format */
330 bmih->biBitCount = PIXELFORMATBPP(format);
331 bmih->biCompression = BI_RGB;
332 bmih->biSizeImage = datalen;
334 if(scan0)
335 memcpy(bmih + 1, scan0, datalen);
336 else
337 memset(bmih + 1, 0, datalen);
339 if(CreateStreamOnHGlobal(buff, TRUE, &stream) != S_OK){
340 ERR("could not make stream\n");
341 GdipFree(*bitmap);
342 GdipFree(buff);
343 return GenericError;
346 if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
347 (LPVOID*) &((*bitmap)->image.picture)) != S_OK){
348 TRACE("Could not load picture\n");
349 IStream_Release(stream);
350 GdipFree(*bitmap);
351 GdipFree(buff);
352 return GenericError;
355 (*bitmap)->image.type = ImageTypeBitmap;
356 (*bitmap)->width = width;
357 (*bitmap)->height = height;
358 (*bitmap)->format = format;
360 return Ok;
363 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
364 GpBitmap **bitmap)
366 GpStatus stat;
368 stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
370 if(stat != Ok)
371 return stat;
373 if((*bitmap)->image.type != ImageTypeBitmap){
374 IPicture_Release((*bitmap)->image.picture);
375 GdipFree(bitmap);
376 return GenericError; /* FIXME: what error to return? */
379 return Ok;
382 /* FIXME: no icm */
383 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
384 GpBitmap **bitmap)
386 return GdipCreateBitmapFromStream(stream, bitmap);
389 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
391 HDC hdc;
393 if(!image)
394 return InvalidParameter;
396 IPicture_get_CurDC(image->picture, &hdc);
397 DeleteDC(hdc);
398 IPicture_Release(image->picture);
399 GdipFree(image);
401 return Ok;
404 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
406 if(!image || !item)
407 return InvalidParameter;
409 return NotImplemented;
412 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
413 GpUnit *srcUnit)
415 if(!image || !srcRect || !srcUnit)
416 return InvalidParameter;
417 if(image->type == ImageTypeMetafile){
418 memcpy(srcRect, &((GpMetafile*)image)->bounds, sizeof(GpRectF));
419 *srcUnit = ((GpMetafile*)image)->unit;
421 else if(image->type == ImageTypeBitmap){
422 srcRect->X = srcRect->Y = 0.0;
423 srcRect->Width = (REAL) ((GpBitmap*)image)->width;
424 srcRect->Height = (REAL) ((GpBitmap*)image)->height;
425 *srcUnit = UnitPixel;
427 else{
428 srcRect->X = srcRect->Y = 0.0;
429 srcRect->Width = ipicture_pixel_width(image->picture);
430 srcRect->Height = ipicture_pixel_height(image->picture);
431 *srcUnit = UnitPixel;
434 TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
435 srcRect->Width, srcRect->Height, *srcUnit);
437 return Ok;
440 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
441 GpGraphics **graphics)
443 HDC hdc;
445 if(!image || !graphics)
446 return InvalidParameter;
448 if(image->type != ImageTypeBitmap){
449 FIXME("not implemented for image type %d\n", image->type);
450 return NotImplemented;
453 IPicture_get_CurDC(image->picture, &hdc);
455 if(!hdc){
456 hdc = CreateCompatibleDC(0);
457 IPicture_SelectPicture(image->picture, hdc, NULL, NULL);
460 return GdipCreateFromHDC(hdc, graphics);
463 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
465 if(!image || !height)
466 return InvalidParameter;
468 if(image->type == ImageTypeMetafile){
469 HDC hdc = GetDC(0);
471 *height = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
472 ((GpMetafile*)image)->bounds.Height);
474 ReleaseDC(0, hdc);
476 else if(image->type == ImageTypeBitmap)
477 *height = ((GpBitmap*)image)->height;
478 else
479 *height = ipicture_pixel_height(image->picture);
481 TRACE("returning %d\n", *height);
483 return Ok;
486 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
488 static int calls;
490 if(!image || !res)
491 return InvalidParameter;
493 if(!(calls++))
494 FIXME("not implemented\n");
496 return NotImplemented;
499 /* FIXME: test this function for non-bitmap types */
500 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
502 if(!image || !format)
503 return InvalidParameter;
505 if(image->type != ImageTypeBitmap)
506 *format = PixelFormat24bppRGB;
507 else
508 *format = ((GpBitmap*) image)->format;
510 return Ok;
513 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
515 static int calls;
517 if(!image || !format)
518 return InvalidParameter;
520 if(!(calls++))
521 FIXME("not implemented\n");
523 return NotImplemented;
526 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
528 if(!image || !type)
529 return InvalidParameter;
531 *type = image->type;
533 return Ok;
536 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
538 static int calls;
540 if(!image || !res)
541 return InvalidParameter;
543 if(!(calls++))
544 FIXME("not implemented\n");
546 return NotImplemented;
549 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
551 if(!image || !width)
552 return InvalidParameter;
554 if(image->type == ImageTypeMetafile){
555 HDC hdc = GetDC(0);
557 *width = roundr(convert_unit(hdc, ((GpMetafile*)image)->unit) *
558 ((GpMetafile*)image)->bounds.Width);
560 ReleaseDC(0, hdc);
562 else if(image->type == ImageTypeBitmap)
563 *width = ((GpBitmap*)image)->width;
564 else
565 *width = ipicture_pixel_width(image->picture);
567 TRACE("returning %d\n", *width);
569 return Ok;
572 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
573 MetafileHeader * header)
575 static int calls;
577 if(!metafile || !header)
578 return InvalidParameter;
580 if(!(calls++))
581 FIXME("not implemented\n");
583 return Ok;
586 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID pid,
587 UINT* size)
589 static int calls;
591 TRACE("%p %x %p\n", image, pid, size);
593 if(!size || !image)
594 return InvalidParameter;
596 if(!(calls++))
597 FIXME("not implemented\n");
599 return NotImplemented;
602 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
603 GDIPCONST GUID* dimensionID, UINT* count)
605 static int calls;
607 if(!image || !dimensionID || !count)
608 return InvalidParameter;
610 if(!(calls++))
611 FIXME("not implemented\n");
613 return NotImplemented;
616 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
617 GUID* dimensionIDs, UINT count)
619 static int calls;
621 if(!image || !dimensionIDs)
622 return InvalidParameter;
624 if(!(calls++))
625 FIXME("not implemented\n");
627 return Ok;
630 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image,
631 GDIPCONST GUID* dimensionID, UINT frameidx)
633 static int calls;
635 if(!image || !dimensionID)
636 return InvalidParameter;
638 if(!(calls++))
639 FIXME("not implemented\n");
641 return Ok;
644 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
646 IPicture *pic;
647 short type;
649 if(!stream || !image)
650 return InvalidParameter;
652 if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
653 (LPVOID*) &pic) != S_OK){
654 TRACE("Could not load picture\n");
655 return GenericError;
658 IStream_AddRef(stream);
660 IPicture_get_Type(pic, &type);
662 if(type == PICTYPE_BITMAP){
663 BITMAPINFO bmi;
664 BITMAPCOREHEADER* bmch;
665 OLE_HANDLE hbm;
666 HDC hdc;
668 *image = GdipAlloc(sizeof(GpBitmap));
669 if(!*image) return OutOfMemory;
670 (*image)->type = ImageTypeBitmap;
672 (*((GpBitmap**) image))->width = ipicture_pixel_width(pic);
673 (*((GpBitmap**) image))->height = ipicture_pixel_height(pic);
675 /* get the pixel format */
676 IPicture_get_Handle(pic, &hbm);
677 IPicture_get_CurDC(pic, &hdc);
679 bmch = (BITMAPCOREHEADER*) (&bmi.bmiHeader);
680 bmch->bcSize = sizeof(BITMAPCOREHEADER);
682 if(!hdc){
683 HBITMAP old;
684 hdc = CreateCompatibleDC(0);
685 old = SelectObject(hdc, (HBITMAP)hbm);
686 GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
687 SelectObject(hdc, old);
688 DeleteDC(hdc);
690 else
691 GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
693 (*((GpBitmap**) image))->format = (bmch->bcBitCount << 8) | PixelFormatGDI;
695 else if(type == PICTYPE_METAFILE || type == PICTYPE_ENHMETAFILE){
696 /* FIXME: missing initialization code */
697 *image = GdipAlloc(sizeof(GpMetafile));
698 if(!*image) return OutOfMemory;
699 (*image)->type = ImageTypeMetafile;
701 else{
702 *image = GdipAlloc(sizeof(GpImage));
703 if(!*image) return OutOfMemory;
704 (*image)->type = ImageTypeUnknown;
707 (*image)->picture = pic;
709 return Ok;
712 /* FIXME: no ICM */
713 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
715 return GdipLoadImageFromStream(stream, image);
718 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
720 static int calls;
722 if(!image)
723 return InvalidParameter;
725 if(!(calls++))
726 FIXME("not implemented\n");
728 return NotImplemented;
731 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
732 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
734 if(!image || !stream)
735 return InvalidParameter;
737 /* FIXME: CLSID, EncoderParameters not used */
739 IPicture_SaveAsFile(image->picture, stream, FALSE, NULL);
741 return Ok;
744 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
745 GDIPCONST ColorPalette *palette)
747 static int calls;
749 if(!image || !palette)
750 return InvalidParameter;
752 if(!(calls++))
753 FIXME("not implemented\n");
755 return NotImplemented;