/******************************Module*Header*******************************\ * * ******************* * * GDI SAMPLE CODE * * ******************* * * Module Name: brush.c * * Content: Handles all brush/pattern initialization and realization. * * Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved. * Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved. \**************************************************************************/ #include "precomp.h" #include "heap.h" //----------------------------------------------------------------------------- // // VOID vRealizeDitherPattern // // Generates an 8x8 dither pattern, in our internal realization format, for // the colour ulRGBToDither. // //----------------------------------------------------------------------------- VOID vRealizeDitherPattern(HDEV hdev, RBrush* prb, ULONG ulRGBToDither) { // // Do the actual dithering // Note: This function is NT5 only. If you want to write a NT4 driver, // you have to implement dither function in the driver // EngDitherColor(hdev, DM_DEFAULT, ulRGBToDither, &prb->aulPattern[0]); // // Initialize the fields we need // prb->ptlBrushOrg.x = LONG_MIN; prb->fl = 0; prb->pbe = NULL; }// vRealizeDitherPattern() //---------------------------Public*Routine------------------------------------ // // BOOL DrvRealizeBrush // // This function allows us to convert GDI brushes into an internal form // we can use. It is called by GDI when we've called BRUSHOBJ_pvGetRbrush // in some other function like DrvBitBlt, and GDI doesn't happen have a cached // realization lying around. // // Parameters: // pbo----------Points to the BRUSHOBJ that is to be realized. All other // parameters, except for psoTarget, can be queried from this // object. Parameter specifications are provided as an // optimization. This parameter is best used only as a parameter // for BRUSHOBJ_pvAllocRBrush, which allocates the memory for the // realized brush. // psoTarget----Points to the surface for which the brush is to be realized. // This surface can be the physical surface for the device, a // device format bitmap, or a standard format bitmap. // psoPattern---Points to the surface that describes the pattern for the brush. // For a raster device, this is a bitmap. For a vector device, // this is one of the pattern surfaces provided by DrvEnablePDEV. // psoMask------Points to a transparency mask for the brush. This is a 1 bit // per pixel bitmap that has the same extent as the pattern. A // mask of zero means the pixel is considered a background pixel // for the brush. (In transparent background mode, the background // pixels are unaffected in a fill.) Plotters can ignore this // parameter because they never draw background information. // pxlo---------Points to a XLATEOBJ that defines the interpretration of colors // in the pattern. A XLATEOBJXxx service routine can be called to // translate the colors to device color indices. Vector devices // should translate color zero through the XLATEOBJ to get the // foreground color for the brush. // ulHatch------Specifies whether psoPattern is one of the hatch brushes // returned by DrvEnablePDEV. This is true if the value of this // parameter is less than HS_API_MAX. // // Return Value // The return value is TRUE if the brush was successfully realized. Otherwise, // it is FALSE, and an error code is logged. // // Comments // To realize a brush, the driver converts a GDI brush into a form that can be // used internally. A realized brush contains information and accelerators the // driver needs to fill an area with a pattern; information that is defined by // the driver and used only by the driver. // // The driver's realization of a brush is written into the buffer allocated by // a call to BRUSHOBJ_pvAllocRbrush. // // DrvRealizeBrush is required for a driver that does any drawing to any // surface. // // ppdev->bRealizeTransparent -- Hint for whether or not the brush should be // realized for transparency. If this hint is // wrong, there will be no error, but the brush // will have to be unnecessarily re-realized. // // Note: You should always set 'ppdev->bRealizeTransparent' before calling // BRUSHOBJ_pvGetRbrush! // //----------------------------------------------------------------------------- BOOL DrvRealizeBrush(BRUSHOBJ* pbo, SURFOBJ* psoDst, SURFOBJ* psoPattern, SURFOBJ* psoMask, XLATEOBJ* pxlo, ULONG ulHatch) { PDev* ppdev = (PDev*)psoDst->dhpdev; BYTE* pbDst; BYTE* pbSrc; LONG i; LONG j; LONG lNumPixelToBeCopied; LONG lSrcDelta; RBrush* prb; ULONG* pulXlate; ULONG ulPatternFormat; PERMEDIA_DECL; DBG_GDI((6, "DrvRealizeBrush called for pbo 0x%x", pbo)); // // We have a fast path for dithers when we set GCAPS_DITHERONREALIZE: // if ( ulHatch & RB_DITHERCOLOR ) { // // Move this test in here since we always support monochrome brushes // as they live in the on-chip area stipple. These dithered brushes // will always be colored requiring available off-screen memory. // if ( !(ppdev->flStatus & STAT_BRUSH_CACHE) ) { // // We only handle brushes if we have an off-screen brush cache // available. If there isn't one, we can simply fail the // realization, and eventually GDI will do the drawing for us // (although a lot slower than we could have done it) // DBG_GDI((6, "brush cache not enabled")); goto ReturnFalse; } DBG_GDI((7, "DITHERONREALIZE")); // // We have a fast path for dithers when we set GCAPS_DITHERONREALIZE // First, we need to allocate memory for the realization of a brush // Note: actually we ask for allocation of a RBRUSH + the brush // stamp size // prb = (RBrush*)BRUSHOBJ_pvAllocRbrush(pbo, sizeof(RBrush) + (TOTAL_BRUSH_SIZE << ppdev->cPelSize)); if ( prb == NULL ) { DBG_GDI((1, "BRUSHOBJ_pvAllocRbrush() in dither return NULL\n")); goto ReturnFalse; } // // Dither and realize the brsuh pattern // vRealizeDitherPattern(psoDst->hdev, prb, ulHatch); goto ReturnTrue; }// if ( ulHatch & RB_DITHERCOLOR ) // // We only handle brushes if we have an off-screen brush cache available // If there isn't one, we can simply fail the realization, and eventually // GDI will do the drawing for us (although a lot slower than we could have // done it). We always succeed for 1bpp patterns since we use the area // stipple unit to do these rather than off-screen memory. // ulPatternFormat = psoPattern->iBitmapFormat; if ( !(ppdev->flStatus & STAT_BRUSH_CACHE) &&(ulPatternFormat != BMF_1BPP) ) { DBG_GDI((1, "brush cache not enabled, or Bitmap is not 1 BPP")); goto ReturnFalse; } // // We only accelerate 8x8 patterns since most of the video card can only // accelerate 8x8 brush // if ( (psoPattern->sizlBitmap.cx != 8) ||(psoPattern->sizlBitmap.cy != 8) ) { DBG_GDI((1, "Brush Bitmap size is not 8x8")); goto ReturnFalse; } // // We need to allocate memory for the realization of a brush // Note: actually we ask for allocation of a RBRUSH + the brush stamp size // prb = (RBrush*)BRUSHOBJ_pvAllocRbrush(pbo, sizeof(RBrush) + (TOTAL_BRUSH_SIZE << ppdev->cPelSize)); if ( prb == NULL ) { DBG_GDI((0, "BRUSHOBJ_pvAllocRbrush() failed")); goto ReturnFalse; } // // Initialize the fields we need // prb->ptlBrushOrg.x = LONG_MIN; prb->fl = 0; prb->pbe = NULL; lSrcDelta = psoPattern->lDelta; pbSrc = (BYTE*)psoPattern->pvScan0; pbDst = (BYTE*)&prb->aulPattern[0]; // // At 8bpp, we handle patterns at 1bpp, 4bpp and 8bpp with/without an xlate // At 16bpp, we handle patterns at 16 bpp without an xlate. // At 32bpp, we handle patterns at 32bpp without an xlate. // We handle all the patterns at 1 bpp with/without an Xlate // // Check if the brush pattern has the same color depth as our current // display color depth // if ( ulPatternFormat == BMF_1BPP ) { DWORD Data; DBG_GDI((7, "Realizing 1bpp brush")); // // We dword align the monochrome bitmap so that every row starts // on a new long (so that we can do long writes later to transfer // the bitmap to the area stipple unit). // for ( i = 8; i != 0; i-- ) { // // Replicate the brush to 32 bits wide, as the TX cannot // span fill 8 bit wide brushes // Data = (*pbSrc) & 0xff; Data |= Data << 8; Data |= Data << 16; *(DWORD*)pbDst = Data; // // Area stipple is loaded with DWORDS // pbDst += sizeof(DWORD); pbSrc += lSrcDelta; } pulXlate = pxlo->pulXlate; prb->fl |= RBRUSH_2COLOR; prb->ulForeColor = pulXlate[1]; prb->ulBackColor = pulXlate[0]; }// Pattern at 1 BPP else if ( (ulPatternFormat == BMF_4BPP)&&(ppdev->iBitmapFormat == BMF_8BPP)) { DBG_GDI((7, "Realizing 4bpp brush")); // // The screen is 8bpp and the pattern is 4bpp: // pulXlate = pxlo->pulXlate; for ( i = 8; i != 0; i-- ) { // // Inner loop is repeated only 4 times because each loop // handles 2 pixels: // for ( j = 4; j != 0; j-- ) { *pbDst++ = (BYTE)pulXlate[*pbSrc >> 4]; *pbDst++ = (BYTE)pulXlate[*pbSrc & 15]; pbSrc++; } pbSrc += lSrcDelta - 4; } }// Pattern 4BPP and Screen 8 BPP else if ( ( ppdev->iBitmapFormat == ulPatternFormat ) &&( (pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL) ) ) { DBG_GDI((7, "Realizing un-translated brush")); // // The pattern is the same colour depth as the screen, and // there's no translation to be done. // Here we first need to calculate how many pixel need to be // copied // lNumPixelToBeCopied = (8 << ppdev->cPelSize); for ( i = 8; i != 0; i-- ) { RtlCopyMemory(pbDst, pbSrc, lNumPixelToBeCopied); pbSrc += lSrcDelta; pbDst += lNumPixelToBeCopied; } }// Pattern and Screen has same color depth, No Xlate else if ( (ppdev->iBitmapFormat == BMF_8BPP) &&(ulPatternFormat == BMF_8BPP) ) { DBG_GDI((7, "Realizing 8bpp translated brush")); // // The screen is 8bpp, and there's translation to be done // So we have to do copy + Xlate one by one // pulXlate = pxlo->pulXlate; for ( i = 8; i != 0; i-- ) { for ( j = 8; j != 0; j-- ) { *pbDst++ = (BYTE)pulXlate[*pbSrc++]; } pbSrc += lSrcDelta - 8; } }// Screen mode and pattern mode all at 8 BPP else { // // We've got a brush whose format we haven't special cased. // goto ReturnFalse; } ReturnTrue: DBG_GDI((6, "DrvRealizeBrush returning true")); return(TRUE); ReturnFalse: if ( psoPattern != NULL ) { DBG_GDI((1, "Failed realization -- Type: %li Format: %li cx: %li cy: %li", psoPattern->iType, psoPattern->iBitmapFormat, psoPattern->sizlBitmap.cx, psoPattern->sizlBitmap.cy)); } DBG_GDI((6, "DrvRealizeBrush returning false")); return(FALSE); }// DrvRealizeBrush() //----------------------------------------------------------------------------- // // BOOL bEnableBrushCache // // Allocates off-screen memory for storing the brush cache. // //----------------------------------------------------------------------------- BOOL bEnableBrushCache(PDev* ppdev) { BrushEntry* pbe; // Pointer to the brush-cache entry LONG i; LONG lDelta; ULONG ulPixOffset; DBG_GDI((6, "bEnableBrushCache")); ASSERTDD(!(ppdev->flStatus & STAT_BRUSH_CACHE), "bEnableBrushCache: unexpected already enabled brush cache"); // // ENABLE_BRUSH_CACHE by default is on. It would be turned off in // bInitializeHw() if 3D buffers runs out of memory // if ( !(ppdev->flStatus & ENABLE_BRUSH_CACHE) ) { DBG_GDI((1, "Brush cache not valid for creation")); goto ReturnTrue; } ppdev->ulBrushVidMem = ulVidMemAllocate(ppdev, CACHED_BRUSH_WIDTH, CACHED_BRUSH_HEIGHT *NUM_CACHED_BRUSHES, ppdev->cPelSize, &lDelta, &ppdev->pvmBrushHeap, &ppdev->ulBrushPackedPP, FALSE); if (ppdev->ulBrushVidMem == 0 ) { DBG_GDI((0, "bEnableBrushCache: failed to allocate video memory")); goto ReturnTrue; // See note about why we can return TRUE... } ASSERTDD(lDelta == (CACHED_BRUSH_WIDTH << ppdev->cPelSize), "bEnableBrushCache: unexpected stride does not match width"); ppdev->cBrushCache = NUM_CACHED_BRUSHES; ulPixOffset = (ULONG) ppdev->ulBrushVidMem >> ppdev->cPelSize; pbe = &ppdev->abe[0]; for (i = 0; i < NUM_CACHED_BRUSHES; i++, pbe++) { pbe->prbVerify = NULL; pbe->ulPixelOffset = ulPixOffset; ulPixOffset += CACHED_BRUSH_SIZE; memset((pbe->ulPixelOffset << ppdev->cPelSize) + ppdev->pjScreen, 0x0, (CACHED_BRUSH_SIZE << ppdev->cPelSize)); } // // We successfully allocated the brush cache, so let's turn // on the switch showing that we can use it: // DBG_GDI((6, "bEnableBrushCache: successfully allocated brush cache")); ppdev->flStatus |= STAT_BRUSH_CACHE; ReturnTrue: // // If we couldn't allocate a brush cache, it's not a catastrophic // failure; patterns will still work, although they'll be a bit // slower since they'll go through GDI. As a result we don't // actually have to fail this call: // DBG_GDI((6, "Passed bEnableBrushCache")); return(TRUE); }// bEnableBrushCache() //----------------------------------------------------------------------------- // // VOID vDisableBrushCache // // Cleans up anything done in bEnableBrushCache. // //----------------------------------------------------------------------------- VOID vDisableBrushCache(PDev* ppdev) { DBG_GDI((6,"vDisableBrushCache")); if(ppdev->flStatus & STAT_BRUSH_CACHE) { DBG_GDI((6,"vDisableBrushCache: freeing brush cache")); VidMemFree(ppdev->pvmBrushHeap->lpHeap, (FLATPTR)(ppdev->ulBrushVidMem)); ppdev->cBrushCache = 0; ppdev->flStatus &= ~STAT_BRUSH_CACHE; DBG_GDI((6,"vDisableBrushCache: freeing brush cache done")); } }// vDisableBrushCache() //----------------------------------------------------------------------------- // // VOID vAssertModeBrushCache // // Resets the brush cache when we exit out of full-screen. // //----------------------------------------------------------------------------- VOID vAssertModeBrushCache(PDev* ppdev, BOOL bEnable) { if ( bEnable ) { bEnableBrushCache(ppdev); } else { vDisableBrushCache(ppdev); } }// vAssertModeBrushCache()