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.
2304 lines
68 KiB
2304 lines
68 KiB
/*----------------------------------------------------------------------+
|
|
| decmprss.c - Microsoft Video 1 Compressor - decompress code |
|
|
| |
|
|
| |
|
|
| Copyright (c) 1990-1994 Microsoft Corporation. |
|
|
| Portions Copyright Media Vision Inc. |
|
|
| All Rights Reserved. |
|
|
| |
|
|
| You have a non-exclusive, worldwide, royalty-free, and perpetual |
|
|
| license to use this source code in developing hardware, software |
|
|
| (limited to drivers and other software required for hardware |
|
|
| functionality), and firmware for video display and/or processing |
|
|
| boards. Microsoft makes no warranties, express or implied, with |
|
|
| respect to the Video 1 codec, including without limitation warranties |
|
|
| of merchantability or fitness for a particular purpose. Microsoft |
|
|
| shall not be liable for any damages whatsoever, including without |
|
|
| limitation consequential damages arising from your use of the Video 1 |
|
|
| codec. |
|
|
| |
|
|
| |
|
|
+----------------------------------------------------------------------*/
|
|
#ifdef _WIN32
|
|
//#ifdef DEBUG DEBUG is not defined on NT until win32.h is included...
|
|
// Always define here so that the ntrtl headers get included
|
|
#ifndef CHICAGO
|
|
#if DBG
|
|
// We only want this stuff in the debug build
|
|
#define MEASURE_PERFORMANCE
|
|
#endif
|
|
#endif
|
|
//#endif
|
|
#endif
|
|
|
|
#ifdef MEASURE_PERFORMANCE // Displays frame decompress times on the debugger
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <win32.h>
|
|
#include "msvidc.h"
|
|
|
|
#ifdef DEBUG
|
|
#undef INLINE // Make debugging easier - less code movement
|
|
#define INLINE
|
|
#else
|
|
#undef MEASURE_PERFORMANCE // Turn it off for non debug builds
|
|
#endif
|
|
|
|
#ifdef MEASURE_PERFORMANCE
|
|
|
|
STATICDT LARGE_INTEGER PC1; /* current counter value */
|
|
STATICDT LARGE_INTEGER PC2; /* current counter value */
|
|
STATICDT LARGE_INTEGER PC3; /* current counter value */
|
|
|
|
STATICFN VOID StartCounting(VOID)
|
|
{
|
|
QueryPerformanceCounter(&PC1);
|
|
return;
|
|
}
|
|
|
|
STATICFN VOID EndCounting(LPSTR szId)
|
|
{
|
|
QueryPerformanceCounter(&PC2);
|
|
PC3.QuadPart = PC2.QuadPart - PC1.QuadPart;
|
|
DPF(("%s: %d ticks", szId, PC3.LowPart));
|
|
return;
|
|
}
|
|
|
|
#else
|
|
#define StartCounting()
|
|
#define EndCounting(x)
|
|
|
|
#endif
|
|
|
|
/*
|
|
* dither table pointers declared and initialised in msvidc.c
|
|
*/
|
|
extern LPVOID lpDitherTable;
|
|
|
|
/*
|
|
* these two pointers point into the lpDitherTable
|
|
*/
|
|
LPBYTE lpLookup;
|
|
LPWORD lpScale;
|
|
|
|
/*
|
|
** Lookup table for expanding 4 bits into 4 bytes
|
|
*/
|
|
CONST DWORD ExpansionTable[16] = {
|
|
0x00000000,
|
|
0x000000FF,
|
|
0x0000FF00,
|
|
0x0000FFFF,
|
|
0x00FF0000,
|
|
0x00FF00FF,
|
|
0x00FFFF00,
|
|
0x00FFFFFF,
|
|
0xFF000000,
|
|
0xFF0000FF,
|
|
0xFF00FF00,
|
|
0xFF00FFFF,
|
|
0xFFFF0000,
|
|
0xFFFF00FF,
|
|
0xFFFFFF00,
|
|
0xFFFFFFFF
|
|
};
|
|
|
|
/*
|
|
* Lookup table to turn a bitmask to a byte mask
|
|
*/
|
|
DWORD Bits2Bytes[13] = {0, 0xffff, 0xffff0000, 0xffffffff,
|
|
0xffff, 0, 0, 0,
|
|
0xffff0000, 0, 0, 0,
|
|
0xffffffff};
|
|
|
|
//#include <limits.h>
|
|
//#include <mmsystem.h>
|
|
//#include <aviffmt.h>
|
|
|
|
#define RGB555toRGBTRIPLE( rgbT, rgb ) rgbT.rgbtRed=(BYTE)((rgb & 0x7c00) >> 7); \
|
|
rgbT.rgbtGreen=(BYTE)((rgb & 0x3e0) >>2); \
|
|
rgbT.rgbtBlue=(BYTE)((rgb & 0x1f) << 3)
|
|
|
|
static WORD edgeBitMask[HEIGHT_CBLOCK*WIDTH_CBLOCK] = {
|
|
0x0001,0x0002,0x0010,0x0020,
|
|
0x0004,0x0008,0x0040,0x0080,
|
|
0x0100,0x0200,0x1000,0x2000,
|
|
0x0400,0x0800,0x4000,0x8000
|
|
};
|
|
|
|
/* make a DWORD that has four copies of the byte x */
|
|
#define MAKE4(x) ( (x << 24) | (x << 16) | (x << 8) | x)
|
|
|
|
/* make a DWORD that has two copies of the byte x (low word) and two of y */
|
|
#define MAKE22(x, y) ( (y << 24) | (y << 16) | (x << 8) | (x))
|
|
|
|
/**************************************************************************
|
|
compute a pointer into a DIB handling correctly "upside" down DIBs
|
|
***************************************************************************/
|
|
STATICFN LPVOID DibXY(LPBITMAPINFOHEADER lpbi, LPBYTE lpBits, LONG x, LONG y, INT FAR *pWidthBytes)
|
|
{
|
|
int WidthBytes;
|
|
|
|
if (x > 0)
|
|
((BYTE FAR *)lpBits) += ((int)x * (int)lpbi->biBitCount) >> 3;
|
|
|
|
WidthBytes = (((((int)lpbi->biWidth * (int)lpbi->biBitCount) >> 3) + 3)&~3);
|
|
|
|
if (lpbi->biHeight < 0)
|
|
{
|
|
WidthBytes = -WidthBytes;
|
|
((BYTE _huge *)lpBits) += lpbi->biSizeImage + WidthBytes;
|
|
}
|
|
|
|
if (y > 0)
|
|
((BYTE _huge *)lpBits) += ((long)y * WidthBytes);
|
|
|
|
if (pWidthBytes)
|
|
*pWidthBytes = WidthBytes;
|
|
|
|
return lpBits;
|
|
}
|
|
|
|
/*
|
|
* 16-bit decompression to 24-bit RGB--------------------------------------
|
|
*/
|
|
|
|
/*************************************************
|
|
purp: decompress a 4 by 4 compression block to RGBDWORD
|
|
entry: uncmp == address of the destination uncompressed image
|
|
cmp == address of the compressed image
|
|
exit: returns updated address of the compressed image
|
|
and 16 pixels are generated
|
|
*************************************************/
|
|
|
|
// note that the skip count is now stored in the parent stack frame
|
|
// and passed as a pointer pSkipCount. This ensures that we are multithread
|
|
// safe.
|
|
|
|
STATICFN HPWORD INLINE DecompressCBlockToRGBTRIPLE(
|
|
HPRGBTRIPLE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG FAR * pSkipCount
|
|
)
|
|
{
|
|
UINT by;
|
|
UINT bx;
|
|
UINT y;
|
|
UINT x;
|
|
WORD mask;
|
|
WORD color0;
|
|
WORD color1;
|
|
WORD bitMask;
|
|
RGBTRIPLE rgbTriple0;
|
|
RGBTRIPLE rgbTriple1;
|
|
HPRGBTRIPLE row;
|
|
HPRGBTRIPLE blockRow;
|
|
HPRGBTRIPLE blockColumn;
|
|
WORD *pEdgeBitMask;
|
|
|
|
|
|
// check for outstanding skips
|
|
|
|
if (*pSkipCount > 0)
|
|
{
|
|
// NOT YET IMPLEMENTED Assert(!"Skip count should be handled by caller");
|
|
(*pSkipCount) --;
|
|
return cmp;
|
|
}
|
|
|
|
// get mask and init bit mask
|
|
mask = *cmp++;
|
|
|
|
// check for a skip or a solid color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
else
|
|
{
|
|
// solid color
|
|
RGB555toRGBTRIPLE( rgbTriple1, mask );
|
|
for( row = uncmp,y=0; y < HEIGHT_CBLOCK; y++, row = NEXT_RGBT_PIXEL_ROW( row, bytesPerRow ) )
|
|
for( x=0; x < WIDTH_CBLOCK; x++ )
|
|
row[x] = rgbTriple1;
|
|
|
|
return cmp;
|
|
}
|
|
}
|
|
|
|
bitMask = 1;
|
|
pEdgeBitMask = edgeBitMask;
|
|
if( (*cmp & 0x8000) != 0 )
|
|
{ // this is an edge with 4 color pairs in four small blocks
|
|
blockRow = uncmp;
|
|
for( by=0; by < 2; by++, blockRow = NEXT_BLOCK_ROW( blockRow, bytesPerRow, EDGE_HEIGHT_CBLOCK ) )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( bx=0; bx < 2; bx++, blockColumn += EDGE_WIDTH_CBLOCK )
|
|
{
|
|
color1 = *cmp++;
|
|
RGB555toRGBTRIPLE( rgbTriple1, color1 );
|
|
color0 = *cmp++;
|
|
RGB555toRGBTRIPLE( rgbTriple0, color0 );
|
|
row = blockColumn;
|
|
for( y=0; y < EDGE_HEIGHT_CBLOCK; y++, row = NEXT_RGBT_PIXEL_ROW( row, bytesPerRow ) )
|
|
{
|
|
for( x=0; x < EDGE_WIDTH_CBLOCK; x++ )
|
|
{
|
|
if( (mask & *pEdgeBitMask++ ) != 0 )
|
|
row[x] = rgbTriple1;
|
|
else
|
|
row[x] = rgbTriple0;
|
|
bitMask <<= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // not an edge with only 1 colour pair and one large block
|
|
color1 = *cmp++;
|
|
RGB555toRGBTRIPLE( rgbTriple1, color1 );
|
|
color0 = *cmp++;
|
|
RGB555toRGBTRIPLE( rgbTriple0, color0 );
|
|
row = uncmp;
|
|
for( y=0; y < HEIGHT_CBLOCK; y++, row = NEXT_RGBT_PIXEL_ROW( row, bytesPerRow ) )
|
|
{
|
|
for( x=0; x < WIDTH_CBLOCK; x++ )
|
|
{
|
|
if( (mask & bitMask ) != 0 )
|
|
row[x] = rgbTriple1;
|
|
else
|
|
row[x] = rgbTriple0;
|
|
bitMask <<= 1;
|
|
}
|
|
}
|
|
}
|
|
return( cmp );
|
|
}
|
|
|
|
|
|
/*************************************************
|
|
purp: decompress the image to RGBTRIPLE
|
|
entry: lpinst = pointer to instance data
|
|
hpCompressed = pointer to compressed data
|
|
exit: returns number of bytes in the uncompressed image
|
|
lpinst->hDib = handle to the uncompressed image
|
|
*************************************************/
|
|
|
|
DWORD FAR PASCAL DecompressFrame24(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
int bix;
|
|
int biy;
|
|
HPRGBTRIPLE blockRow;
|
|
HPRGBTRIPLE blockColumn;
|
|
int bytesPerRow;
|
|
DWORD actualSize;
|
|
LONG SkipCount = 0;
|
|
|
|
DPF(("DecompressFrame24:\n"));
|
|
bix = (UINT)((UINT)lpbiIn->biWidth / WIDTH_CBLOCK);
|
|
biy = (UINT)((UINT)lpbiIn->biHeight / HEIGHT_CBLOCK);
|
|
|
|
StartCounting();
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
|
|
for( y=0; y < biy; y++, blockRow = NEXT_BLOCK_ROW( blockRow, bytesPerRow, HEIGHT_CBLOCK ) )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( x=0; x < bix; x++, blockColumn += WIDTH_CBLOCK )
|
|
{
|
|
cmp = DecompressCBlockToRGBTRIPLE( blockColumn, cmp, bytesPerRow, &SkipCount);
|
|
}
|
|
}
|
|
|
|
actualSize = bytesPerRow*biy*HEIGHT_CBLOCK;
|
|
EndCounting("Decompress frame24 took");
|
|
return( actualSize );
|
|
}
|
|
|
|
/*************************************************
|
|
*************************************************/
|
|
/*
|
|
* -------- 8-bit decompression ----------------------------------------
|
|
*
|
|
*
|
|
* The input stream consists of four cases, handled like this:
|
|
*
|
|
* SKIP lower 10 bits have skip count
|
|
* Return the skip count to the caller (must be multi-thread safe).
|
|
* Caller will advance the source pointer past the correct number of
|
|
* of skipped cells.
|
|
*
|
|
* SOLID lower 8 bits is solid colour for entire cell
|
|
* Write the colour to each pixel, four pixels (one DWORD) at
|
|
* a time.
|
|
*
|
|
* Mask + 2 colours
|
|
* 1s in the mask represent the first colour, 0s the second colour.
|
|
* Pixels are represented thus:
|
|
*
|
|
* C D E F
|
|
* 8 9 A B
|
|
* 4 5 6 7
|
|
* 0 1 2 3
|
|
*
|
|
* To write four pixels at once, we rely on the fact that:
|
|
* (a ^ b) ^ a == b
|
|
* and also that a ^ 0 == a.
|
|
* We create a DWORD (Czero) containing four copies of the colour 0, and
|
|
* another DWORD (Cxor) containing four copies of (colour 0 ^ colour 1).
|
|
* Then we convert each bit in the mask (1 or 0) into a byte (0xff or 0),
|
|
* and combining four mask bytes into a DWORD. Then we can select
|
|
* four pixels at once (AND the mask with Czero and then XOR with Cxor).
|
|
*
|
|
* Mask + 8 colours.
|
|
* 1s and 0s represent two colours as before, but the cell is divided
|
|
* into 4 subcells with two colours per subcell. The first pair of
|
|
* colours are for subcell 0145, then 2367, 89cd and abef.
|
|
*
|
|
* We use the same algorithm as for the mask+2 case except that when
|
|
* making the mask, we need colours from the second pair in the top
|
|
* two bytes of Czero and Cxor, and that we need to change colours
|
|
* again after two rows.
|
|
*
|
|
* -----------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* DecompressCBlockTo8
|
|
*
|
|
*
|
|
* decompress one cell to 16 8-bit pixels.
|
|
*
|
|
* parameters:
|
|
* uncmp- pointer to de-compressed buffer for this block.
|
|
* cmp - pointer to compressed data for this block
|
|
* bytes.. - size of one row of de-compressed data
|
|
* pSkipCount - place to return the skipcount if non-zero.
|
|
*
|
|
* returns:
|
|
* pointer to the next block of compressed data to use.
|
|
*/
|
|
STATICFN HPWORD INLINE DecompressCBlockTo8(
|
|
HPBYTE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG FAR * pSkipCount
|
|
)
|
|
{
|
|
UINT y;
|
|
WORD mask;
|
|
BYTE b0,b1;
|
|
HPBYTE row;
|
|
BYTE b2, b3;
|
|
DWORD Czero, Cxor;
|
|
DWORD dwBytes;
|
|
|
|
|
|
// skip counts should be handled by caller
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount == 0);
|
|
#endif
|
|
|
|
/* first word is the escape word or bit mask */
|
|
mask = *cmp++;
|
|
|
|
/*
|
|
* is this an escape ?
|
|
*/
|
|
if (mask & 0x8000)
|
|
{
|
|
|
|
/* yes - this is either a SKIP code, a solid colour, or an edge
|
|
* cell (mask + 8 colours).
|
|
*/
|
|
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--; // the current cell
|
|
return cmp;
|
|
}
|
|
else if ((mask & ~SKIP_MASK) == SOLID_MAGIC)
|
|
{
|
|
// solid color
|
|
DWORD dw;
|
|
|
|
//b0 = LOBYTE(mask);
|
|
//dw = b0 | b0<<8 | b0<<16 | b0<<24;
|
|
dw = LOBYTE(mask);
|
|
dw = MAKE4(dw);
|
|
|
|
#ifdef _WIN32
|
|
Assert(HEIGHT_CBLOCK == 4); // If this ever changes...
|
|
Assert(WIDTH_CBLOCK == 4);
|
|
#endif
|
|
|
|
for(y = 0, row = uncmp; y < HEIGHT_CBLOCK;y++, row+= bytesPerRow) {
|
|
|
|
// We know we will iterate 4 times (WIDTH_CBLOCK) storing
|
|
// 4 bytes of colour b0 in 4 adjacent rows
|
|
*(DWORD UNALIGNED HUGE *)row = dw;
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
else // this is an edge with 4 color pairs in four small blocks
|
|
{
|
|
|
|
/* read 4 colours, and make AND and XOR masks */
|
|
b0 = *((LPBYTE)cmp)++;
|
|
b1 = *((LPBYTE)cmp)++;
|
|
b2 = *((LPBYTE)cmp)++;
|
|
b3 = *((LPBYTE)cmp)++;
|
|
Czero = MAKE22(b1, b3);
|
|
Cxor = Czero ^ MAKE22(b0, b2);
|
|
|
|
row = uncmp;
|
|
|
|
/* first two rows - top two subcells */
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* turn bitmask into byte mask */
|
|
dwBytes = ExpansionTable[mask & 0x0f];
|
|
|
|
/* select colours and write to dest */
|
|
*( (DWORD UNALIGNED HUGE *)row) = (dwBytes & Cxor) ^ Czero;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
|
|
/* second two rows - bottom two subcells */
|
|
|
|
/* read last four colours and make masks */
|
|
b0 = *((LPBYTE)cmp)++;
|
|
b1 = *((LPBYTE)cmp)++;
|
|
b2 = *((LPBYTE)cmp)++;
|
|
b3 = *((LPBYTE)cmp)++;
|
|
Czero = MAKE22(b1, b3);
|
|
Cxor = Czero ^ MAKE22(b0, b2);
|
|
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* turn bitmask into byte mask */
|
|
dwBytes = ExpansionTable[mask & 0x0f];
|
|
|
|
/* select both colours and write to dest */
|
|
*( (DWORD UNALIGNED HUGE *)row) = (dwBytes & Cxor) ^ Czero;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
}
|
|
}
|
|
else // not an edge with only 1 colour pair and one large block
|
|
{
|
|
/* use and, xor to map several colours at once.
|
|
* relies on (Czero ^ Cone) ^ Czero == Cone and Czero ^ 0 == Czero.
|
|
*/
|
|
|
|
|
|
/* read colours */
|
|
b1 = *((LPBYTE)cmp)++;
|
|
b0 = *((LPBYTE)cmp)++;
|
|
row = uncmp;
|
|
|
|
/* make two DWORDs, one with four copies of colour 0, and one
|
|
* with four copies of (b0 ^ b1).
|
|
*/
|
|
Czero = MAKE4(b0);
|
|
Cxor = Czero ^ MAKE4(b1);
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
|
|
/* turn bitmask into byte mask */
|
|
dwBytes = ExpansionTable[mask & 0x0f];
|
|
|
|
/* select both colours and write to dest */
|
|
*( (DWORD UNALIGNED HUGE *)row) = (dwBytes & Cxor) ^ Czero;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
}
|
|
return( cmp );
|
|
}
|
|
|
|
/*************************************************
|
|
*************************************************/
|
|
|
|
/*
|
|
* decompress a CRAM-8 DIB to an 8-bit DIB
|
|
*
|
|
* Loop calling DecompressCBlockTo8 for each cell in the input
|
|
* stream. This writes a block of 16 pixels and returns us the
|
|
* pointer for the next block.
|
|
*/
|
|
DWORD FAR PASCAL DecompressFrame8(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
int bix;
|
|
int biy;
|
|
HPBYTE blockRow;
|
|
HPBYTE blockColumn;
|
|
LONG SkipCount8 = 0; // multithread-safe - cannot be static
|
|
|
|
int bytesPerRow;
|
|
|
|
DPF(("DecompressFrame8:\n"));
|
|
bix = (int)((UINT)lpbiIn->biWidth / WIDTH_CBLOCK);
|
|
biy = (int)((UINT)lpbiIn->biHeight / HEIGHT_CBLOCK);
|
|
|
|
StartCounting();
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
for( y=biy; y--; blockRow += bytesPerRow * HEIGHT_CBLOCK )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( x=bix; x--; blockColumn += WIDTH_CBLOCK )
|
|
{
|
|
cmp = DecompressCBlockTo8(blockColumn, cmp, bytesPerRow, &SkipCount8);
|
|
|
|
// See if the SkipCount has been set. If so we want to move to
|
|
// the next location rather than calling DecompressCBlock every
|
|
// time around the loop. Keep the test simple to minimise the
|
|
// overhead on every iteration that the Skipcount is 0.
|
|
if (SkipCount8) {
|
|
|
|
if ((x -= SkipCount8) <0) { // extends past this row
|
|
LONG SkipRows;
|
|
|
|
// More than just the remainder of this row to skip
|
|
SkipCount8 =-x; // These bits are on the next row(s)
|
|
// SkipCount8 will be >0 otherwise we would have gone
|
|
// down the else leg.
|
|
|
|
// Calculate how many complete and partial rows to skip.
|
|
// We know we have skipped at least one row. The plan
|
|
// is to restart the X loop at some point along the row.
|
|
// If the skipcount takes us exactly to the end of a row
|
|
// we drop out of the x loop, and let the outer y loop do
|
|
// the decrement. This takes care of the case when the
|
|
// skipcount takes us to the very end of the image.
|
|
|
|
SkipRows = 1 + (SkipCount8-1)/bix;
|
|
|
|
// Decrement the row count and set new blockrow start
|
|
|
|
#ifdef _WIN32
|
|
if (y<SkipRows) {
|
|
Assert(y >= SkipRows);
|
|
SkipRows = y;
|
|
}
|
|
#endif
|
|
|
|
// Unless we have finished we need to reset blockRow
|
|
y -= SkipRows;
|
|
// y might be 0, but we must still complete the last row
|
|
blockRow += bytesPerRow*HEIGHT_CBLOCK*SkipRows;
|
|
|
|
// Calculate the offset into the next row we will process
|
|
x = SkipCount8%bix; // This may be 0
|
|
|
|
if (x) {
|
|
|
|
// Set block column by the amount along the row
|
|
// this iteration is starting, making allowance for
|
|
// the "for x..." loop iterating blockColumn once.
|
|
blockColumn = blockRow + ((x-1)*WIDTH_CBLOCK);
|
|
|
|
x=bix-x; // Get the counter correct
|
|
}
|
|
|
|
SkipCount8 = 0; // Skip count now exhausted (so am I)
|
|
|
|
} else {
|
|
// SkipCount has been exhausted by this row
|
|
// Either the row has completed, or there is more data
|
|
// on this row. Check...
|
|
if (x) {
|
|
// More of this row left
|
|
// Worry about moving blockColumn on the right amount
|
|
blockColumn += WIDTH_CBLOCK*SkipCount8;
|
|
} // else x==0 and we will drop out of the "for x..." loop
|
|
// blockColumn will be reset when we reenter the x loop
|
|
SkipCount8=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EndCounting("Decompress 8bit took");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
/* ---- 8-bit X2 decompress - in asm for Win16 ---------------------------*/
|
|
|
|
/*
|
|
* decompress one block, stretching by 2.
|
|
*
|
|
* parameters:
|
|
* uncmp- pointer to de-compressed buffer for this block.
|
|
* cmp - pointer to compressed data for this block
|
|
* bytes.. - size of one row of de-compressed data
|
|
*
|
|
* returns:
|
|
* pointer to the next block of compressed data.
|
|
*
|
|
* Given same incoming data, write a block of four pixels for every
|
|
* pixel in original compressed image. Uses same techniques as
|
|
* unstretched routine, masking and writing four pixels (one dword)
|
|
* at a time.
|
|
*
|
|
* Stretching by 2 is done by simple pixel duplication.
|
|
* Experiments were done (x86) to only store every other line, then to use
|
|
* memcpy to fill in the gaps. This is slower than writing two identical
|
|
* lines as you proceed.
|
|
*
|
|
* Skip counts are returned (via pSkipCount) to the caller, who will handle
|
|
* advancing the source pointer accordingly.
|
|
*
|
|
*/
|
|
|
|
STATICFN HPWORD INLINE DecompressCBlockTo8X2(
|
|
HPBYTE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG FAR * pSkipCount)
|
|
{
|
|
UINT y;
|
|
UINT dx, dy;
|
|
WORD mask;
|
|
BYTE b0,b1;
|
|
HPBYTE row;
|
|
DWORD Czero, Cxor, dwBytes;
|
|
DWORD Ctwo, Cxor2;
|
|
|
|
// skip counts should be handled by caller
|
|
#ifdef _WIN32
|
|
Assert (*pSkipCount == 0);
|
|
#endif
|
|
|
|
// get mask and init bit mask
|
|
mask = *cmp++;
|
|
|
|
// check for a skip or a solid color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
else if ((mask & ~SKIP_MASK) == SOLID_MAGIC)
|
|
{
|
|
// solid color
|
|
DWORD dw;
|
|
|
|
//b0 = LOBYTE(mask);
|
|
//dw = b0 | b0<<8 | b0<<16 | b0<<24;
|
|
dw = LOBYTE(mask);
|
|
dw = MAKE4(dw);
|
|
|
|
#ifdef _WIN32
|
|
Assert(HEIGHT_CBLOCK == 4); // If this ever changes...
|
|
Assert(WIDTH_CBLOCK == 4);
|
|
#endif
|
|
|
|
dx = WIDTH_CBLOCK * 2;
|
|
dy = HEIGHT_CBLOCK * 2;
|
|
for(row = uncmp; dy--; row+= bytesPerRow) {
|
|
|
|
// We know we will iterate 8 times (dx) value storing
|
|
// 4 bytes of colour b0 in eight adjacent rows
|
|
*(DWORD UNALIGNED HUGE *)row = dw;
|
|
*((DWORD UNALIGNED HUGE *)row+1) = dw;
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
else // this is an edge with 4 color pairs in four small blocks
|
|
{
|
|
/* read 2 colours, and make AND and XOR masks for first subcell*/
|
|
b0 = *((LPBYTE)cmp)++;
|
|
b1 = *((LPBYTE)cmp)++;
|
|
Czero = MAKE4(b1);
|
|
Cxor = Czero ^ MAKE4(b0);
|
|
|
|
/* colour masks for second subcell */
|
|
b0 = *((LPBYTE)cmp)++;
|
|
b1 = *((LPBYTE)cmp)++;
|
|
Ctwo = MAKE4(b1);
|
|
Cxor2 = Ctwo ^ MAKE4(b0);
|
|
|
|
row = uncmp;
|
|
|
|
/* first two rows - top two subcells */
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* --- first subcell (two pixels) ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
*( (DWORD UNALIGNED HUGE *)(row + bytesPerRow)) = dwBytes;
|
|
|
|
/* ---- second subcell (two pixels) --- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor2) ^ Ctwo;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD) + bytesPerRow)) = dwBytes;
|
|
|
|
row += bytesPerRow * 2;
|
|
mask >>= 4;
|
|
|
|
}
|
|
|
|
/* second two rows - bottom two subcells */
|
|
|
|
/* read 2 colours, and make AND and XOR masks for first subcell*/
|
|
b0 = *((LPBYTE)cmp)++;
|
|
b1 = *((LPBYTE)cmp)++;
|
|
Czero = MAKE4(b1);
|
|
Cxor = Czero ^ MAKE4(b0);
|
|
|
|
/* colour masks for second subcell */
|
|
b0 = *((LPBYTE)cmp)++;
|
|
b1 = *((LPBYTE)cmp)++;
|
|
Ctwo = MAKE4(b1);
|
|
Cxor2 = Ctwo ^ MAKE4(b0);
|
|
|
|
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* --- first subcell (two pixels) ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
*( (DWORD UNALIGNED HUGE *)(row + bytesPerRow)) = dwBytes;
|
|
|
|
/* ---- second subcell (two pixels) --- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor2) ^ Ctwo;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD) + bytesPerRow)) = dwBytes;
|
|
|
|
row += bytesPerRow * 2;
|
|
mask >>= 4;
|
|
|
|
}
|
|
}
|
|
}
|
|
else // not an edge with only 1 color pair and one large block
|
|
{
|
|
/* use and, xor to map several colours at once.
|
|
* relies on (Czero ^ Cone) ^ Czero == Cone and Czero ^ 0 == Czero.
|
|
*/
|
|
|
|
/* read colours */
|
|
b1 = *((LPBYTE)cmp)++;
|
|
b0 = *((LPBYTE)cmp)++;
|
|
row = uncmp;
|
|
|
|
/* make two DWORDs, one with four copies of colour 0, and one
|
|
* with four copies of (b0 ^ b1).
|
|
*/
|
|
Czero = MAKE4(b0);
|
|
Cxor = Czero ^ MAKE4(b1);
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
|
|
/* --- first two pixels in row ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
*( (DWORD UNALIGNED HUGE *)(row + bytesPerRow)) = dwBytes;
|
|
|
|
|
|
/* ---- second two pixels in row ---- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD) + bytesPerRow)) = dwBytes;
|
|
|
|
row += bytesPerRow * 2;
|
|
mask >>= 4;
|
|
}
|
|
}
|
|
return( cmp );
|
|
}
|
|
|
|
/*
|
|
* decompress one frame, stretching by 2.
|
|
*
|
|
* parameters:
|
|
* lpbiIn pointer to compressed buffer for this frame
|
|
* lpIn pointer to compressed data for this block
|
|
* lpbiOut pointer to decompressed bitmap header
|
|
* lpOut pointer to where to store the decompressed data
|
|
*
|
|
* returns:
|
|
* 0 on success
|
|
*
|
|
* Uses DecompressCBlockTo8X2 (see above) to do the decompression.
|
|
* This also returns (via a pointer to SkipCount8X2) the count of cells
|
|
* to skip. We can then move the source and target pointers on
|
|
* until the SkipCount is exhausted.
|
|
*/
|
|
|
|
DWORD FAR PASCAL DecompressFrame8X2C(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
int bix;
|
|
int biy;
|
|
HPBYTE blockRow;
|
|
HPBYTE blockColumn;
|
|
LONG SkipCount8X2 = 0;
|
|
int bytesPerRow;
|
|
|
|
|
|
DPF(("DecompressFrame8X2C:\n"));
|
|
bix = (int)(lpbiIn->biWidth) / (WIDTH_CBLOCK);
|
|
biy = (int)(lpbiIn->biHeight) / (HEIGHT_CBLOCK);
|
|
|
|
StartCounting();
|
|
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
|
|
for( y=biy; y--; blockRow += bytesPerRow * HEIGHT_CBLOCK*2 )
|
|
{
|
|
blockColumn = blockRow;
|
|
|
|
for( x=bix; x--; blockColumn += WIDTH_CBLOCK*2 )
|
|
{
|
|
cmp = DecompressCBlockTo8X2(blockColumn, cmp, bytesPerRow, &SkipCount8X2);
|
|
|
|
// See if the SkipCount has been set. If so we want to move to
|
|
// the next location rather than calling DecompressCBlock every
|
|
// time around the loop. Keep the test simple to minimise the
|
|
// overhead on every iteration that the Skipcount is 0.
|
|
if (SkipCount8X2) {
|
|
|
|
if ((x -= SkipCount8X2) <0) { // extends past this row
|
|
LONG SkipRows;
|
|
|
|
// More than just the remainder of this row to skip
|
|
SkipCount8X2 =-x; // These bits are on the next row(s)
|
|
// SkipCount8X2 will be >0 otherwise we would have gone
|
|
// down the else leg.
|
|
|
|
// Calculate how many complete and partial rows to skip.
|
|
// We know we have skipped at least one row. The plan
|
|
// is to restart the X loop at some point along the row.
|
|
// If the skipcount takes us exactly to the end of a row
|
|
// we drop out of the x loop, and let the outer y loop do
|
|
// the decrement. This takes care of the case when the
|
|
// skipcount takes us to the very end of the image.
|
|
|
|
SkipRows = 1 + (SkipCount8X2-1)/bix;
|
|
|
|
// Decrement the row count and set new blockrow start
|
|
|
|
#ifdef _WIN32
|
|
if (y<SkipRows) {
|
|
Assert(y >= SkipRows);
|
|
SkipRows = y;
|
|
}
|
|
#endif
|
|
|
|
// Unless we have finished we need to reset blockRow
|
|
y -= SkipRows;
|
|
// y might be 0, but we must still complete the last row
|
|
blockRow += bytesPerRow*HEIGHT_CBLOCK*2*SkipRows;
|
|
|
|
// Calculate the offset into the next row we will process
|
|
x = SkipCount8X2%bix; // This may be 0
|
|
|
|
if (x) {
|
|
|
|
// Set block column by the amount along the row
|
|
// this iteration is starting, making allowance for
|
|
// the "for x..." loop iterating blockColumn once.
|
|
blockColumn = blockRow + ((x-1)*WIDTH_CBLOCK*2);
|
|
|
|
x=bix-x; // Get the counter correct
|
|
}
|
|
|
|
SkipCount8X2 = 0; // Skip count now exhausted (so am I)
|
|
|
|
} else {
|
|
// SkipCount has been exhausted by this row
|
|
// Either the row has completed, or there is more data
|
|
// on this row. Check...
|
|
if (x) {
|
|
// More of this row left
|
|
// Worry about moving blockColumn on the right amount
|
|
blockColumn += WIDTH_CBLOCK*2*SkipCount8X2;
|
|
} // else x==0 and we will drop out of the "for x..." loop
|
|
// blockColumn will be reset when we reenter the x loop
|
|
SkipCount8X2=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EndCounting("Decompress and stretch 8x2 took");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* -------- 16-bit decompression ----------------------------------------
|
|
*
|
|
*
|
|
* CRAM-16 has 16-bit mask or escape code, together with 16-bit (RGB555)
|
|
* colour words. We decode to 16 bits, to 24-bits (above), and to 8 bits
|
|
* stretched 1:1 and 1:2 (this case DecompressFrame16To8X2C does
|
|
* decompression, dithering and stretching in one pass.
|
|
*
|
|
* The input stream consists of four cases:
|
|
*
|
|
* SOLID top bit set, lower 15 bits is solid colour for entire cell
|
|
* If the red element (bits 9-14) = '00001', then this is not a solid
|
|
* colour but a skip count.
|
|
* Write the colour to each pixel, two pixels (one DWORD) at
|
|
* a time.
|
|
*
|
|
* SKIP top 6 bits = 100001xxxxxxxxxx, lower 10 bits have skip count
|
|
* Store the skip count via a pointer to a variable passed by the.
|
|
* parent - this way the skip count is maintained across calls
|
|
*
|
|
* Mask + 2 colours (top bit 0, bit 15 of first colour word also 0)
|
|
* 1s in the mask represent the first colour, 0s the second colour.
|
|
* Pixels are represented thus:
|
|
*
|
|
* C D E F
|
|
* 8 9 A B
|
|
* 4 5 6 7
|
|
* 0 1 2 3
|
|
*
|
|
*
|
|
* Mask + 8 colours. (top bit 0, bit 15 of first colour word == 1)
|
|
*
|
|
* 1s and 0s represent two colours as before, but the cell is divided
|
|
* into 4 subcells with two colours per subcell. The first pair of
|
|
* colours are for subcell 0145, then 2367, 89cd and abef.
|
|
*
|
|
*
|
|
* Dithering:
|
|
*
|
|
* we use the table method from drawdib\dith775.c, and we import the
|
|
* same tables and palette by including their header file. We have a fixed
|
|
* palette in which we have 7 levels of red, 7 levels of green and 5 levels of
|
|
* blue (= 245 combinations) in a 256-colour palette. We use tables
|
|
* to quantize the colour elements to 7 levels, combine them into an 8-bit
|
|
* value and then lookup in a table that maps this combination to the actual
|
|
* palette. Before quantizing, we add on small corrections (less than one
|
|
* level) based on the x,y position of the pixel to balance the
|
|
* colour over a 4x4 pixel area: this makes the decompression slightly more
|
|
* awkward since we dither differently for any x, y position within the cell.
|
|
*
|
|
* -----------------------------------------------------------------------
|
|
*/
|
|
|
|
/* ---- 16-bit decompress to 16 bits ----------------------------------*/
|
|
|
|
|
|
/*
|
|
* decompress one 16bpp block to RGB555.
|
|
*
|
|
* parameters:
|
|
* uncmp- pointer to de-compressed buffer for this block.
|
|
* cmp - pointer to compressed data for this block
|
|
* bytes.. - size of one row of de-compressed data
|
|
* pSkipCount - outstanding count of cells to skip - set here and just stored
|
|
* in parent stack frame for multi-thread-safe continuity.
|
|
*
|
|
* returns:
|
|
* pointer to the next block of compressed data.
|
|
*
|
|
*/
|
|
|
|
STATICFN HPWORD INLINE
|
|
DecompressCBlock16To555(
|
|
HPBYTE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG FAR *pSkipCount
|
|
)
|
|
{
|
|
UINT y;
|
|
WORD mask;
|
|
WORD col0, col1;
|
|
HPBYTE row;
|
|
DWORD Czero, Cxor, Ctwo, Cxor2, dwBytes;
|
|
|
|
// check for outstanding skips
|
|
|
|
if (*pSkipCount > 0)
|
|
{
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
|
|
// get mask and init bit mask
|
|
mask = *cmp++;
|
|
|
|
// check for a skip or a solid color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
else /* must be solid colour */
|
|
{
|
|
|
|
/* write four rows of 4 2-byte pixels of col0 */
|
|
|
|
/* solid colour is lower 15 bits of mask */
|
|
col0 = mask & 0x7fff;
|
|
Czero = col0 | (col0 << 16);
|
|
|
|
for(row = uncmp, y = 0; y < HEIGHT_CBLOCK; y++, row+= bytesPerRow) {
|
|
|
|
|
|
*(DWORD UNALIGNED HUGE *)row = Czero;
|
|
*((DWORD UNALIGNED HUGE *)row+1) = Czero;
|
|
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
}
|
|
|
|
|
|
/* in 16-bit CRAM, both 4-pair and 1-pair cells have bit 15 of mask set
|
|
* to zero. We distinguish between them based on bit 15 of the first
|
|
* colour. if this is set, this is the 4-pair edge case cell.
|
|
*/
|
|
if (*cmp & 0x8000) {
|
|
// this is an edge with 4 colour pairs in four small blocks
|
|
|
|
/* read 2 colours, and make AND and XOR masks for first subcell*/
|
|
col0 = *cmp++;
|
|
col1 = *cmp++;
|
|
Czero = col1 | (col1 << 16);
|
|
Cxor = Czero ^ (col0 | (col0 << 16));
|
|
|
|
/* colour masks for second subcell */
|
|
col0 = *cmp++;
|
|
col1 = *cmp++;
|
|
Ctwo = col1 | (col1 << 16);
|
|
Cxor2 = Ctwo ^ (col0 | (col0 << 16));
|
|
|
|
|
|
row = uncmp;
|
|
|
|
/* first two rows - top two subcells */
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* --- first subcell (two pixels) ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
|
|
|
|
/* ---- second subcell (two pixels) --- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor2) ^ Ctwo;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
|
|
}
|
|
|
|
/* second two rows - bottom two subcells */
|
|
|
|
/* read 2 colours, and make AND and XOR masks for first subcell*/
|
|
col0 = *cmp++;
|
|
col1 = *cmp++;
|
|
Czero = col1 | (col1 << 16);
|
|
Cxor = Czero ^ (col0 | (col0 << 16));
|
|
|
|
/* colour masks for second subcell */
|
|
col0 = *cmp++;
|
|
col1 = *cmp++;
|
|
Ctwo = col1 | (col1 << 16);
|
|
Cxor2 = Ctwo ^ (col0 | (col0 << 16));
|
|
|
|
|
|
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* --- first subcell (two pixels) ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
|
|
|
|
/* ---- second subcell (two pixels) --- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor2) ^ Ctwo;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
|
|
} else {
|
|
// not an edge with only 1 colour pair and one large block
|
|
|
|
|
|
/* read colours */
|
|
col0 = *cmp++;
|
|
col1 = *cmp++;
|
|
Czero = col1 | (col1 << 16);
|
|
Cxor = Czero ^ (col0 | (col0 << 16));
|
|
|
|
row = uncmp;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
|
|
|
|
/* --- first two pixels in row ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
|
|
|
|
/* ---- second two pixels in row ---- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
}
|
|
return( cmp );
|
|
|
|
}
|
|
|
|
|
|
DWORD FAR PASCAL DecompressFrame16To555C(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
INT bix;
|
|
INT biy;
|
|
HPBYTE blockRow;
|
|
HPBYTE blockColumn;
|
|
LONG SkipCount = 0;
|
|
INT bytesPerRow;
|
|
|
|
DPF(("DecompressFrame16To555C:\n"));
|
|
bix = (UINT)(lpbiIn->biWidth) / (WIDTH_CBLOCK); // No negative values in
|
|
biy = (UINT)(lpbiIn->biHeight) / (HEIGHT_CBLOCK); // width or height fields
|
|
|
|
StartCounting();
|
|
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
|
|
for( y=0; y < biy; y++, blockRow += bytesPerRow * HEIGHT_CBLOCK )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( x=0; x < bix; x++, blockColumn += (WIDTH_CBLOCK * sizeof(WORD)))
|
|
{
|
|
cmp = DecompressCBlock16To555(blockColumn, cmp, bytesPerRow, &SkipCount);
|
|
}
|
|
}
|
|
|
|
EndCounting("Decompress Frame16To555C took");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// 16-bit 565 decompression
|
|
|
|
|
|
// macro to convert a 15-bit 555 colour to a 16-bit 565 colour
|
|
#define RGB555_TO_RGB565(c) (c = ( ((c & 0x7fe0) << 1) | (c & 0x1f)))
|
|
|
|
|
|
/*
|
|
* decompress one 16bpp block to RGB565.
|
|
*
|
|
* same as RGB555 but we need a colour translation between 555->565
|
|
*
|
|
* parameters:
|
|
* uncmp- pointer to de-compressed buffer for this block.
|
|
* cmp - pointer to compressed data for this block
|
|
* bytes.. - size of one row of de-compressed data
|
|
* pSkipCount - outstanding count of cells to skip - set here and just stored
|
|
* in parent stack frame for multi-thread-safe continuity.
|
|
*
|
|
* returns:
|
|
* pointer to the next block of compressed data.
|
|
*
|
|
*/
|
|
|
|
STATICFN HPWORD INLINE
|
|
DecompressCBlock16To565(
|
|
HPBYTE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG FAR * pSkipCount
|
|
)
|
|
{
|
|
UINT y;
|
|
WORD mask;
|
|
WORD col0, col1;
|
|
HPBYTE row;
|
|
DWORD Czero, Cxor, Ctwo, Cxor2, dwBytes;
|
|
|
|
// check for outstanding skips
|
|
|
|
if (*pSkipCount > 0)
|
|
{
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
|
|
// get mask and init bit mask
|
|
mask = *cmp++;
|
|
|
|
// check for a skip or a solid color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
else /* must be solid colour */
|
|
{
|
|
|
|
/* write four rows of 4 2-byte pixels of col0 */
|
|
|
|
/* solid colour is lower 15 bits of mask */
|
|
col0 = mask & 0x7fff;
|
|
RGB555_TO_RGB565(col0);
|
|
Czero = col0 | (col0 << 16);
|
|
|
|
for(row = uncmp, y = 0; y < HEIGHT_CBLOCK; y++, row+= bytesPerRow) {
|
|
|
|
|
|
*(DWORD UNALIGNED HUGE *)row = Czero;
|
|
*((DWORD UNALIGNED HUGE *)row+1) = Czero;
|
|
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
}
|
|
|
|
|
|
/* in 16-bit CRAM, both 4-pair and 1-pair cells have bit 15 of mask set
|
|
* to zero. We distinguish between them based on bit 15 of the first
|
|
* colour. if this is set, this is the 4-pair edge case cell.
|
|
*/
|
|
if (*cmp & 0x8000) {
|
|
// this is an edge with 4 colour pairs in four small blocks
|
|
|
|
/* read 2 colours, and make AND and XOR masks for first subcell*/
|
|
col0 = *cmp++;
|
|
RGB555_TO_RGB565(col0);
|
|
col1 = *cmp++;
|
|
RGB555_TO_RGB565(col1);
|
|
Czero = col1 | (col1 << 16);
|
|
Cxor = Czero ^ (col0 | (col0 << 16));
|
|
|
|
/* colour masks for second subcell */
|
|
col0 = *cmp++;
|
|
RGB555_TO_RGB565(col0);
|
|
col1 = *cmp++;
|
|
RGB555_TO_RGB565(col1);
|
|
Ctwo = col1 | (col1 << 16);
|
|
Cxor2 = Ctwo ^ (col0 | (col0 << 16));
|
|
|
|
|
|
row = uncmp;
|
|
|
|
/* first two rows - top two subcells */
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* --- first subcell (two pixels) ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
|
|
|
|
/* ---- second subcell (two pixels) --- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor2) ^ Ctwo;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
|
|
}
|
|
|
|
/* second two rows - bottom two subcells */
|
|
|
|
/* read 2 colours, and make AND and XOR masks for first subcell*/
|
|
col0 = *cmp++;
|
|
RGB555_TO_RGB565(col0);
|
|
col1 = *cmp++;
|
|
RGB555_TO_RGB565(col1);
|
|
Czero = col1 | (col1 << 16);
|
|
Cxor = Czero ^ (col0 | (col0 << 16));
|
|
|
|
/* colour masks for second subcell */
|
|
col0 = *cmp++;
|
|
RGB555_TO_RGB565(col0);
|
|
col1 = *cmp++;
|
|
RGB555_TO_RGB565(col1);
|
|
Ctwo = col1 | (col1 << 16);
|
|
Cxor2 = Ctwo ^ (col0 | (col0 << 16));
|
|
|
|
|
|
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
/* --- first subcell (two pixels) ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
|
|
|
|
/* ---- second subcell (two pixels) --- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor2) ^ Ctwo;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
|
|
} else {
|
|
// not an edge with only 1 colour pair and one large block
|
|
|
|
|
|
/* read colours */
|
|
col0 = *cmp++;
|
|
RGB555_TO_RGB565(col0);
|
|
col1 = *cmp++;
|
|
RGB555_TO_RGB565(col1);
|
|
Czero = col1 | (col1 << 16);
|
|
Cxor = Czero ^ (col0 | (col0 << 16));
|
|
|
|
row = uncmp;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
|
|
|
|
/* --- first two pixels in row ---- */
|
|
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 1) ? 0xffff: 0) |
|
|
((mask & 2) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&3];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)row) = dwBytes;
|
|
|
|
|
|
/* ---- second two pixels in row ---- */
|
|
/* turn bitmask into byte mask */
|
|
#if 0
|
|
dwBytes = ((mask & 4) ? 0xffff: 0) |
|
|
((mask & 8) ? 0xffff0000 : 0);
|
|
#else
|
|
dwBytes = Bits2Bytes[mask&0xc];
|
|
#endif
|
|
|
|
/* select both colours and write to dest */
|
|
dwBytes = (dwBytes & Cxor) ^ Czero;
|
|
*( (DWORD UNALIGNED HUGE *)(row + sizeof(DWORD))) = dwBytes;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
}
|
|
return( cmp );
|
|
|
|
}
|
|
|
|
DWORD FAR PASCAL DecompressFrame16To565C(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
INT bix;
|
|
INT biy;
|
|
HPBYTE blockRow;
|
|
HPBYTE blockColumn;
|
|
LONG SkipCount = 0;
|
|
INT bytesPerRow;
|
|
|
|
DPF(("DecompressFrame16To565C:\n"));
|
|
bix = (UINT)(lpbiIn->biWidth) / (WIDTH_CBLOCK); // No negative values in
|
|
biy = (UINT)(lpbiIn->biHeight) / (HEIGHT_CBLOCK); // width or height fields
|
|
|
|
StartCounting();
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
|
|
for( y=0; y < biy; y++, blockRow += bytesPerRow * HEIGHT_CBLOCK )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( x=0; x < bix; x++, blockColumn += (WIDTH_CBLOCK * sizeof(WORD)))
|
|
{
|
|
cmp = DecompressCBlock16To565(blockColumn, cmp, bytesPerRow, &SkipCount);
|
|
}
|
|
}
|
|
EndCounting("Decompress Frame16To565C took");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ---- 16-bit decompress & dither to 8 bit - in asm for Win16 ---------------------------*/
|
|
|
|
/*
|
|
* dither using SCALE method. see dcram168.asm or drawdib\dith775a.asm
|
|
*
|
|
* 8-bit colour = lookup[ scale[ rgb555] + err]
|
|
*
|
|
* where error is one of the values in the 4x4 array below to balance
|
|
* the colour.
|
|
*/
|
|
|
|
/*
|
|
* dither error array - values to add to rgb value after scaling before
|
|
* converting to 8 bits. Balances colour over a 4x4 matrix
|
|
*/
|
|
int ditherr[4][4] = {
|
|
{0, 3283, 4924, 8207},
|
|
{6565, 6566, 1641, 1642},
|
|
{3283, 0, 8207, 4924},
|
|
{6566, 4925, 3282, 1641}
|
|
};
|
|
|
|
/* scale the rgb555 first by lookup in lpScale[rgb555] */
|
|
#define DITHER16TO8(col, x, y) lpLookup[col + ditherr[(y)&3][(x)&3]]
|
|
|
|
|
|
/*
|
|
* decompress one 16bpp block, and dither to 8 bpp using table dither method.
|
|
*
|
|
* parameters:
|
|
* uncmp- pointer to de-compressed buffer for this block.
|
|
* cmp - pointer to compressed data for this block
|
|
* bytes.. - size of one row of de-compressed data
|
|
* pSkipCount - skipcount stored in parent stack frame
|
|
*
|
|
* returns:
|
|
* pointer to the next block of compressed data.
|
|
*
|
|
*/
|
|
|
|
STATICFN HPWORD INLINE
|
|
DecompressCBlock16To8(
|
|
HPBYTE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG * pSkipCount
|
|
)
|
|
{
|
|
UINT y;
|
|
WORD mask;
|
|
WORD col0, col1, col2, col3;
|
|
HPBYTE row;
|
|
DWORD Czero, Cone, Cxor, dwBytes;
|
|
|
|
// check for outstanding skips
|
|
|
|
if (*pSkipCount > 0)
|
|
{
|
|
Assert(!"Skip count should be handled by caller");
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
|
|
// get mask and init bit mask
|
|
mask = *cmp++;
|
|
|
|
// check for a skip or a solid color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
else /* must be solid colour */
|
|
{
|
|
|
|
/* solid colour is lower 15 bits of mask */
|
|
col0 = lpScale[mask & 0x7fff];
|
|
|
|
for(row = uncmp, y = 0; y < HEIGHT_CBLOCK; y++, row+= bytesPerRow) {
|
|
|
|
/* convert colour once for each row */
|
|
Czero = (DITHER16TO8(col0, 0, y) ) |
|
|
(DITHER16TO8(col0, 1, y) << 8 ) |
|
|
(DITHER16TO8(col0, 2, y) << 16 ) |
|
|
(DITHER16TO8(col0, 3, y) << 24 );
|
|
|
|
*(DWORD UNALIGNED HUGE *)row = Czero;
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
}
|
|
|
|
|
|
/* in 16-bit CRAM, both 4-pair and 1-pair cells have bit 15 of mask set
|
|
* to zero. We distinguish between them based on bit 15 of the first
|
|
* colour. if this is set, this is the 4-pair edge case cell.
|
|
*/
|
|
if (*cmp & 0x8000) {
|
|
// this is an edge with 4 color pairs in four small blocks
|
|
|
|
col0 = lpScale[(*cmp++) & 0x7fff];
|
|
col1 = lpScale[(*cmp++) & 0x7fff];
|
|
col2 = lpScale[(*cmp++) & 0x7fff];
|
|
col3 = lpScale[(*cmp++) & 0x7fff];
|
|
|
|
row = uncmp;
|
|
|
|
/* first two rows - top two subcells */
|
|
for (y = 0; y < 2; y++) {
|
|
|
|
|
|
/* dithering requires that we make different
|
|
* colour masks depending on x and y position - and
|
|
* therefore re-do it each row
|
|
*/
|
|
Czero = (DITHER16TO8(col1, 0, y) ) |
|
|
(DITHER16TO8(col1, 1, y) << 8 ) |
|
|
(DITHER16TO8(col3, 2, y) << 16 ) |
|
|
(DITHER16TO8(col3, 3, y) << 24 );
|
|
|
|
Cone = (DITHER16TO8(col0, 0, y) ) |
|
|
(DITHER16TO8(col0, 1, y) << 8 ) |
|
|
(DITHER16TO8(col2, 2, y) << 16 ) |
|
|
(DITHER16TO8(col2, 3, y) << 24 );
|
|
|
|
Cxor = Czero ^ Cone;
|
|
|
|
/* turn bitmask into byte mask */
|
|
dwBytes = ExpansionTable[mask & 0x0f];
|
|
|
|
/* select colours and write to dest */
|
|
*( (DWORD UNALIGNED HUGE *)row) = (dwBytes & Cxor) ^ Czero;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
|
|
/* second two rows - bottom two subcells */
|
|
|
|
/* read last four colours */
|
|
col0 = lpScale[(*cmp++) & 0x7fff];
|
|
col1 = lpScale[(*cmp++) & 0x7fff];
|
|
col2 = lpScale[(*cmp++) & 0x7fff];
|
|
col3 = lpScale[(*cmp++) & 0x7fff];
|
|
|
|
|
|
for (; y < 4; y++) {
|
|
|
|
/* dithering requires that we make different
|
|
* colour masks depending on x and y position - and
|
|
* therefore re-do it each row
|
|
*/
|
|
Czero = (DITHER16TO8(col1, 0, y) ) |
|
|
(DITHER16TO8(col1, 1, y) << 8 ) |
|
|
(DITHER16TO8(col3, 2, y) << 16 ) |
|
|
(DITHER16TO8(col3, 3, y) << 24 );
|
|
|
|
Cone = (DITHER16TO8(col0, 0, y) ) |
|
|
(DITHER16TO8(col0, 1, y) << 8 ) |
|
|
(DITHER16TO8(col2, 2, y) << 16 ) |
|
|
(DITHER16TO8(col2, 3, y) << 24 );
|
|
|
|
Cxor = Czero ^ Cone;
|
|
|
|
/* turn bitmask into byte mask */
|
|
dwBytes = ExpansionTable[mask & 0x0f];
|
|
|
|
/* select both colours and write to dest */
|
|
*( (DWORD UNALIGNED HUGE *)row) = (dwBytes & Cxor) ^ Czero;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
} else {
|
|
// not an edge with only 1 colour pair and one large block
|
|
|
|
|
|
/* read colours */
|
|
col0 = lpScale[(*cmp++) & 0x7fff];
|
|
col1 = lpScale[(*cmp++) & 0x7fff];
|
|
|
|
row = uncmp;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
|
|
Czero = (DITHER16TO8(col1, 0, y) ) |
|
|
(DITHER16TO8(col1, 1, y) << 8 ) |
|
|
(DITHER16TO8(col1, 2, y) << 16 ) |
|
|
(DITHER16TO8(col1, 3, y) << 24 );
|
|
|
|
Cone = (DITHER16TO8(col0, 0, y) ) |
|
|
(DITHER16TO8(col0, 1, y) << 8 ) |
|
|
(DITHER16TO8(col0, 2, y) << 16 ) |
|
|
(DITHER16TO8(col0, 3, y) << 24 );
|
|
|
|
Cxor = Czero ^ Cone;
|
|
|
|
/* turn bitmask into byte mask */
|
|
dwBytes = ExpansionTable[mask & 0x0f];
|
|
|
|
/* select both colours and write to dest */
|
|
*( (DWORD UNALIGNED HUGE *)row) = (dwBytes & Cxor) ^ Czero;
|
|
|
|
row += bytesPerRow;
|
|
mask >>= 4;
|
|
|
|
}
|
|
}
|
|
return( cmp );
|
|
|
|
}
|
|
|
|
DWORD FAR PASCAL DecompressFrame16To8C(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
INT bix;
|
|
INT biy;
|
|
HPBYTE blockRow;
|
|
HPBYTE blockColumn;
|
|
LONG SkipCount = 0;
|
|
INT bytesPerRow;
|
|
|
|
DPF(("DecompressFrame16To8C:\n"));
|
|
/* init dither table pointers. lpDitherTable is inited in msvidc. */
|
|
lpScale = lpDitherTable;
|
|
lpLookup = (LPBYTE) &lpScale[32768];
|
|
|
|
bix = (UINT)(lpbiIn->biWidth) / (WIDTH_CBLOCK); // No negative values in
|
|
biy = (UINT)(lpbiIn->biHeight) / (HEIGHT_CBLOCK); // width or height fields
|
|
|
|
StartCounting();
|
|
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
|
|
for( y=biy; y--; blockRow += bytesPerRow * HEIGHT_CBLOCK )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( x=bix; x--; blockColumn += WIDTH_CBLOCK)
|
|
{
|
|
cmp = DecompressCBlock16To8(blockColumn, cmp, bytesPerRow, &SkipCount);
|
|
|
|
// See if the SkipCount has been set. If so we want to move to
|
|
// the next location rather than calling DecompressCBlock every
|
|
// time around the loop. Keep the test simple to minimise the
|
|
// overhead on every iteration that the Skipcount is 0.
|
|
if (SkipCount) {
|
|
|
|
if ((x -= SkipCount) <0) { // extends past this row
|
|
LONG SkipRows;
|
|
|
|
// More than just the remainder of this row to skip
|
|
SkipCount =-x; // These bits are on the next row(s)
|
|
// SkipCount will be >0 otherwise we would have gone
|
|
// down the else leg.
|
|
|
|
// Calculate how many complete and partial rows to skip.
|
|
// We know we have skipped at least one row. The plan
|
|
// is to restart the X loop at some point along the row.
|
|
// If the skipcount takes us exactly to the end of a row
|
|
// we drop out of the x loop, and let the outer y loop do
|
|
// the decrement. This takes care of the case when the
|
|
// skipcount takes us to the very end of the image.
|
|
|
|
SkipRows = 1 + (SkipCount-1)/bix;
|
|
|
|
// Decrement the row count and set new blockrow start
|
|
|
|
#ifdef _WIN32
|
|
if (y<SkipRows) {
|
|
Assert(y >= SkipRows);
|
|
SkipRows = y;
|
|
}
|
|
#endif
|
|
|
|
// Unless we have finished we need to reset blockRow
|
|
y -= SkipRows;
|
|
// y might be 0, but we must still complete the last row
|
|
blockRow += bytesPerRow*HEIGHT_CBLOCK*SkipRows;
|
|
|
|
// Calculate the offset into the next row we will process
|
|
x = SkipCount%bix; // This may be 0
|
|
|
|
if (x) {
|
|
|
|
// Set block column by the amount along the row
|
|
// this iteration is starting, making allowance for
|
|
// the "for x..." loop iterating blockColumn once.
|
|
blockColumn = blockRow + ((x-1)*WIDTH_CBLOCK);
|
|
|
|
x=bix-x; // Get the counter correct
|
|
}
|
|
|
|
SkipCount = 0; // Skip count now exhausted (so am I)
|
|
|
|
} else {
|
|
// SkipCount has been exhausted by this row
|
|
// Either the row has completed, or there is more data
|
|
// on this row. Check...
|
|
if (x) {
|
|
// More of this row left
|
|
// Worry about moving blockColumn on the right amount
|
|
blockColumn += WIDTH_CBLOCK*SkipCount;
|
|
} // else x==0 and we will drop out of the "for x..." loop
|
|
// blockColumn will be reset when we reenter the x loop
|
|
SkipCount=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EndCounting("Decompress Frame16To8C took");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -- 16-bit decompress to 8-bit X2 -----------------------------------*/
|
|
|
|
/*
|
|
* given a 16-bit CRAM input stream, decompress and dither to 8
|
|
* bits and stretch by 2 in both dimensions (ie draw each pixel 4 times).
|
|
*/
|
|
|
|
/*
|
|
* decompress one 16bpp block, and dither to 8 bpp using table dither method.
|
|
* write each pixel 4 times to stretch X 2.
|
|
*
|
|
* parameters:
|
|
* uncmp- pointer to de-compressed buffer for this block.
|
|
* cmp - pointer to compressed data for this block
|
|
* bytes.. - size of one row of de-compressed data
|
|
* pSkipCount - skip count held in parent stack frame
|
|
*
|
|
* returns:
|
|
* pointer to the next block of compressed data.
|
|
*
|
|
*/
|
|
|
|
STATICFN HPWORD INLINE
|
|
DecompressCBlock16To8X2(
|
|
HPBYTE uncmp,
|
|
HPWORD cmp,
|
|
INT bytesPerRow,
|
|
LONG * pSkipCount
|
|
)
|
|
{
|
|
UINT x, y;
|
|
WORD mask;
|
|
WORD col0, col1, col2, col3;
|
|
HPBYTE row, col;
|
|
DWORD Czero;
|
|
|
|
// check for outstanding skips
|
|
|
|
if (*pSkipCount > 0)
|
|
{
|
|
Assert(!"Skip count should be handled by caller");
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
|
|
// get mask and init bit mask
|
|
mask = *cmp++;
|
|
|
|
// check for a skip or a solid color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
*pSkipCount = (mask & SKIP_MASK);
|
|
|
|
#ifdef _WIN32
|
|
Assert(*pSkipCount != 0); // break (on debug builds) if SkipCount == 0
|
|
#endif
|
|
|
|
(*pSkipCount)--;
|
|
return cmp;
|
|
}
|
|
else /* must be solid colour */
|
|
{
|
|
|
|
/* solid colour is lower 15 bits of mask */
|
|
col0 = lpScale[mask & 0x7fff];
|
|
|
|
|
|
for(row = uncmp, y = 0; y < HEIGHT_CBLOCK*2; y++, row+= bytesPerRow) {
|
|
|
|
/* convert colour once for each row */
|
|
Czero = (DITHER16TO8(col0, 0, (y&3)) ) |
|
|
(DITHER16TO8(col0, 1, (y&3)) << 8 ) |
|
|
(DITHER16TO8(col0, 2, (y&3)) << 16 ) |
|
|
(DITHER16TO8(col0, 3, (y&3)) << 24 );
|
|
|
|
*(DWORD UNALIGNED HUGE *)row = Czero;
|
|
*((DWORD UNALIGNED HUGE *)row + 1) = Czero;
|
|
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
}
|
|
|
|
|
|
/* in 16-bit CRAM, both 4-pair and 1-pair cells have bit 15 of mask set
|
|
* to zero. We distinguish between them based on bit 15 of the first
|
|
* colour. if this is set, this is the 4-pair edge case cell.
|
|
*/
|
|
if (*cmp & 0x8000) {
|
|
// this is an edge with 4 colour pairs in four small blocks
|
|
|
|
row = uncmp;
|
|
|
|
/* first two rows - top two subcells */
|
|
for (y = 0; y < HEIGHT_CBLOCK*2; y += 2) {
|
|
|
|
/* read colours at start, and again half-way through */
|
|
if ((y == 0) || (y == HEIGHT_CBLOCK)) {
|
|
|
|
col0 = lpScale[(*cmp++) & 0x7fff];
|
|
col1 = lpScale[(*cmp++) & 0x7fff];
|
|
col2 = lpScale[(*cmp++) & 0x7fff];
|
|
col3 = lpScale[(*cmp++) & 0x7fff];
|
|
|
|
}
|
|
|
|
col = row;
|
|
|
|
/* first two pixels (first subcell) */
|
|
for (x = 0; x < WIDTH_CBLOCK; x += 2) {
|
|
if (mask & 1) {
|
|
*col = DITHER16TO8(col0, (x & 3), (y&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col0, (x&3), ((y+1) & 3));
|
|
|
|
|
|
col++;
|
|
|
|
*col = DITHER16TO8(col0, ((x+1)&3), ((y)&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col0, ((x+1)&3), ((y+1)&3));
|
|
|
|
} else {
|
|
*col = DITHER16TO8(col1, (x & 3), (y&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col1, (x&3), ((y+1) & 3));
|
|
|
|
col++;
|
|
|
|
*col = DITHER16TO8(col1, ((x+1)&3), ((y)&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col1, ((x+1)&3), ((y+1)&3));
|
|
}
|
|
col++;
|
|
mask >>= 1;
|
|
}
|
|
|
|
/* second two pixels (second subcell) */
|
|
for (; x < WIDTH_CBLOCK*2; x += 2) {
|
|
if (mask & 1) {
|
|
*col = DITHER16TO8(col2, (x & 3), (y&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col2, (x&3), ((y+1) & 3));
|
|
|
|
col++;
|
|
|
|
*col = DITHER16TO8(col2, ((x+1)&3), ((y)&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col2, ((x+1)&3), ((y+1)&3));
|
|
} else {
|
|
*col = DITHER16TO8(col3, (x & 3), (y&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col3, (x&3), ((y+1) & 3));
|
|
|
|
col++;
|
|
*col = DITHER16TO8(col3, ((x+1)&3), ((y)&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col3, ((x+1)&3), ((y+1)&3));
|
|
}
|
|
col++;
|
|
mask >>= 1;
|
|
}
|
|
row += bytesPerRow * 2;
|
|
}
|
|
|
|
} else {
|
|
// not an edge with only 1 colour pair and one large block
|
|
|
|
|
|
/* read colours */
|
|
col0 = lpScale[(*cmp++) & 0x7fff];
|
|
col1 = lpScale[(*cmp++) & 0x7fff];
|
|
|
|
row = uncmp;
|
|
|
|
for (y = 0; y < HEIGHT_CBLOCK*2; y += 2) {
|
|
|
|
col = row;
|
|
for (x = 0; x < WIDTH_CBLOCK*2; x += 2) {
|
|
if (mask & 1) {
|
|
*col = DITHER16TO8(col0, (x & 3), (y&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col0, (x&3), ((y+1) & 3));
|
|
|
|
col++;
|
|
*col = DITHER16TO8(col0, ((x+1)&3), ((y)&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col0, ((x+1)&3), ((y+1)&3));
|
|
|
|
} else {
|
|
*col = DITHER16TO8(col1, (x & 3), (y&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col1, (x&3), ((y+1) & 3));
|
|
|
|
col++;
|
|
|
|
*col = DITHER16TO8(col1, ((x+1)&3), ((y)&3));
|
|
*(col + bytesPerRow) =
|
|
DITHER16TO8(col1, ((x+1)&3), ((y+1)&3));
|
|
}
|
|
col++;
|
|
mask >>= 1;
|
|
}
|
|
row += bytesPerRow * 2;
|
|
}
|
|
}
|
|
return( cmp );
|
|
|
|
}
|
|
|
|
|
|
DWORD FAR PASCAL DecompressFrame16To8X2C(LPBITMAPINFOHEADER lpbiIn, LPVOID lpIn,
|
|
LPBITMAPINFOHEADER lpbiOut, LPVOID lpOut, LONG x, LONG y)
|
|
{
|
|
HPWORD cmp = (HPWORD)lpIn;
|
|
INT bix;
|
|
INT biy;
|
|
HPBYTE blockRow;
|
|
HPBYTE blockColumn;
|
|
LONG SkipCount = 0;
|
|
INT bytesPerRow;
|
|
|
|
DPF(("DecompressFrame16To8X2C:\n"));
|
|
/* init dither table pointers. lpDitherTable is inited in msvidc. */
|
|
lpScale = lpDitherTable;
|
|
lpLookup = (LPBYTE) &lpScale[32768];
|
|
|
|
StartCounting();
|
|
|
|
bix = (UINT)(lpbiIn->biWidth) / (WIDTH_CBLOCK); // No negative values in
|
|
biy = (UINT)(lpbiIn->biHeight) / (HEIGHT_CBLOCK); // width or height fields
|
|
|
|
blockRow = DibXY(lpbiOut, lpOut, x, y, &bytesPerRow);
|
|
|
|
for( y=biy; y--; blockRow += bytesPerRow * HEIGHT_CBLOCK *2 )
|
|
{
|
|
blockColumn = blockRow;
|
|
for( x=bix; x--; blockColumn += WIDTH_CBLOCK*2)
|
|
{
|
|
cmp = DecompressCBlock16To8X2(blockColumn, cmp, bytesPerRow, &SkipCount);
|
|
|
|
// See if the SkipCount has been set. If so we want to move to
|
|
// the next location rather than calling DecompressCBlock every
|
|
// time around the loop. Keep the test simple to minimise the
|
|
// overhead on every iteration that the Skipcount is 0.
|
|
if (SkipCount) {
|
|
|
|
if ((x -= SkipCount) <0) { // extends past this row
|
|
LONG SkipRows;
|
|
|
|
// More than just the remainder of this row to skip
|
|
SkipCount =-x; // These bits are on the next row(s)
|
|
// SkipCount will be >0 otherwise we would have gone
|
|
// down the else leg.
|
|
|
|
// Calculate how many complete and partial rows to skip.
|
|
// We know we have skipped at least one row. The plan
|
|
// is to restart the X loop at some point along the row.
|
|
// If the skipcount takes us exactly to the end of a row
|
|
// we drop out of the x loop, and let the outer y loop do
|
|
// the decrement. This takes care of the case when the
|
|
// skipcount takes us to the very end of the image.
|
|
|
|
SkipRows = 1 + (SkipCount-1)/bix;
|
|
|
|
// Decrement the row count and set new blockrow start
|
|
|
|
#ifdef _WIN32
|
|
if (y<SkipRows) {
|
|
Assert(y >= SkipRows);
|
|
SkipRows = y;
|
|
}
|
|
#endif
|
|
|
|
// Unless we have finished we need to reset blockRow
|
|
y -= SkipRows;
|
|
// y might be 0, but we must still complete the last row
|
|
blockRow += bytesPerRow*HEIGHT_CBLOCK*2*SkipRows;
|
|
|
|
// Calculate the offset into the next row we will process
|
|
x = SkipCount%bix; // This may be 0
|
|
|
|
if (x) {
|
|
|
|
// Set block column by the amount along the row
|
|
// this iteration is starting, making allowance for
|
|
// the "for x..." loop iterating blockColumn once.
|
|
blockColumn = blockRow + ((x-1)*WIDTH_CBLOCK*2);
|
|
|
|
x=bix-x; // Get the counter correct
|
|
}
|
|
|
|
SkipCount = 0; // Skip count now exhausted (so am I)
|
|
|
|
} else {
|
|
// SkipCount has been exhausted by this row
|
|
// Either the row has completed, or there is more data
|
|
// on this row. Check...
|
|
if (x) {
|
|
// More of this row left
|
|
// Worry about moving blockColumn on the right amount
|
|
blockColumn += WIDTH_CBLOCK*2*SkipCount;
|
|
} // else x==0 and we will drop out of the "for x..." loop
|
|
// blockColumn will be reset when we reenter the x loop
|
|
SkipCount=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EndCounting("Decompress Frame16To8x2C took");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|