gdi32: Add a helper function that implements PutImage on a bitmap.
[wine] / dlls / gdi32 / bitmap.c
1 /*
2  * GDI bitmap objects
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           1998 Huw D M Davies
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "gdi_private.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(bitmap);
33
34
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 );
38
39 static const struct gdi_obj_funcs bitmap_funcs =
40 {
41     BITMAP_SelectObject,  /* pSelectObject */
42     BITMAP_GetObject,     /* pGetObjectA */
43     BITMAP_GetObject,     /* pGetObjectW */
44     NULL,                 /* pUnrealizeObject */
45     BITMAP_DeleteObject   /* pDeleteObject */
46 };
47
48
49 /******************************************************************************
50  * CreateBitmap [GDI32.@]
51  *
52  * Creates a bitmap with the specified info.
53  *
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
60  *
61  * RETURNS
62  *    Success: Handle to bitmap
63  *    Failure: 0
64  */
65 HBITMAP WINAPI CreateBitmap( INT width, INT height, UINT planes,
66                              UINT bpp, LPCVOID bits )
67 {
68     BITMAP bm;
69
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;
77
78     return CreateBitmapIndirect( &bm );
79 }
80
81 /******************************************************************************
82  * CreateCompatibleBitmap [GDI32.@]
83  *
84  * Creates a bitmap compatible with the DC.
85  *
86  * PARAMS
87  *    hdc    [I] Handle to device context
88  *    width  [I] Width of bitmap
89  *    height [I] Height of bitmap
90  *
91  * RETURNS
92  *    Success: Handle to bitmap
93  *    Failure: 0
94  */
95 HBITMAP WINAPI CreateCompatibleBitmap( HDC hdc, INT width, INT height)
96 {
97     char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
98     BITMAPINFO *bi = (BITMAPINFO *)buffer;
99     DIBSECTION dib;
100
101     TRACE("(%p,%d,%d)\n", hdc, width, height);
102
103     if (GetObjectType( hdc ) != OBJ_MEMDC)
104         return CreateBitmap( width, height,
105                              GetDeviceCaps(hdc, PLANES), GetDeviceCaps(hdc, BITSPIXEL), NULL );
106
107     switch (GetObjectW( GetCurrentObject( hdc, OBJ_BITMAP ), sizeof(dib), &dib ))
108     {
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 );
111
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 );
121
122     default:
123         return 0;
124     }
125 }
126
127
128 /******************************************************************************
129  * CreateBitmapIndirect [GDI32.@]
130  *
131  * Creates a bitmap with the specified info.
132  *
133  * PARAMS
134  *  bmp [I] Pointer to the bitmap info describing the bitmap
135  *
136  * RETURNS
137  *    Success: Handle to bitmap
138  *    Failure: NULL. Use GetLastError() to determine the cause.
139  *
140  * NOTES
141  *  If a width or height of 0 are given, a 1x1 monochrome bitmap is returned.
142  */
143 HBITMAP WINAPI CreateBitmapIndirect( const BITMAP *bmp )
144 {
145     BITMAP bm;
146     BITMAPOBJ *bmpobj;
147     HBITMAP hbitmap;
148
149     if (!bmp || bmp->bmType)
150     {
151         SetLastError( ERROR_INVALID_PARAMETER );
152         return NULL;
153     }
154
155     if (bmp->bmWidth > 0x7ffffff || bmp->bmHeight > 0x7ffffff)
156     {
157         SetLastError( ERROR_INVALID_PARAMETER );
158         return 0;
159     }
160
161     bm = *bmp;
162
163     if (!bm.bmWidth || !bm.bmHeight)
164     {
165         return GetStockObject( DEFAULT_BITMAP );
166     }
167     else
168     {
169         if (bm.bmHeight < 0)
170             bm.bmHeight = -bm.bmHeight;
171         if (bm.bmWidth < 0)
172             bm.bmWidth = -bm.bmWidth;
173     }
174
175     if (bm.bmPlanes != 1)
176     {
177         FIXME("planes = %d\n", bm.bmPlanes);
178         SetLastError( ERROR_INVALID_PARAMETER );
179         return NULL;
180     }
181
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;
193     }
194
195     /* Windows ignores the provided bm.bmWidthBytes */
196     bm.bmWidthBytes = get_bitmap_stride( bm.bmWidth, bm.bmBitsPixel );
197     /* XP doesn't allow to create bitmaps larger than 128 Mb */
198     if (bm.bmHeight > 128 * 1024 * 1024 / bm.bmWidthBytes)
199     {
200         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
201         return 0;
202     }
203
204     /* Create the BITMAPOBJ */
205     if (!(bmpobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*bmpobj) )))
206     {
207         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
208         return 0;
209     }
210
211     bmpobj->dib.dsBm = bm;
212     bmpobj->dib.dsBm.bmBits = NULL;
213
214     if (!(hbitmap = alloc_gdi_handle( &bmpobj->header, OBJ_BITMAP, &bitmap_funcs )))
215     {
216         HeapFree( GetProcessHeap(), 0, bmpobj );
217         return 0;
218     }
219
220     if (bm.bmBits)
221         SetBitmapBits( hbitmap, bm.bmHeight * bm.bmWidthBytes, bm.bmBits );
222
223     TRACE("%dx%d, bpp %d planes %d: returning %p\n", bm.bmWidth, bm.bmHeight,
224           bm.bmBitsPixel, bm.bmPlanes, hbitmap);
225
226     return hbitmap;
227 }
228
229
230 /***********************************************************************
231  * GetBitmapBits [GDI32.@]
232  *
233  * Copies bitmap bits of bitmap to buffer.
234  *
235  * RETURNS
236  *    Success: Number of bytes copied
237  *    Failure: 0
238  */
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 */
243 {
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 );
250
251     if (!bmp) return 0;
252
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 > max) count = max;
257     ret = count;
258
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;
266
267     if (!get_image_from_bitmap( bmp, info, &src_bits, &src ))
268     {
269         const char *src_ptr = src_bits.ptr;
270         int src_stride = get_dib_stride( info->bmiHeader.biWidth, info->bmiHeader.biBitCount );
271
272         /* GetBitmapBits returns 16-bit aligned data */
273
274         if (info->bmiHeader.biHeight > 0)
275         {
276             src_ptr += (info->bmiHeader.biHeight - 1) * src_stride;
277             src_stride = -src_stride;
278         }
279         src_ptr += src.visrect.top * src_stride;
280
281         if (src_stride == dst_stride) memcpy( bits, src_ptr, count );
282         else while (count > 0)
283         {
284             memcpy( bits, src_ptr, min( count, dst_stride ) );
285             src_ptr += src_stride;
286             bits = (char *)bits + dst_stride;
287             count -= dst_stride;
288         }
289         if (src_bits.free) src_bits.free( &src_bits );
290     }
291     else ret = 0;
292
293 done:
294     GDI_ReleaseObj( hbitmap );
295     return ret;
296 }
297
298
299 /******************************************************************************
300  * SetBitmapBits [GDI32.@]
301  *
302  * Sets bits of color data for a bitmap.
303  *
304  * RETURNS
305  *    Success: Number of bytes used in setting the bitmap bits
306  *    Failure: 0
307  */
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 */
312 {
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;
321
322     if (!bits) return 0;
323
324     bmp = GDI_GetObjPtr( hbitmap, OBJ_BITMAP );
325     if (!bmp) return 0;
326
327     if (count < 0) {
328         WARN("(%d): Negative number of bytes passed???\n", count );
329         count = -count;
330     }
331
332     src_stride = get_bitmap_stride( bmp->dib.dsBm.bmWidth, bmp->dib.dsBm.bmBitsPixel );
333     count = min( count, src_stride * bmp->dib.dsBm.bmHeight );
334
335     dst_stride = get_dib_stride( bmp->dib.dsBm.bmWidth, bmp->dib.dsBm.bmBitsPixel );
336
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;
342
343     if (count % src_stride)
344     {
345         HRGN last_row;
346         int extra_pixels = ((count % src_stride) << 3) / bmp->dib.dsBm.bmBitsPixel;
347
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 );
356     }
357
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 );
361
362     if (src_stride == dst_stride)
363     {
364         src_bits.ptr = (void *)bits;
365         src_bits.is_copy = FALSE;
366         src_bits.free = NULL;
367     }
368     else
369     {
370         if (!(src_bits.ptr = HeapAlloc( GetProcessHeap(), 0, dst.height * dst_stride )))
371         {
372             GDI_ReleaseObj( hbitmap );
373             return 0;
374         }
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 );
381     }
382
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 );
396
397     if (!err || err == ERROR_BAD_FORMAT)
398     {
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 );
403     }
404     if (err) count = 0;
405
406     if (clip) DeleteObject( clip );
407     if (src_bits.free) src_bits.free( &src_bits );
408     GDI_ReleaseObj( hbitmap );
409     return count;
410 }
411
412
413 /***********************************************************************
414  *           BITMAP_SelectObject
415  */
416 static HGDIOBJ BITMAP_SelectObject( HGDIOBJ handle, HDC hdc )
417 {
418     HGDIOBJ ret;
419     BITMAPOBJ *bitmap;
420     DC *dc;
421     PHYSDEV physdev;
422
423     if (!(dc = get_dc_ptr( hdc ))) return 0;
424
425     if (GetObjectType( hdc ) != OBJ_MEMDC)
426     {
427         ret = 0;
428         goto done;
429     }
430     ret = dc->hBitmap;
431     if (handle == dc->hBitmap) goto done;  /* nothing to do */
432
433     if (!(bitmap = GDI_GetObjPtr( handle, OBJ_BITMAP )))
434     {
435         ret = 0;
436         goto done;
437     }
438
439     if (bitmap->header.selcount && (handle != GetStockObject(DEFAULT_BITMAP)))
440     {
441         WARN( "Bitmap already selected in another DC\n" );
442         GDI_ReleaseObj( handle );
443         ret = 0;
444         goto done;
445     }
446
447     if (bitmap->dib.dsBm.bmBitsPixel != 1 &&
448         bitmap->dib.dsBm.bmBitsPixel != GetDeviceCaps( hdc, BITSPIXEL ))
449     {
450         WARN( "Wrong format bitmap %u bpp\n", bitmap->dib.dsBm.bmBitsPixel );
451         GDI_ReleaseObj( handle );
452         ret = 0;
453         goto done;
454     }
455
456     physdev = GET_DC_PHYSDEV( dc, pSelectBitmap );
457     if (!physdev->funcs->pSelectBitmap( physdev, handle ))
458     {
459         GDI_ReleaseObj( handle );
460         ret = 0;
461     }
462     else
463     {
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 );
475     }
476
477  done:
478     release_dc_ptr( dc );
479     return ret;
480 }
481
482
483 /***********************************************************************
484  *           BITMAP_DeleteObject
485  */
486 static BOOL BITMAP_DeleteObject( HGDIOBJ handle )
487 {
488     BITMAPOBJ *bmp = free_gdi_handle( handle );
489
490     if (!bmp) return FALSE;
491     HeapFree( GetProcessHeap(), 0, bmp->dib.dsBm.bmBits );
492     return HeapFree( GetProcessHeap(), 0, bmp );
493 }
494
495
496 /***********************************************************************
497  *           BITMAP_GetObject
498  */
499 static INT BITMAP_GetObject( HGDIOBJ handle, INT count, LPVOID buffer )
500 {
501     INT ret = 0;
502     BITMAPOBJ *bmp = GDI_GetObjPtr( handle, OBJ_BITMAP );
503
504     if (!bmp) return 0;
505
506     if (!buffer) ret = sizeof(BITMAP);
507     else if (count >= sizeof(BITMAP))
508     {
509         BITMAP *bitmap = buffer;
510         *bitmap = bmp->dib.dsBm;
511         bitmap->bmBits = NULL;
512         ret = sizeof(BITMAP);
513     }
514     GDI_ReleaseObj( handle );
515     return ret;
516 }
517
518
519 /******************************************************************************
520  * CreateDiscardableBitmap [GDI32.@]
521  *
522  * Creates a discardable bitmap.
523  *
524  * RETURNS
525  *    Success: Handle to bitmap
526  *    Failure: NULL
527  */
528 HBITMAP WINAPI CreateDiscardableBitmap(
529     HDC hdc,    /* [in] Handle to device context */
530     INT width,  /* [in] Bitmap width */
531     INT height) /* [in] Bitmap height */
532 {
533     return CreateCompatibleBitmap( hdc, width, height );
534 }
535
536
537 /******************************************************************************
538  * GetBitmapDimensionEx [GDI32.@]
539  *
540  * Retrieves dimensions of a bitmap.
541  *
542  * RETURNS
543  *    Success: TRUE
544  *    Failure: FALSE
545  */
546 BOOL WINAPI GetBitmapDimensionEx(
547     HBITMAP hbitmap, /* [in]  Handle to bitmap */
548     LPSIZE size)     /* [out] Address of struct receiving dimensions */
549 {
550     BITMAPOBJ * bmp = GDI_GetObjPtr( hbitmap, OBJ_BITMAP );
551     if (!bmp) return FALSE;
552     *size = bmp->size;
553     GDI_ReleaseObj( hbitmap );
554     return TRUE;
555 }
556
557
558 /******************************************************************************
559  * SetBitmapDimensionEx [GDI32.@]
560  *
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.
564  *
565  * RETURNS
566  *    Success: TRUE
567  *    Failure: FALSE
568  */
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 */
574 {
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;
582 }