ddraw/tests: Add another invalid arguments test for surface QI.
[wine.git] / dlls / gdi32 / bitmap.c
blob768911a5a47f5408b71534f8ef5afae63613b381
1 /*
2 * GDI bitmap objects
4 * Copyright 1993 Alexandre Julliard
5 * 1998 Huw D M Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "gdi_private.h"
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(bitmap);
35 static HGDIOBJ BITMAP_SelectObject( HGDIOBJ handle, HDC hdc );
36 static INT BITMAP_GetObject( HGDIOBJ handle, INT count, LPVOID buffer );
37 static BOOL BITMAP_DeleteObject( HGDIOBJ handle );
39 static const struct gdi_obj_funcs bitmap_funcs =
41 BITMAP_SelectObject, /* pSelectObject */
42 BITMAP_GetObject, /* pGetObjectA */
43 BITMAP_GetObject, /* pGetObjectW */
44 NULL, /* pUnrealizeObject */
45 BITMAP_DeleteObject /* pDeleteObject */
49 /******************************************************************************
50 * CreateBitmap [GDI32.@]
52 * Creates a bitmap with the specified info.
54 * PARAMS
55 * width [I] bitmap width
56 * height [I] bitmap height
57 * planes [I] Number of color planes
58 * bpp [I] Number of bits to identify a color
59 * bits [I] Pointer to array containing color data
61 * RETURNS
62 * Success: Handle to bitmap
63 * Failure: 0
65 HBITMAP WINAPI CreateBitmap( INT width, INT height, UINT planes,
66 UINT bpp, LPCVOID bits )
68 BITMAP bm;
70 bm.bmType = 0;
71 bm.bmWidth = width;
72 bm.bmHeight = height;
73 bm.bmWidthBytes = get_bitmap_stride( width, bpp );
74 bm.bmPlanes = planes;
75 bm.bmBitsPixel = bpp;
76 bm.bmBits = (LPVOID)bits;
78 return CreateBitmapIndirect( &bm );
81 /******************************************************************************
82 * CreateCompatibleBitmap [GDI32.@]
84 * Creates a bitmap compatible with the DC.
86 * PARAMS
87 * hdc [I] Handle to device context
88 * width [I] Width of bitmap
89 * height [I] Height of bitmap
91 * RETURNS
92 * Success: Handle to bitmap
93 * Failure: 0
95 HBITMAP WINAPI CreateCompatibleBitmap( HDC hdc, INT width, INT height)
97 char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
98 BITMAPINFO *bi = (BITMAPINFO *)buffer;
99 DIBSECTION dib;
101 TRACE("(%p,%d,%d)\n", hdc, width, height);
103 if (GetObjectType( hdc ) != OBJ_MEMDC)
104 return CreateBitmap( width, height,
105 GetDeviceCaps(hdc, PLANES), GetDeviceCaps(hdc, BITSPIXEL), NULL );
107 switch (GetObjectW( GetCurrentObject( hdc, OBJ_BITMAP ), sizeof(dib), &dib ))
109 case sizeof(BITMAP): /* A device-dependent bitmap is selected in the DC */
110 return CreateBitmap( width, height, dib.dsBm.bmPlanes, dib.dsBm.bmBitsPixel, NULL );
112 case sizeof(DIBSECTION): /* A DIB section is selected in the DC */
113 bi->bmiHeader = dib.dsBmih;
114 bi->bmiHeader.biWidth = width;
115 bi->bmiHeader.biHeight = height;
116 if (dib.dsBmih.biCompression == BI_BITFIELDS) /* copy the color masks */
117 memcpy(bi->bmiColors, dib.dsBitfields, sizeof(dib.dsBitfields));
118 else if (dib.dsBmih.biBitCount <= 8) /* copy the color table */
119 GetDIBColorTable(hdc, 0, 256, bi->bmiColors);
120 return CreateDIBSection( hdc, bi, DIB_RGB_COLORS, NULL, NULL, 0 );
122 default:
123 return 0;
128 /******************************************************************************
129 * CreateBitmapIndirect [GDI32.@]
131 * Creates a bitmap with the specified info.
133 * PARAMS
134 * bmp [I] Pointer to the bitmap info describing the bitmap
136 * RETURNS
137 * Success: Handle to bitmap
138 * Failure: NULL. Use GetLastError() to determine the cause.
140 * NOTES
141 * If a width or height of 0 is given, a 1x1 monochrome bitmap is returned.
143 HBITMAP WINAPI CreateBitmapIndirect( const BITMAP *bmp )
145 BITMAP bm;
146 BITMAPOBJ *bmpobj;
147 HBITMAP hbitmap;
149 if (!bmp || bmp->bmType)
151 SetLastError( ERROR_INVALID_PARAMETER );
152 return NULL;
155 if (bmp->bmWidth > 0x7ffffff || bmp->bmHeight > 0x7ffffff)
157 SetLastError( ERROR_INVALID_PARAMETER );
158 return 0;
161 bm = *bmp;
163 if (!bm.bmWidth || !bm.bmHeight)
165 return GetStockObject( DEFAULT_BITMAP );
167 else
169 if (bm.bmHeight < 0)
170 bm.bmHeight = -bm.bmHeight;
171 if (bm.bmWidth < 0)
172 bm.bmWidth = -bm.bmWidth;
175 if (bm.bmPlanes != 1)
177 FIXME("planes = %d\n", bm.bmPlanes);
178 SetLastError( ERROR_INVALID_PARAMETER );
179 return NULL;
182 /* Windows only uses 1, 4, 8, 16, 24 and 32 bpp */
183 if(bm.bmBitsPixel == 1) bm.bmBitsPixel = 1;
184 else if(bm.bmBitsPixel <= 4) bm.bmBitsPixel = 4;
185 else if(bm.bmBitsPixel <= 8) bm.bmBitsPixel = 8;
186 else if(bm.bmBitsPixel <= 16) bm.bmBitsPixel = 16;
187 else if(bm.bmBitsPixel <= 24) bm.bmBitsPixel = 24;
188 else if(bm.bmBitsPixel <= 32) bm.bmBitsPixel = 32;
189 else {
190 WARN("Invalid bmBitsPixel %d, returning ERROR_INVALID_PARAMETER\n", bm.bmBitsPixel);
191 SetLastError(ERROR_INVALID_PARAMETER);
192 return NULL;
195 /* Windows ignores the provided bm.bmWidthBytes */
196 bm.bmWidthBytes = get_bitmap_stride( bm.bmWidth, bm.bmBitsPixel );
197 /* XP doesn't allow creating bitmaps larger than 128 MB */
198 if (bm.bmHeight > 128 * 1024 * 1024 / bm.bmWidthBytes)
200 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
201 return 0;
204 /* Create the BITMAPOBJ */
205 if (!(bmpobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*bmpobj) )))
207 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
208 return 0;
211 bmpobj->dib.dsBm = bm;
212 bmpobj->dib.dsBm.bmBits = NULL;
214 if (!(hbitmap = alloc_gdi_handle( bmpobj, OBJ_BITMAP, &bitmap_funcs )))
216 HeapFree( GetProcessHeap(), 0, bmpobj );
217 return 0;
220 if (bm.bmBits)
221 SetBitmapBits( hbitmap, bm.bmHeight * bm.bmWidthBytes, bm.bmBits );
223 TRACE("%dx%d, bpp %d planes %d: returning %p\n", bm.bmWidth, bm.bmHeight,
224 bm.bmBitsPixel, bm.bmPlanes, hbitmap);
226 return hbitmap;
230 /***********************************************************************
231 * GetBitmapBits [GDI32.@]
233 * Copies bitmap bits of bitmap to buffer.
235 * RETURNS
236 * Success: Number of bytes copied
237 * Failure: 0
239 LONG WINAPI GetBitmapBits(
240 HBITMAP hbitmap, /* [in] Handle to bitmap */
241 LONG count, /* [in] Number of bytes to copy */
242 LPVOID bits) /* [out] Pointer to buffer to receive bits */
244 char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
245 BITMAPINFO *info = (BITMAPINFO *)buffer;
246 struct gdi_image_bits src_bits;
247 struct bitblt_coords src;
248 int dst_stride, max, ret;
249 BITMAPOBJ *bmp = GDI_GetObjPtr( hbitmap, OBJ_BITMAP );
251 if (!bmp) return 0;
253 dst_stride = get_bitmap_stride( bmp->dib.dsBm.bmWidth, bmp->dib.dsBm.bmBitsPixel );
254 ret = max = dst_stride * bmp->dib.dsBm.bmHeight;
255 if (!bits) goto done;
256 if (count < 0 || count > max) count = max;
257 ret = count;
259 src.visrect.left = 0;
260 src.visrect.right = bmp->dib.dsBm.bmWidth;
261 src.visrect.top = 0;
262 src.visrect.bottom = (count + dst_stride - 1) / dst_stride;
263 src.x = src.y = 0;
264 src.width = src.visrect.right - src.visrect.left;
265 src.height = src.visrect.bottom - src.visrect.top;
267 if (!get_image_from_bitmap( bmp, info, &src_bits, &src ))
269 const char *src_ptr = src_bits.ptr;
270 int src_stride = info->bmiHeader.biSizeImage / abs( info->bmiHeader.biHeight );
272 /* GetBitmapBits returns 16-bit aligned data */
274 if (info->bmiHeader.biHeight > 0)
276 src_ptr += (info->bmiHeader.biHeight - 1) * src_stride;
277 src_stride = -src_stride;
279 src_ptr += src.visrect.top * src_stride;
281 if (src_stride == dst_stride) memcpy( bits, src_ptr, count );
282 else while (count > 0)
284 memcpy( bits, src_ptr, min( count, dst_stride ) );
285 src_ptr += src_stride;
286 bits = (char *)bits + dst_stride;
287 count -= dst_stride;
289 if (src_bits.free) src_bits.free( &src_bits );
291 else ret = 0;
293 done:
294 GDI_ReleaseObj( hbitmap );
295 return ret;
299 /******************************************************************************
300 * SetBitmapBits [GDI32.@]
302 * Sets bits of color data for a bitmap.
304 * RETURNS
305 * Success: Number of bytes used in setting the bitmap bits
306 * Failure: 0
308 LONG WINAPI SetBitmapBits(
309 HBITMAP hbitmap, /* [in] Handle to bitmap */
310 LONG count, /* [in] Number of bytes in bitmap array */
311 LPCVOID bits) /* [in] Address of array with bitmap bits */
313 char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
314 BITMAPINFO *info = (BITMAPINFO *)buffer;
315 BITMAPOBJ *bmp;
316 DWORD err;
317 int i, src_stride, dst_stride;
318 struct bitblt_coords src, dst;
319 struct gdi_image_bits src_bits;
320 HRGN clip = NULL;
322 if (!bits) return 0;
324 bmp = GDI_GetObjPtr( hbitmap, OBJ_BITMAP );
325 if (!bmp) return 0;
327 if (count < 0) {
328 WARN("(%d): Negative number of bytes passed???\n", count );
329 count = -count;
332 src_stride = get_bitmap_stride( bmp->dib.dsBm.bmWidth, bmp->dib.dsBm.bmBitsPixel );
333 count = min( count, src_stride * bmp->dib.dsBm.bmHeight );
335 dst_stride = get_dib_stride( bmp->dib.dsBm.bmWidth, bmp->dib.dsBm.bmBitsPixel );
337 src.visrect.left = src.x = 0;
338 src.visrect.top = src.y = 0;
339 src.visrect.right = src.width = bmp->dib.dsBm.bmWidth;
340 src.visrect.bottom = src.height = (count + src_stride - 1 ) / src_stride;
341 dst = src;
343 if (count % src_stride)
345 HRGN last_row;
346 int extra_pixels = ((count % src_stride) << 3) / bmp->dib.dsBm.bmBitsPixel;
348 if ((count % src_stride << 3) % bmp->dib.dsBm.bmBitsPixel)
349 FIXME( "Unhandled partial pixel\n" );
350 clip = CreateRectRgn( src.visrect.left, src.visrect.top,
351 src.visrect.right, src.visrect.bottom - 1 );
352 last_row = CreateRectRgn( src.visrect.left, src.visrect.bottom - 1,
353 src.visrect.left + extra_pixels, src.visrect.bottom );
354 CombineRgn( clip, clip, last_row, RGN_OR );
355 DeleteObject( last_row );
358 TRACE("(%p, %d, %p) %dx%d %d bpp fetched height: %d\n",
359 hbitmap, count, bits, bmp->dib.dsBm.bmWidth, bmp->dib.dsBm.bmHeight,
360 bmp->dib.dsBm.bmBitsPixel, src.height );
362 if (src_stride == dst_stride)
364 src_bits.ptr = (void *)bits;
365 src_bits.is_copy = FALSE;
366 src_bits.free = NULL;
368 else
370 if (!(src_bits.ptr = HeapAlloc( GetProcessHeap(), 0, dst.height * dst_stride )))
372 GDI_ReleaseObj( hbitmap );
373 return 0;
375 src_bits.is_copy = TRUE;
376 src_bits.free = free_heap_bits;
377 for (i = 0; i < count / src_stride; i++)
378 memcpy( (char *)src_bits.ptr + i * dst_stride, (char *)bits + i * src_stride, src_stride );
379 if (count % src_stride)
380 memcpy( (char *)src_bits.ptr + i * dst_stride, (char *)bits + i * src_stride, count % src_stride );
383 /* query the color info */
384 info->bmiHeader.biSize = sizeof(info->bmiHeader);
385 info->bmiHeader.biPlanes = 1;
386 info->bmiHeader.biBitCount = bmp->dib.dsBm.bmBitsPixel;
387 info->bmiHeader.biCompression = BI_RGB;
388 info->bmiHeader.biXPelsPerMeter = 0;
389 info->bmiHeader.biYPelsPerMeter = 0;
390 info->bmiHeader.biClrUsed = 0;
391 info->bmiHeader.biClrImportant = 0;
392 info->bmiHeader.biWidth = 0;
393 info->bmiHeader.biHeight = 0;
394 info->bmiHeader.biSizeImage = 0;
395 err = put_image_into_bitmap( bmp, 0, info, NULL, NULL, NULL );
397 if (!err || err == ERROR_BAD_FORMAT)
399 info->bmiHeader.biWidth = bmp->dib.dsBm.bmWidth;
400 info->bmiHeader.biHeight = -dst.height;
401 info->bmiHeader.biSizeImage = dst.height * dst_stride;
402 err = put_image_into_bitmap( bmp, clip, info, &src_bits, &src, &dst );
404 if (err) count = 0;
406 if (clip) DeleteObject( clip );
407 if (src_bits.free) src_bits.free( &src_bits );
408 GDI_ReleaseObj( hbitmap );
409 return count;
413 /***********************************************************************
414 * BITMAP_SelectObject
416 static HGDIOBJ BITMAP_SelectObject( HGDIOBJ handle, HDC hdc )
418 HGDIOBJ ret;
419 BITMAPOBJ *bitmap;
420 DC *dc;
421 PHYSDEV physdev;
423 if (!(dc = get_dc_ptr( hdc ))) return 0;
425 if (GetObjectType( hdc ) != OBJ_MEMDC)
427 ret = 0;
428 goto done;
430 ret = dc->hBitmap;
431 if (handle == dc->hBitmap) goto done; /* nothing to do */
433 if (!(bitmap = GDI_GetObjPtr( handle, OBJ_BITMAP )))
435 ret = 0;
436 goto done;
439 if (handle != GetStockObject(DEFAULT_BITMAP) && GDI_get_ref_count( handle ))
441 WARN( "Bitmap already selected in another DC\n" );
442 GDI_ReleaseObj( handle );
443 ret = 0;
444 goto done;
447 if (bitmap->dib.dsBm.bmBitsPixel != 1 &&
448 bitmap->dib.dsBm.bmBitsPixel != GetDeviceCaps( hdc, BITSPIXEL ))
450 WARN( "Wrong format bitmap %u bpp\n", bitmap->dib.dsBm.bmBitsPixel );
451 GDI_ReleaseObj( handle );
452 ret = 0;
453 goto done;
456 physdev = GET_DC_PHYSDEV( dc, pSelectBitmap );
457 if (!physdev->funcs->pSelectBitmap( physdev, handle ))
459 GDI_ReleaseObj( handle );
460 ret = 0;
462 else
464 dc->hBitmap = handle;
465 GDI_inc_ref_count( handle );
466 dc->dirty = 0;
467 dc->vis_rect.left = 0;
468 dc->vis_rect.top = 0;
469 dc->vis_rect.right = bitmap->dib.dsBm.bmWidth;
470 dc->vis_rect.bottom = bitmap->dib.dsBm.bmHeight;
471 dc->device_rect = dc->vis_rect;
472 GDI_ReleaseObj( handle );
473 DC_InitDC( dc );
474 GDI_dec_ref_count( ret );
477 done:
478 release_dc_ptr( dc );
479 return ret;
483 /***********************************************************************
484 * BITMAP_DeleteObject
486 static BOOL BITMAP_DeleteObject( HGDIOBJ handle )
488 BITMAPOBJ *bmp = free_gdi_handle( handle );
490 if (!bmp) return FALSE;
491 HeapFree( GetProcessHeap(), 0, bmp->dib.dsBm.bmBits );
492 return HeapFree( GetProcessHeap(), 0, bmp );
496 /***********************************************************************
497 * BITMAP_GetObject
499 static INT BITMAP_GetObject( HGDIOBJ handle, INT count, LPVOID buffer )
501 INT ret = 0;
502 BITMAPOBJ *bmp = GDI_GetObjPtr( handle, OBJ_BITMAP );
504 if (!bmp) return 0;
506 if (!buffer) ret = sizeof(BITMAP);
507 else if (count >= sizeof(BITMAP))
509 BITMAP *bitmap = buffer;
510 *bitmap = bmp->dib.dsBm;
511 bitmap->bmBits = NULL;
512 ret = sizeof(BITMAP);
514 GDI_ReleaseObj( handle );
515 return ret;
519 /******************************************************************************
520 * CreateDiscardableBitmap [GDI32.@]
522 * Creates a discardable bitmap.
524 * RETURNS
525 * Success: Handle to bitmap
526 * Failure: NULL
528 HBITMAP WINAPI CreateDiscardableBitmap(
529 HDC hdc, /* [in] Handle to device context */
530 INT width, /* [in] Bitmap width */
531 INT height) /* [in] Bitmap height */
533 return CreateCompatibleBitmap( hdc, width, height );
537 /******************************************************************************
538 * GetBitmapDimensionEx [GDI32.@]
540 * Retrieves dimensions of a bitmap.
542 * RETURNS
543 * Success: TRUE
544 * Failure: FALSE
546 BOOL WINAPI GetBitmapDimensionEx(
547 HBITMAP hbitmap, /* [in] Handle to bitmap */
548 LPSIZE size) /* [out] Address of struct receiving dimensions */
550 BITMAPOBJ * bmp = GDI_GetObjPtr( hbitmap, OBJ_BITMAP );
551 if (!bmp) return FALSE;
552 *size = bmp->size;
553 GDI_ReleaseObj( hbitmap );
554 return TRUE;
558 /******************************************************************************
559 * SetBitmapDimensionEx [GDI32.@]
561 * Assigns dimensions to a bitmap.
562 * MSDN says that this function will fail if hbitmap is a handle created by
563 * CreateDIBSection, but that's not true on Windows 2000.
565 * RETURNS
566 * Success: TRUE
567 * Failure: FALSE
569 BOOL WINAPI SetBitmapDimensionEx(
570 HBITMAP hbitmap, /* [in] Handle to bitmap */
571 INT x, /* [in] Bitmap width */
572 INT y, /* [in] Bitmap height */
573 LPSIZE prevSize) /* [out] Address of structure for orig dims */
575 BITMAPOBJ * bmp = GDI_GetObjPtr( hbitmap, OBJ_BITMAP );
576 if (!bmp) return FALSE;
577 if (prevSize) *prevSize = bmp->size;
578 bmp->size.cx = x;
579 bmp->size.cy = y;
580 GDI_ReleaseObj( hbitmap );
581 return TRUE;