Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3036 lines
102 KiB

/***************************** 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;
}