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.
595 lines
18 KiB
595 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1996 - 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
compress.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of compression formats for sending data to devices.
|
|
|
|
Environment:
|
|
|
|
Windows NT Unidrv driver
|
|
|
|
Revision History:
|
|
|
|
12/15/96 -alvins-
|
|
Created
|
|
|
|
--*/
|
|
|
|
#include "raster.h"
|
|
#include "compress.h" /* Function prototypes */
|
|
|
|
//*************************************************************
|
|
int
|
|
iCompTIFF(
|
|
BYTE *pbOBuf,
|
|
BYTE *pbIBuf,
|
|
int iBCnt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to compress a scan line of data using
|
|
TIFF v4 compression.
|
|
|
|
Arguments:
|
|
|
|
pbOBuf Pointer to output buffer PRESUMED LARGE ENOUGH
|
|
pbIBuf Pointer to data buffer to compress
|
|
iBCnt Number of bytes to compress
|
|
|
|
Return Value:
|
|
|
|
Number of compressed bytes
|
|
|
|
Note:
|
|
The output buffer is presumed large enough to hold the output.
|
|
In the worst case (NO REPETITIONS IN DATA) there is an extra
|
|
byte added every 128 bytes of input data. So, you should make
|
|
the output buffer at least 1% larger than the input buffer.
|
|
|
|
--*/
|
|
{
|
|
BYTE *pbOut; /* Output byte location */
|
|
BYTE *pbStart; /* Start of current input stream */
|
|
BYTE *pb; /* Miscellaneous usage */
|
|
BYTE *pbEnd; /* The last byte of input */
|
|
BYTE jLast; /* Last byte, for match purposes */
|
|
BYTE bLast;
|
|
|
|
int iSize; /* Bytes in the current length */
|
|
int iSend; /* Number to send in this command */
|
|
|
|
|
|
pbOut = pbOBuf;
|
|
pbStart = pbIBuf;
|
|
pbEnd = pbIBuf + iBCnt; /* The last byte */
|
|
|
|
#if (TIFF_MIN_RUN >= 4)
|
|
// this is a faster algorithm for calculating TIFF compression
|
|
// that assumes a minimum RUN of at least 4 bytes. If the
|
|
// third and fourth byte don't equal then the first/second bytes are
|
|
// irrelevant. This means we can determine non-run data three times
|
|
// as fast since we only check every third byte pair.
|
|
|
|
if (iBCnt > TIFF_MIN_RUN)
|
|
{
|
|
// make sure the last two bytes aren't equal so we don't have to check
|
|
// for the buffer end when looking for runs
|
|
bLast = pbEnd[-1];
|
|
pbEnd[-1] = ~pbEnd[-2];
|
|
while( (pbIBuf += 3) < pbEnd )
|
|
{
|
|
if (*pbIBuf == pbIBuf[-1])
|
|
{
|
|
// save the run start pointer, pb, and check whether the first
|
|
// bytes are also part of the run
|
|
//
|
|
pb = pbIBuf-1;
|
|
if (*pbIBuf == pbIBuf[-2])
|
|
{
|
|
pb--;
|
|
if (*pbIBuf == pbIBuf[-3])
|
|
pb--;
|
|
}
|
|
|
|
// Find out how long this run is
|
|
jLast = *pb;
|
|
do {
|
|
pbIBuf++;
|
|
} while (*pbIBuf == jLast);
|
|
|
|
// test whether last byte is also part of the run
|
|
//
|
|
if (jLast == bLast && pbIBuf == (pbEnd-1))
|
|
pbIBuf++;
|
|
|
|
// Determine if the run is longer that the required
|
|
// minimum run size.
|
|
//
|
|
if ((iSend = (int)(pbIBuf - pb)) >= (TIFF_MIN_RUN))
|
|
{
|
|
/*
|
|
* Worth recording as a run, so first set the literal
|
|
* data which may have already been scanned before recording
|
|
* this run.
|
|
*/
|
|
|
|
if( (iSize = (int)(pb - pbStart)) > 0 )
|
|
{
|
|
/* There is literal data, so record it now */
|
|
while (iSize > TIFF_MAX_LITERAL)
|
|
{
|
|
iSize -= TIFF_MAX_LITERAL;
|
|
*pbOut++ = TIFF_MAX_LITERAL-1;
|
|
CopyMemory(pbOut, pbStart, TIFF_MAX_LITERAL);
|
|
pbStart += TIFF_MAX_LITERAL;
|
|
pbOut += TIFF_MAX_LITERAL;
|
|
}
|
|
*pbOut++ = iSize - 1;
|
|
CopyMemory(pbOut, pbStart, iSize);
|
|
pbOut += iSize;
|
|
}
|
|
|
|
/*
|
|
* Now for the repeat pattern. Same logic, but only
|
|
* one byte is needed per entry.
|
|
*/
|
|
iSize = iSend;
|
|
while (iSize > TIFF_MAX_RUN)
|
|
{
|
|
*((char *)pbOut)++ = 1 - TIFF_MAX_RUN;
|
|
*pbOut++ = jLast;
|
|
iSize -= TIFF_MAX_RUN;
|
|
}
|
|
*pbOut++ = 1 - iSize;
|
|
*pbOut++ = jLast;
|
|
|
|
pbStart = pbIBuf; /* Ready for the next one! */
|
|
}
|
|
}
|
|
}
|
|
pbEnd[-1] = bLast;
|
|
}
|
|
#else
|
|
jLast = *pbIBuf++;
|
|
|
|
while( pbIBuf < pbEnd )
|
|
{
|
|
if( jLast == *pbIBuf )
|
|
{
|
|
/* Find out how long this run is. Then decide on using it */
|
|
pb = pbIBuf;
|
|
do {
|
|
pbIBuf++;
|
|
} while (pbIBuf < pbEnd && *pbIBuf == jLast);
|
|
|
|
/*
|
|
* Note that pb points at the SECOND byte of the pattern!
|
|
* AND also that pbIBuf points at the first byte AFTER the run.
|
|
*/
|
|
|
|
if ((iSend = pbIBuf - pb) >= (TIFF_MIN_RUN - 1))
|
|
{
|
|
/*
|
|
* Worth recording as a run, so first set the literal
|
|
* data which may have already been scanned before recording
|
|
* this run.
|
|
*/
|
|
|
|
if( (iSize = pb - pbStart - 1) > 0 )
|
|
{
|
|
/* There is literal data, so record it now */
|
|
while (iSize > TIFF_MAX_LITERAL)
|
|
{
|
|
iSize -= TIFF_MAX_LITERAL;
|
|
*pbOut++ = TIFF_MAX_LITERAL-1;
|
|
CopyMemory(pbOut, pbStart, TIFF_MAX_LITERAL);
|
|
pbStart += TIFF_MAX_LITERAL;
|
|
pbOut += TIFF_MAX_LITERAL;
|
|
}
|
|
*pbOut++ = iSize - 1;
|
|
CopyMemory(pbOut, pbStart, iSize);
|
|
pbOut += iSize;
|
|
}
|
|
|
|
/*
|
|
* Now for the repeat pattern. Same logic, but only
|
|
* one byte is needed per entry.
|
|
*/
|
|
|
|
iSize = iSend + 1;
|
|
while (iSize > TIFF_MAX_RUN)
|
|
{
|
|
*((char *)pbOut)++ = 1 - TIFF_MAX_RUN;
|
|
*pbOut++ = jLast;
|
|
iSize -= TIFF_MAX_RUN;
|
|
}
|
|
*pbOut++ = 1 - iSize;
|
|
*pbOut++ = jLast;
|
|
|
|
pbStart = pbIBuf; /* Ready for the next one! */
|
|
}
|
|
if (pbIBuf == pbEnd)
|
|
break;
|
|
}
|
|
|
|
jLast = *pbIBuf++; /* Onto the next byte */
|
|
|
|
}
|
|
#endif
|
|
|
|
if ((iSize = (int)(pbEnd - pbStart)) > 0)
|
|
{
|
|
/* Left some dangling. This can only be literal data. */
|
|
|
|
while( (iSend = min( iSize, TIFF_MAX_LITERAL )) > 0 )
|
|
{
|
|
*pbOut++ = iSend - 1;
|
|
CopyMemory( pbOut, pbStart, iSend );
|
|
pbOut += iSend;
|
|
pbStart += iSend;
|
|
iSize -= iSend;
|
|
}
|
|
}
|
|
|
|
return (int)(pbOut - pbOBuf);
|
|
}
|
|
//**********************************************************
|
|
int
|
|
iCompFERLE(
|
|
BYTE *pbOBuf,
|
|
BYTE *pbIBuf,
|
|
int iBCnt,
|
|
int iMaxCnt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to compress a scan line of data using
|
|
Far East Run length encoding.
|
|
|
|
Arguments:
|
|
|
|
pbOBuf Pointer to output buffer PRESUMED LARGE ENOUGH
|
|
pbIBuf Pointer to data buffer to compress
|
|
iBCnt Number of bytes to compress
|
|
iMaxCnt Maximum number of bytes to create on output
|
|
|
|
Return Value:
|
|
|
|
Number of compressed bytes or -1 if too large for buffer
|
|
|
|
--*/
|
|
{
|
|
BYTE *pbO; /* Record output location */
|
|
BYTE *pbI; /* Scanning for runs */
|
|
BYTE *pbIEnd; /* First byte past end of input data */
|
|
BYTE *pbStart; /* Start of current data stream */
|
|
BYTE *pbTmp;
|
|
BYTE jLast; /* Previous byte */
|
|
|
|
int iSize; /* Number of bytes in the run */
|
|
|
|
if (iBCnt == 0)
|
|
return 0;
|
|
|
|
pbO = pbOBuf; /* Working copy */
|
|
pbIEnd = pbIBuf + iBCnt; /* Gone too far if we reach here */
|
|
|
|
/*
|
|
* Calculate the maximum amount of data we will generate
|
|
*/
|
|
|
|
pbStart = pbIBuf;
|
|
|
|
while (++pbIBuf < pbIEnd)
|
|
{
|
|
if (*pbIBuf == pbIBuf[-1])
|
|
{
|
|
// valid run but we will first output any literal data
|
|
if ((iSize = (int)(pbIBuf - pbStart) - 1) > 0)
|
|
{
|
|
if ((iMaxCnt -= iSize) < 0) // test for output overflow
|
|
return -1;
|
|
CopyMemory(pbO,pbStart,iSize);
|
|
pbO += iSize;
|
|
}
|
|
|
|
// determine the run length
|
|
jLast = *pbIBuf;
|
|
pbI = pbIBuf;
|
|
pbTmp = pbIBuf + FERLE_MAX_RUN - 1;
|
|
if (pbTmp > pbIEnd)
|
|
pbTmp = pbIEnd;
|
|
do {
|
|
pbIBuf++;
|
|
} while (pbIBuf < pbTmp && *pbIBuf == jLast);
|
|
|
|
iSize = (int)(pbIBuf - pbI) + 1; /* Number of times */
|
|
|
|
// output the RLE strings
|
|
if ((iMaxCnt -= 3) < 0) // test for output overflow
|
|
return -1;
|
|
*pbO++ = jLast; // copy data byte twice
|
|
*pbO++ = jLast;
|
|
*pbO++ = (BYTE)iSize;
|
|
|
|
// test if we are done
|
|
if( pbIBuf == pbIEnd )
|
|
return (int)(pbO - pbOBuf);
|
|
|
|
// setup for continuation of loop
|
|
pbStart = pbIBuf;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Since the data did not end in a run we must output the last
|
|
* literal data if we haven't overflowed the buffer.
|
|
*/
|
|
iSize = (int)(pbIBuf - pbStart);
|
|
|
|
if (iMaxCnt < iSize)
|
|
return -1;
|
|
|
|
CopyMemory(pbO,pbStart,iSize);
|
|
pbO += iSize;
|
|
return (int)(pbO - pbOBuf);
|
|
}
|
|
|
|
//****************************************************
|
|
int
|
|
iCompDeltaRow(
|
|
BYTE *pbOBuf,
|
|
BYTE *pbIBuf,
|
|
BYTE *pbPBuf,
|
|
int iBCnt,
|
|
int iLimit
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to compress a scan line of data using
|
|
delta row compression.
|
|
|
|
Arguments:
|
|
|
|
pbOBuf Pointer to output buffer
|
|
pbIBuf Pointer to data buffer to compress
|
|
pbPBuf Pointer to previous row data buffer
|
|
iBCnt Number of bytes in the above
|
|
iLimit Don't exceed this number of compressed bytes
|
|
|
|
Return Value:
|
|
|
|
Number of compressed bytes or -1 if too large for buffer
|
|
|
|
Note:
|
|
A return value of 0 is valid since it implies the two lines
|
|
are identical.
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef _X86_
|
|
BYTE *pbO; /* Record output location */
|
|
BYTE *pbOEnd; /* As far as we will go in the output buffer */
|
|
BYTE *pbIEnd;
|
|
BYTE *pbStart;
|
|
BYTE *pb;
|
|
int iDelta;
|
|
int iOffset; // index of current data stream
|
|
int iSize; /* Number of bytes in the run */
|
|
|
|
/*
|
|
* Limit the amount of data we will generate. For performance
|
|
* reasons we will ignore the effects of an offset value
|
|
* greater than 30 since it implies we were able to already skip
|
|
* that many bytes. However, for safety sake we will reduce the
|
|
* max allowable size by 2 bytes.
|
|
*/
|
|
pbO = pbOBuf; /* Working copy */
|
|
pbOEnd = pbOBuf + iLimit - 2;
|
|
iDelta = pbPBuf - pbIBuf;
|
|
pbIEnd = pbIBuf + iBCnt;
|
|
pbStart = pbIBuf;
|
|
|
|
//
|
|
// this is the main loop for compressing the data
|
|
//
|
|
while (pbIBuf < pbIEnd)
|
|
{
|
|
// fast skip for matching dwords
|
|
//
|
|
if (!((ULONG_PTR)pbIBuf & 3))
|
|
{
|
|
while (pbIBuf <= (pbIEnd-4) && *(DWORD *)pbIBuf == *(DWORD *)&pbIBuf[iDelta])
|
|
pbIBuf += 4;
|
|
if (pbIBuf >= pbIEnd)
|
|
break;
|
|
}
|
|
// test for non-matching bytes and output the necessary compression string
|
|
//
|
|
if (*pbIBuf != pbIBuf[iDelta])
|
|
{
|
|
// determine the run length
|
|
pb = pbIBuf;
|
|
do {
|
|
pb++;
|
|
} while (pb < pbIEnd && *pb != pb[iDelta]);
|
|
|
|
iSize = (int)(pb - pbIBuf);
|
|
|
|
// Lets make sure we have room in the buffer before
|
|
// we continue this, this compression algorithm adds
|
|
// 1 byte for every 8 bytes of data worst case.
|
|
//
|
|
if (((iSize * 9 + 7) >> 3) > (pbOEnd - pbO)) // gives tighter code
|
|
return -1;
|
|
iOffset = (int)(pbIBuf - pbStart);
|
|
if (iOffset > 30)
|
|
{
|
|
if (iSize < 8)
|
|
*pbO++ = ((iSize-1) << 5) + 31;
|
|
else
|
|
*pbO++ = (7 << 5) + 31;
|
|
iOffset -= 31;
|
|
while (iOffset >= 255)
|
|
{
|
|
iOffset -= 255;
|
|
*pbO++ = 255;
|
|
}
|
|
*pbO++ = (BYTE)iOffset;
|
|
if (iSize > 8)
|
|
goto FastEightByteRun;
|
|
}
|
|
else if (iSize > 8)
|
|
{
|
|
*pbO++ = (7 << 5) + iOffset;
|
|
FastEightByteRun:
|
|
while (1)
|
|
{
|
|
CopyMemory(pbO,pbIBuf,8);
|
|
pbIBuf += 8;
|
|
pbO += 8;
|
|
if ((iSize -= 8) <= 8)
|
|
break;
|
|
*pbO++ = (7 << 5);
|
|
}
|
|
*pbO++ = (iSize-1) << 5;
|
|
}
|
|
else
|
|
*pbO++ = ((iSize-1) << 5) + iOffset;
|
|
|
|
CopyMemory (pbO,pbIBuf,iSize);
|
|
pbIBuf += iSize;
|
|
pbO += iSize;
|
|
pbStart = pbIBuf;
|
|
}
|
|
pbIBuf++;
|
|
}
|
|
return (int)(pbO - pbOBuf);
|
|
#else
|
|
BYTE *pbO; /* Record output location */
|
|
BYTE *pbOEnd; /* As far as we will go in the output buffer */
|
|
BYTE *pbIEnd;
|
|
BYTE *pbStart;
|
|
BYTE *pb;
|
|
int iOffset; // index of current data stream
|
|
int iSize; /* Number of bytes in the run */
|
|
|
|
/*
|
|
* Limit the amount of data we will generate. For performance
|
|
* reasons we will ignore the effects of an offset value
|
|
* greater than 30 since it implies we were able to already skip
|
|
* that many bytes. However, for safety sake we will reduce the
|
|
* max allowable size by 2 bytes.
|
|
*/
|
|
pbO = pbOBuf; /* Working copy */
|
|
pbOEnd = pbOBuf + iLimit - 2;
|
|
pbIEnd = pbIBuf + iBCnt;
|
|
pbStart = pbIBuf;
|
|
|
|
//
|
|
// this is the main loop for compressing the data
|
|
//
|
|
while (pbIBuf < pbIEnd)
|
|
{
|
|
// fast skip for matching dwords
|
|
//
|
|
if (!((ULONG_PTR)pbIBuf & 3))
|
|
{
|
|
while (pbIBuf <= (pbIEnd-4) && *(DWORD *)pbIBuf == *(DWORD *)pbPBuf)
|
|
{
|
|
pbIBuf += 4;
|
|
pbPBuf += 4;
|
|
}
|
|
if (pbIBuf >= pbIEnd)
|
|
break;
|
|
}
|
|
// test for non-matching bytes and output the necessary compression string
|
|
//
|
|
if (*pbIBuf != *pbPBuf)
|
|
{
|
|
// determine the run length
|
|
pb = pbIBuf;
|
|
do {
|
|
pb++;
|
|
pbPBuf++;
|
|
} while (pb < pbIEnd && *pb != *pbPBuf);
|
|
|
|
iSize = (int)(pb - pbIBuf);
|
|
|
|
// Lets make sure we have room in the buffer before
|
|
// we continue this, this compression algorithm adds
|
|
// 1 byte for every 8 bytes of data worst case.
|
|
//
|
|
if (((iSize * 9 + 7) >> 3) > (int)(pbOEnd - pbO))
|
|
return -1;
|
|
|
|
// special case the initial offset value since it
|
|
// occurs only once and may require extra bytes
|
|
//
|
|
if ((iOffset = (int)(pbIBuf - pbStart)))
|
|
{
|
|
int iSend = min (iSize,8);
|
|
if (iOffset > 30)
|
|
{
|
|
*pbO++ = ((iSend-1) << 5) + 31;
|
|
iOffset -= 31;
|
|
while (iOffset >= 255)
|
|
{
|
|
*pbO++ = 255;
|
|
iOffset -= 255;
|
|
}
|
|
*pbO++ = (BYTE)iOffset;
|
|
}
|
|
else
|
|
{
|
|
*pbO++ = ((iSend-1) << 5) + iOffset;
|
|
}
|
|
// output the initial changed bytes
|
|
CopyMemory(pbO,pbIBuf,iSend);
|
|
pbIBuf += iSend;
|
|
pbO += iSend;
|
|
iSize -= iSend;
|
|
}
|
|
|
|
// now output any remaining changed data
|
|
//
|
|
while (iSize)
|
|
{
|
|
if (iSize >= 8)
|
|
{
|
|
*pbO++ = (8 - 1) << 5;
|
|
CopyMemory(pbO,pbIBuf,8);
|
|
pbIBuf += 8;
|
|
pbO += 8;
|
|
iSize -= 8;
|
|
}
|
|
else
|
|
{
|
|
*pbO++ = (iSize-1) << 5;
|
|
CopyMemory(pbO,pbIBuf,iSize);
|
|
pbIBuf += iSize;
|
|
pbO += iSize;
|
|
break;
|
|
}
|
|
}
|
|
pbStart = pbIBuf;
|
|
}
|
|
pbIBuf++;
|
|
pbPBuf++;
|
|
}
|
|
return (int)(pbO - pbOBuf);
|
|
#endif
|
|
}
|