/**************************************************************************\ * * Copyright (c) 1998 Microsoft Corporation * * Abstract: * * Device bitmap APIs and internals. * * Revision History: * * 12/02/1998 andrewgo * Created it. * \**************************************************************************/ #include "precomp.hpp" #include "compatibleDIB.hpp" /**************************************************************************\ * * Function Description: * * Temporary function to see if the bitmap is a standard format type * (5-5-5, 5-6-5, 24bpp or 32bpp). * * Notes: * * Code which calls this assumes that there are no standard formats which * support alpha. * * History: * * 12/04/1998 andrewgo * Created it. * \**************************************************************************/ BOOL DpBitmap::StandardFormat( VOID ) { INT BitsPerPixel = GetPixelFormatSize(PixelFormat); BOOL standardFormat = FALSE; if ((RedMask == 0x00ff0000) && (GreenMask == 0x0000ff00) && (BlueMask == 0x000000ff)) { if (BitsPerPixel == 24) { standardFormat = TRUE; } else if (BitsPerPixel == 32) { standardFormat = TRUE; } } else if ((RedMask == 0x00007c00) && (GreenMask == 0x000003e0) && (BlueMask == 0x0000001f) && (BitsPerPixel == 16)) { standardFormat = TRUE; } else if ((RedMask == 0x0000f800) && (GreenMask == 0x000007e0) && (BlueMask == 0x0000001f) && (BitsPerPixel == 16)) { standardFormat = TRUE; } return(standardFormat); } /**************************************************************************\ * * Function Description: * * This function computes the PixelFormatID corresponding to a particular * combination of bit depth and color channel masks in the DpBitmap. * * Notes: * * Code which calls this assumes that there are no standard formats which * support alpha. * * History: * * 05/17/2000 asecchia * Created it. * \**************************************************************************/ PixelFormatID DpBitmap::GetPixelFormatFromBitDepth(INT bits) { switch(bits) { // !!! [asecchia] not sure if we support these indexed modes // from this codepath. case 1: return PixelFormat1bppIndexed; case 4: return PixelFormat4bppIndexed; case 8: return PixelFormat8bppIndexed; case 16: if (RedMask == 0x00007c00) { return PixelFormat16bppRGB555; } if (RedMask == 0x0000f800) { return PixelFormat16bppRGB565; } break; case 24: if (RedMask == 0x00ff0000) { return PixelFormat24bppRGB; } if (RedMask == 0x000000ff) { return PIXFMT_24BPP_BGR; } break; case 32: if (RedMask == 0x00ff0000) { return PixelFormat32bppRGB; } break; } WARNING(("Unsupported pixel format")); return PixelFormatUndefined; } /**************************************************************************\ * * Function Description: * * Initializes a bitmap for drawing on via the GDI routines. * * Arguments: * * [IN] device - Identifies the device * [IN] width - Bitmap width * [IN] height - Bitmap height * [OUT] driver - Driver interface to be used * * History: * * 12/06/1998 andrewgo * Created it. * \**************************************************************************/ VOID DpBitmap::InitializeForGdiBitmap( GpDevice *device, INT width, INT height ) { SurfaceTransparency = TransparencyNoAlpha; // !!![andrewgo] Disable this assert until MetaFiles stop calling // with a zero dimension surface // // ASSERTMSG((width > 0) && (height > 0), ("Dimensions must be positive")); Width = width; Height = height; NumBytes = 0; Uniqueness = (DWORD)GpObject::GenerateUniqueness(); PixelFormat = ExtractPixelFormatFromHDC(device->DeviceHdc); Scan = device->ScanGdi; SetValid(TRUE); Bits = NULL; Delta = 0; DdrawSurface7 = NULL; Type = GDI; } /**************************************************************************\ * * Function Description: * * Initializes a bitmap for drawing on via the DCI routines, if possible. * * Arguments: * * [IN] device - Identifies the device * [IN] width - Bitmap width * [IN] height - Bitmap height * [OUT] driver - Driver interface to be used * * History: * * 12/06/1998 andrewgo * Created it. * \**************************************************************************/ VOID DpBitmap::InitializeForGdiScreen( GpDevice *device, INT width, INT height ) { InitializeForGdiBitmap(device, width, height); // Even if GDI bitmaps change to support alpha, the screen doesn't. SurfaceTransparency = TransparencyNoAlpha; ASSERT(!IsAlphaPixelFormat(PixelFormat)); if(device->pdds != NULL) { DdrawSurface7 = device->pdds; DdrawSurface7->AddRef(); } Scan = device->ScanDci; } /**************************************************************************\ * * Function Description: * * Initializes a bitmap for drawing on via D3D/DD access. * * Arguments: * * [IN] device - Identifies the device * [IN] width - Bitmap width * [IN] height - Bitmap height * [OUT] driver - Driver interface to be used * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 09/28/1999 bhouse * Created it. * \**************************************************************************/ BOOL DpBitmap::InitializeForD3D( HDC hdc, INT *width, INT *height, DpDriver **driver ) { HRESULT ddVal; HDC hdcDevice; DDSURFACEDESC2 ddsd; if(!InitializeDirectDrawGlobals()) return FALSE; IDirectDrawSurface7 * surface; ddVal = Globals::DirectDraw->GetSurfaceFromDC(hdc, &surface); if(ddVal != DD_OK) return(FALSE); return InitializeForD3D(surface, width, height, driver); } /**************************************************************************\ * * Function Description: * * Initializes a bitmap for drawing on via D3D/DD access. * * Arguments: * * [IN] device - Identifies the device * [IN] width - Bitmap width * [IN] height - Bitmap height * [OUT] driver - Driver interface to be used * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 09/28/1999 bhouse * Created it. * \**************************************************************************/ BOOL DpBitmap::InitializeForD3D( IDirectDrawSurface7 * surface, INT *width, INT *height, DpDriver **driver ) { HRESULT ddVal; HDC hdcDevice; DDSURFACEDESC2 ddsd; GpDevice * device = Globals::DeviceList->FindD3DDevice(surface); if(device == NULL || device->pd3d == NULL) return FALSE; DdrawSurface7 = surface; ddsd.dwSize = sizeof(ddsd); ddVal = DdrawSurface7->GetSurfaceDesc(&ddsd); if (ddVal == DD_OK) { // Initialize bitmap class stuff: Bits = NULL; Delta = ddsd.lPitch; Width = ddsd.dwWidth; Height = ddsd.dwHeight; // AlphaMask is initialized to zero because we don't use it - // non-alpha format. AlphaMask = 0x00000000; RedMask = ddsd.ddpfPixelFormat.dwRBitMask; GreenMask = ddsd.ddpfPixelFormat.dwGBitMask; BlueMask = ddsd.ddpfPixelFormat.dwBBitMask; PixelFormat = GetPixelFormatFromBitDepth(ddsd.ddpfPixelFormat.dwRGBBitCount); if (StandardFormat()) { // Our standard formats don't have alpha. SurfaceTransparency = TransparencyNoAlpha; *driver = Globals::D3DDriver; Scan = &Globals::DesktopDevice->ScanEngine; NumBytes = 0; Uniqueness = (DWORD)GpObject::GenerateUniqueness(); Type = D3D; SetValid(TRUE); // Return some stuff: *width = Width; *height = Height; // Grab a reference: DdrawSurface7->AddRef(); return(TRUE); } } return(FALSE); } /**************************************************************************\ * * Function Description: * * Initializes a bitmap for drawing on via printer routines, if possible. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 12/06/1998 andrewgo * Created it. * \**************************************************************************/ BOOL DpBitmap::InitializeForPrinter( GpPrinterDevice *device, INT width, INT height ) { InitializeForGdiBitmap(device, width, height); // Even if GDI bitmaps change to support alpha, printers don't. SurfaceTransparency = TransparencyNoAlpha; ASSERT(!IsAlphaPixelFormat(PixelFormat)); Scan = &device->ScanPrint; return TRUE; } /**************************************************************************\ * * Function Description: * * Initializes a bitmap for drawing on via direct access to the * DIBsection bits. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 12/06/1998 andrewgo * Created it. * \**************************************************************************/ BOOL DpBitmap::InitializeForDibsection( HDC hdc, HBITMAP hbitmap, // [IN] Bitmap handle, needed for determing // if bitmap is really top-down or not GpDevice *device, // [IN] Identifies the device DIBSECTION *dib, // [IN] Structure describing the bitmap INT *width, // [OUT] Bitmap width INT *height, // [OUT] Bitmap height DpDriver **driver // [OUT] Driver interface to be used ) { BOOL isTopDown; // On NT5, drivers have the option of supporting GetDC with DirectDraw // surfaces in such a way that the surfaces are not Locked when GDI // does the GetDC on them. This means that there may be no user-mode // mapping of the underlying surface. So we have to check here for // that case, because we obviously cannot render directly to those // surfaces via software: // NOTE: if the surface is actually a DDraw surface, this check is not // actually enough. It is up to the driver to return a pointer here and // on occasion it simply returns its KM address. I.e. it will return a // non-NULL pointer that we can't access. // See the DDraw special case below. // This has been verified on a number of video drivers on Windows 2000 // and Windows XP. (for instance, the inbox win2k permedia driver). if (dib->dsBm.bmBits == NULL) { return(FALSE); } LONG scans = abs(dib->dsBm.bmHeight); LONG widthInBytes = dib->dsBm.bmWidthBytes; // For backwards compatibility with Get/SetBitmapBits, GDI does // not accurately report the bitmap pitch in bmWidthBytes. It // always computes bmWidthBytes assuming WORD-aligned scanlines // regardless of the platform. // // Therefore, if the platform is WinNT, which uses DWORD-aligned // scanlines, adjust the bmWidthBytes value. if (Globals::IsNt) { widthInBytes = (widthInBytes + 3) & ~3; } DWORD* topDown = (DWORD*) dib->dsBm.bmBits; DWORD* bottomUp = (DWORD*) ((ULONG_PTR) topDown + (scans - 1) * widthInBytes); if (Globals::IsNt) { // Unfortunately, on NT there is no simple means of determining // whether the DIB-section or DDraw surface is bottom-up or // top-down. (NT should really set biHeight as Win9x does, but // unfortunately this is a bug that due to compatibility with // older versions of NT, will never be fixed.) // // At least we know that DirectDraw surfaces will always be // top-down, and we can recognize DDraw surfaces by the fact // that they have biSizeImage set to 0. (Note that we can't let // this fall into the SetBitmapBits case because NT5 doesn't // permit SetBitmapBits calls on DDraw surface handles.) if (dib->dsBmih.biSizeImage == 0) { // This is a DirectDraw surface. // Currently we don't support direct rendering on DDraw surfaces // that are not backed by a system memory DIB Section so we simply // fail here and drop into the GDI fallback code if we detect // this condition. isTopDown = TRUE; if(!InitializeDirectDrawGlobals() || (Globals::GetDdrawSurfaceFromDcFunction == NULL)) { // If we can't talk to the DDraw surface, we simply fall back // to our GDI rendering codepath. return FALSE; } HDC driverHdc; LPDIRECTDRAWSURFACE pDDS = NULL; HRESULT hr = Globals::GetDdrawSurfaceFromDcFunction( hdc, &pDDS, &driverHdc ); if (FAILED(hr) || (pDDS == NULL)) { // Bail out if we can't get a DirectDraw Surface object. return FALSE; } // Lock the surface so we can see what the user mode bits pointer // is. If it's the same as the one in dib->dsBm.bmBits, then // the DDraw surface is backed by a DIB section and we can continue // to treat this bitmap as a DIB. Otherwise we must fall back // to GDI. DDSURFACEDESC2 DDSD; DDSD.dwSize = sizeof(DDSURFACEDESC); hr = pDDS->Lock( NULL, (LPDDSURFACEDESC)&DDSD, DDLOCK_WAIT, NULL ); if (FAILED(hr)) { pDDS->Release(); return FALSE; } // Get the correct pitch from the DDSD. Note this may not be the // same as the pitch in the dib info structure. widthInBytes = DDSD.lPitch; // If the lpSurface is not the same as the dib->dsBm.bmBits then // this is not a DIB backed DDraw surface, so we (currently) have // no way of drawing on it besides our GDI fallback codepath. // Fail this call and release resources so that we can pick up // the StretchBlt fallback case. if(DDSD.lpSurface != dib->dsBm.bmBits) { pDDS->Unlock(NULL); pDDS->Release(); return FALSE; } pDDS->Unlock(NULL); pDDS->Release(); // We're set: this is a DIB backed DDraw surface so we can continue // to treat it as a DIB - now that we have the correct pitch. } else { // When it's not a DDraw surface, we have to go through a // somewhat more indirect method to figure out where pixel // (0, 0) is in memory. // // We use SetBitmapBits instead of something like SetPixel // or PatBlt because those would need to use the 'hdc' // given to us by the application, which might have a // transform set that maps (0, 0) to something other than // the top-left pixel of the bitmap. DWORD top = *topDown; DWORD bottom = *bottomUp; DWORD setBits = 0x000000ff; // Our SetBitmapBits call will set the top-left dword of // the bitmap to 0x000000ff. If it's a top-down bitmap, // that will have modified the value at address 'topDown': *topDown = 0; LONG bytes = SetBitmapBits(hbitmap, sizeof(setBits), &setBits); isTopDown = (*topDown != 0); // The scanlines are guaranteed to be DWORD aligned, so there // really is at least a DWORD that we can directly access via // the pointer. However, if the bitmap dimensions are such // that there is less than a DWORD of active data per scanline // (for example, a 3x3 8bpp bitmap or a 1x1 16bpp bitmap), // SetBitmapBits may use less than a DWORD of data. ASSERT(bytes > 0); // Restore the bitmap portions that we may have modified: *topDown = top; *bottomUp = bottom; } } else { // On Win9x, we can simply look at the sign of 'biHeight' to // determine whether the surface is top-down or bottom-up: isTopDown = (dib->dsBmih.biHeight < 0); } // Fill in our bitmap fields: if (isTopDown) { Bits = (BYTE*) topDown; Delta = widthInBytes; } else { Bits = (BYTE*) bottomUp; Delta = -widthInBytes; } Width = dib->dsBm.bmWidth; Height = dib->dsBm.bmHeight; // Note that this code doesn't handle palettes! if (dib->dsBmih.biCompression == BI_BITFIELDS) { RedMask = dib->dsBitfields[0]; GreenMask = dib->dsBitfields[1]; BlueMask = dib->dsBitfields[2]; } else { if((dib->dsBmih.biCompression == BI_RGB) && (dib->dsBm.bmBitsPixel == 16)) { // According to MSDN, 16bpp BI_RGB implies 555 format. RedMask = 0x00007c00; GreenMask = 0x000003e0; BlueMask = 0x0000001F; } else { RedMask = 0x00ff0000; GreenMask = 0x0000ff00; BlueMask = 0x000000ff; } } // DibSections don't have alpha, but we don't want to leave this // field uninitialized because we peek at it occasionally. AlphaMask = 0x00000000; PixelFormat = GetPixelFormatFromBitDepth(dib->dsBm.bmBitsPixel); // if we are here and the bits per pel is 8, this is a DIB // with halftone colortable if ((dib->dsBm.bmBitsPixel == 8) || StandardFormat()) { // Our standard formats don't have alpha. SurfaceTransparency = TransparencyNoAlpha; *driver = Globals::EngineDriver; Scan = &device->ScanEngine; NumBytes = 0; Uniqueness = (DWORD)GpObject::GenerateUniqueness(); Type = GDIDIBSECTION; SetValid(TRUE); // Return some stuff: *width = Width; *height = Height; return(TRUE); } return(FALSE); } /**************************************************************************\ * * Function Description: * * Initializes a GDI+ bitmap for drawing on via GpBitmap.Lock/UnlockBits. * * Arguments: * * [IN] bitmap - Specifies the target GpBitmap * * Return Value: * * TRUE is successful, FALSE otherwise. * * History: * * 09/22/1999 gilmanw * Created it based on DpBitmap::InitializeForGdiBitmap. * \**************************************************************************/ VOID DpBitmap::InitializeForGdipBitmap( INT width, INT height, ImageInfo * imageInfo, EpScanBitmap * scanBitmap, BOOL isDisplay ) { Width = width; Height = height; NumBytes = 0; Uniqueness = (DWORD)GpObject::GenerateUniqueness(); AlphaMask = 0xff000000; RedMask = 0x00ff0000; GreenMask = 0x0000ff00; BlueMask = 0x000000ff; SetValid(TRUE); Bits = NULL; Delta = 0; Type = GPBITMAP; Scan = scanBitmap; PaletteTable = NULL; PixelFormat = imageInfo->PixelFormat; // GetTransparencyHint is called from DrvDrawImage // bitmap->GetTransparencyHint(&SurfaceTransparency); IsDisplay = isDisplay; DpiX = (REAL)imageInfo->Xdpi; DpiY = (REAL)imageInfo->Ydpi; } /**************************************************************************\ * * Function Description: * * Bitmap destructor * \**************************************************************************/ DpBitmap::~DpBitmap() { if (PaletteTable != NULL) GpFree(PaletteTable); if (DdrawSurface7 != NULL) DdrawSurface7->Release(); SetValid(FALSE); // so we don't use a deleted object } /**************************************************************************\ * * Function Description: * * Flush any rendering pending to this surface * \**************************************************************************/ VOID DpBitmap::Flush( GpFlushIntention intention ) { Scan->Flush(); }