/* * GDI bitmap objects * * Copyright 1993 Alexandre Julliard * 1998 Huw D M Davies * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include <stdlib.h> #include <string.h> #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "wine/winbase16.h" #include "gdi_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(bitmap); static HGDIOBJ BITMAP_SelectObject( HGDIOBJ handle, HDC hdc ); static INT BITMAP_GetObject( HGDIOBJ handle, void *obj, INT count, LPVOID buffer ); static BOOL BITMAP_DeleteObject( HGDIOBJ handle, void *obj ); static const struct gdi_obj_funcs bitmap_funcs = { BITMAP_SelectObject, /* pSelectObject */ BITMAP_GetObject, /* pGetObjectA */ BITMAP_GetObject, /* pGetObjectW */ NULL, /* pUnrealizeObject */ BITMAP_DeleteObject /* pDeleteObject */ }; /*********************************************************************** * BITMAP_GetWidthBytes * * Return number of bytes taken by a scanline of 16-bit aligned Windows DDB * data. */ INT BITMAP_GetWidthBytes( INT bmWidth, INT bpp ) { switch(bpp) { case 1: return 2 * ((bmWidth+15) >> 4); case 24: bmWidth *= 3; /* fall through */ case 8: return bmWidth + (bmWidth & 1); case 32: return bmWidth * 4; case 16: case 15: return bmWidth * 2; case 4: return 2 * ((bmWidth+3) >> 2); default: WARN("Unknown depth %d, please report.\n", bpp ); } return -1; } /****************************************************************************** * CreateBitmap [GDI32.@] * * Creates a bitmap with the specified info. * * PARAMS * width [I] bitmap width * height [I] bitmap height * planes [I] Number of color planes * bpp [I] Number of bits to identify a color * bits [I] Pointer to array containing color data * * RETURNS * Success: Handle to bitmap * Failure: 0 */ HBITMAP WINAPI CreateBitmap( INT width, INT height, UINT planes, UINT bpp, LPCVOID bits ) { BITMAP bm; bm.bmType = 0; bm.bmWidth = width; bm.bmHeight = height; bm.bmWidthBytes = BITMAP_GetWidthBytes( width, bpp ); bm.bmPlanes = planes; bm.bmBitsPixel = bpp; bm.bmBits = (LPVOID)bits; return CreateBitmapIndirect( &bm ); } /****************************************************************************** * CreateCompatibleBitmap [GDI32.@] * * Creates a bitmap compatible with the DC. * * PARAMS * hdc [I] Handle to device context * width [I] Width of bitmap * height [I] Height of bitmap * * RETURNS * Success: Handle to bitmap * Failure: 0 */ HBITMAP WINAPI CreateCompatibleBitmap( HDC hdc, INT width, INT height) { HBITMAP hbmpRet = 0; TRACE("(%p,%d,%d) =\n", hdc, width, height); if (GetObjectType( hdc ) != OBJ_MEMDC) { hbmpRet = CreateBitmap(width, height, GetDeviceCaps(hdc, PLANES), GetDeviceCaps(hdc, BITSPIXEL), NULL); } else /* Memory DC */ { DIBSECTION dib; HBITMAP bitmap = GetCurrentObject( hdc, OBJ_BITMAP ); INT size = GetObjectW( bitmap, sizeof(dib), &dib ); if (!size) return 0; if (size == sizeof(BITMAP)) { /* A device-dependent bitmap is selected in the DC */ hbmpRet = CreateBitmap(width, height, dib.dsBm.bmPlanes, dib.dsBm.bmBitsPixel, NULL); } else { /* A DIB section is selected in the DC */ BITMAPINFO *bi; void *bits; /* Allocate memory for a BITMAPINFOHEADER structure and a color table. The maximum number of colors in a color table is 256 which corresponds to a bitmap with depth 8. Bitmaps with higher depths don't have color tables. */ bi = HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); if (bi) { bi->bmiHeader.biSize = sizeof(bi->bmiHeader); bi->bmiHeader.biWidth = width; bi->bmiHeader.biHeight = height; bi->bmiHeader.biPlanes = dib.dsBmih.biPlanes; bi->bmiHeader.biBitCount = dib.dsBmih.biBitCount; bi->bmiHeader.biCompression = dib.dsBmih.biCompression; bi->bmiHeader.biSizeImage = 0; bi->bmiHeader.biXPelsPerMeter = dib.dsBmih.biXPelsPerMeter; bi->bmiHeader.biYPelsPerMeter = dib.dsBmih.biYPelsPerMeter; bi->bmiHeader.biClrUsed = dib.dsBmih.biClrUsed; bi->bmiHeader.biClrImportant = dib.dsBmih.biClrImportant; if (bi->bmiHeader.biCompression == BI_BITFIELDS) { /* Copy the color masks */ CopyMemory(bi->bmiColors, dib.dsBitfields, 3 * sizeof(DWORD)); } else if (bi->bmiHeader.biBitCount <= 8) { /* Copy the color table */ GetDIBColorTable(hdc, 0, 256, bi->bmiColors); } hbmpRet = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, &bits, NULL, 0); HeapFree(GetProcessHeap(), 0, bi); } } } TRACE("\t\t%p\n", hbmpRet); return hbmpRet; } /****************************************************************************** * CreateBitmapIndirect [GDI32.@] * * Creates a bitmap with the specified info. * * PARAMS * bmp [I] Pointer to the bitmap info describing the bitmap * * RETURNS * Success: Handle to bitmap * Failure: NULL. Use GetLastError() to determine the cause. * * NOTES * If a width or height of 0 are given, a 1x1 monochrome bitmap is returned. */ HBITMAP WINAPI CreateBitmapIndirect( const BITMAP *bmp ) { BITMAP bm; BITMAPOBJ *bmpobj; HBITMAP hbitmap; if (!bmp || bmp->bmType) { SetLastError( ERROR_INVALID_PARAMETER ); return NULL; } if (bmp->bmWidth > 0x7ffffff || bmp->bmHeight > 0x7ffffff) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } bm = *bmp; if (!bm.bmWidth || !bm.bmHeight) { return GetStockObject( DEFAULT_BITMAP ); } else { if (bm.bmHeight < 0) bm.bmHeight = -bm.bmHeight; if (bm.bmWidth < 0) bm.bmWidth = -bm.bmWidth; } if (bm.bmPlanes != 1) { FIXME("planes = %d\n", bm.bmPlanes); SetLastError( ERROR_INVALID_PARAMETER ); return NULL; } /* Windows only uses 1, 4, 8, 16, 24 and 32 bpp */ if(bm.bmBitsPixel == 1) bm.bmBitsPixel = 1; else if(bm.bmBitsPixel <= 4) bm.bmBitsPixel = 4; else if(bm.bmBitsPixel <= 8) bm.bmBitsPixel = 8; else if(bm.bmBitsPixel <= 16) bm.bmBitsPixel = 16; else if(bm.bmBitsPixel <= 24) bm.bmBitsPixel = 24; else if(bm.bmBitsPixel <= 32) bm.bmBitsPixel = 32; else { WARN("Invalid bmBitsPixel %d, returning ERROR_INVALID_PARAMETER\n", bm.bmBitsPixel); SetLastError(ERROR_INVALID_PARAMETER); return NULL; } /* Windows ignores the provided bm.bmWidthBytes */ bm.bmWidthBytes = BITMAP_GetWidthBytes( bm.bmWidth, bm.bmBitsPixel ); /* XP doesn't allow to create bitmaps larger than 128 Mb */ if (bm.bmHeight * bm.bmWidthBytes > 128 * 1024 * 1024) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return 0; } /* Create the BITMAPOBJ */ bmpobj = GDI_AllocObject( sizeof(BITMAPOBJ), BITMAP_MAGIC, (HGDIOBJ *)&hbitmap, &bitmap_funcs ); if (!bmpobj) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return NULL; } TRACE("%dx%d, %d colors returning %p\n", bm.bmWidth, bm.bmHeight, 1 << (bm.bmPlanes * bm.bmBitsPixel), hbitmap); bmpobj->size.cx = 0; bmpobj->size.cy = 0; bmpobj->bitmap = bm; bmpobj->bitmap.bmBits = NULL; bmpobj->funcs = NULL; bmpobj->dib = NULL; bmpobj->segptr_bits = 0; bmpobj->color_table = NULL; bmpobj->nb_colors = 0; if (bm.bmBits) SetBitmapBits( hbitmap, bm.bmHeight * bm.bmWidthBytes, bm.bmBits ); GDI_ReleaseObj( hbitmap ); return hbitmap; } /*********************************************************************** * GetBitmapBits [GDI32.@] * * Copies bitmap bits of bitmap to buffer. * * RETURNS * Success: Number of bytes copied * Failure: 0 */ LONG WINAPI GetBitmapBits( HBITMAP hbitmap, /* [in] Handle to bitmap */ LONG count, /* [in] Number of bytes to copy */ LPVOID bits) /* [out] Pointer to buffer to receive bits */ { BITMAPOBJ *bmp = (BITMAPOBJ *) GDI_GetObjPtr( hbitmap, BITMAP_MAGIC ); LONG height, ret; if (!bmp) return 0; if (bmp->dib) /* simply copy the bits from the DIB */ { DIBSECTION *dib = bmp->dib; const char *src = dib->dsBm.bmBits; INT width_bytes = BITMAP_GetWidthBytes(dib->dsBm.bmWidth, dib->dsBm.bmBitsPixel); DWORD max = width_bytes * bmp->bitmap.bmHeight; if (!bits) { ret = max; goto done; } if (count > max) count = max; ret = count; /* GetBitmapBits returns not 32-bit aligned data */ if (bmp->dib->dsBmih.biHeight >= 0) /* not top-down, need to flip contents vertically */ { src += dib->dsBm.bmWidthBytes * dib->dsBm.bmHeight; while (count > 0) { src -= dib->dsBm.bmWidthBytes; memcpy( bits, src, min( count, width_bytes ) ); bits = (char *)bits + width_bytes; count -= width_bytes; } } else { while (count > 0) { memcpy( bits, src, min( count, width_bytes ) ); src += dib->dsBm.bmWidthBytes; bits = (char *)bits + width_bytes; count -= width_bytes; } } goto done; } /* If the bits vector is null, the function should return the read size */ if(bits == NULL) { ret = bmp->bitmap.bmWidthBytes * bmp->bitmap.bmHeight; goto done; } if (count < 0) { WARN("(%d): Negative number of bytes passed???\n", count ); count = -count; } /* Only get entire lines */ height = count / bmp->bitmap.bmWidthBytes; if (height > bmp->bitmap.bmHeight) height = bmp->bitmap.bmHeight; count = height * bmp->bitmap.bmWidthBytes; if (count == 0) { WARN("Less than one entire line requested\n"); ret = 0; goto done; } TRACE("(%p, %d, %p) %dx%d %d colors fetched height: %d\n", hbitmap, count, bits, bmp->bitmap.bmWidth, bmp->bitmap.bmHeight, 1 << bmp->bitmap.bmBitsPixel, height ); if(bmp->funcs && bmp->funcs->pGetBitmapBits) { TRACE("Calling device specific BitmapBits\n"); ret = bmp->funcs->pGetBitmapBits(hbitmap, bits, count); } else { if(!bmp->bitmap.bmBits) { TRACE("Bitmap is empty\n"); memset(bits, 0, count); ret = count; } else { memcpy(bits, bmp->bitmap.bmBits, count); ret = count; } } done: GDI_ReleaseObj( hbitmap ); return ret; } /****************************************************************************** * SetBitmapBits [GDI32.@] * * Sets bits of color data for a bitmap. * * RETURNS * Success: Number of bytes used in setting the bitmap bits * Failure: 0 */ LONG WINAPI SetBitmapBits( HBITMAP hbitmap, /* [in] Handle to bitmap */ LONG count, /* [in] Number of bytes in bitmap array */ LPCVOID bits) /* [in] Address of array with bitmap bits */ { BITMAPOBJ *bmp = (BITMAPOBJ *) GDI_GetObjPtr( hbitmap, BITMAP_MAGIC ); LONG height, ret; if ((!bmp) || (!bits)) return 0; if (count < 0) { WARN("(%d): Negative number of bytes passed???\n", count ); count = -count; } if (bmp->dib) /* simply copy the bits into the DIB */ { DIBSECTION *dib = bmp->dib; char *dest = dib->dsBm.bmBits; DWORD max = dib->dsBm.bmWidthBytes * dib->dsBm.bmHeight; if (count > max) count = max; ret = count; if (bmp->dib->dsBmih.biHeight >= 0) /* not top-down, need to flip contents vertically */ { dest += dib->dsBm.bmWidthBytes * dib->dsBm.bmHeight; while (count > 0) { dest -= dib->dsBm.bmWidthBytes; memcpy( dest, bits, min( count, dib->dsBm.bmWidthBytes ) ); bits = (const char *)bits + dib->dsBm.bmWidthBytes; count -= dib->dsBm.bmWidthBytes; } } else memcpy( dest, bits, count ); GDI_ReleaseObj( hbitmap ); return ret; } /* Only get entire lines */ height = count / bmp->bitmap.bmWidthBytes; if (height > bmp->bitmap.bmHeight) height = bmp->bitmap.bmHeight; count = height * bmp->bitmap.bmWidthBytes; TRACE("(%p, %d, %p) %dx%d %d colors fetched height: %d\n", hbitmap, count, bits, bmp->bitmap.bmWidth, bmp->bitmap.bmHeight, 1 << bmp->bitmap.bmBitsPixel, height ); if(bmp->funcs && bmp->funcs->pSetBitmapBits) { TRACE("Calling device specific BitmapBits\n"); ret = bmp->funcs->pSetBitmapBits(hbitmap, bits, count); } else { if(!bmp->bitmap.bmBits) /* Alloc enough for entire bitmap */ bmp->bitmap.bmBits = HeapAlloc( GetProcessHeap(), 0, count ); if(!bmp->bitmap.bmBits) { WARN("Unable to allocate bit buffer\n"); ret = 0; } else { memcpy(bmp->bitmap.bmBits, bits, count); ret = count; } } GDI_ReleaseObj( hbitmap ); return ret; } /********************************************************************** * BITMAP_CopyBitmap * */ HBITMAP BITMAP_CopyBitmap(HBITMAP hbitmap) { HBITMAP res = 0; BITMAP bm; if (!GetObjectW( hbitmap, sizeof(bm), &bm )) return 0; res = CreateBitmapIndirect(&bm); if(res) { char *buf = HeapAlloc( GetProcessHeap(), 0, bm.bmWidthBytes * bm.bmHeight ); GetBitmapBits (hbitmap, bm.bmWidthBytes * bm.bmHeight, buf); SetBitmapBits (res, bm.bmWidthBytes * bm.bmHeight, buf); HeapFree( GetProcessHeap(), 0, buf ); } return res; } /*********************************************************************** * BITMAP_SetOwnerDC * * Set the type of DC that owns the bitmap. This is used when the * bitmap is selected into a device to initialize the bitmap function * table. */ BOOL BITMAP_SetOwnerDC( HBITMAP hbitmap, DC *dc ) { BITMAPOBJ *bitmap; BOOL ret; /* never set the owner of the stock bitmap since it can be selected in multiple DCs */ if (hbitmap == GetStockObject(DEFAULT_BITMAP)) return TRUE; if (!(bitmap = GDI_GetObjPtr( hbitmap, BITMAP_MAGIC ))) return FALSE; ret = TRUE; if (!bitmap->funcs) /* not owned by a DC yet */ { if (dc->funcs->pCreateBitmap) ret = dc->funcs->pCreateBitmap( dc->physDev, hbitmap, bitmap->bitmap.bmBits ); if (ret) bitmap->funcs = dc->funcs; } else if (bitmap->funcs != dc->funcs) { FIXME( "Trying to select bitmap %p in different DC type\n", hbitmap ); ret = FALSE; } GDI_ReleaseObj( hbitmap ); return ret; } /*********************************************************************** * BITMAP_SelectObject */ static HGDIOBJ BITMAP_SelectObject( HGDIOBJ handle, HDC hdc ) { HGDIOBJ ret; BITMAPOBJ *bitmap; DC *dc; if (!(dc = get_dc_ptr( hdc ))) return 0; if (GetObjectType( hdc ) != OBJ_MEMDC) { ret = 0; goto done; } ret = dc->hBitmap; if (handle == dc->hBitmap) goto done; /* nothing to do */ if (!(bitmap = GDI_GetObjPtr( handle, BITMAP_MAGIC ))) { ret = 0; goto done; } if (bitmap->header.dwCount && (handle != GetStockObject(DEFAULT_BITMAP))) { WARN( "Bitmap already selected in another DC\n" ); GDI_ReleaseObj( handle ); ret = 0; goto done; } if (!bitmap->funcs && !BITMAP_SetOwnerDC( handle, dc )) { GDI_ReleaseObj( handle ); ret = 0; goto done; } if (dc->funcs->pSelectBitmap && !dc->funcs->pSelectBitmap( dc->physDev, handle )) { GDI_ReleaseObj( handle ); ret = 0; } else { dc->hBitmap = handle; GDI_inc_ref_count( handle ); dc->dirty = 0; SetRectRgn( dc->hVisRgn, 0, 0, bitmap->bitmap.bmWidth, bitmap->bitmap.bmHeight); GDI_ReleaseObj( handle ); DC_InitDC( dc ); GDI_dec_ref_count( ret ); } done: release_dc_ptr( dc ); return ret; } /*********************************************************************** * BITMAP_DeleteObject */ static BOOL BITMAP_DeleteObject( HGDIOBJ handle, void *obj ) { BITMAPOBJ * bmp = obj; if (bmp->funcs && bmp->funcs->pDeleteBitmap) bmp->funcs->pDeleteBitmap( handle ); HeapFree( GetProcessHeap(), 0, bmp->bitmap.bmBits ); if (bmp->dib) { DIBSECTION *dib = bmp->dib; if (dib->dsBm.bmBits) { if (dib->dshSection) { SYSTEM_INFO SystemInfo; GetSystemInfo( &SystemInfo ); UnmapViewOfFile( (char *)dib->dsBm.bmBits - (dib->dsOffset % SystemInfo.dwAllocationGranularity) ); } else if (!dib->dsOffset) VirtualFree(dib->dsBm.bmBits, 0L, MEM_RELEASE ); } HeapFree(GetProcessHeap(), 0, dib); bmp->dib = NULL; if (bmp->segptr_bits) { /* free its selector array */ WORD sel = SELECTOROF(bmp->segptr_bits); WORD count = (GetSelectorLimit16(sel) / 0x10000) + 1; int i; for (i = 0; i < count; i++) FreeSelector16(sel + (i << __AHSHIFT)); } HeapFree(GetProcessHeap(), 0, bmp->color_table); } return GDI_FreeObject( handle, obj ); } /*********************************************************************** * BITMAP_GetObject */ static INT BITMAP_GetObject( HGDIOBJ handle, void *obj, INT count, LPVOID buffer ) { BITMAPOBJ *bmp = obj; if( !buffer ) return sizeof(BITMAP); if (count < sizeof(BITMAP)) return 0; if (bmp->dib) { if (count >= sizeof(DIBSECTION)) { memcpy( buffer, bmp->dib, sizeof(DIBSECTION) ); return sizeof(DIBSECTION); } else /* if (count >= sizeof(BITMAP)) */ { DIBSECTION *dib = bmp->dib; memcpy( buffer, &dib->dsBm, sizeof(BITMAP) ); return sizeof(BITMAP); } } else { memcpy( buffer, &bmp->bitmap, sizeof(BITMAP) ); ((BITMAP *) buffer)->bmBits = NULL; return sizeof(BITMAP); } } /****************************************************************************** * CreateDiscardableBitmap [GDI32.@] * * Creates a discardable bitmap. * * RETURNS * Success: Handle to bitmap * Failure: NULL */ HBITMAP WINAPI CreateDiscardableBitmap( HDC hdc, /* [in] Handle to device context */ INT width, /* [in] Bitmap width */ INT height) /* [in] Bitmap height */ { return CreateCompatibleBitmap( hdc, width, height ); } /****************************************************************************** * GetBitmapDimensionEx [GDI32.@] * * Retrieves dimensions of a bitmap. * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI GetBitmapDimensionEx( HBITMAP hbitmap, /* [in] Handle to bitmap */ LPSIZE size) /* [out] Address of struct receiving dimensions */ { BITMAPOBJ * bmp = (BITMAPOBJ *) GDI_GetObjPtr( hbitmap, BITMAP_MAGIC ); if (!bmp) return FALSE; *size = bmp->size; GDI_ReleaseObj( hbitmap ); return TRUE; } /****************************************************************************** * SetBitmapDimensionEx [GDI32.@] * * Assigns dimensions to a bitmap. * MSDN says that this function will fail if hbitmap is a handle created by * CreateDIBSection, but that's not true on Windows 2000. * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetBitmapDimensionEx( HBITMAP hbitmap, /* [in] Handle to bitmap */ INT x, /* [in] Bitmap width */ INT y, /* [in] Bitmap height */ LPSIZE prevSize) /* [out] Address of structure for orig dims */ { BITMAPOBJ * bmp = (BITMAPOBJ *) GDI_GetObjPtr( hbitmap, BITMAP_MAGIC ); if (!bmp) return FALSE; if (prevSize) *prevSize = bmp->size; bmp->size.cx = x; bmp->size.cy = y; GDI_ReleaseObj( hbitmap ); return TRUE; }