Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2795 lines
89 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 - 1993, Microsoft Corporation
*
***************************************************************************/
#include <stddef.h>
#include <windows.h>
#include <winddi.h>
#include "libproto.h"
#include "win30def.h"
#include "udmindrv.h"
#include "udpfm.h"
#include "uddevice.h"
#include "udresrc.h"
#include "pdev.h"
#include "udresid.h"
#include "winres.h"
#include "ntmindrv.h"
#include "compress.h"
#include "posnsort.h"
#include "rasdd.h"
#include "stretch.h"
#include "udrender.h"
#include "udfnprot.h"
typedef unsigned int uint;
/*
* 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 0x10000 /* Even 64k */
//used when we can grow the block height
#define MAXBLOCK_SIZE 0x10000 /* Even 64k */
/*
* 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 i3to4Bytes( UD_PDEV *, BYTE *, int );
int iLineOut( PDEV *, RENDER *, BYTE *, int, int );
void vInvertBits( DWORD *, int );
void vInvertBitsCMY( DWORD *, int );
BOOL bCanSetColour( UD_PDEV * );
BOOL bLookAheadOut( PDEV *, int, RENDER *, int );
/************************ 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 */
UD_PDEV *pUDPDev; /* The unidrive PDEV - printer details */
RENDER *pRD; /* Miscellaneous rendering data */
BOOL fGpcVersion2;
/*
* Allocate storage for our RENDER structure, then set it all to
* zero, so that it is in a known safe state.
*/
pUDPDev = pPDev->pUDPDev;
if( !(pRD = (RENDER *)
DRVALLOC( ( pUDPDev->bBanding) ? sizeof(RENDER) * 2 : sizeof( RENDER ))))
return FALSE;
pPDev->pvRenderData = pRD;
pPDev->pvRenderDataTmp = ( pUDPDev->bBanding ) ? pRD+1 : NULL;
/* 0 is safe default */
ZeroMemory( pRD,
( pUDPDev->bBanding) ? sizeof(RENDER) * 2 : sizeof( RENDER ) );
fGpcVersion2 = pUDPDev->pdh->wVersion < GPC_VERSION3; //GPC2 or GPC3
/*
* Various operations depend upon what format bitmap we have. So
* now is the time to set this all up.
*/
switch( iFormat )
{
case BMF_1BPP:
iBPP = 1;
pRD->vLtoPTransFn = vTrans8N;
break;
case BMF_4BPP:
iBPP = 4;
pRD->vLtoPTransFn = vTrans8N4BPP;
break;
case BMF_8BPP:
iBPP = 8;
pRD->vLtoPTransFn = vTrans8BPP;
break;
// sandram
case BMF_24BPP:
iBPP = 24;
pRD->vLtoPTransFn = vTrans24BPP;
break;
default:
#if DBG
DbgPrint( "Rasdd!bRender: not 1, 4 or 8 bits per pel bitmap\n" );
#endif
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
pRD->iBPP = iBPP;
if( pUDPDev->Resolution.sMinBlankSkip == 0 )
{
/* Presume this means skip should not be performed. */
pUDPDev->Resolution.fBlockOut &= ~RES_BO_ENCLOSED_BLNKS;
}
pRD->iCursor = pUDPDev->Resolution.fCursor;
pRD->iFlags = 0; /* Nothing set, yet */
pRD->fDump = pUDPDev->Resolution.fDump;
pRD->Trans.pdwTransTab = pPDev->pdwTrans;
pRD->pdwBitMask = pPDev->pdwBitMask;
pRD->pdwColrSep = pPDev->pdwColrSep; /* Colour translation */
pRD->pUDPDev = pUDPDev;
pRD->ix = sizl.cx;
pRD->iy = sizl.cy;
pRD->iBPP = iBPP;
pRD->iSendCmd = CMD_RES_SENDBLOCK;
pRD->cBLine = pRD->ix * iBPP; /* Bits in scanline */
pRD->cDWLine = (pRD->cBLine + DWBITS - 1) / DWBITS;
pRD->iPassHigh = pUDPDev->Resolution.sNPins;
// Derryd : Minidriver Callback
if (pUDPDev->fMode & PF_BLOCK_IS_BAND ) //means callback wants entire band as one block
{
//don't want any stripping, because would mean creaing new buffers
pUDPDev->Resolution.fBlockOut &= ~(RES_BO_LEADING_BLNKS |
RES_BO_TRAILING_BLNKS | RES_BO_ENCLOSED_BLNKS);
pRD->fDump &= ~RES_DM_LEFT_BOUND;
pRD->iPassHigh = pRD->iy ;
}
// end
//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 = pUDPDev->Resolution.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( "rasdd!bRenderInit: Printer Interlace too big to handle\n" );
#endif
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
/*
* 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( pUDPDev->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 64k & use that for compression & white
// space stripping buffers.
// Calculate what the corresponding max number of scanlines should be.
if (pUDPDev->Resolution.fBlockOut & RES_BO_MULTIPLE_ROWS)
{
int tmp;
tmp = MAXBLOCK_SIZE - (MAXBLOCK_SIZE % pRD->iNumScans);
pRD->iMaxNumScans = tmp / ((cbOutBand * iBPP + DWBYTES - 1) / DWBYTES);
cbOutBand = tmp;
}
else
{
pRD->iMaxNumScans = pRD->iHeight;
cbOutBand = cbOneBlock;
}
/* The output write function - usually WriteSpoolBuf */
if( pUDPDev->Resolution.fBlockOut & RES_BO_OEMGRXFILTER )
{
/* Minidriver contains the function to use for output */
HANDLE hModule;
bSFAFN bSFA;
NTMD_INIT ntmdInit; /* Function addresses for minidriver */
//hModule = ((WINRESDATA *)pPDev->pvWinResData)->hMod;
if (!(hModule = pPDev->hImageMod) )
{
RIP("NULL hImageMod in bRenderInit\n");
return(FALSE);
}
pRD->iWriteFn = (OFN)EngFindImageProcAddress( hModule, "CBFilterGraphics" );
bSFA = (bSFAFN)EngFindImageProcAddress( hModule, "bSetFuncAddr" );
if( !pRD->iWriteFn || !bSFA )
{
#if DBG
DbgPrint( "Rasdd!bRenderInit: Missing CBFilterGraphics in bRender\n" );
#endif
RIP("EngFindImageProcAddress Failed\n");
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
ntmdInit.wSize = sizeof( NTMD_INIT );
ntmdInit.wVersion = NTMD_INIT_VER;
ntmdInit.WriteSpoolBuf = (WSBFN)WriteSpoolBuf;
if( !bSFA( &ntmdInit ) )
{
#if DBG
DbgPrint( "Rasdd!bSetFuncAddr returns FALSE\n" );
#endif
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
else
pRD->iWriteFn = WriteSpoolBuf;
/*
* Set up the function calls to check for empty (graphics) lines.
* These are distinguished by whether this is an RGB colour bitmap,
* or otherwise (CMY colour and monochrome are the same).
*/
if( pUDPDev->Resolution.fBlockOut & RES_BO_ALL_GRAPHICS )
{
/* Printer requires sending all graphics, so never find white */
pRD->bWhiteLine = bIsNeverWhite;
pRD->bWhiteBand = bIsNeverWhite;
}
else
{
/* Printer has cursor addressing ability, so select white scan fns */
if( iBPP > 1 && (pUDPDev->fColorFormat & DC_PRIMARY_RGB) )
{
if (pUDPDev->fMode & PF_8BPP)
{
pRD->bWhiteLine = bIs8BPPLineWhite;
pRD->bWhiteBand = bIs8BPPBandWhite;
}
else if (pUDPDev->fMode & PF_24BPP)
{
pRD->bWhiteLine = bIs24BPPLineWhite;
pRD->bWhiteBand = bIs24BPPBandWhite;
}
else
{
pRD->bWhiteLine = bIsRGBLineWhite;
pRD->bWhiteBand = bIsRGBBandWhite;
}
}
else
{
pRD->bWhiteLine = bIsLineWhite;
pRD->bWhiteBand = bIsBandWhite;
}
// We'll need to scan the bitmap in blocks rather than single lines
if (pRD->iNumScans > 1)
pRD->bWhiteLine = pRD->bWhiteBand;
}
switch( iBPP )
{
case 4: /* 4 bits per pel - printer is planar */
/* Colour, so select the colour rendering function */
pRD->bPassProc = bOneColourPass;
pRD->Trans.pdwTransTab = pPDev->pdwColrSep;
/*
* Should also set up the flag bits needed by bOneColourPass().
* Basic technique is to determine whether this printer has separate
* commands to switch colour and send graphics, or whether they
* are combined. Examples are Epson LQ2550 and HP PaintJet, respectively.
*/
/* !!!LindsayH - Unidrv could be better here! Why not have a flag to specify! */
if( bCanSetColour( pUDPDev ) )
pRD->iFlags |= RD_SET_COLOUR; /* Colour commands are separate */
/* Initialise the colour index translation table - CMYK models esp */
iBytesPCol = (pRD->iBitsPCol + BBITS - 1) / BBITS;
if( (pUDPDev->fColorFormat & DC_EXTRACT_BLK) )
{
/*
* Black is extracted, we need to change the order in which
* colours are sent to the printer. The minidriver data assumes
* we send black first, whereas we choose to send it last.
* Hence, basically shift the indices around by 1. This really
* only applies to CYM printers, and will only be used in
* that case by bOneColourPass().
*
* NOTE that this is only done if we can reverse the order
* in which the data is sent to the printer. Typically this
* is possible for dot matrix printers, but not HP printers.
*/
if( pRD->iFlags & RD_SET_COLOUR && fGpcVersion2)
{
/*
* Can print in arbitrary order, so print black last.
* This means shifting the select black command to last
* and also to process the black bytes last.
*
* This is okay for GPC2 minidrivers. For GPC3 we should
* respect the order given to us
*/
for( iIndex = 0; iIndex < COLOUR_MAX; ++iIndex )
{
pRD->iColXlate[ iIndex ] = (iIndex + 1) % COLOUR_MAX;
pRD->iColOff[ iIndex ] = iBytesPCol *
(COLOUR_MAX - 1 - iIndex);
}
}
else
{
/*
* Follow the order in the specification. That is,
* black is sent first, followed by CMY or RGB, as
* appropriate.
* For GPC3 minidrivers rgbOrder will specify the plane
* ordering desired by minidriver developer
*/
for( iIndex = 0; iIndex < COLOUR_MAX; ++iIndex )
{
int tmp;
// rgbOrder valid values are emum {none,r,g,b,c,m,y,k}
tmp = pUDPDev->rgbOrder[iIndex] -4;
pRD->iColXlate[ iIndex ] = tmp;
pRD->iColOff[ iIndex ] = iBytesPCol *
((COLOUR_MAX - 1 -tmp) % COLOUR_MAX);
}
}
}
else
{
/*
* The more conventional colour index case. This is simply
* a one to one mapping. We are only interested in the first
* three values, as this is the 3 plane case.
*/
int offset;
// rgbOrder valid values are emum {none,r,g,b,c,m,y,k}
offset = (pUDPDev->fColorFormat & DC_PRIMARY_RGB) ? 1 : 4 ;
for( iIndex = 0; iIndex < COLOUR_MAX; ++iIndex )
{
int tmp;
tmp = pUDPDev->rgbOrder[iIndex] -offset;
pRD->iColXlate[ iIndex ] = tmp;
pRD->iColOff[ iIndex ] = iBytesPCol * (COLOUR_MAX - 1 - tmp);
}
}
pRD->pbColSplit = HeapAlloc( pPDev->hheap, 0, cbOutBand / iBPP );
if( pRD->pbColSplit == 0 )
return FALSE;
//GPC3 allows minidriver developer specify this.
if( (pRD->iBitsPCol == 1 && fGpcVersion2) ||
(pUDPDev->fColorFormat & DC_SEND_ALL_PLANES) )
{
pRD->iFlags |= RD_ALL_COLOUR; /* PaintJet - HACK!! */
/* !!!LindsayH - temporary hack until optimisation is fixed */
pUDPDev->Resolution.fBlockOut &= ~(RES_BO_LEADING_BLNKS | RES_BO_TRAILING_BLNKS | RES_BO_ENCLOSED_BLNKS);
pRD->fDump &= ~RES_DM_LEFT_BOUND;
}
break;
case 1: /* 1 bit per pel - monochrome */
case 8: /* Seiko special - 8 bits per pel */
pRD->bPassProc = bOnePassOut;
pRD->Trans.pdwTransTab = pPDev->pdwTrans;
pRD->pbColSplit = 0; /* No storage allocated either! */
break;
case 24:
pRD->bPassProc = b24BitOnePassOut;
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 (pUDPDev->pdh->fTechnology != GPC_TECH_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 = HeapAlloc( pPDev->hheap, 0, cbOutBand )) )
{
HeapFree( pPDev->hheap, 0, pRD->pbColSplit );
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 */
//When printing a block of scanlines (GDI style graphics) our data will
//not be contiguous.
//We need a buffer to copy the relavent data to before printing
//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) || (pUDPDev->Resolution.fBlockOut & RES_BO_MULTIPLE_ROWS))
&& (!(pUDPDev->fMode & PF_BLOCK_IS_BAND ) ))
{
if ( !(pRD->pStripBlanks = HeapAlloc( pPDev->hheap, 0, cbOutBand )) )
return FALSE;
if (pRD->iNumScans > 1)
if ( !(pRD->pdwTrailingScans = HeapAlloc( pPDev->hheap, 0, cbOneBlock)) )
return FALSE;
}
/*
* If compression is available, we need to allocate a buffer for it.
* The size is available, so check for compression and allocate if
* it is to be used.
*/
if(( pUDPDev->iCompMode >= CMP_ID_FIRST ) &&
!( pUDPDev->Resolution.fBlockOut & RES_BO_OEMGRXFILTER ))
{
/* Allocate storage AND arrange the function address too! */
int cbBuf; /* Calculate storage request size */
cbBuf = 0; /* In case no method is available */
switch( pUDPDev->iCompMode )
{
case CMP_ID_RLE:
pRD->iCompress = iCompRLE;
cbBuf = cbOutBand + RLE_OVERSIZE;
break;
case CMP_ID_DELTAROW:
default:
#if DBG
DbgPrint( "Rasdd!bRender: Invalid compression type %ld\n",
pUDPDev->iCompMode );
#endif
break;
case CMP_ID_TIFF40:
pRD->iCompress = iCompTIFF;
cbBuf = cbOutBand + (cbOutBand >> 4);
break;
}
if( cbBuf > 0 )
{
pRD->pCompress = HeapAlloc( pPDev->hheap, 0, cbBuf );
if( !pRD->pCompress )
pRD->iCompress = NULL; /* Avoid compression */
}
}
/*
* Adjustments to whether we rotate the bitmap, and if so, which way.
*/
if( pUDPDev->fMode & PF_ROTATE )
{
/* Rotation is our responsibility */
if( pUDPDev->fMode & PF_CCW_ROTATE )
{
/* Counter clockwise rotation - LaserJet style */
pRD->iyPrtLine = pUDPDev->szlPage.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 *********************************
* bRenderPageStart
* 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 */
{
UD_PDEV *pUDPDev;
pUDPDev = pPDev->pUDPDev;
/*
* If the printer can handle rules, now is the time to initialise
* the rule finding code.
*/
if( pUDPDev->fMode & PF_RECT_FILL )
vRuleInit( pPDev, pPDev->pvRenderData );
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;
{
BOOL bRetn; /* The return code */
UD_PDEV *pUDPDev;
pUDPDev = pPDev->pUDPDev;
/* Finish up with the rules code - includes freeing memory */
if( pUDPDev->fMode & PF_RECT_FILL )
vRuleEndPage( pPDev );
bRetn = TRUE;
if( pPDev->pvWhiteText )
{
/*
* This page contains white text. This is stored away in a
* separate buffer. Now is the time to play it back. This is
* required because the LJ III etc require this data be sent
* after the graphics.
*/
bRetn = bPlayWhiteText( pPDev );
}
return bRetn;
}
/************************ Function Header *********************************
* bCanSetColour
* Determine whether this printer has commands to set the colour of
* a graphics plane, or if it is automatic. Basically we look to
* see if the colour plane selection commands are the same as the
* send graphics block command. If they are, presume that the
* printer has no colour setting ability - e.g. PaintJet, DeskJet.
*
* RETURNS:
* TRUE if the select colour plane cmd differs from send graphics
*
* HISTORY:
* Friday December 3 1993 -by- Norman Hendley [normanh]
* Added support for GPC3 bit DC_EXPLICIT_COLOR
*
* 19:35 on Thu 07 Jan 1993 -by- Lindsay Harris [lindsayh]
* Needs to be more complex for the DeskJet.
*
****************************************************************************/
BOOL
bCanSetColour( pUDPDev )
UD_PDEV *pUDPDev; /* Has all that we want */
{
int iI; /* Loop parameter */
CD *pCDGr; /* The CD for graphics usage */
CD *pCDCol; /* For each of the send colour planes */
CD **ppCDCol; /* Where the above come from */
if ( (pUDPDev->pdh->wVersion >= GPC_VERSION3) &&
(pUDPDev->fColorFormat & DC_EXPLICIT_COLOR) )
return TRUE;
//For the moment it is safer not return else FALSE here
//Not all GPC3 minidrivers will correctly set this flag
pCDGr = pUDPDev->apcdCmd[ CMD_RES_SENDBLOCK ];
ppCDCol = &pUDPDev->apcdCmd[ CMD_DC_GC_PLANE1 ];
/*
* Scan through all the colour planes. Return FALSE as soon as
* we find a match.
*/
/*
* If we are in 8 bit mode or 24 bit mode - we know that
* it can print color so return true
*/
if ((pUDPDev->fMode & PF_8BPP) || (pUDPDev->fMode & PF_24BPP))
return TRUE;
for( iI = 0; iI < (int)pUDPDev->sDevPlanes; ++iI )
{
pCDCol = *ppCDCol++;
if( pCDCol == pCDGr ||
(pCDCol->fType == pCDCol->fType &&
pCDCol->sCount == pCDCol->sCount &&
pCDCol->wLength == pCDGr->wLength &&
strncmp( pCDCol->rgchCmd, pCDGr->rgchCmd, pCDGr->wLength ) == 0) )
{
return FALSE;
}
}
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 */
MDEV *pMDev = NULL; /* Pointer to Minidriver PDEV structure */
if( pRD = pPDev->pvRenderData )
{
if( pRD->pvTransBuf )
{
/*
* Dot matrix printers require a transpose for each print head
* pass, so we now free the memory used for that.
*/
HeapFree( pPDev->hheap, 0, pRD->pvTransBuf );
}
if( pRD->pbColSplit )
HeapFree( pPDev->hheap, 0, pRD->pbColSplit );
if( pRD->pCompress )
HeapFree( pPDev->hheap, 0, pRD->pCompress );
if( pRD->pStripBlanks )
HeapFree( pPDev->hheap, 0, pRD->pStripBlanks );
if( pRD->pdwTrailingScans)
HeapFree( pPDev->hheap, 0, pRD->pdwTrailingScans );
if( pRD->plrWhite)
{
WARNING("Freeing plrWhite in vRenderFree\n");
HeapFree( pPDev->hheap, 0, pRD->plrWhite );
}
if ( pMDev = ((UD_PDEV *)pPDev->pUDPDev)->pMDev )
{
//Derryd callback buffer for minidriver
if ( pMDev->pMemBuf)
{
HeapFree( pPDev->hheap, 0, pMDev->pMemBuf );
pMDev->pMemBuf = NULL; /* No access */
}
//end
/* Reset MiniPDev rendering specific values */
pMDev->iyPrtLine = 0;
}
HeapFree( pPDev->hheap, 0, (LPSTR)pRD );
pPDev->pvRenderData = NULL; /* Won't do it again! */
}
if( ((UD_PDEV *)pPDev->pUDPDev)->fMode & PF_RECT_FILL )
vRuleFree( pPDev ); /* Could do this in DisableSurface */
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.
*
*
* 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.
*
****************************************************************************/
/**************************************************************************\
*
* The code between these two sets of comments is for doing timings. By
* increasing the gcCopies value, a page will be rendered multiple times.
* Setting gbRender to 0 allows you to measure the overhead of allocating
* and making the copy of the bits.
*
\**************************************************************************/
#ifdef RASDDPERF
BOOL bRender2(PDEV *,RENDER *,SIZEL,DWORD *);
int gcCopies = 1;
BOOL gbRender = 1;
BOOL
bRender( pPDev, pRD, sizl, pBits )
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 */
{
PVOID pv;
ULONG cj;
int i;
cj = pRD->cDWLine * sizl.cy * 4;
pv = (PVOID)DRVALLOC(cj);
if (pv)
{
RtlCopyMemory(pv,pBits,cj);
for (i = 0; i < gcCopies; ++i)
{
pRD->iyBase = 0;
pRD->ixOrg = 0;
RtlCopyMemory(pBits,pv,cj);
if (gbRender)
bRender2(pPDev,pRD,sizl,pBits);
}
DRVFREE(pv);
}
return(TRUE);
}
BOOL
bRender2( pPDev, pRD, sizl, pBits )
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 */
/**************************************************************************\
*
* PERFORMANCE CODE ABOVE HERE
*
\**************************************************************************/
#else
BOOL
bRender( pPDev, pRD, sizl, pBits )
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 */
#endif
{
BOOL bRet; /* Return value */
int iBPP; /* Bits per pel - expect 1 or 4 */
UD_PDEV *pUDPDev; /* The unidrive PDEV - printer details */
pUDPDev = pPDev->pUDPDev;
iBPP = pRD->iBPP; /* Speedier access as local variable */
/*
* 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->bInverted = FALSE;
pRD->plrWhite = NULL;
pRD->plrCurrent = NULL;
pRD->clr = 0;
/*
* Various operations depend upon what format bitmap we have. So
* now is the time to set this all up.
*/
if( pRD->iBPP == 1 || !(pUDPDev->fColorFormat & DC_PRIMARY_RGB) )
{
/*
* Monochrome uses an inverted colour palette to stop
* breaking things like BitBlt, and many other assumptions
* about 0 being black. However, the remainder of the code
* presumes that 1 is black, so do the inversion now.
*/
/* !!!LindsayH - consider changing code to work with inverted palette */
if( pRD->iBPP == 1 )
{
/*
* don't invert the bits yet if we may do it later. Currently
* the only time this is done later is in the rule detection
* code for the HP laserjet. In the future, this should always
* be done at a later time. Making an extra pass causes reading
* AND writing an entire MEGABYTE in the 300dpi 8.5X11 inch page
* which is guaranteed to completely flush the external cache.
* (erick 12/30/93)
*/
if(!( ((UD_PDEV *)(pPDev->pUDPDev))->fMode & PF_RECT_FILL ))
{
pRD->bInverted = TRUE;
vInvertBits( pBits, pRD->cDWLine * sizl.cy );
}
}
else
{
pRD->bInverted = TRUE;
vInvertBitsCMY( pBits, pRD->cDWLine * sizl.cy );
}
}
else
{
pRD->bInverted = TRUE;
}
/*
* Check if rotation is required. If so, allocate storage,
* start transposing, etc.
*/
if( pUDPDev->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 )
{
/* !!!LindsayH - will not work for 24bpp */
/*
* LaserJet/PaintJet style, so round to byte alignment.
*/
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 )
{
/* !!!LindsayH - will not work for 24bpp */
/* sandram - if the printer is in 24 bit
* color mode than round down.
*/
if (pUDPDev->sBitsPixel == 24)
iTHigh -= iTHigh % pRD->iPassHigh;
else
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.icbL = tpBig.cDWL * DWBYTES;
tpBig.pdwTransTab = pPDev->pdwTrans; /* For L -> P rotation */
if( !(tpBig.pvBuf = HeapAlloc( pPDev->hheap, 0, 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 /= 4;
pRD->cDWLine = (pRD->cBLine + DWBITS - 1) / DWBITS;
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( pUDPDev->fMode & PF_CCW_ROTATE )
{
/*
* 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 */
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;
}
HeapFree( pPDev->hheap, 0, 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;
}
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*/
UD_PDEV * pUDPDev;
PAL_DATA *pPD;
int iWhiteIndex;
int iILDone[ MAX_INTERLACE ]; /* For head pass reduction */
PLEFTRIGHT plr = NULL; /* left/right of non white area */
pUDPDev = pPDev->pUDPDev;
pPD = pPDev->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 && (pUDPDev->fMode & PF_RECT_FILL ))
{
if (!bRuleProc( pPDev, pRData, pBits ))
{
// couldn't process rules, better invert the bits
vInvertBits( pBits, pRData->cDWLine * pRData->iy );
}
}
iHeadLine = 0;
for( iLine = 0; iLine < pRData->iInterlace; ++iLine )
iILDone[ iLine ] = 0;
iTHKeep = pRData->iTransHigh;
iNumScans = pRData->iNumScans;
plr = (pRData->iMaxNumScans > 1) ? NULL : pRData->plrWhite;
//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( ((UD_PDEV *)(pPDev->pUDPDev))->fMode & PF_ABORTED )
return FALSE;
// derryd : need to update the CAP, for minidriver blockout
pUDPDev->pMDev->iyPrtLine = pRData->iyPrtLine;
//end
if (plr != NULL)
{
if (plr[iLine].left > plr[iLine].right)
bIsWhite = TRUE;
pRData->plrCurrent = plr + iLine;
}
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 = 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)
{
ZeroMemory( pRData->pdwTrailingScans, cDWPass);
CopyMemory(pRData->pdwTrailingScans,pBits,
pRData->cDWLine * (pRData->iy -iLine ));
//end of page so we can do this.
pBits = pRData->pdwTrailingScans;
}
}
}
else
{
if (plr == NULL)
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) &&
!(pRData->bWhiteBand((DWORD *)(pBits + cDWPass),pRData,iWhiteIndex)) &&
((iLine + iHeight) < pRData->iy ) )
{
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)
*/
ASSERTRASDD(plr == NULL,"RASDD!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;
}
}
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->pUDPDev->fMDGeneral & MD_SERIAL) && pPDev->pPSHeader )
{
/* Possible text, so go to it */
BOOL bRetn;
if( pRData->pUDPDev->iLookAhead > 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 from graphics mode, to be civilised.
*/
if( pRData->iFlags & RD_GRAPHICS )
{
if( WriteChannel( pPDev->pUDPDev, CMD_RES_ENDGRAPHICS ) != NOOCD )
pRData->iFlags &= ~RD_GRAPHICS;
}
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 iByteMax; /* Number of bytes across page */
int iBytesPCol; /* Bytes per column */
int iTemp;
BYTE bSum; /* Check for empty line */
UD_PDEV *pUDPDev; /* For convenience */
pUDPDev = pPDev->pUDPDev;
iBytesPCol = (pRData->iBitsPCol + BBITS - 1) / BBITS;
iColourMax = pUDPDev->sDevPlanes;
iByteMax = pRData->cDWLine * DWBYTES * pRData->iBitsPCol * pRData->iNumScans;
iTemp = pRData->cDWLine;
/*
* 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->cDWLine = (iTemp + COLOUR_MAX - 1) / 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 = pUDPDev->Resolution.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( !(pRData->iFlags & RD_SET_COLOUR) )
pRData->iSendCmd = CMD_DC_GC_FIRST + iColour;
if( iColour == (iColourMax - 1) )
{
/* Reinstate the automatic cursor position adjustment */
pRData->iCursor |= pUDPDev->Resolution.fCursor & RES_CUR_Y_POS_AUTO;
}
pbIn = pbData + pRData->iColOff[ iColour ];
pbOut = pRData->pbColSplit; /* Colour splitting data */
bSum = 0;
iByte = 0;
if( iBytesPCol == 1 )
{
/* PaintJet/DeskJet special case */
while( iByte < iByteMax )
{
bSum |= *pbOut++ = *pbIn;
pbIn += COLOUR_MAX; /* Next data group */
iByte += COLOUR_MAX;
}
}
else
{
/* General dot matrix case - > 8 pins */
while( iByte < iByteMax )
{
for( iBPC = 0; iBPC < iBytesPCol; ++iBPC )
bSum |= *pbOut++ = *pbIn++;
pbIn += (COLOUR_MAX - 1) * iBytesPCol; /* Next data group */
iByte += COLOUR_MAX * iBytesPCol;
}
}
/*
* 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 )
{
/*
* Data to go, so set the colour and off to bOnePassOut.
*/
if( pRData->iFlags & RD_SET_COLOUR )
SelectColor( pPDev->pUDPDev, pRData->iColXlate[ iColour ] );
if( !bOnePassOut( pPDev, pRData->pbColSplit, pRData ) )
{
pRData->cDWLine = iTemp;
return FALSE;
}
}
}
pRData->cDWLine = iTemp; /* Correct value for other parts */
return TRUE;
}
/************************** 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 */
UD_PDEV *pUDPDev; /* Unidrv's pdev */
int iWhiteIndex = 0;
PLEFTRIGHT plr = pRData->plrCurrent;
pUDPDev = pPDev->pUDPDev;
fDump = pRData->fDump;
fCursor = pUDPDev->Resolution.fCursor;
fBlockOut = pUDPDev->Resolution.fBlockOut;
iBytesPCol = (pRData->iBitsPCol + BBITS - 1) / BBITS;
iMinSkip = (int)pUDPDev->Resolution.sMinBlankSkip;
iNumScans= pRData->iNumScans;
iWidth = pRData->cDWLine * DWBYTES; // convert to bytes
iSzBlock= iWidth * iNumScans;
if (!(pUDPDev->fMode & PF_SEIKO))
{
if (pUDPDev->fMode & PF_8BPP)
iWhiteIndex =((PAL_DATA*)(pPDev->pPalData))->iWhiteIndex;
}
iRight = pRData->iMaxBytesSend;
/*
* 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)
{
ASSERTRASDD((WORD)iRight >= (plr->right * sizeof(DWORD)),"RASDD!bOnePassOut - invalid right\n");
ASSERTRASDD(fBlockOut & RES_BO_TRAILING_BLNKS,"RASDD!bOnePassOut - invalid fBlockOut\n");
iLeft = plr->left * sizeof(DWORD);
iRight = (plr->right+1) * sizeof(DWORD);
}
for( ; iLeft < iRight && pbData[ iLeft ] == iWhiteIndex; ++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] == iWhiteIndex ;pos += iWidth)
;
iLeft--;
/*
* If less than the minimum skip amount, ignore it.
*/
if( iLeft < iMinSkip )
iLeft = 0;
}
}
else
{
ASSERTRASDD(plr == NULL,"RASDD!bOnePassOut - plrWhite invalid\n");
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)
{
while( iRight > iLeft && pbData[ --iRight ] == iWhiteIndex )
;
iRight += iBytesPCol - (iRight % iBytesPCol);
}
else
{
int pos;
pos = iSzBlock +1;
while(iRight > iLeft && pos > iSzBlock)
for (pos = --iRight; pos < iSzBlock && pbData[ pos] == iWhiteIndex ;pos += iWidth)
;
iRight++;
}
}
/*
* 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( pUDPDev, CMD_CM_UNI_DIR );
}
#if 0
// do not allow consecutive bits to be set
if( fBlockOut & RES_BO_NO_ADJACENT || pUDPDev->fMDGeneral & MD_NO_ADJACENT )
ResetAdjacent( pbData, iLength, pRData->iBitsPCol );
#endif
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
{
/* 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;
}
// sandram
#define CMD_DC_GC_SOURCEWIDTH_AND_HEIGHT CMD_DC_GC_PLANE1
/************************** 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 )
// add by DerryD
PDEV *pPDev; /* The key to everything */
// end
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 iMinSkip; /* Minimum number of bytes to skip */
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*/
WORD fDump; /* Device capabilities */
int iCursor; /* Cursor behavious */
int iSourceWidth; /* local copy of SourceWidth */
BYTE *pbSend; /* Address of data to send out */
UD_PDEV *pUDPDev;
pUDPDev = pRData->pUDPDev;
iNumScans = pRData->iNumScans;
iMinSkip = pUDPDev->Resolution.sMinBlankSkip;
fDump = pRData->fDump;
iCursor = pRData->iCursor;
/*
* Set the Y position - safe to do so at anytime.
*/
pRData->iYMoveFn( pUDPDev, pRData->iyPrtLine, MV_GRAPHICS );
if( pRData->iBitsPCol == 1 )
{
/* Laserjet style */
iBytesPCol = 1;
}
else
iBytesPCol = pRData->iBitsPCol / BBITS;
#if DBG
if( (ixEnd - ixPos) % iBytesPCol )
{
DbgPrint( "RasDD!iLineOut: cbOut = %ld, NOT multiple of iBytesPCol = %ld\n",
ixEnd - ixPos, iBytesPCol );
return 0;
}
#endif
/*
* Set the Source width. If in graphics mode - need to send a
* end graphics command and then set the source width.
*/
if (pUDPDev->fMode & PF_24BPP)
{
if( ixPos < pRData->ixOrg || (pRData->ixOrg + iMinSkip) < ixPos )
;
else
{
// we can't optimize the left edge, better make it white
ASSERTRASDD(pRData->plrCurrent == NULL, "RASDD!iLineOut - plrCurrent should be NULL\n");
ixPos = pRData->ixOrg;
}
iSourceWidth = ((ixEnd - ixPos) * BBITS)/pUDPDev->sBitsPixel;
if (iSourceWidth != pRData->iSourceWidth)
{
if( pRData->iFlags & RD_GRAPHICS )
{
pUDPDev->fMode &= ~PF_COMPRESS_ON;
pRData->iFlags &= ~RD_GRAPHICS;
WriteChannel( pUDPDev, CMD_CMP_END );
WriteChannel( pUDPDev, CMD_RES_ENDGRAPHICS );
}
WriteChannel (pUDPDev, CMD_DC_GC_SOURCEWIDTH_AND_HEIGHT,
iSourceWidth);
pRData->iSourceWidth = iSourceWidth;
}
}
/*
* 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( fDump & RES_DM_LEFT_BOUND )
{
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 )
{
pUDPDev->fMode &= ~PF_COMPRESS_ON;
pRData->iFlags &= ~RD_GRAPHICS;
WriteChannel( pUDPDev, CMD_CMP_END );
WriteChannel( pUDPDev, CMD_RES_ENDGRAPHICS );
}
}
else
{
// we can't optimize the left edge, better make it white
if (pRData->plrCurrent != NULL)
{
int i;
for (i = pRData->ixOrg; i < ixPos; ++i)
pbOut[i] = 0;
}
ixPos = pRData->ixOrg;
}
}
/*
* Adjust the right side position to dot column version.
*/
if( pRData->iBitsPCol == 1 )
{
/* Laserjet style - work in byte units */
if (pUDPDev->fMode & PF_8BPP)
ixLeft = ixPos; /* In dot/column units */
else if (pUDPDev->fMode & PF_24BPP)
ixLeft = (ixPos * BBITS) / pUDPDev->sBitsPixel;
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( pUDPDev, 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 PRINT_INFO
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,
*/
ixLeft -= ixErr;
ixPos = ixLeft * iBytesPCol;
}
}
if( !(pRData->iFlags & RD_GRAPHICS) )
{
/* Must first switch to graphics mode */
pRData->iFlags |= RD_GRAPHICS;
pUDPDev->fMode |= PF_COMPRESS_ON;
WriteChannel( pUDPDev, CMD_RES_BEGINGRAPHICS );
WriteChannel( pUDPDev, CMD_CMP_BEGIN );
/*
* Remember the graphics origin if this type of
* printer responds to that.
*/
if( fDump & RES_DM_LEFT_BOUND )
pRData->ixOrg = ixPos;
/* Reset the text Color */
pUDPDev->ctl.ulTextColor = 0xffffffff;
}
// 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
// derryd : add check - we don't want to do any stripping
if(( iNumScans > 1 ) && !( pUDPDev->fMode & PF_BLOCK_IS_BAND ))
{
cbOut = iStripBlanks( pRData->pStripBlanks, pbOut, ixPos,
ixEnd, iNumScans,
pRData->cDWLine * DWBYTES);
ixEnd = ixEnd - ixPos;
ixPos = 0;
pbOut = pRData->pStripBlanks;
}
/*
* Calculate the number of dot columns AND the number of bytes to send.
* If compression is available, use it first.
*/
iScanWidth = ixEnd - ixPos;
cbOut = iScanWidth * iNumScans ;
pbSend = &pbOut[ ixPos ];
if( pRData->iCompress )
{
/* There is a compression function, so use it! */
int cbComp; /* Size after compression */
if( cbComp = pRData->iCompress( pRData->pCompress, pbSend, cbOut ) )
{
/*
* Compression functions return 0 if the "compressed" data
* is substantially larger than the original. This can
* happen with RLE, but is negligible for TIFF.
*/
if( !(pUDPDev->fMode & PF_COMPRESS_ON) )
{
/* Enable compression */
WriteChannel( pUDPDev, CMD_CMP_BEGIN );
pUDPDev->fMode |= PF_COMPRESS_ON;
}
cbOut = cbComp;
pbSend = pRData->pCompress;
}
else
{
/* Must disable compression mode - if enabled */
if( pUDPDev->fMode & PF_COMPRESS_ON )
{
WriteChannel( pUDPDev, CMD_CMP_END );
pUDPDev->fMode &= ~PF_COMPRESS_ON;
}
}
}
WriteChannel( pUDPDev, pRData->iSendCmd, cbOut / iBytesPCol, iNumScans, iScanWidth );
iRet = pRData->iWriteFn( pUDPDev, pbSend, cbOut );
if ( (iRet == -2 ) && (pUDPDev->pMDev->pMemBuf == 0))
{
//DerryD setup buffer for minidriver use
pUDPDev->pMDev->pMemBuf = HeapAlloc( pPDev->hheap, 0, pUDPDev->pMDev->iMemReq );
if( pUDPDev->pMDev->pMemBuf == 0 )
return FALSE;
ZeroMemory( pUDPDev->pMDev->pMemBuf, pUDPDev->pMDev->iMemReq ) ;
iRet = pRData->iWriteFn( pUDPDev, pbSend, cbOut );
//end
}
WriteChannel( pUDPDev, CMD_RES_ENDBLOCK );
/*
* 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( pUDPDev, 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( pUDPDev, 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( pUDPDev, pRData->iPosnAdv,
MV_UPDATE | MV_RELATIVE | MV_GRAPHICS );
}
return iRet;
}
/****************************** Function Header *****************************
* vInvertBits
* Function to invert a group of bits. The need for this function is
* because of the overwhelming presumption throughout the system (and
* especially in APPS!) that 0 bits are black and 1 bits are white. If
* the palette is not set this way, many BitBlt rops fail. Thus,
* we need to invert the bits before sending them to the printer.
*
* RETURNS:
* Nothing.
*
* HISTORY:
* First rasdd incarnation.
*
***************************************************************************/
void
vInvertBits( pBits, cDW )
register DWORD *pBits; /* Start of area to invert */
register int cDW; /* Number of DWORDS to invert */
{
while( --cDW >= 0 )
*pBits++ ^= ~((DWORD)0); /* Word length independent */
return;
}
/****************************** Function Header *****************************
* vInvertBitsCMY
* Function to invert a group of bits. The need for this function is
* because of the overwhelming presumption throughout the system (and
* especially in APPS!) that 0 bits are black and 1 bits are white. If
* the palette is not set this way, many BitBlt rops fail. Thus,
* we need to invert the bits before sending them to the printer.
*
* RETURNS:
* Nothing.
*
* HISTORY:
* 17:46 on Mon 28 Jun 1993 -by- Lindsay Harris [lindsayh]
* For CMY printers, based on vInvertBits.
*
***************************************************************************/
void
vInvertBitsCMY( pBits, cDW )
register DWORD *pBits; /* Start of area to invert */
register int cDW; /* Number of DWORDS to invert */
{
while( --cDW >= 0 )
{
*pBits = (*pBits ^ ~0) & 0x77777777;
++pBits;
}
return;
}
/************************** 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 */
UD_PDEV *pUDPDev; /* The active stuff */
pUDPDev = pPDev->pUDPDev;
iTextBox = iLookAheadMax( pPDev->pPSHeader, iyVal, pUDPDev->iLookAhead );
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;
}
/********************** SEIKO HACK FUNCTION HEADER **************************/
#include "stretch.h"
/****************************** Function Header ****************************
* vSeikoLoadPal
* Download the palette to the Seiko Professional ColorPoint in 8BPP
* mode. Takes the data we retrieved from the HT code during
* DrvEnablePDEV.
*
* RETURNS:
* Nothing.
*
* HISTORY:
* 15:10 on Sat 22 May 1993 -by- Lindsay Harris [lindsayh]
* Written months ago, this is just the clean up.
*
****************************************************************************/
void
vSeikoLoadPal( pPDev )
PDEV *pPDev;
{
/*
* Hardcoded to rummage through our data structures: first we
* send down the mode + here is palette command, then the palette
* data.
*/
int iI;
UD_PDEV *pUDPDev;
PAL_DATA *pPD;
BYTE aj[ 3 ]; /* Colour data */
pUDPDev = pPDev->pUDPDev;
pPD = pPDev->pPalData;
WriteSpoolBuf( pUDPDev, ",LUT:", 5 );
for( iI = 0; iI < pPD->cPal; ++iI )
{
aj[ 0 ] = (BYTE)((pPD->ulPalCol[ iI ] >> 16) & 0xff);
aj[ 1 ] = (BYTE)((pPD->ulPalCol[ iI ] >> 8) & 0xff);
aj[ 2 ] = (BYTE)(pPD->ulPalCol[ iI ] & 0xff);
WriteSpoolBuf( pUDPDev, aj, 3 );
}
/* Fill in anything left over */
aj[ 0 ] = aj[ 1 ] = aj[ 2 ] = 0xff;
for( ; iI < 256; ++iI )
{
WriteSpoolBuf( pUDPDev, aj, 3 );
}
return;
}