/************************************************************************** * * Copyright (c) 2000 Microsoft Corporation * * Module Name: * * Software rasterizer code for drawing a CachedBitmap * * Created: * * 05/18/2000 asecchia * Created it. * **************************************************************************/ #include "precomp.hpp" // Class to output a clipped span for a CachedBitmap class DpOutputCachedBitmapSpan : public DpOutputSpan { EpScanRecord *InputBuffer; DpScanBuffer *Scan; INT XOff, YOff; // coordinates of the top left corner. INT PixelSize; public: DpOutputCachedBitmapSpan( DpScanBuffer *scan, INT x, INT y ) { Scan = scan; InputBuffer = NULL; XOff = x; YOff = y; } virtual GpStatus OutputSpan(INT y, INT xMin, INT xMax) { // Can't draw anything if the buffer hasn't been set correctly. ASSERT(InputBuffer != NULL); ASSERT(YOff + InputBuffer->Y == y); ASSERT(xMax-xMin <= InputBuffer->Width); // Get an output buffer. void *buffer; buffer = Scan->NextBuffer( xMin, y, xMax-xMin, InputBuffer->BlenderNum ); // Get a pointer to the start of the scanline. void *ib = InputBuffer->GetColorBuffer(); INT pixelSize = PixelSize; // InputBuffer->X + XOff is the x starting position on the screen. // Make sure we're not trying to draw off to the left of our data. ASSERT(xMin >= (InputBuffer->X+XOff)); ib = (void *) ( (BYTE *)(ib) + (xMin - (InputBuffer->X+XOff))*pixelSize); // Copy the input buffer to the output buffer. GpMemcpy(buffer, ib, (xMax-xMin)*pixelSize); // Cannot fail this routine. return Ok; } // Initialize the class with a new scan record. // This is used when we want to start processing a new batch record. void SetInputBuffer( EpScanRecord *ib, INT pixelSize ) { InputBuffer = ib; PixelSize = pixelSize; } // We're always valid. virtual int IsValid() const {return TRUE;} }; /************************************************************************** * * Function Description: * * Software Rasterizer code for drawing a DpCachedBitmap * * Arguments: * * context - the graphics context. * src - the DpCachedBitmap to draw from * dst - where the output goes * x, y - offset - position to draw the top left corner of the CachedBitmap. * * Return Value: * * GpStatus. * * Notes: * * This driver entry point expects the input to be in device coordinates * so the caller has to pre compute the world to device transform. * This is contrary to how most of our driver entry points work. * * Created: * * 05/18/2000 asecchia * Created it. * **************************************************************************/ GpStatus DpDriver::DrawCachedBitmap( DpContext *context, DpCachedBitmap *src, DpBitmap *dst, INT x, INT y // Device coordinates! ) { ASSERT(context); ASSERT(src); ASSERT(dst); // Let's go make sure the Surface pixel format and the CachedBitmap // opaque format match. // The exception is for 32bppRGB which can draw onto anything. // This format is used in the multi-mon case where the individual // screen devices can be in multiple formats. // When 64bpp formats become first class citizens, we may want to // update this condition. if((dst->PixelFormat != src->OpaqueFormat) && (src->OpaqueFormat != PixelFormat32bppRGB)) { return WrongState; } // Ignore the world to device transform - this driver entry point is // somewhat unique in that it expects device coordinates. // Initialize the DpScanBuffer. // This hooks us up to the appropriate DpScanXXX class for outputting our // data to the destination device. DpScanBuffer scanBuffer( dst->Scan, this, context, dst, FALSE, EpScanTypeBlend, src->SemiTransparentFormat, src->OpaqueFormat ); if(!scanBuffer.IsValid()) { return(GenericError); } // Set up the clipping. DpRegion::Visibility visibility = DpRegion::TotallyVisible; DpClipRegion *clipRegion = NULL; if(context->VisibleClip.GetRectVisibility( x, y, x+src->Width, y+src->Height ) != DpRegion::TotallyVisible ) { clipRegion = &(context->VisibleClip); } GpRect clippedRect; if(clipRegion) { visibility = clipRegion->GetRectVisibility( x, y, x+src->Width, y+src->Height, &clippedRect ); } // Decide on our clipping strategy. switch (visibility) { case DpRegion::TotallyVisible: // no clipping is needed { // Copy the scanlines to the destination buffer // ProcessBatch requests that the DpScan class handle the entire // batch as a single block. If it can't it will return FALSE and // we fall through into the general purpose code below. BOOL batchSupported = scanBuffer.ProcessBatch( src->RecordStart, src->RecordEnd, x, y, x+src->Width, y+src->Height ); if(batchSupported) { // The scanBuffer supports ProcessBatch; we're done. break; } // The scanBuffer doesn't support the ProcessBatch routine. // Lets manually enumerate the batch structure into the destination. // Fall through into the manual enumeration code: } // !!! PERF [asecchia] We have a perf problem when there is no clipping // except the standard surface bounds. DCI/GDI would be able to clip // this directly, but we aren't sure how to robustly detect this // optimization and ignore the clip rectangle. This does not impact // the fully visible case. Also it's not appropriate to make this // optimization unless we're using DpScanGdiDci as our output device. case DpRegion::ClippedVisible: case DpRegion::PartiallyVisible: // some clipping is needed { // Create the OutputSpan class for the CachedBitmap. // Create this on the stack because it has very little storage // and we can avoid a malloc. It gets cleaned up when we go out // of scope. DpOutputCachedBitmapSpan output( &scanBuffer, x, y ); // Initialize the clipper to point to the clip region and // create the clpping chain by tacking the output created above // on to the end of the list. DpOutputSpan *clipper; if(clipRegion) { clipper = clipRegion; clipRegion->InitClipping(&output, y); } else { // no clipping required - possible due to the fallthrough case // in the fully visible codepath. clipper = &output; } // Lets manually enumerate the batch structure into the destination // taking into account clipping // First set up the running record pointer to the beginning of the // batch and a sentinel for the end. EpScanRecord *record = src->RecordStart; EpScanRecord *batchEnd = src->RecordEnd; // For all the batch records, Draw them on the destination. while(record < batchEnd) { PixelFormatID format; if (record->BlenderNum == 0) { format = src->SemiTransparentFormat; } else { ASSERT(record->BlenderNum == 1); format = src->OpaqueFormat; } INT pixelSize = GetPixelFormatSize(format) >> 3; // Set the output span buffer output.SetInputBuffer(record, pixelSize); // Draw this span INT x1 = x+record->X; INT x2 = x+record->X+record->Width; clipper->OutputSpan(y+record->Y, x1, x2); // Advance to the next record: record = record->NextScanRecord(pixelSize); } } break; case DpRegion::Invisible: // nothing on screen - quit break; } return Ok; }