|
|
/***************************** Module Header ********************************
* render.c * High level functions associated with rendering a bitmap to a * printer. Basic operation depends upon whether we are going to * rotate the bitmap - either because the printer cannot, or it * is faster if we do it. * With rotation, allocate a chunk of memory and transpose the * output bitmap into it. Call the normal processing code, but * with this allocated memory and new fake bitmap info. After * processing this chunk, transpose the next and process. Repeat * until the entire bitmap has been rendered. Free the memory, return. * Without rotation, simply pass the bitmap onto the rendering * code, to process in one hit. * * * Copyright (C) 1991 - 1999, Microsoft Corporation * ***************************************************************************/
#include "raster.h"
#include "compress.h"
#include "rmrender.h"
#include "fontif.h"
#include "rmdebug.h"
#include "xlraster.h"
/*
* Constants used to calculate the amount of memory to request if we * need to transpose the engine's bitmap before sending to the printer. * If at least one head pass will fit within the the TRANSPOSE_SIZE * buffer, then request this amount of storage. If not, calculate how * much is needed and request that much. */
#define TRANSPOSE_SIZE 0x20000 /* 128k */
//used when we can grow the block height
#define DEF_BLOCK_SIZE 0x08000 /* 32k */
#define MAX_BLOCK_SIZE 0x40000 /* 256k */
/*
* Set a limit to the number of interlaced lines that we can print. * Interlacing is used to increase the resolution of dot matrix printers, * by printing lines in between lines. Typically the maximum interlace * will be 2, but we allow more just in case. This size determines the * size of an array allocated on the stack. The array is of ints, so * there is not much storage consumed by setting this high a value. */ #define MAX_INTERLACE 10 /* Dot matrix style interlace factor */
/*
* Local function prototypes. */
BOOL bRealRender( PDEV *, DWORD *, RENDER * ); BOOL bOnePassOut( PDEV *, BYTE *, RENDER * ); BOOL bOneColourPass( PDEV *, BYTE *, RENDER * ); INT iLineOut( PDEV *, RENDER *, BYTE *, INT, INT ); void vInvertBits( DWORD *, INT ); //void vFindWhiteInvertBits ( RASTERPDEV *, RENDER *, DWORD *);
BOOL bLookAheadOut( PDEV *, INT, RENDER *, INT );
#ifdef TIMING
#include <stdio.h>
void DrvDbgPrint( char * pch, ...) { va_list ap; char buffer[256];
va_start(ap, pch);
EngDebugPrint("",pch,ap);
va_end(ap); } #endif
/************************ Function Header ***********************************
* bRenderInit * Called during DrvEnableSurface time - we initialise a RENDER_DATA * structure which will be used for the duration of this surface. * * RETURNS: * TRUE/FALSE, FALSE means a minidriver problem or no memory. * * HISTORY: * Monday November 29 1993 -by- Norman Hendley [normanh] * Implement multiple scanline printing; fixed & variable block height. * * 10:58 on Tue 10 Nov 1992 -by- Lindsay Harris [lindsayh] * Moved from start of bRender() - second incarnation. * ****************************************************************************/
BOOL bRenderInit( pPDev, sizl, iFormat ) PDEV *pPDev; /* Our key to the universe */ SIZEL sizl; /* Size of processing band, <= sizlPage */ INT iFormat; /* GDI bitmap format */ {
INT cbOutBand; /* Bytes per output band: based on # pins */ INT cbOneBlock; /* Bytes per minimum sized block if block variable */ INT iBPP; /* Bits per pel - expect 1 or 4 */ INT iIndex; /* Loop paramter */ INT iBytesPCol; /* Bytes per column - only for colour */
RASTERPDEV *pRPDev; /* The unidrive PDEV - printer details */
RENDER *pRD; /* Miscellaneous rendering data */
#ifdef TIMING
ENG_TIME_FIELDS TimeTab; pRPDev = pPDev->pRasterPDEV; EngQueryLocalTime(&TimeTab); pRPDev->dwTiming = (((TimeTab.usMinute*60)+TimeTab.usSecond)*1000)+ TimeTab.usMilliseconds; #endif
ASSERTMSG(!(sizl.cx == 0 || sizl.cy == 0),("unidrv!bRenderInit - null shadow bitmap\n"));
/*
* Allocate storage for our RENDER structure, then set it all to * zero, so that it is in a known safe state. */
pRPDev = pPDev->pRasterPDEV; if( !(pRD = (RENDER *) MemAllocZ( ( pPDev->bBanding) ? sizeof(RENDER) * 2 : sizeof( RENDER )))) return FALSE;
pRPDev->pvRenderData = pRD;
pRPDev->pvRenderDataTmp = ( pPDev->bBanding ) ? pRD+1 : NULL;
/*
* Various operations depend upon what format bitmap we have. So * now is the time to set this all up. */ pRD->dwDevWhiteIndex = 0; switch( iFormat ) { case BMF_1BPP: iBPP = 1; pRD->bWhiteLine = bIsLineWhite; pRD->bWhiteBand = bIsBandWhite; pRD->ubFillWhite = 0; pRD->vLtoPTransFn = vTrans8N; break;
case BMF_4BPP: iBPP = 4; if ((pRPDev->fColorFormat & DC_OEM_BLACK) && !(pRPDev->fColorFormat & DC_PRIMARY_RGB)) { pRD->bWhiteLine = bIsLineWhite; pRD->bWhiteBand = bIsBandWhite; pRD->ubFillWhite = 0; } else { if (pRPDev->fColorFormat & DC_PRIMARY_RGB) pRD->dwDevWhiteIndex = 0x77777777; pRD->bWhiteLine = bIsRGBLineWhite; pRD->bWhiteBand = bIsRGBBandWhite; pRD->ubFillWhite = 0x77; } pRD->vLtoPTransFn = vTrans8N4BPP; break;
case BMF_8BPP: iBPP = 8; pRD->bWhiteLine = bIs8BPPLineWhite; pRD->bWhiteBand = bIs8BPPBandWhite; pRD->dwDevWhiteIndex = pRD->ubFillWhite = (BYTE)pRPDev->pPalData->iWhiteIndex; pRD->dwDevWhiteIndex |= pRD->dwDevWhiteIndex << 8; pRD->dwDevWhiteIndex |= pRD->dwDevWhiteIndex << 16; pRD->vLtoPTransFn = vTrans8BPP; break;
case BMF_24BPP: iBPP = 24; pRD->bWhiteLine = bIs24BPPLineWhite; pRD->bWhiteBand = bIs24BPPBandWhite; pRD->ubFillWhite = 0xff; pRD->dwDevWhiteIndex = ~(DWORD)0; pRD->vLtoPTransFn = vTrans24BPP; break;
default: #if DBG
DbgPrint( "Unidrv!bRender: not 1, 4, 8 or 24 bits per pel bitmap\n" ); #endif
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE; }
pRD->iBPP = iBPP;
// If there is no Y movement commands we need to send every scan
// so we don't want to find white lines
// PCLXL GPD files don't have YMOVE commands. But it needs to find white
// lines.
if (pPDev->arCmdTable[CMD_YMOVERELDOWN] == NULL && pPDev->arCmdTable[CMD_YMOVEABSOLUTE] == NULL && pPDev->arCmdTable[CMD_SETLINESPACING] == NULL && pPDev->ePersonality != kPCLXL_RASTER) { pRD->bWhiteLine = pRD->bWhiteBand = bIsNeverWhite; pRPDev->fBlockOut |= RES_BO_NO_YMOVE_CMD; }
// initialize bTTY for TTY device
pRD->PrinterType = pPDev->pGlobals->printertype;
if( pRPDev->sMinBlankSkip == 0 || iBPP == 24) { /* Presume this means skip should not be performed. */ pRPDev->fBlockOut &= ~RES_BO_ENCLOSED_BLNKS; }
pRD->iCursor = pRPDev->fCursor;
pRD->iFlags = 0; /* Nothing set, yet */ pRD->fDump = pRPDev->fDump; pRD->Trans.pdwTransTab = pRPDev->pdwTrans; pRD->pdwBitMask = pRPDev->pdwBitMask; pRD->pdwColrSep = pRPDev->pdwColrSep; /* Colour translation */
pRD->ix = sizl.cx; pRD->iy = sizl.cy;
pRD->iSendCmd = CMD_SENDBLOCKDATA; //CMD_RES_SENDBLOCK;
pRD->cBLine = pRD->ix * iBPP; /* Bits in scanline */ pRD->cDWLine = (pRD->cBLine + DWBITS - 1) / DWBITS; pRD->cBYLine = pRD->cDWLine * DWBYTES;
pRD->iPassHigh = pRPDev->sNPins;
// Derryd : Minidriver Callback
if (pRPDev->fRMode & PFR_BLOCK_IS_BAND ) //means callback wants entire band as one block
{ //don't want any stripping, because would mean creating new buffers
pRPDev->fBlockOut &= ~(RES_BO_LEADING_BLNKS | RES_BO_TRAILING_BLNKS | RES_BO_ENCLOSED_BLNKS); pRD->fDump &= ~RES_DM_LEFT_BOUND; pRD->iPassHigh = pRD->iy ; } // end
if (pPDev->ePersonality == kPCLXL_RASTER) { pRPDev->fBlockOut |= RES_BO_LEADING_BLNKS | RES_BO_TRAILING_BLNKS; pRPDev->fBlockOut &= ~RES_BO_ENCLOSED_BLNKS; }
//Set the key fields which enable us print multiple scanlines
if (pRD->fDump & RES_DM_GDI) //GDI style graphics
{ // No interlacing on these devices
pRD->iInterlace = 1;
// iHeight is fixed & the minimum size block we can print
pRD->iHeight= pRD->iPassHigh;
//iNumScans can grow if device allows it.
pRD->iNumScans= pRD->iHeight;
//in case minidriver developer sets otherwise
//Existing code relies on this being one for GDI style graphics
pRD->iBitsPCol = 1; } else //Old dot matrix column style graphics
{ pRD->iBitsPCol = pRPDev->sPinsPerPass; pRD->iInterlace = pRD->iPassHigh / pRD->iBitsPCol;
// questionable choice, but enables easier checking later
pRD->iNumScans= 1;
//our one constant between graphics modes
pRD->iHeight= pRD->iBitsPCol; }
pRD->iPosnAdv = pRD->iHeight; // can be negative
if( pRD->iInterlace > MAX_INTERLACE ) { #if DBG
DbgPrint( "unidrv!bRenderInit: Printer Interlace too big to handle\n" ); #endif
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE; }
// We'll need to scan the bitmap in blocks rather than single lines
//
if (pRD->iNumScans > 1) pRD->bWhiteLine = pRD->bWhiteBand;
/*
* Calculate the size needed for the output transpose buffer. This * is the buffer used to convert the data into the pin order needed * for dot matrix printers. */
if( pPDev->fMode & PF_ROTATE ) { /* We do the rotation, so the Y dimension is the one to use. */ cbOutBand = pRD->iy; } else cbOutBand = pRD->ix; /* Format as it comes in */
//used for dangling scanline scenario
cbOneBlock = ((cbOutBand * iBPP + DWBITS - 1) / DWBITS) * DWBYTES * pRD->iHeight;
// In this case we don't know how large our final blocks will be.
// Set a reasonable limit of 32k & use that for compression & white
// space stripping buffers.
// Calculate what the corresponding max number of scanlines should be.
if (pRPDev->fBlockOut & RES_BO_MULTIPLE_ROWS) { INT tmp = ((cbOutBand * iBPP + DWBITS - 1) / DWBITS) * DWBYTES; cbOutBand = pPDev->pGlobals->dwMaxMultipleRowBytes; if (cbOutBand < tmp) cbOutBand = DEF_BLOCK_SIZE; else if (cbOutBand > MAX_BLOCK_SIZE) cbOutBand = MAX_BLOCK_SIZE; pRD->iMaxNumScans = cbOutBand / tmp; } else { pRD->iMaxNumScans = pRD->iHeight; cbOutBand = cbOneBlock; }
//
// If each data byte needs to be mirrored before output
// we will generate the table here
//
if (pRPDev->fBlockOut & RES_BO_MIRROR) { INT i; if ((pRD->pbMirrorBuf = MemAlloc(256)) == NULL) return FALSE; for (i = 0;i < 256;i++) { BYTE bOut = 0; if (i & 0x01) bOut |= 0x80; if (i & 0x02) bOut |= 0x40; if (i & 0x04) bOut |= 0x20; if (i & 0x08) bOut |= 0x10; if (i & 0x10) bOut |= 0x08; if (i & 0x20) bOut |= 0x04; if (i & 0x40) bOut |= 0x02; if (i & 0x80) bOut |= 0x01; pRD->pbMirrorBuf[i] = bOut; } }
//
// time to do more color depth specific calculations
//
switch( iBPP ) { case 4: /* 4 bits per pel - printer is planar */
/* Colour, so select the colour rendering function */ pRD->bPassProc = bOneColourPass; pRD->Trans.pdwTransTab = pRPDev->pdwColrSep;
//
// map the color order to the required data offset for planar data
// rgbOrder valid values are emum {none,r,g,b,c,m,y,k}
//
iBytesPCol = (pRD->iBitsPCol + BBITS - 1) / BBITS; { INT offset = (pRPDev->fColorFormat & DC_PRIMARY_RGB) ? 1 : 4 ;
for( iIndex = 0; iIndex < COLOUR_MAX; ++iIndex ) { INT tmp = pRPDev->rgbOrder[iIndex] - offset;
pRD->iColOff[ iIndex ] = iBytesPCol * (COLOUR_MAX - 1 - tmp); } } pRD->pbColSplit = MemAlloc( cbOutBand / 4 ); if( pRD->pbColSplit == 0 ) return FALSE;
//
// If we need to send every color plane we must disable LEADING and ENCLOSED
// white space removal for 4bpp. However, as long as we're not sending RGB data
// we can still enable TRAILING white space removal.
//
if (pRPDev->fColorFormat & DC_SEND_ALL_PLANES) { pRD->iFlags |= RD_ALL_COLOUR; pRD->fDump &= ~RES_DM_LEFT_BOUND;
if (pRPDev->fColorFormat & DC_PRIMARY_RGB) pRPDev->fBlockOut &= ~(RES_BO_LEADING_BLNKS | RES_BO_ENCLOSED_BLNKS | RES_BO_TRAILING_BLNKS); else pRPDev->fBlockOut &= ~(RES_BO_LEADING_BLNKS | RES_BO_ENCLOSED_BLNKS); }
break;
case 1: /* 1 bit per pel - monochrome */ case 8: /* Seiko special - 8 bits per pel */
pRD->bPassProc = bOnePassOut; pRD->Trans.pdwTransTab = pRPDev->pdwTrans; pRD->pbColSplit = 0; /* No storage allocated either! */
break;
case 24:
pRD->bPassProc = bOnePassOut; pRD->Trans.pdwTransTab = NULL; pRD->pbColSplit = 0; /* No storage allocated either! */
break; }
/*
* There are potentially 2 transpose operations. For printers * which print more than one line per pass, AND which require the * data in column order across the page (this defines dot matrix * printers), we need to transpose per output head pass. This is * not required for devices like laser printers, which require * the data one scan line at a time. * Note also that this operation is unrelated to the larger * question of rotating the PAGE image before rendering - for sending * a landscape image to a printer that can only print portrait mode. */
if (pRD->fDump & RES_DM_GDI) // GDI style graphics
{
if( iBPP == 4 ) { /* Paintjet style printer - need to colour separate */ pRD->vTransFn = vTransColSep; } else { /* LaserJet style printer - one pin per head pass */ pRD->vTransFn = 0; /* Nothing to call */ } //This allows us use iIsBandWhite with multi scanline printing
pRD->iTransHigh = pRD->iHeight; } else { /*
* General dot matrix case. Apart from selecting an active * transpose function, we must allocate a transpose buffer; * this is required for fiddling with the bit order in the lines * of data to be sent to the printer. */
pRD->iTransWide = pRD->ix * iBPP; pRD->iTransHigh = pRD->iBitsPCol; pRD->iTransSkip = (pRD->iTransHigh + BBITS - 1) / BBITS;
/* How to change the address pointer during transpose operations */ pRD->cbTLine = pRD->cDWLine * DWBYTES * pRD->iInterlace;
if( pRD->iBitsPCol == BBITS ) { /*
* When the printer has 8 pins, we have a special transpose * function which is faster than the more general case. * So, use that one! */ pRD->vTransFn = vTrans8x8; } else if (pRD->PrinterType != PT_TTY) pRD->vTransFn = vTrans8N; /* The general case */ else pRD->vTransFn = NULL; /* Txtonly no need to transpose */ }
if( pRD->vTransFn ) { /*
* Determine the amount of memory needed for the transpose buffer. * The scan lines are DWORD aligned, but there may be any number of * scan lines involved. The presumption is that the output of * the transpose function will be packed on byte boundaries, so * storage size only needs to be rounded up to the nearest byte size. */
if( !(pRD->pvTransBuf = MemAlloc( cbOutBand )) ) return FALSE; } else pRD->pvTransBuf = 0; /* No store, nothing to free */
pRD->iyBase = 0; /* When multiple passes are required */ pRD->ixOrg = 0; /* Graphics origin - laserjet style */
//We need a buffer so we can strip leading and/or trailing white space
//on multiple scan line devices.
//We also need to set up a buffer to mask non-interesting data at end
//of page if ScanLines_Left < iNumScans
//This is not a concern for old dot matrix style graphics as the
//transpose code takes care of it.
if ( ((pRD->iNumScans > 1) || (pRPDev->fBlockOut & RES_BO_MULTIPLE_ROWS)) && (!(pRPDev->fRMode & PFR_BLOCK_IS_BAND ) )) { if ( !(pRD->pStripBlanks = MemAlloc(cbOutBand))) return FALSE; if (pRD->iNumScans > 1) if ( !(pRD->pdwTrailingScans = MemAlloc( cbOneBlock)) ) return FALSE; }
//
// We need to determine which compression modes are enabled
//
if (pRPDev->fRMode & PFR_COMP_TIFF) { pRD->pdwCompCmds[pRD->dwNumCompCmds++] = CMD_ENABLETIFF4; } if (pRPDev->fRMode & PFR_COMP_FERLE) { pRD->pdwCompCmds[pRD->dwNumCompCmds++] = CMD_ENABLEFERLE; } if (pRPDev->fRMode & PFR_COMP_DRC) { pRD->pdwCompCmds[pRD->dwNumCompCmds++] = CMD_ENABLEDRC; } if (pRPDev->fRMode & PFR_COMP_OEM) { pRD->pdwCompCmds[pRD->dwNumCompCmds++] = CMD_ENABLEOEMCOMP; } //
// if compression is available we need to allocate a buffer for
// each active compression type
//
if (pRD->dwNumCompCmds) { INT i = pRD->dwNumCompCmds; //
// calculate the size for the buffers
//
pRD->dwCompSize = cbOutBand + (cbOutBand >> 5) + COMP_FUDGE_FACTOR;
//
// if there is FERLE or OEM compression we can enable
// no compression as a valid compression type
//
if (pRPDev->fRMode & (PFR_COMP_FERLE | PFR_COMP_OEM)) { if (COMMANDPTR(pPDev->pDriverInfo,CMD_DISABLECOMPRESSION)) { pRD->pdwCompCmds[pRD->dwNumCompCmds++] = CMD_DISABLECOMPRESSION; } //
// need to allocate a larger buffer for RLE or OEM compression if
// no compression is not an option since it must have enough space
//
else pRD->dwCompSize = cbOutBand + (cbOutBand >> 1) + COMP_FUDGE_FACTOR; }
//
// loop once per compression type to allocate buffers
//
while (--i >= 0) { // allocate compression buffer
//
pRD->pCompBufs[i] = MemAlloc (pRD->dwCompSize); if (!pRD->pCompBufs[i]) return FALSE; //
// if delta row, allocate buffer for previous row
// and initialize it to zero assuming blank row
//
if (pRD->pdwCompCmds[i] == CMD_ENABLEDRC) { pRD->pDeltaRowBuffer = MemAlloc(cbOutBand); if (!pRD->pDeltaRowBuffer) return FALSE; } } } pRD->dwLastCompCmd = CMD_DISABLECOMPRESSION; /*
* Adjustments to whether we rotate the bitmap, and if so, which way. */
if( pPDev->fMode & PF_ROTATE ) { /* Rotation is our responsibility */
if( pPDev->fMode & PF_CCW_ROTATE90 ) { /* Counter clockwise rotation - LaserJet style */ pRD->iyPrtLine = pPDev->sf.szImageAreaG.cx - 1; pRD->iPosnAdv = -pRD->iPosnAdv; pRD->iXMoveFn = YMoveTo; pRD->iYMoveFn = XMoveTo; } else { /* Clockwise rotation - dot matrix style */ pRD->iyPrtLine = 0; pRD->iXMoveFn = XMoveTo; pRD->iYMoveFn = YMoveTo; } } else { /* No rotation: either portrait, or printer does it */ pRD->iyPrtLine = 0; pRD->iXMoveFn = XMoveTo; pRD->iYMoveFn = YMoveTo; } pRD->iyLookAhead = pRD->iyPrtLine; /* For DeskJet lookahead */
/*
* When we hit the lower level functions, we want to know how many * bytes are in the buffer of stuff to be sent to the printer. This * depends upon the number of bits per pel, number of pels and the * the number of scan lines processed at the same time. * The only oddity is that when we have a 4 BPP device, the * planes are split before we get to the lowest level, and so we * we need to reduce the size by 4 to obtain the real length. */
// Note when printing a block of scanlines iMaxBytesSend will be the max byte
// count for each scanline, not of the block , which is dword aligned.
pRD->iMaxBytesSend = (pRD->cBLine * pRD->iBitsPCol + BBITS - 1) / BBITS;
if( iBPP == 4 ) pRD->iMaxBytesSend = (pRD->iMaxBytesSend + 3) / 4;
return TRUE; /* Must be OK if we made it this far */
}
/************************ Function Header *********************************
* bRenderStartPage * Called at the start of a new page. This is mostly to assist in * banding, where much of the per page initialisation would be * done more than once. * * RETURNS: * TRUE/FALSE, FALSE largely being failure to allocate memory. * * HISTORY: * 09:42 on Fri 19 Feb 1993 -by- Lindsay Harris [lindsayh] * First incarnation, to solve some banding problems. * ***************************************************************************/
BOOL bRenderStartPage( pPDev ) PDEV *pPDev; /* Access to everything */ { #ifndef DISABLE_RULES
RASTERPDEV *pRPDev;
pRPDev = pPDev->pRasterPDEV;
/*
* If the printer can handle rules, now is the time to initialise * the rule finding code. */
if( pRPDev->fRMode & PFR_RECT_FILL ) vRuleInit( pPDev, pRPDev->pvRenderData ); #endif
return TRUE; }
/************************ Function Header *********************************
* bRenderPageEnd * Called at the end of rendering a page. Basically frees up the * per page memory, cleans up any dangling bits and pieces, and * otherwise undoes vRenderPageStart. * * RETURNS: * TRUE/FALSE, FALSE being a failure of memory freeing operations. * * HISTORY: * 15:16 on Fri 09 Apr 1993 -by- Lindsay Harris [lindsayh] * White text support. * * 09:44 on Fri 19 Feb 1993 -by- Lindsay Harris [lindsayh] * First incarnation. * **************************************************************************/
BOOL bRenderPageEnd( pPDev ) PDEV *pPDev; { #ifndef DISABLE_RULES
RASTERPDEV *pRPDev = pPDev->pRasterPDEV;
/* Finish up with the rules code - includes freeing memory */ if( pRPDev->fRMode & PFR_RECT_FILL ) vRuleEndPage( pPDev ); #endif
return TRUE; }
/************************ Function Header ***********************************
* vRenderFree * Free up any and all memory used by rendering. Basically this is * the complementary function to bRenderInit(). * * RETURNS: * Nothing. * * HISTORY: * 12:46 on Tue 10 Nov 1992 -by- Lindsay Harris [lindsayh] * Removed from bRender() when initialisation code was moved out. * *****************************************************************************/
void vRenderFree( pPDev ) PDEV *pPDev; /* All that we need */ {
/*
* First verify that we have a RENDER structure to free! */
RENDER *pRD; /* For our convenience */ PRASTERPDEV pRPDev = pPDev->pRasterPDEV;
if( pRD = pRPDev->pvRenderData ) { if( pRD->pvTransBuf ) { /*
* Dot matrix printers require a transpose for each print head * pass, so we now free the memory used for that. */
MemFree ( pRD->pvTransBuf ); }
if( pRD->pbColSplit ) MemFree ( pRD->pbColSplit ); if( pRD->pStripBlanks ) MemFree ( pRD->pStripBlanks );
if( pRD->pdwTrailingScans) MemFree ( pRD->pdwTrailingScans );
if (pRD->pbMirrorBuf) MemFree (pRD->pbMirrorBuf);
if( pRD->plrWhite) { // WARNING(("Freeing plrWhite in vRenderFree\n"));
MemFree ( pRD->plrWhite ); } // free compression buffers
//
if (pRD->dwNumCompCmds) { DWORD i; for (i = 0;i < pRD->dwNumCompCmds;i++) { if (pRD->pCompBufs[i]) MemFree (pRD->pCompBufs[i]); } if (pRD->pDeltaRowBuffer) MemFree(pRD->pDeltaRowBuffer); } MemFree ( (LPSTR)pRD ); pRPDev->pvRenderData = NULL; /* Won't do it again! */ }
#ifndef DISABLE_RULES
if( pRPDev->fRMode & PFR_RECT_FILL ) vRuleFree( pPDev ); /* Could do this in DisableSurface */ #endif
return;
}
/************************ Function Header ***********************************
* bRender * Function to take a bitmap and render it to the printer. This is the * high level function that basically hides the requirement of * bitmap transposing from the real rendering code. * * Auguments * SURFOBJ *pso; Surface object * PDEV *pPDev; Our PDEV: key to everything * RENDER *pRD; The RENDER structure of our dreams * SIZEL sizl; Bitmap size * DWORD *pBits; Actual data to process * * This code still has a lot of room for optimization. The current * implementation makes multiple passes over the entire bitmap. This * guarantees that there will rarely be an internal (8K or 16K) or * external (64K to 256K) cache hit slowing things down significantly. * Any possiblity to make all passes a count of scans totaling * 8K (minus code) or less will have significant performance advantages. * Also attempting to avoid writes will have significant advantages. * * As a first pass at attempting this, the HP laserjet code has been * optimized to merge the invertion pass in with the rule processing * pass along with the detection of blank scans. It also eliminates * the inversion of inverting (writing) the left and right edges of * scans that are white. It processes the scans in 34 scan bands which * will have great cache effects if the area between the left and * right edges of non white data total less than 4K to 6K and reasonable * cache effects in all cases since it at least stay in the external * cache. In the future, this code should also be modified to ouput * the scans as soon as it is done processing the rules for each band. * Currently it processes all scans on the page for rules and then * calls the routine to output the scans. This would trully make it * a one pass alogrithm. * * As of 12/30/93, only the HP laserjets have been optimized in this way. * All raster printers could probably be optimized particularly when * any transposing is necessary, detecting the left and right edges * of scans that are white. Transposing is expensive. It is probably * less important for dot matrix printers that take so long to output * but it is still burning up CPU time that might be better served * giving a USER bet responsiveness from apps while printing in the * back ground. * * The optimizations to the HP LaserJet had the following results. All * numbers are in terms of number of instructions and were pretty closely * matched with total times to render an entire 8.5 X 11 300dpi page. * * OLD OPTIMIZED * Blank page 8,500,000 950,000 * full text page 15,500,000 8,000,000 * * * RETURNS: * TRUE for successful completion, else FALSE. * * HISTORY: * 30-Dec-1993 -by- Eric Kutter [erick] * optimized for HP laserjet * * 14:23 on Tue 10 Nov 1992 -by- Lindsay Harris [lindsayh] * Split up for journalling - initialisation moved up above. * * 16:11 on Fri 11 Jan 1991 -by- Lindsay Harris [lindsayh] * Created it, before DDI spec'd on how we are called. * ****************************************************************************/
BOOL bRender( SURFOBJ *pso, PDEV *pPDev, RENDER * pRD, SIZEL sizl, DWORD *pBits ) {
BOOL bRet; /* Return value */ INT iBPP; /* Bits per pel - expect 1 or 4 */
RASTERPDEV *pRPDev; /* The unidrive PDEV - printer details */
#ifdef TIMING
ENG_TIME_FIELDS TimeTab; DWORD sTime,eTime;
pRPDev = pPDev->pRasterPDEV; EngQueryLocalTime(&TimeTab); sTime = (((TimeTab.usMinute*60)+TimeTab.usSecond)*1000)+ TimeTab.usMilliseconds; { char buf[80]; sprintf (buf,"Unidrv!bRender: GDI=%d, %dx%dx%d\n", sTime-pRPDev->dwTiming,sizl.cx,sizl.cy,pRD->iBPP); DrvDbgPrint(buf); } #else
pRPDev = pPDev->pRasterPDEV; #endif
// if all scan lines need to be output then we need to
// make sure we don't send the extra scan lines at the
// end of the last band
//
if (pRPDev->fBlockOut & RES_BO_NO_YMOVE_CMD) { if (!(pPDev->fMode & PF_ROTATE)) { if ((pRD->iyPrtLine + sizl.cy) > pPDev->rcClipRgn.bottom) sizl.cy = pPDev->rcClipRgn.bottom - pRD->iyPrtLine; } } //
// if this band is blank we will update our position
// and then just return
//
else if (!(pPDev->fMode & PF_SURFACE_USED) && (pRD->PrinterType == PT_PAGE || !pPDev->iFonts)) { if (pPDev->fMode & PF_ROTATE) { if (pPDev->fMode & PF_CCW_ROTATE90) pRD->iyPrtLine -= sizl.cx; else pRD->iyPrtLine += sizl.cx; } else pRD->iyPrtLine += sizl.cy;
#ifdef TIMING
DrvDbgPrint ("Unidrv!bRender: Skipping blank band\n"); #endif
return TRUE; }
iBPP = pRD->iBPP; /* Speedier access as local variable */
// this code filters the raster data so that any set pixel
// will have at least one adjacent horizontal pixel set and
// at least one adjacent vertical pixel set
//
if (iBPP == 1 && (pPDev->fMode & PF_SINGLEDOT_FILTER)) { INT cy,i; cy = sizl.cy; while (--cy >= 0) { DWORD *pdwC,*pdwB,*pdwA;
// Calculate pointers
//
pdwC = &pBits[pRD->cDWLine*cy]; //
// We will do horizontal filter first
//
if (pPDev->pbRasterScanBuf[cy / LINESPERBLOCK]) { BYTE bA,bL,*pC; // skip leading white space
for (i = 0;i < pRD->cDWLine;i++) if (pdwC[i] != ~0) break;
pC = (BYTE *)pdwC; bL = (BYTE)~0; i *= DWBYTES; while (i < pRD->cBYLine) { bA = pC[i]; pC[i] &= (((bA >> 1) | (bL << 7)) | ~((bA >> 2) | (bL << 6))); bL = bA; i++; } } // Test if adjacent scan line is blank
//
if (cy && pPDev->pbRasterScanBuf[(cy-1) / LINESPERBLOCK]) { // test if scan line is blank
pdwB = &pdwC[-pRD->cDWLine]; pdwA = &pdwB[-pRD->cDWLine]; if (pPDev->pbRasterScanBuf[cy / LINESPERBLOCK] == 0) { RECTL rcTmp; // test if anything needs to be set here
//
i = pRD->cDWLine; while (--i >= 0) if ((~pdwA[i] | pdwB[i]) != ~0) break; //
// if line is blank we can skip it
if (i < 0) continue;
// we need to clear this block
//
rcTmp.top = cy; rcTmp.bottom = cy; rcTmp.left = rcTmp.right = 0; CheckBitmapSurface(pso,&rcTmp); } // this is the normal case
//
if (cy > 1 && pPDev->pbRasterScanBuf[(cy-2) / LINESPERBLOCK]) { for (i = 0;i < pRD->cDWLine;i++) { pdwC[i] &= ~pdwA[i] | pdwB[i]; } } // in this case line A is blank
//
else { for (i = 0;i < pRD->cDWLine;i++) { pdwC[i] &= pdwB[i]; } } } } } // test whether we need to erase the rest of the bitmap
//
if (pPDev->bTTY) // bug fix 194505
{ pPDev->fMode &= ~PF_SURFACE_USED; } else if ((pRPDev->fBlockOut & RES_BO_NO_YMOVE_CMD) || (pPDev->fMode & PF_SURFACE_USED && ((pPDev->fMode & PF_ROTATE) || pRPDev->sPinsPerPass != 1 || pRPDev->sNPins != 1))) { CheckBitmapSurface(pso,NULL); } #ifdef TIMING
if (!(pPDev->fMode & PF_SURFACE_ERASED) && pPDev->pbRasterScanBuf) { INT i,j,k; char buf[80]; k = (pPDev->szBand.cy + LINESPERBLOCK - 1) / LINESPERBLOCK; for (i = 0, j = 0;i < k;i++) if (pPDev->pbRasterScanBuf[i] == 0) j++; sprintf (buf,"Unidrv!bRender: Skipped %d of %d blocks\n",j,k); DrvDbgPrint(buf); } #endif
/*
* Initialize the fields for optimizing the rendering of the bitmap. * The main purpose is to have a single pass over the bits. This * can significantly speed up rendering due to cache effects. In * the old days, we took at least 3 passes over the entire bitmap for * laserjets. One to invert the bits, one+ to find rules, and then * a third to output the data. We now delay the invertion of the * bits until after rules are found. We also keep left/right information * of non white space for each row. This way, any white on the edges * or completely white rows are only touch once and from then on, only * DWORDS with black need be touched. Also, the invertion is expensive * since it causes writing every DWORD. */
pRD->plrWhite = NULL; pRD->plrCurrent = NULL; pRD->clr = 0;
//
// Set flag to clear delta row buffer
//
pRD->iFlags |= RD_RESET_DRC;
/*
* Various operations depend upon what format bitmap we have. So * now is the time to set this all up. */
//
// If 1 bit per pixel mode we need to explicitly invert the
// data, but we may do it later in rules.
// The only other data this is allowed to be inverted is
// 4BPP and it is done in the transform functions
//
if (pRD->iBPP == 1 && (pPDev->fMode & PF_SURFACE_USED)) pRD->bInverted = FALSE; else pRD->bInverted = TRUE;
/*
* Check if rotation is required. If so, allocate storage, * start transposing, etc. */
if( pPDev->fMode & PF_ROTATE ) { /*
* Rotation is the order of the day. First chew up some memory * for the transpose function. */
INT iTHigh; /* Height after transpose */ INT cTDWLine; /* DWORDS per line after transpose */ INT iAddrInc; /* Address increment AFTER transpose */ INT iDelta; /* Transpose book keeping */ INT cbTransBuf; /* Bytes needed for L -> P transpose */ INT ixTemp; /* Maintain pRD->ix around this op */
TRANSPOSE tpBig; /* For the landscape transpose */ TRANSPOSE tpSmall; /* For the per print head pass */ TRANSPOSE tp; /* Banding: restore after we clobber */
/*
* First step is to determine how large to make the area * wherein the data will be transposed for later rendering. * Take the number of scan lines, and round this up to a * multiple of DWORDS. Then find out how many of these will * fit into a reasonable size chunk of memory. The number * should be a multiple of the number of pins per pass - * this to make sure we don't have partial head passes, if * that is possible. */
/*
* OPTIMIZATION POTENTIAL - deterimine left/right edges of non * white area and only transpose that portion (at least for * laser printers). There are often areas of white at the * top and or bottom. In the case of the HP laser printers, * only the older printers (I believe series II) go through * this code. LaserJet III and beyond can do graphics in * landscape so don't need this. (erick 12/20/93) */
tp = pRD->Trans; /* Keep a safe copy for later use */ ixTemp = pRD->ix;
cTDWLine = (sizl.cy * iBPP + DWBITS - 1) / DWBITS;
cbTransBuf = DWBYTES * cTDWLine * pRD->iPassHigh; if( cbTransBuf < TRANSPOSE_SIZE ) cbTransBuf = TRANSPOSE_SIZE;
iTHigh = cbTransBuf / (cTDWLine * DWBYTES);
if( iTHigh > sizl.cx ) { /* Bigger than we need, so shrink to actual size */ iTHigh = sizl.cx; /* Scan lines we have to process */
/* Make multiple of pins per pass - round up */ if( pRD->iPassHigh == 1 ) { /*
* LaserJet/PaintJet style, so round to byte alignment. */ if (iBPP < BBITS) iTHigh = (iTHigh + BBITS / iBPP - 1) & ~(BBITS / iBPP - 1); } else iTHigh += (pRD->iPassHigh - (iTHigh % pRD->iPassHigh)) % pRD->iPassHigh; } else { /* Make multiple of pins per pass - round down */ if( pRD->iPassHigh == 1 ) { if (iBPP < BBITS) iTHigh &= ~(BBITS / iBPP - 1); /* Byte alignment for LJs */ } else iTHigh -= iTHigh % pRD->iPassHigh; }
cbTransBuf = iTHigh * cTDWLine * DWBYTES;
pRD->iy = iTHigh;
/* Set up data for the transpose function */ tpBig.iHigh = sizl.cy; tpBig.iSkip = cTDWLine * DWBYTES; /* Bytes per transpose output */ tpBig.iWide = iTHigh * iBPP; /* Scanlines we will process */ tpBig.cBL = pRD->ix * iBPP;
pRD->ix = sizl.cy;
tpBig.cDWL = (tpBig.cBL + DWBITS - 1) / DWBITS; tpBig.iIntlace = 1; /* Landscape -> portrait: no interlace */ tpBig.cBYL = tpBig.cDWL * DWBYTES; tpBig.icbL = tpBig.cDWL * DWBYTES; tpBig.pdwTransTab = pRPDev->pdwTrans; /* For L -> P rotation */
if( !(tpBig.pvBuf = MemAlloc( cbTransBuf )) ) { bRet = FALSE; } else { /* Have the memory, start pounding away */ INT iAdj; /* Alignment adjustment, first band */
bRet = TRUE; /* Until proven guilty */
/*
* Recompute some of the transpose data for the smaller * bitmap produced from our call to transpose. */ pRD->iTransWide = sizl.cy * iBPP; /* Smaller size */ pRD->cBLine = pRD->ix * iBPP; pRD->iMaxBytesSend = (pRD->cBLine * pRD->iBitsPCol + BBITS - 1) / BBITS; if( iBPP == 4 ) pRD->iMaxBytesSend = (pRD->iMaxBytesSend+3) / 4;
pRD->cDWLine = (pRD->cBLine + DWBITS - 1) / DWBITS; pRD->cBYLine = pRD->cDWLine * DWBYTES; pRD->cbTLine = pRD->cDWLine * DWBYTES * pRD->iInterlace; tpSmall = pRD->Trans; /* Keep it for later reuse */
/*
* Set up the move commands required when rendering. In this * instance, the X and Y operations are interchanged. */
iAddrInc = (pRD->iy * iBPP) / BBITS; /* Bytes per scanline */
if( pPDev->fMode & PF_CCW_ROTATE90 ) { /*
* This is typified by the LaserJet Series II case. * The output bitmap should be rendered from the end * to the beginning, the scan line number decreases from * one line to the next (moving down the output page), * and the X and Y move functions are interchanged. */
tpSmall.icbL = -tpSmall.icbL; /* Scan direction */
/*
* Need to process bitmap in reverse order. This means * shifting the address to the right hand end of the * first scan line, then coming back one transpose pass * width. Also set the address increment to be negative, * so that we work our way towards the beginning. */
/*
* To simplify the transpose loop following, we start * rendering the bitmap from the RHS. The following * statement does just that: the sizl.cx / BBITS is * the number of used bytes in the scan line, iAddrInc * is the number per transpose pass, so subtracting it * will put us at the beginning of the last full band * on this transpose. */
/* !!!LindsayH - should sizl.cx be sizl.cx * iBPP ????? */ iAdj = (BBITS - (sizl.cx & (BBITS - 1))) % BBITS; sizl.cx += iAdj; /* Byte multiple */
(BYTE *)pBits += (sizl.cx * iBPP) / BBITS - iAddrInc;
iAddrInc = -iAddrInc; } else { /*
* Typified by HP PaintJet printers - those that have no * landscape mode, and where the output is rendered from * the start of the bitmap towards the end, where the * scan line number INCREASES by one from one line to the * the next (moving down the output page), and the X and Y * move functions are as expected. */ pBits += tpBig.cDWL * (sizl.cy - 1); /* Start of last row */ tpBig.icbL = -tpBig.icbL; /* Backwards through memory */
iAdj = 0; }
while( bRet && (iDelta = (int)sizl.cx) > 0 ) { pRD->Trans = tpBig; /* For the chunk transpose */
if( (iDelta * iBPP) < pRD->iTransWide ) { /* Last band - reduce the number of rows */ pRD->iTransWide = iDelta * iBPP; /* The remainder */ pRD->iy = iDelta; /* For bRealRender */ if( iAddrInc < 0 ) { iDelta = -iDelta; /* The OTHER dirn */ (BYTE *)pBits += -iAddrInc + (iDelta * iBPP) / BBITS; } }
/* Transpose this chunk of data unless it is empty */ if (pPDev->fMode & PF_SURFACE_USED) pRD->vLtoPTransFn( (BYTE *)pBits, pRD );
pRD->Trans = tpSmall;
pRD->iy -= iAdj; bRet = bRealRender( pPDev, tpBig.pvBuf, pRD ); pRD->iy += iAdj; iAdj = 0;
/* Skip to the next chunk of input data */ (BYTE *)pBits += iAddrInc; /* May go backwards */ pRD->iyBase += pRD->iy; sizl.cx -= pRD->iy; }
MemFree ( tpBig.pvBuf ); }
pRD->Trans = tp; pRD->ix = ixTemp; } else { /*
* Simple case - no rotation, so process the bitmap as is. * This means starting at the FIRST scan line which we have * set to the top of the image. * Set up the move commands required when rendering. In this * instance, the X and Y operations are their normal way. */
INT iyTemp;
iyTemp = pRD->iy; pRD->iy = sizl.cy;
bRet = bRealRender( pPDev, pBits, pRD );
pRD->iy = iyTemp; }
/*
* Turn unidirection off */ if (pRD->iFlags & RD_UNIDIR) { pRD->iFlags &= ~RD_UNIDIR; WriteChannel (pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_UNIDIRECTIONOFF)); }
/*
* Return from graphics mode, to be civilised. */ if( pRD->iFlags & RD_GRAPHICS) { if (pRD->dwLastCompCmd != CMD_DISABLECOMPRESSION) { pRD->dwLastCompCmd = CMD_DISABLECOMPRESSION; WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_DISABLECOMPRESSION)); } WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_ENDRASTER)); pRD->iFlags &= ~RD_GRAPHICS;
}
#ifdef TIMING
EngQueryLocalTime(&TimeTab); eTime = (((TimeTab.usMinute*60)+TimeTab.usSecond)*1000)+ TimeTab.usMilliseconds; eTime -= sTime; { char buf[80]; sprintf (buf,"Unidrv!bRender: %ld\n",eTime); DrvDbgPrint(buf); } #endif
return bRet; }
/************************ Function Header ***********************************
* bRealRender * The REAL rendering function. By the time we reach here, the bitmap * is in the correct orientation, and so we need to be serious * about rendering it. * * RETURNS: * TRUE for successful rendering, else FALSE. * * HISTORY: * Friday 26 November -by- Norman Hendley [normanh] * Added multiple scanline per send block support * * 16:22 on Fri 11 Jan 1991 -by- Lindsay Harris [lindsayh] * Started on it. * ****************************************************************************/
BOOL bRealRender( pPDev, pBits, pRData ) PDEV *pPDev; /* Our PDEV: key to everything */ DWORD *pBits; /* Actual data to process */ RENDER *pRData; /* Details of rendering process */ {
/*
* Process the bitmap in groups of scan lines. The number in the * the group is determined by the printer. Laser printers are * processed one scan line at a time, while dot matrix are processed * according to the number of pins they can fire at once. This * information is generated by our caller from the printer * characterisation data, or otherwise! */
INT iLine; /* Current scan line */ INT cDWPass; /* DWORDS per head pass */ INT iDWLine; /* DWORDS processed per interlace scan */ INT iILAdv; /* Line advance per interlace operation */ INT iHeadLine; /* Decide when graphics pass required */ INT iTHKeep; /* Local copy of iTransHigh: we change it */ INT iHeight; INT iNumScans; /* local copy*/ BOOL bCheckBlocks;
RASTERPDEV * pRPDev; PAL_DATA *pPD; INT iWhiteIndex;
INT iILDone[ MAX_INTERLACE ]; /* For head pass reduction */
PLEFTRIGHT plr = NULL; /* left/right of non white area */
pRPDev = pPDev->pRasterPDEV; pPD = pRPDev->pPalData; iWhiteIndex = pPD->iWhiteIndex;
iHeight = pRData->iHeight; cDWPass = pRData->cDWLine * iHeight;
if( pRData->iPosnAdv < 0 ) { /* Data needs to be sent in reverse order, so adjust now */ pBits += cDWPass * (pRData->iy / pRData->iPassHigh - 1); cDWPass = -cDWPass; iDWLine = -pRData->cDWLine; iILAdv = -1; } else { /* Usual case, but some special local variables */ iDWLine = pRData->cDWLine; iILAdv = 1; }
/* if the bits have already been inverted, don't bother with the rule proc.
* The bits will be inverted for the multi scan line devices inside * bRuleProc because multi scan line implementation assumes * that bits are inverted. The function bRuleProc is changed to take * take care of multi scan line support (erick) */ if(!pRData->bInverted) { if (pRPDev->fRMode & PFR_RECT_FILL) { if (!bRuleProc( pPDev, pRData, pBits )) vInvertBits(pBits, pRData->iy * pRData->cDWLine); } else if (pRData->iNumScans != 1 || pRData->iPassHigh != 1 || (pRPDev->fBlockOut & RES_BO_NO_YMOVE_CMD)) vInvertBits(pBits, pRData->iy * pRData->cDWLine); else { pRData->bWhiteLine = bIsNegatedLineWhite; pRData->bWhiteBand = bIsNegatedLineWhite; } }
iHeadLine = 0; for( iLine = 0; iLine < pRData->iInterlace; ++iLine ) iILDone[ iLine ] = 0;
iTHKeep = pRData->iTransHigh;
plr = (pRData->iMaxNumScans > 1) ? NULL : pRData->plrWhite;
if (!(pPDev->fMode & PF_SURFACE_ERASED) && pPDev->pbRasterScanBuf) bCheckBlocks = TRUE; else bCheckBlocks = FALSE; //normanh This code could be made tighter. My concern in adding multiple
//scanline support was not to risk breaking existing code.
//For improved performance, having separate code paths for GDI style &
//old dot matrix style graphics could be considered
for( iLine = 0; iLine < pRData->iy; iLine += iNumScans ) {
/*
* Check to see if there is graphics data in the current * print pass. This only happens once at the start of each * print pass. */
BOOL bIsWhite = FALSE; /* Set if no graphics in this pass*/ BYTE *pbData; /* pointer to data we will send */ /*
* Have we been aborted? If so, return failure NOW. */
if(pPDev->fMode & PF_ABORTED ) return FALSE;
iNumScans = pRData->iNumScans; if (!(pPDev->fMode & PF_SURFACE_USED)) { bIsWhite = TRUE; } else if (plr != NULL) { if (plr[iLine].left > plr[iLine].right) bIsWhite = TRUE;
pRData->plrCurrent = plr + iLine; } else if (bCheckBlocks && pPDev->pbRasterScanBuf[iLine / LINESPERBLOCK] == 0) { //
// Since this whole block is white we will try to skip to the first line
// of the next block rather than loop for each scan line. However we need
// to make sure we don't skip past the end of the band.
//
if (((pRData->PrinterType == PT_PAGE) || !(pPDev->iFonts)) && (pRData->iInterlace == 1)) { if ((iNumScans = pRData->iy - iLine) > LINESPERBLOCK) iNumScans = LINESPERBLOCK; } bIsWhite = TRUE; }
if( iILDone[ iHeadLine ] == 0 ) { if( (pRData->iy - iLine) < pRData->iPassHigh ) { /*
* MESSY: the end of the page, and there are some * dangling scan lines. Since this IS the end of the * page, we can fiddle with RENDER information, since * this will no longer be used after this time. iTransHigh * is used for rendering operations. They will be * adjusted now so that we do not flow off the end of * the engine's bitmap. */
pRData->iTransHigh = (pRData->iy - iLine + pRData->iInterlace - 1) / pRData->iInterlace;
if (plr == NULL && !bIsWhite) bIsWhite = pRData->bWhiteBand( pBits, pRData, iWhiteIndex );
/*
* If this band is all white, we can set the iLDone * entry, since we now know that this remaining part * of the page/band is white, and so we do not wish to * consider it further. Note that interlaced output * allows the possibility that some other lines in this * area will be output. * Note that the value (iBitsPCol - 1) may be larger than * the number of lines remaining in this band. However * this is safe to do, since we drop out of this function * before reaching the excess lines, and the array data * is initialised on every call to this function. */ if( bIsWhite ) iILDone[ iHeadLine ] = pRData->iBitsPCol - 1; else { /*
* Need to consider a special case in here. If the * printer has > 8 pins, and there are 8 or more * scan lines to be dropped off the bottom, then the * transpose function will not clear the remaining * part of the buffer, since it only zeroes up * to 7 scan lines at the bottom of the transpose area. * Hence, if we meet these conditions, we zero the * area before calling the transpose operation. * * It can be argued that this should happen in the * transpose code, but it is really a special case that * can only happen at this location. */ if( pRData->vTransFn && (iHeight - pRData->iTransHigh) >= BBITS ) { /* Set the memory to zero. */ ZeroMemory( pRData->pvTransBuf, DWBYTES * pRData->cDWLine * pRData->iHeight ); }
// Another special case; block of scanlines
// Copy the data we're interesed in , into a white buffer of
// block size
if (iNumScans > 1) { DWORD iDataLeft = DWBYTES * pRData->cDWLine * (pRData->iy - iLine); FillMemory((PBYTE)pRData->pdwTrailingScans+iDataLeft, (pRData->cDWLine * iHeight * DWBYTES) - iDataLeft, pRData->ubFillWhite); CopyMemory(pRData->pdwTrailingScans,pBits,iDataLeft); pBits = pRData->pdwTrailingScans; } } } else { if (plr == NULL && !bIsWhite) { bIsWhite = pRData->bWhiteLine( pBits, pRData, iWhiteIndex ); } }
/* Data to go, so go send it to the printer */
if( !bIsWhite ) {
pbData = (BYTE *)pBits; /* What we are given */
// This is not elegant. This code is not structured to what we need to
// do here when printing multiple scanlines.
// What we do is basically take control from the outer loop, increase the
// block size to what we want to print, print it & then increase outer
// loop counters appropriately
// Found First non-white scanline
// Grow the block height until we hit a white scanline,
// reach the max block height, or end of page
// Note the following loop will execute only if the device is
// capable of increasing the block height: iHeight < iMaxNumScans
while (((pRData->iNumScans + iHeight) < pRData->iMaxNumScans) && ((iLine + iHeight + iHeight) <= pRData->iy) && (!bCheckBlocks || pPDev->pbRasterScanBuf[(iLine+iHeight) / LINESPERBLOCK]) && !(pRData->bWhiteBand((DWORD *)(pBits + cDWPass),pRData,iWhiteIndex))) { pRData->iNumScans += iHeight; pBits += cDWPass; iLine += iHeight; }
/*
* Time to transpose the data into the order required to be * sent to the printer. For single pin printers (Laserjets), * nothing happens at this stage, but for dot matrix printers, * typically n scan lines are sent in bit column order, so now * the bits are transposed into that order. */
if( pRData->vTransFn ) { /*
* this will not work with lazy invertion used with rule * detection for HP laserjet's. (erick 12/20/93) */
ASSERTMSG(plr == NULL,("unidrv!bRealRender - vTrans with rules\n"));
/* Transpose activity - do some transposing now */ pRData->vTransFn( pbData, pRData );
pbData = pRData->pvTransBuf; /* Data to process */ }
if( !pRData->bPassProc( pPDev, pbData, pRData ) ) return FALSE;
// Have we grown the block height
if (pRData->iNumScans > iHeight) { // Update our Y cursor position remembering iTLAdv can be negative
pRData->iyPrtLine += iILAdv * (pRData->iNumScans - iHeight);
// Reset to minimum block height
pRData->iNumScans = iHeight; }
iILDone[ iHeadLine ] = pRData->iBitsPCol -1; } //
// Set flag to clear delta row buffer since we are
// skipping white lines
//
else pRData->iFlags |= RD_RESET_DRC;
} else --iILDone[ iHeadLine ];
/*
* Output some text. The complication here is that we have just * printed a bunch of scan lines, so we need to print text that * is positioned within any of those. This means we need to * scan through all those lines now, and print any fonts that * are positioned within them. */ if ((pRData->PrinterType != PT_PAGE) && (pPDev->iFonts) ) { /* Possible text, so go to it */
BOOL bRetn;
if( pPDev->dwLookAhead > 0 ) { /* DeskJet style lookahead region to handle */ bRetn = bLookAheadOut( pPDev, pRData->iyPrtLine, pRData, iILAdv ); } else { /* Plain vanilla dot matrix */ bRetn = BDelayGlyphOut( pPDev, pRData->iyPrtLine ); }
if( !bRetn ) return FALSE; /* Bad news no matter how you see it */
} pRData->iyPrtLine += iILAdv * iNumScans; /* Next line to print */
pBits += iDWLine * iNumScans; /* May step backward */
/*
* Keep track of the location of the head relative to the * graphics band. For multiple pin printers, we only print * graphics data on the first few scan lines, the exact number * depending upon the interlace factor. For example, an 8 pin printer * with interlace set to 1, then graphics data is output only * on scan lines 0, 8, 16, 24,..... We proces all of the scan * lines for text, since the text may appear on any line. */
iHeadLine = (iHeadLine + 1) % pRData->iInterlace; } pRData->iTransHigh = iTHKeep; return TRUE; }
/************************** Function Header *******************************
* bOneColourPass * Transforms an output pass consisting of colour data (split into * sequences of bytes per colour) into a single, contiguous array * of data that is then passed to bOnePassOut. We also check that * some data is to be set, and set the colour as required. * * RETURNS: * TRUE/FALSE, as returned from bOnePassOut * * HISTORY: * Friday December 3rd 1993 -by- Norman Hendley [norman] * Trivial change to allow multiple scanlines * * 14:11 on Tue 25 Jun 1991 -by- Lindsay Harris [lindsayh] * Created it to complete (untested) colour support. * *************************************************************************/
BOOL bOneColourPass( pPDev, pbData, pRData ) PDEV *pPDev; /* The key to everything */ BYTE *pbData; /* Actual bitmap data */ RENDER *pRData; /* Information about rendering operations */ {
register BYTE *pbIn, *pbOut; /* Copying data */ register INT iBPC;
INT iColour; /* Colour we are handling */ INT iColourMax; /* Number of colour iterations */
INT iByte; /* Byte number of output */
INT iBytesPCol; /* Bytes per column */
INT iTemp;
BYTE bSum; /* Check for empty line */
RASTERPDEV *pRPDev; /* For convenience */
pRPDev = pPDev->pRasterPDEV;
iBytesPCol = (pRData->iBitsPCol + BBITS - 1) / BBITS;
iColourMax = pRPDev->sDevPlanes;
iTemp = pRData->cBYLine;
/*
* The RENDERDATA structure value for the count of DWORDS per * scanline should now be reduced to the number of bits per plane. * The reason is that colour separation takes place in here, so * bOnePassOut() only sees the data for a single plane. This means * that bOnePassOut() is then independent of colour/monochrome. */
pRData->cBYLine = iTemp / COLOUR_MAX;
/*
* Disable the automatic cursor adjustment at the end of the line. * This only happens on the last colour pass, so we delay accounting * for the printing until then. */ pRData->iCursor = pRPDev->fCursor & ~RES_CUR_Y_POS_AUTO;
for( iColour = 0; iColour < iColourMax; ++iColour ) { /*
* Separate out the data for this particular colour. Basically, * it means copy n bytes, skip COLOUR_MAX * n bytes, copy n bytes * etc, up to the end of the line. Then call bOnePassOut with * this data. */
if( iColour == (iColourMax - 1) ) { /* Reinstate the automatic cursor position adjustment */ pRData->iCursor |= pRPDev->fCursor & RES_CUR_Y_POS_AUTO; } pbIn = pbData + pRData->iColOff[ iColour ];
pbOut = pRData->pbColSplit; /* Colour splitting data */ bSum = 0;
// now we need to repack the color data
//
iByte = pRData->iMaxBytesSend * pRData->iNumScans;
if (iBytesPCol == 1) { // This repacks a specific color plane
// into concurrent planar data
// It does multiple bytes per loop
// for performance.
DWORD dwSum = 0; while (iByte >= 4) { pbOut[0] = pbIn[0]; pbOut[1] = pbIn[4]; pbOut[2] = pbIn[8]; pbOut[3] = pbIn[12]; dwSum |= *((DWORD *)pbOut)++; pbIn += COLOUR_MAX*4; iByte -= 4; } bSum = dwSum ? 1 : 0; while (--iByte >= 0) { bSum |= *pbOut++ = *pbIn; pbIn += 4; } } else if (iBytesPCol == 3) { // special case 24 pin printers
do { bSum |= *pbOut = *pbIn; bSum |= pbOut[1] = pbIn[1]; bSum |= pbOut[2] = pbIn[2]; pbIn += 3 * COLOUR_MAX; pbOut += 3; } while ((iByte -= 3) > 0); } else { // generic data repacking for V_BYTE devices
//
do { iBPC = iBytesPCol; do { bSum |= *pbOut++ = *pbIn++; } while (--iBPC); pbIn += (COLOUR_MAX-1) * iBytesPCol; } while ((iByte -= iBytesPCol) > 0); }
/*
* Check to see if any of this colour is to be printed. We are * called here if there is any non-white on the line. However, * it could, for instance, be red only, and so it is wasteful * of printer time to send a null green pass! */ if( (pRData->iFlags & RD_ALL_COLOUR) || bSum ) { //
// Send a separate color command from the data to select
// the color plane
//
if( pRPDev->fColorFormat & DC_EXPLICIT_COLOR ) SelectColor (pPDev, iColour);
//
// The color command is sent with the data so we determine
// which command should be used.
//
else pRData->iSendCmd = pRPDev->rgbCmdOrder[iColour];
//
// OK, lets output 1 color plane of data
//
if( !bOnePassOut( pPDev, pRData->pbColSplit, pRData ) ) { pRData->cBYLine = iTemp; return FALSE; } }
} pRData->cBYLine = iTemp; /* Correct value for other parts */
return TRUE; } /************************* Function Header ***********************************
* SelectColor * Selects color, 0 must be the last color to be selected. * Assumes that color info contains parameters for selecting * black cyan magenta yellow * Keep track of the current color selection, to reduce the amount * of data sent to the printer * * RETURNS: * Nothing. * * HISTORY: * *****************************************************************************/
void SelectColor( PDEV *pPDev, INT color ) { PRASTERPDEV pRPDev = (PRASTERPDEV)pPDev->pRasterPDEV;
if( color >= 0 && color != pPDev->ctl.sColor ) { // check to see if to send CR or not.
if( pRPDev->fColorFormat & DC_CF_SEND_CR ) XMoveTo( pPDev, 0, MV_PHYSICAL );
WriteChannel(pPDev,COMMANDPTR(pPDev->pDriverInfo, pRPDev->rgbCmdOrder[color])); pPDev->ctl.sColor = (short)color; } }
/************************** Function Header ********************************
* bOnePassOut * Function to process a group of scan lines and turn the data into * commands for the printer. * * RETURNS: * TRUE for success, else FALSE. * * HISTORY: * 30-Dec-1993 -by- Eric Kutter [erick] * optimized for HP laserjet * 14:26 on Thu 17 Jan 1991 -by- Lindsay Harris [lindsayh] * Started on it, VERY loosely based on Windows 16 UNIDRV. * * Thu 25 Nov 1993 -by- Norman Hendley [normanh] * Enabled multple scanlines & multiple parameters * ***************************************************************************/
BOOL bOnePassOut( pPDev, pbData, pRData ) PDEV *pPDev; /* The key to everything */ BYTE *pbData; /* Actual bitmap data */ register RENDER *pRData; /* Information about rendering operations */ {
INT iLeft; /* Left bound of output buffer, as a byte index */ INT iRight; /* Right bound, as array index of output buffer */ INT iBytesPCol; /* Bytes per column of print data */ INT iMinSkip; /* Minimum null byte count before skipping */ INT iNumScans; /* Number Of Scanlines in Block */ INT iWidth; /* Width of one scanline in multiscanline printing
* before stripping */ INT iSzBlock; /* size of Block */
WORD fCursor; /* Temporary copy of cursor modes in Resolution */ WORD fDump; /* Device capabilities */ WORD fBlockOut; /* Output minimising details */
RASTERPDEV *pRPDev; /* Unidrv's pdev */ DWORD dwWhiteIndex;
PLEFTRIGHT plr = pRData->plrCurrent;
pRPDev = pPDev->pRasterPDEV;
fDump = pRData->fDump; fCursor = pRPDev->fCursor; fBlockOut = pRPDev->fBlockOut;
if (pRData->iBPP == 24) iBytesPCol = 3; else iBytesPCol = (pRData->iBitsPCol + BBITS - 1) / BBITS;
iMinSkip = (int)pRPDev->sMinBlankSkip;
iNumScans= pRData->iNumScans; iWidth = pRData->cBYLine; // bytes per line
iSzBlock= iWidth * iNumScans;
iRight = pRData->iMaxBytesSend;
dwWhiteIndex = pRData->dwDevWhiteIndex;
/*
* IF we can skip any leading null data, then do so now. This * reduces the amount of data sent to the printer, and so can * be beneficial to speed up data transmission time. */
if ((fBlockOut & RES_BO_LEADING_BLNKS) || ( fDump & RES_DM_LEFT_BOUND )) { if (iNumScans == 1) //Don't slow the single scanline code
{ /* Look for the first non zero column */
iLeft = 0;
if (plr != NULL) { ASSERTMSG ((WORD)iRight >= (plr->right * sizeof(DWORD)),("unidrv!bOnePassOut - invalid right\n")); ASSERTMSG (fBlockOut & RES_BO_TRAILING_BLNKS,("unidrv!bOnePassOut - invalid fBlockOut\n")); iLeft = plr->left * sizeof(DWORD); iRight = (plr->right+1) * sizeof(DWORD); } // since the left margin is zero this buffer will be DWORD aligned
// this allows for faster white space detection
// NOTE: we don't currently support 8 bit indexed mode
else { while ((iLeft+4) <= iRight && *(DWORD *)&pbData[iLeft] == dwWhiteIndex) iLeft += 4; } while (iLeft < iRight && pbData[iLeft] == (BYTE)dwWhiteIndex) iLeft++;
/* Round it to the nearest column */ iLeft -= iLeft % iBytesPCol;
/*
* If less than the minimum skip amount, ignore it. */ if((plr == NULL) && (iLeft < iMinSkip)) iLeft = 0;
} else { INT pos;
pos = iSzBlock +1; for (iLeft=0; iRight > iLeft && pos >= iSzBlock ;iLeft++) for (pos =iLeft; pos < iSzBlock && pbData[ pos] == (BYTE)dwWhiteIndex ;pos += iWidth) ;
iLeft--;
/* Round it to the nearest column */ iLeft -= iLeft % iBytesPCol;
/*
* If less than the minimum skip amount, ignore it. */
if( iLeft < iMinSkip ) iLeft = 0; } } else { iLeft = 0; }
/*
* Check for eliminating trailing blanks. If possible, now * is the time to find the right end of the data. */
if( fBlockOut & RES_BO_TRAILING_BLNKS ) { /* Scan from the RHS to the first non-zero byte */
if (iNumScans == 1) { if (plr != NULL) iRight = (plr->right+1) * sizeof(DWORD);
// if the number of bytes to check is large
// we will optimize to check it using DWORDS
else if ((iRight - iLeft) >= 8) { // first we need to DWORD align the right position
// we will be aligned when the 2 LSB's of iRight are 0
//
while (iRight & 3) if (pbData[--iRight] != (BYTE)dwWhiteIndex) goto DoneTestingBlanks;
// OK now that we are DWORD aligned we can check
// for white space a DWORD at a time
//
while ((iRight -= 4) >= iLeft && *(DWORD *)&pbData[iRight] == dwWhiteIndex); iRight += 4; } // now we can quickly test any remaining bytes
//
while (--iRight >= iLeft && pbData[iRight] == (BYTE)dwWhiteIndex); } else { INT pos;
pos = iSzBlock +1; while(iRight > iLeft && pos > iSzBlock) for (pos = --iRight; pos < iSzBlock && pbData[ pos] == (BYTE)dwWhiteIndex ;pos += iWidth) ; } DoneTestingBlanks: iRight += iBytesPCol - (iRight % iBytesPCol); }
/*
* If possible, switch to unidirectional printing for graphics. * The reason is to improve output quality, since head position * is not as reproducible in bidirectional mode. */ if( (fBlockOut & RES_BO_UNIDIR) && !(pRData->iFlags & RD_UNIDIR) ) { pRData->iFlags |= RD_UNIDIR; WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_UNIDIRECTIONON) ); }
if( fBlockOut & RES_BO_ENCLOSED_BLNKS ) { /*
* We can skip blank patches in the middle of the scan line. * This is only worthwhile when the number of blank columns * is > iMinSkip, because there is also overhead in not * sending blanks, especially the need to reposition the cursor. */
INT iIndex; /* Scan between iLeft and iRight */ INT iBlank; /* Start of blank area */ INT iMax; INT iIncrement;
iBlank = 0; /* None to start with */
if (iNumScans ==1) { iMax = iBytesPCol; iIncrement =1; } else { iMax = iSzBlock; iIncrement = iWidth; }
for( iIndex = iLeft; iIndex < iRight; iIndex += iBytesPCol ) { INT iI; for( iI = 0; iI < iMax; iI +=iIncrement ) { if( pbData[iIndex + iI] ) break; }
if( iI < iMax ) { /*
* If this is the end of a blank stretch, then consider * the possibility of not sending the blank part. */ if( iBlank && (iIndex - iBlank) >= iMinSkip ) { /* Skip it! */
iLineOut( pPDev, pRData, pbData, iLeft, iBlank ); iLeft = iIndex; } iBlank = 0; /* Back in the printed zone */ } else { /*
* A blank column - remember it if this is the first. */ if( iBlank == 0 ) iBlank = iIndex; /* Record start of blank */ }
} /* What's left over needs to go too! */ if( iLeft != iIndex ) iLineOut( pPDev, pRData, pbData, iLeft, iIndex ); } else { //
// PCLXL raster mode
//
if (pPDev->ePersonality == kPCLXL_RASTER) { DWORD dwcbOut;
if (S_OK != PCLXLSetCursor((PDEVOBJ)pPDev, pRData->ixOrg, pRData->iyPrtLine) || S_OK != PCLXLSendBitmap((PDEVOBJ)pPDev, pRData->iBPP, pRData->iNumScans, pRData->cBYLine, iLeft, iRight, pbData, &dwcbOut)) { return FALSE; }
} else { /* Write the whole of the (remaining) scan line out */ /* For multiple scanlines, iRight is right side of top scanline */
iLineOut( pPDev, pRData, pbData, iLeft, iRight ); }
} return TRUE; }
/************************** Function Header *********************************
* iLineOut * Sends the passed in line of graphics data to the printer, after * setting the X position, etc. * * RETURNS: * Value from WriteSpoolBuf: number of bytes written. * * HISTORY: * 30-Dec-1993 -by- Eric Kutter [erick] * optimized for HP laserjet * Mon 29th November 1993 -by- Norman Hendley [normanh] * Added multiple scanline support * * 10:38 on Wed 15 May 1991 -by- Lindsay Harris [lindsayh] * Created it during render speed ups * ****************************************************************************/
int iLineOut( pPDev, pRData, pbOut, ixPos, ixEnd ) PDEV *pPDev; /* The key to everything */ RENDER *pRData; /* Critical rendering information */ BYTE *pbOut; /* Area containing data to send */ INT ixPos; /* X location to start the output */ INT ixEnd; /* Byte address of first byte to NOT send */ {
INT iBytesPCol; /* Bytes per output col; dot matrix */ INT ixErr; /* Error in setting X location */ INT ixLeft; /* Left position in dots */ INT cbOut; /* Number of bytes to send */ INT iRet; /* Return value from output function */ INT iNumScans; /* local copy */ INT iScanWidth; /* Width of scanline, used for multi-scanline printing*/ INT iCursor; /* Cursor behavior flag */ DWORD fRMode; // local copy
BYTE *pbSend; /* Address of data to send out */
RASTERPDEV *pRPDev;
if (ixPos < 0) { ERR (("Unidrv!iLineOut: ixPos < 0\n")); ixPos = 0; }
pRPDev = pPDev->pRasterPDEV; fRMode = pRPDev->fRMode;
iNumScans = pRData->iNumScans; iCursor = pRData->iCursor;
/*
* Set the Y position - safe to do so at anytime. */ pRData->iYMoveFn( pPDev, pRData->iyPrtLine, MV_GRAPHICS );
if ((iBytesPCol = pRData->iBitsPCol) != 1) iBytesPCol /= BBITS;
#if DBG
if( (ixEnd - ixPos) % iBytesPCol ) { DbgPrint( "unidrv!iLineOut: cbOut = %ld, NOT multiple of iBytesPCol = %ld\n", ixEnd - ixPos, iBytesPCol );
return 0; } #endif
/*
* Set the preferred left limit and number of columns to send. * Note that the left limit may be adjusted to the left if the * command to set the X position cannot set it exactly. * Note also that some printers are unable to set the x position * while in graphics mode, so for these, we ignore what may be * able to be skipped. */
if( pRData->fDump & RES_DM_LEFT_BOUND) { INT iMinSkip = pRPDev->sMinBlankSkip; if (!(pRData->iFlags & RD_GRAPHICS)) iMinSkip >>= 2; if (ixPos < pRData->ixOrg || (pRData->ixOrg + iMinSkip) < ixPos) { /*
* Need to move left boundary. This may mean * exiting graphics mode if we are already there, since * that is the only way to change the origin! */
if( pRData->iFlags & RD_GRAPHICS ) { pRData->iFlags &= ~RD_GRAPHICS; if (pRData->dwLastCompCmd != CMD_DISABLECOMPRESSION) { pRData->dwLastCompCmd = CMD_DISABLECOMPRESSION; WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_DISABLECOMPRESSION)); } WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_ENDRASTER)); } //
// Save the new graphics origin
//
pRData->ixOrg = ixPos; } else { // we can't optimize the left edge, better make it white
if (pRData->plrCurrent != NULL) ZeroMemory(&pbOut[pRData->ixOrg], ixPos - pRData->ixOrg);
ixPos = pRData->ixOrg; } } /*
* Adjust the right side position to dot column version. */
if( pRData->iBitsPCol == 1 ) { /* Laserjet style - work in byte units */ if (pRData->iBPP == 8) ixLeft = ixPos; /* In dot/column units */ else if (pRData->iBPP == 24) ixLeft = (ixPos * BBITS) / 24; else ixLeft = ixPos * BBITS; } else { /* Dot matrix printers */ ixLeft = ixPos / iBytesPCol; }
/*
* Move as close as possible to the position along this scanline. * This is true regardless of orientation - this move is ALONG the * direction of the scan line. */ if( ixErr = pRData->iXMoveFn( pPDev, ixLeft, MV_GRAPHICS ) ) { /*
* Fiddle factor - the head location could not * be exactly set, so send extra graphics data to * compensate. * NOTE: Presumption is that this will NEVER try to move * the head past the left most position. If it does, then * we will be referencing memory lower than the scan line * buffer! */
if( pRData->iBitsPCol == 1 ) { /*
* We should not come in here - there are some difficulties * in adjusting the position because there is also a byte * alignment requirement. */ #if DBG
DbgPrint( "+++BAD NEWS: ixErr != 0 for 1 pin printer\n" ); #endif
} else { /*
* Should adjust our position by the number of additional cols * we wish to send. Also recalculate the array index position * corresponding to the new graphical position, */ if (ixLeft <= ixErr) ixPos = 0; else ixPos = (ixLeft - ixErr) * iBytesPCol; }
}
// For a multiple scanline block the printable data will not be contiguous.
// We have already identified where to strip white space
// Only now can we actually remove the white data
if(( iNumScans > 1 ) && !( fRMode & PFR_BLOCK_IS_BAND )) { cbOut = iStripBlanks( pRData->pStripBlanks, pbOut, ixPos, ixEnd, iNumScans, pRData->cBYLine); ixEnd = ixEnd - ixPos; ixPos = 0; pbOut = pRData->pStripBlanks; }
// Calculate the width of the source data in bytes and check
// whether we need to output this to the device. If so, we
// evidently need to exit raster mode first
iScanWidth = ixEnd - ixPos; if ((DWORD)iScanWidth != pPDev->dwWidthInBytes) { pPDev->dwWidthInBytes = iScanWidth; if (fRMode & PFR_SENDSRCWIDTH) { if( pRData->iFlags & RD_GRAPHICS ) { pRData->iFlags &= ~RD_GRAPHICS; if (pRData->dwLastCompCmd != CMD_DISABLECOMPRESSION) { pRData->dwLastCompCmd = CMD_DISABLECOMPRESSION; WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_DISABLECOMPRESSION) ); } WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_ENDRASTER) ); } WriteChannel (pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_SETSRCBMPWIDTH)); } }
//
// Check whether we should send the source height to the device
//
if ((DWORD)iNumScans != pPDev->dwHeightInPixels) { pPDev->dwHeightInPixels = iNumScans; if (fRMode & PFR_SENDSRCHEIGHT) WriteChannel (pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_SETSRCBMPHEIGHT)); }
//
// Make sure we are in raster mode at this point
//
if( !(pRData->iFlags & RD_GRAPHICS)) { pRData->iFlags |= RD_GRAPHICS; if (fRMode & PFR_SENDBEGINRASTER) WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,CMD_BEGINRASTER)); }
//
// Calculate the number of bytes to send.
// If compression is available, use it first.
//
cbOut = iScanWidth * iNumScans ;
pbSend = &pbOut[ ixPos ];
//
// Mirror each data byte if required
//
if (pRData->pbMirrorBuf) { INT i = cbOut; PBYTE pMirror = pRData->pbMirrorBuf; while (--i >= 0) pbSend[i] = pMirror[pbSend[i]]; } //
// If there are compression modes we want to determine the
// most efficient algorithm of those that are enabled.
//
if (pRData->dwNumCompCmds) { DWORD i; INT iBestCompSize; DWORD dwBestCompCmd; INT iCompLimit; INT iLastCompLimit; PBYTE pBestCompPtr;
//
// test whether to initialize dwDeltaRowBuffer
//
if (pRData->pDeltaRowBuffer && pRData->iFlags & RD_RESET_DRC) { pRData->iFlags &= ~RD_RESET_DRC; ZeroMemory(pRData->pDeltaRowBuffer,pRData->iMaxBytesSend); }
// initialize to size of buffer
//
iCompLimit = iLastCompLimit = pRData->dwCompSize; dwBestCompCmd = 0;
// loop until we've compressed using all active compression modes
// and have found the most efficient
//
for (i = 0;i < pRData->dwNumCompCmds;i++) { INT iTmpCompSize; PBYTE pTmpCompBuffer = pRData->pCompBufs[i]; DWORD dwTmpCompCmd = pRData->pdwCompCmds[i]; //
// do the appropriate compression
//
iTmpCompSize = -1; switch (dwTmpCompCmd) { case CMD_ENABLETIFF4: iTmpCompSize = iCompTIFF(pTmpCompBuffer,pbSend,cbOut); break; case CMD_ENABLEFERLE: iTmpCompSize = iCompFERLE(pTmpCompBuffer,pbSend,cbOut,iLastCompLimit); break; case CMD_ENABLEOEMCOMP: FIX_DEVOBJ(pPDev,EP_OEMCompression); // also add these members to the struct _PDEV (unidrv2\inc\pdev.h)
// POEM_PLUGINS pOemEntry;
// note add macro FIX_DEVOBJ in unidrv2\inc\oemkm.h so it also does this:
// (pPDev)->pOemEntry = ((pPDev)->pOemHookInfo[ep].pOemEntry)
// (pOemEntry is defined as type POEM_PLUGIN_ENTRY in printer5\inc\oemutil.h)
//
if(pPDev->pOemEntry) { if(((POEM_PLUGIN_ENTRY)pPDev->pOemEntry)->pIntfOem ) // OEM plug in uses COM and function is implemented.
{ HRESULT hr ; hr = HComCompression((POEM_PLUGIN_ENTRY)pPDev->pOemEntry, (PDEVOBJ)pPDev,pbSend,pTmpCompBuffer,cbOut,iLastCompLimit, &iTmpCompSize); if(SUCCEEDED(hr)) ; // cool !
} else { iTmpCompSize = pRPDev->pfnOEMCompression((PDEVOBJ)pPDev,pbSend,pTmpCompBuffer,cbOut,iLastCompLimit); } } break; case CMD_ENABLEDRC: iTmpCompSize = iCompDeltaRow(pTmpCompBuffer,pbSend, pRData->pDeltaRowBuffer,pRData->iMaxBytesSend,iLastCompLimit); break; case CMD_DISABLECOMPRESSION: iTmpCompSize = cbOut; pTmpCompBuffer = pbSend; break; } //
// decide if new compression is smaller than last
//
if (iTmpCompSize >= 0) { if (dwTmpCompCmd == pRData->dwLastCompCmd) { if (iTmpCompSize >= iLastCompLimit) continue;
iLastCompLimit = iCompLimit = iTmpCompSize - COMP_FUDGE_FACTOR; } else if (iTmpCompSize < iCompLimit) { iCompLimit = iTmpCompSize; if (iLastCompLimit > (iTmpCompSize + COMP_FUDGE_FACTOR)) iLastCompLimit = iTmpCompSize + COMP_FUDGE_FACTOR; } else continue;
iBestCompSize = iTmpCompSize; pBestCompPtr = pTmpCompBuffer; dwBestCompCmd = dwTmpCompCmd; if (iCompLimit <= 1) break; } }
// if DRC is enabled we need to save the scan line
//
if (pRData->pDeltaRowBuffer) CopyMemory (pRData->pDeltaRowBuffer,pbSend,pRData->iMaxBytesSend);
//
// verify we found a valid compression technique
// otherwise use no compression mode
//
if (dwBestCompCmd == 0) { dwBestCompCmd = CMD_DISABLECOMPRESSION; if (!(COMMANDPTR(pPDev->pDriverInfo,CMD_DISABLECOMPRESSION))) { ERR (("Unidrv: No valid compression found\n")); pPDev->fMode |= PF_ABORTED; } } else { // update the output pointer and size with the best
// compression method.
//
pbSend = pBestCompPtr; cbOut = iBestCompSize; }
// if we've changed compression modes we need to
// output the new mode to the printer
//
if (dwBestCompCmd != pRData->dwLastCompCmd) { // DbgPrint ("New Comp: %ld,y=%ld,size=%ld\n",dwBestCompCmd,
// pRData->iyPrtLine,cbOut);
pRData->dwLastCompCmd = dwBestCompCmd; WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,dwBestCompCmd)); } }
// update data block size
// output the raster command and
// output the actual raster data
//
pPDev->dwNumOfDataBytes = cbOut;
WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo,pRData->iSendCmd));
//
// if callback, adjust the pdev and make the OEM callback
//
if (pRPDev->pfnOEMFilterGraphics) { FIX_DEVOBJ(pPDev,EP_OEMFilterGraphics);
if(pPDev->pOemEntry) { if(((POEM_PLUGIN_ENTRY)pPDev->pOemEntry)->pIntfOem ) // OEM plug in uses COM and function is implemented.
{ HRESULT hr ; hr = HComFilterGraphics((POEM_PLUGIN_ENTRY)pPDev->pOemEntry, (PDEVOBJ)pPDev, pbSend, cbOut); if(SUCCEEDED(hr)) iRet = cbOut; // cool !
else iRet = 0 ; // hackey, the OEM function should return # bytes written to spooler
// but too late to change the interface now.
} else { iRet = pRPDev->pfnOEMFilterGraphics((PDEVOBJ)pPDev, pbSend, cbOut); } }
} //
// otherwise we just write out the data ourselves
//
else iRet = WriteSpoolBuf(pPDev, pbSend, cbOut );
// Test whether to send end of block command
//
if (fRMode & PFR_ENDBLOCK) WriteChannel (pPDev,COMMANDPTR(pPDev->pDriverInfo,CMD_ENDBLOCKDATA));
//
// Test whether to reset fonts after sending raster data
//
if (pPDev->fMode & PF_RESELECTFONT_AFTER_GRXDATA) { VResetFont(pPDev); }
/*
* Adjust our idea of the printer's cursor position. IF the printer * does not change the cursor's X position after printing, then we leave * it where it now is, otherwise we set to what the printer has. */
if( !(iCursor & RES_CUR_X_POS_ORG) ) { if( iCursor & RES_CUR_X_POS_AT_0 ) { /*
* This type of printer sets the cursor to the left hand * side after printing, so set that as our current position. */ pRData->iXMoveFn( pPDev, 0, MV_PHYSICAL | MV_UPDATE ); } else { /*
* Cursor remains at end of output. So, set that as our * position too. But first, calculate the RHS dot position. */
INT ixRight;
if( pRData->iBitsPCol == 1 ) ixRight = ixEnd * BBITS; /* Laserjet style */ else ixRight = ixEnd / iBytesPCol; /* Dot matrix printers */
pRData->iXMoveFn( pPDev, ixRight, MV_UPDATE | MV_GRAPHICS ); } }
/*
* If the printer moves the Y position after printing, then now * is the time to adjust our Y position. */ if( iCursor & RES_CUR_Y_POS_AUTO ) { pRData->iYMoveFn( pPDev, pRData->iPosnAdv, MV_UPDATE | MV_RELATIVE | MV_GRAPHICS ); }
return iRet; } //*******************************************************
void vInvertBits ( DWORD *pBits, INT cDW ) /*++
Routine Description:
This function inverts a group of bits. This is used to convert 1 bit data from 0 = black and 1 = white to the opposite.
Arguments:
pRD Pointer to RENDER structure pBits Pointer to data buffer to invert
Return Value:
none
--*/ { #ifndef _X86_
INT cDWT = cDW >> 2; while( --cDWT >= 0 ) { pBits[0] ^= ~((DWORD)0); pBits[1] ^= ~((DWORD)0); pBits[2] ^= ~((DWORD)0); pBits[3] ^= ~((DWORD)0); pBits += 4; } cDWT = cDW & 3; while (--cDWT >= 0) *pBits++ ^= ~((DWORD)0);
#else
//
// if intel processor, do it in assembly, for some reason
// the compiler always does the NOT in three vs one instruction
//
__asm { mov ecx,cDW mov eax,pBits sar ecx,2 jz SHORT IB2 IB1: not DWORD PTR [eax] not DWORD PTR [eax+4] not DWORD PTR [eax+8] not DWORD PTR [eax+12] add eax,16 dec ecx jnz IB1 IB2: mov ecx,cDW and ecx,3 jz SHORT IB4 IB3: not DWORD PTR [eax] add eax,4 dec ecx jnz IB3 IB4: } #endif
}
#if 0
//*******************************************************
void vFindWhiteInvertBits ( RASTERPDEV *pRPDev, RENDER *pRD, DWORD *pBits ) /*++
Routine Description:
This function determines the leading and trailing white space for this buffer and inverts all the necessary bits such that 0's are white and 1's are black.
Arguments: pRPDev Pointer to RASTERPDEV structure pRD Pointer to RENDER structure pBits Pointer to data buffer to invert
Return Value:
none
--*/ { DWORD cDW = pRD->cDWLine; DWORD cLine = pRD->iy;
//
// if the MaxNumScans is 1 then it is useful to determine
// the first and last non-white dword and store them as left
// and right in the plrWhite structure. Only the non-white
// data needs to be inverted in this case
//
if (pRD->iMaxNumScans == 1 && ((pRPDev->fBlockOut & RES_BO_LEADING_BLNKS) || (pRD->fDump & RES_DM_LEFT_BOUND)) && (pRPDev->fBlockOut & RES_BO_TRAILING_BLNKS)) { PLEFTRIGHT plr; DWORD dwMask = pRD->pdwBitMask[pRD->cBLine % DWBITS]; if (dwMask != 0) dwMask = ~dwMask; // allocate blank space structure
//
if (pRD->plrWhite == NULL || (pRD->clr < cLine)) { if (pRD->plrWhite != NULL) MemFree (pRD->plrWhite); pRD->plrWhite = MemAlloc(sizeof(LEFTRIGHT) * cLine); pRD->clr = cLine;
// can't allocate structure so invert everything
//
if (pRD->plrWhite == NULL) { vInvertBits( pBits, cDW * cLine ); return; } } plr = pRD->plrWhite; while (cLine-- > 0) { DWORD *pdwIn = pBits; DWORD *pdwLast = &pBits[cDW-1]; DWORD dwLast = *pdwLast | dwMask;
// find leading blanks, set last dword to zero
// for faster checking
//
*pdwLast = 0; while (*pdwIn == -1) pdwIn++;
*pdwLast = dwLast;
// find trailing blanks
//
if (dwLast == (DWORD)-1) { pdwLast--; if (pdwIn < pdwLast) { while (*pdwLast == (DWORD)-1) pdwLast--; } } plr->left = pdwIn - pBits; plr->right = pdwLast - pBits;
// invert remaining dwords
//
while (pdwIn <= pdwLast) *pdwIn++ ^= ~((DWORD)0);
// increment to next line
pBits += cDW; plr++; } } // MaxNumScans > 1 so invert everything
//
else vInvertBits( pBits, cDW * cLine );
} #endif
/************************** Function Header *********************************
* bLookAheadOut * Process text for printers requiring a lookahead region. These are * typified by the HP DeskJet family, where the output needs to be * sent before the printer reaches that point in the raster scan. * The algorithm is explained in the DeskJet manual. * * RETURNS: * TRUE/FALSE, FALSE being some substantial failure. * * HISTORY: * 10:43 on Mon 11 Jan 1993 -by- Lindsay Harris [lindsayh] * Created it to support the DeskJet. * ****************************************************************************/
BOOL bLookAheadOut( pPDev, iyVal, pRD, iILAdv ) PDEV *pPDev; /* Our PDEV, gives us access to all our data */ INT iyVal; /* Scan line being processed. */ RENDER *pRD; /* The myriad of data about what we do */ INT iILAdv; /* Add to scan line number to get next one */ { /*
* First step is to find the largest font in the lookahead region. * The position sorting code does this for us. */
INT iTextBox; /* Scan lines to look for text to send */ INT iIndex; /* Loop parameter */
RASTERPDEV *pRPDev; /* The active stuff */
pRPDev = (PRASTERPDEV)pPDev->pRasterPDEV;
iTextBox = ILookAheadMax( pPDev, iyVal, pPDev->dwLookAhead );
iIndex = pRD->iyLookAhead - iyVal; iyVal = pRD->iyLookAhead; /* Base address of scan */
while( iIndex < iTextBox ) { if( !BDelayGlyphOut( pPDev, iyVal ) ) return FALSE; /* Doomsday is here */
++iIndex; ++iyVal; }
pRD->iyLookAhead = iyVal;
return TRUE; }
|