/**************************************************************************\ * * Copyright (c) 1999-2000 Microsoft Corporation * * Abstract: * * Contains all the 32-bit scan-buffer routines for the default supported * bitmap formats. * * Revision History: * * 12/08/1998 andrewgo * Created it. * \**************************************************************************/ #include "precomp.hpp" /**************************************************************************\ * * Function Description: * * Scan class helper function that SrcOver alpha blends two ARGB buffers. * * Arguments: * * [IN] driver - Driver interface * [IN] context - Drawing context * [IN] surface - Destination surface * [OUT] nextBuffer - Points to a EpScan:: type function to return * the next buffer * [IN] scanType - The type of scan. * [IN] pixFmtGeneral - the input pixel format for the color data, * in the "Blend" and "CT" scan types. * [IN] pixFmtOpaque - the input pixel format for the color data, * in the "Opaque" scan type. * [IN] solidColor - the solid fill color for "*SolidFill" scan types. * * Return Value: * * FALSE if all the necessary buffers couldn't be created * * History: * * 12/04/1998 andrewgo * Created it. * \**************************************************************************/ BOOL EpScanEngine::Start( DpDriver *driver, DpContext *context, DpBitmap *surface, NEXTBUFFERFUNCTION *nextBuffer, EpScanType scanType, PixelFormatID pixFmtGeneral, PixelFormatID pixFmtOpaque, ARGB solidColor ) { // Inherit initialization EpScan::Start( driver, context, surface, nextBuffer, scanType, pixFmtGeneral, pixFmtOpaque, solidColor ); // DIBSection destinations don't have an alpha channel ASSERT(surface->SurfaceTransparency == TransparencyNoAlpha); Surface = surface; if(surface->Type == DpBitmap::D3D) { DDSURFACEDESC2 ddsd; memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); HRESULT err; err = Surface->DdrawSurface7->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); if(err != DD_OK) return(FALSE); Surface->Bits = ddsd.lpSurface; Surface->Delta = ddsd.lPitch; } Dst = NULL; Stride = surface->Delta; Bits = (BYTE*) surface->Bits; PixelSize = GetPixelFormatSize(surface->PixelFormat) >> 3; // [agodfrey] This Scan class is only designed for use with formats // which are supported natively, so it ignores the DIBSection and // corresponding PixelFormatID returned by GetScanBuffers. PixelFormatID dstFormat = surface->PixelFormat; ASSERTMSG(dstFormat != PIXFMT_UNDEFINED,(("Unexpected surface format"))); *nextBuffer = (NEXTBUFFERFUNCTION) EpScanEngine::NextBuffer; if (!driver->Device->GetScanBuffers( surface->Width, NULL, NULL, NULL, Buffers) ) { return NULL; } // initialize the AlphaBlenders. BlenderConfig[0].Initialize( dstFormat, context, context->Palette, Buffers, TRUE, FALSE, solidColor ); BlenderConfig[1].Initialize( dstFormat, context, context->Palette, Buffers, TRUE, FALSE, solidColor ); return TRUE; } /**************************************************************************\ * * Function Description: * * Flushes the previous buffer (if there was), and returns the * next buffer for doing a SrcOver blend. * * Arguments: * * [IN] x - Destination pixel coordinate in destination surface * [IN] y - "" * [IN] width - Number of pixels needed for the next buffer (can be 0) * [IN] updateWidth - Number of pixels to update in the current buffer * * Return Value: * * Points to the resulting scan buffer * * History: * * 12/04/1998 andrewgo * Created it. * \**************************************************************************/ VOID *EpScanEngine::NextBuffer( INT x, INT y, INT newWidth, INT updateWidth, INT blenderNum ) { if (updateWidth != 0) { // Make sure we're not drawing outside the bounds of the surface. // If these ASSERTs are triggered, the clipping code is broken. // This class absolutely must have input clipped to the surface // bounds otherwise we will AV writing on bad memory, or corrupt some // other data structure. ASSERT( CurrentX >= 0 ); ASSERT( CurrentY >= 0 ); ASSERT( CurrentX + updateWidth <= Surface->Width ); ASSERT( CurrentY < Surface->Height ); // Handle the previous scanline segment. BlenderConfig[LastBlenderNum].AlphaBlender.Blend( Dst, Buffers[3], updateWidth, CurrentX - DitherOriginX, CurrentY - DitherOriginY, static_cast(Buffers[4]) ); } // Now move on to processing this scanline segment. // The actual blend will be done on the next call through this routine // when we know the width and the bits have been set into the buffer // we're returning. LastBlenderNum = blenderNum; // Remember the x and y for the brush offset (halftone & dither). CurrentX = x; CurrentY = y; // Calculate the destination for the scan: Dst = Bits + (y * Stride) + (x * PixelSize); return (Buffers[3]); } /**************************************************************************\ * * Function Description: * * Denotes the end of the use of the scan buffer. * * Arguments: * * NONE * * Return Value: * * NONE * * History: * * 12/04/1998 andrewgo * Created it. * \**************************************************************************/ VOID EpScanEngine::End( INT updateWidth ) { // Flush the last scan: NextBuffer(0, 0, 0, updateWidth, 0); if(Surface->Type == DpBitmap::D3D) { Surface->DdrawSurface7->Unlock(NULL); Surface->Bits = NULL; Surface->Delta = 0; } } /**************************************************************************\ * * Function Description: * * Scan class helper function that SrcOver alpha blends two ARGB buffers. * * Arguments: * * [IN] driver - Driver interface * [IN] context - Drawing context * [IN] surface - Destination surface * [OUT] nextBuffer - Points to a EpScan:: type function to return * the next buffer * [IN] scanType - The type of scan. * [IN] pixFmtGeneral - the input pixel format for the color data, * in the "Blend" and "CT" scan types. * [IN] pixFmtOpaque - the input pixel format for the color data, * in the "Opaque" scan type. * [IN] solidColor - the solid fill color for "*SolidFill" scan types. * * Return Value: * * FALSE if all the necessary buffers couldn't be created * * History: * * 09/22/1999 gilmanw * Created it using EpScanEngine as a template * \**************************************************************************/ BOOL EpScanBitmap::Start( DpDriver *driver, DpContext *context, DpBitmap *surface, NEXTBUFFERFUNCTION *nextBuffer, EpScanType scanType, PixelFormatID pixFmtGeneral, PixelFormatID pixFmtOpaque, ARGB solidColor ) { // Inherit initialization EpScan::Start( driver, context, surface, nextBuffer, scanType, pixFmtGeneral, pixFmtOpaque, solidColor ); GpStatus status; BOOL writeOnly = FALSE; GpCompositingMode compositingMode = context->CompositingMode; Surface = surface; if (scanType == EpScanTypeOpaque) { writeOnly = TRUE; } else { // Work out if this operation will write transparent pixels (alpha != 1) // into the surface for the first time. switch (surface->SurfaceTransparency) { case TransparencyUnknown: case TransparencyNoAlpha: break; case TransparencyOpaque: // If the surface contains only opaque pixels, the SourceOver // operation will produce only opaque pixels. So for SourceOver, // a transition from TransparencyOpaque to TransparencyUnknown is // impossible. if ( (scanType == EpScanTypeBlend) && (compositingMode == CompositingModeSourceOver)) { break; } // Else, fall through: case TransparencySimple: // !!![agodfrey]: Theoretically, if the destination pixel format // is 1555, we could set it to 'TransparencySimple' here. surface->SurfaceTransparency = TransparencyUnknown; Bitmap->SetTransparencyHint(surface->SurfaceTransparency); break; default: RIP(("Unrecognized surface transparency")); break; } } // Pick the appropriate blending function based on the format of the // bitmap. ASSERTMSG(Bitmap != NULL, ("EpScanBitmap not initialized")); PixelFormatID dstFormat; if (FAILED(Bitmap->GetPixelFormatID(&dstFormat))) return FALSE; switch (dstFormat) { case PIXFMT_16BPP_RGB555: case PIXFMT_16BPP_RGB565: case PIXFMT_24BPP_RGB: case PIXFMT_32BPP_RGB: case PIXFMT_32BPP_ARGB: case PIXFMT_24BPP_BGR: case PIXFMT_32BPP_PARGB: // Since we're doing just one lock of the whole image, we have // to allow read-modify-write since only a portion of the bitmap // may be written. BitmapLockFlags = (IMGLOCK_WRITE | IMGLOCK_READ); *nextBuffer = (NEXTBUFFERFUNCTION) EpScanBitmap::NextBufferNative; EndFunc = (SCANENDFUNCTION) EpScanBitmap::EndNative; status = Bitmap->LockBits(NULL, BitmapLockFlags, dstFormat, &LockedBitmapData); if (status == Ok) { CurrentScan = NULL; PixelSize = GetPixelFormatSize(dstFormat) >> 3; break; } // else fall into the generic case and use 32bpp ARGB default: // When locking a scanline at a time and the mode is SourceCopy, // the read is unnecessary. if (writeOnly) { BitmapLockFlags = IMGLOCK_WRITE; } else { BitmapLockFlags = (IMGLOCK_WRITE | IMGLOCK_READ); } dstFormat = PIXFMT_32BPP_ARGB; *nextBuffer = (NEXTBUFFERFUNCTION) EpScanBitmap::NextBuffer32ARGB; EndFunc = (SCANENDFUNCTION) EpScanBitmap::End32ARGB; break; } // Allocate the temporary buffers. // Buffers[3] will be given to the caller to be used to pass scans to us. // Buffers[4] will be used for ClearType data. if (Buffers[0] == NULL) { Size bitmapSize; status = Bitmap->GetSize(&bitmapSize); if (status == Ok) { Width = bitmapSize.Width; Height = bitmapSize.Height; Buffers[0] = GpMalloc(sizeof(ARGB64) * bitmapSize.Width * 5); if (Buffers[0]) { int i; for (i=1;i<5;i++) { Buffers[i] = static_cast(Buffers[i-1]) + sizeof(ARGB64) * bitmapSize.Width; } } else { ONCE(WARNING(("(once) Buffer allocation failed"))); return FALSE; } } else { ONCE(WARNING(("(once) GetSize failed"))); return FALSE; } } // initialize the AlphaBlenders. BlenderConfig[0].Initialize( dstFormat, context, context->Palette, Buffers, TRUE, FALSE, solidColor ); BlenderConfig[1].Initialize( dstFormat, context, context->Palette, Buffers, TRUE, FALSE, solidColor ); return TRUE; } /**************************************************************************\ * * Function Description: * * NextBuffer function used when we have low-level functions that match * native format of the GpBitmap and we can read/write directly into the * bitmap bits. * * Flushes the previous buffer (if there was), and returns the * next buffer for doing a SrcOver blend. * * Arguments: * * [IN] x - Destination pixel coordinate in destination surface * [IN] y - "" * [IN] width - Number of pixels needed for the next buffer (can be 0) * [IN] updateWidth - Number of pixels to update in the current buffer * * Return Value: * * Points to the resulting scan buffer * * History: * * 09/22/1999 gilmanw * Created it using EpScanEngine as a template * \**************************************************************************/ VOID *EpScanBitmap::NextBufferNative( INT x, INT y, INT newWidth, INT updateWidth, INT blenderNum ) { // Flush the previous buffer: if ((updateWidth != 0) && (CurrentScan != NULL)) { ASSERTMSG(Buffers[0] != NULL, ("no buffers")); ASSERTMSG(updateWidth <= Width, ("updateWidth too big")); // Handle the previous scanline segment. BlenderConfig[LastBlenderNum].AlphaBlender.Blend( CurrentScan, Buffers[3], updateWidth, CurrentX - DitherOriginX, CurrentY - DitherOriginY, static_cast(Buffers[4]) ); } // Now move on to processing this scanline segment. // The actual blend will be done on the next call through this routine // when we know the width and the bits have been set into the buffer // we're returning. LastBlenderNum = blenderNum; // Remember the x and y for the brush offset (halftone & dither). CurrentX = x; CurrentY = y; // Get the next destination scan: CurrentScan = NULL; // Check that surface clipping has been done properly. if((y >= 0) && (y < Height) && (x >= 0) && (x < Width)) { // Clip against the right edge of the bitmap. newWidth is an upper // bound only - not guaranteed to be clipped. if (newWidth > (Width - x)) { newWidth = Width - x; } if (newWidth > 0) { CurrentScan = static_cast (static_cast(LockedBitmapData.Scan0) + (y * LockedBitmapData.Stride) + (x * PixelSize)); } } else { // If we hit this, we're hosed. The OutputSpan routines in the // DpOutputSpan classes are built assuming correct clipping (at least // to the data buffer) and hence, if we hit this assert, we're going // to crash horibly later writing all over memory when we start writing // outside of the bounds of the destination allocation. // if you're here, someone broke clipping or the dpi computation. ASSERTMSG(!((y >= 0) && (y < Height) && (x >= 0) && (x < Width)), (("EpScanBitmap::NextBufferNative: x, y out of bounds"))); } return (Buffers[3]); } /**************************************************************************\ * * Function Description: * * Generic NextBuffer function that accesses GpBitmap bits * via GpBitmap::Lock/UnlockBits for each scan. * * Flushes the previous buffer (if there was), and returns the * next buffer for doing a SrcOver blend. * * Arguments: * * [IN] x - Destination pixel coordinate in destination surface * [IN] y - "" * [IN] width - Number of pixels needed for the next buffer (can be 0) * [IN] updateWidth - Number of pixels to update in the current buffer * * Return Value: * * Points to the resulting scan buffer * * History: * * 09/22/1999 gilmanw * Created it using EpScanEngine as a template * \**************************************************************************/ VOID *EpScanBitmap::NextBuffer32ARGB( INT x, INT y, INT newWidth, INT updateWidth, INT blenderNum ) { // Flush the previous buffer: if (updateWidth != 0 && BitmapLocked) { ASSERTMSG(Buffers[0] != NULL, ("no buffers")); ASSERTMSG(LockedBitmapData.Scan0 != NULL, ("no previous buffer")); // Handle the previous scanline segment. BlenderConfig[LastBlenderNum].AlphaBlender.Blend( LockedBitmapData.Scan0, Buffers[3], updateWidth, CurrentX - DitherOriginX, CurrentY - DitherOriginY, static_cast(Buffers[4]) ); Bitmap->UnlockBits(&LockedBitmapData); BitmapLocked = FALSE; } else if (BitmapLocked) { EpScanBitmap::Flush(); } // Now move on to processing this scanline segment. // The actual blend will be done on the next call through this routine // when we know the width and the bits have been set into the buffer // we're returning. LastBlenderNum = blenderNum; // Remember the x and y for the brush offset (halftone & dither). CurrentX = x; CurrentY = y; // Lock the next destination: // Check that surface clipping has been done properly. if((y >= 0) && (y < Height) && (x >= 0) && (x < Width)) { // Clip against the right edge of the bitmap. newWidth is an upper // bound only - not guaranteed to be clipped. LockBits needs it // to be clipped. if (newWidth > (Width - x)) { newWidth = Width - x; } if (newWidth > 0) { GpRect nextRect(x, y, newWidth, 1); GpStatus status = Bitmap->LockBits( &nextRect, BitmapLockFlags, PixelFormat32bppARGB, &LockedBitmapData ); if (status == Ok) BitmapLocked = TRUE; } } else { // If we hit this, we're hosed. The OutputSpan routines in the // DpOutputSpan classes are built assuming correct clipping (at least // to the data buffer) and hence, if we hit this assert, we're going // to crash horibly later writing all over memory when we start writing // outside of the bounds of the destination allocation. // if you're here, someone broke clipping or the dpi computation. ASSERTMSG(!((y >= 0) && (y < Height) && (x >= 0) && (x < Width)), (("EpScanBitmap::NextBufferNative: x, y out of bounds"))); } return (Buffers[3]); } /**************************************************************************\ * * Function Description: * * Denotes the end of the use of the scan buffer. * * Arguments: * * NONE * * Return Value: * * NONE * * History: * * 09/22/1999 gilmanw * Created it using EpScanEngine as a template * \**************************************************************************/ VOID EpScanBitmap::End32ARGB( INT updateWidth ) { // Flush the last scan: EpScanBitmap::NextBuffer32ARGB(0, 0, 0, updateWidth, 0); } VOID EpScanBitmap::EndNative( INT updateWidth ) { // Flush the last scan and release bitmap access: EpScanBitmap::NextBufferNative(0, 0, 0, updateWidth, 0); Bitmap->UnlockBits(&LockedBitmapData); } VOID EpScanBitmap::End( INT updateWidth ) { (this->*EndFunc)(updateWidth); // Lock/UnlockBitmap has to be very aggressive about setting // TransparancyUnknown in the GpBitmap since the caller could be // doing anything to the alpha channel. However, the EpScanBitmap // knows what it is doing, so the surface->SurfaceTransparency is // more accurate. Bitmap->SetTransparencyHint(Surface->SurfaceTransparency); } /**************************************************************************\ * * Function Description: * * Flush any batched rendering and optionally wait for rendering to finish. * * Return Value: * * NONE * * History: * * 09/22/1999 gilmanw * Created it using EpScanEngine as a template * \**************************************************************************/ VOID EpScanBitmap::Flush() { if (BitmapLocked && Bitmap) { Bitmap->UnlockBits(&LockedBitmapData); BitmapLocked = FALSE; } }