1400 lines
43 KiB
C
1400 lines
43 KiB
C
/*----------------------------------------------------------------------+
|
|
| compress.c - Microsoft Video 1 Compressor - compress 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. |
|
|
| |
|
|
| |
|
|
+----------------------------------------------------------------------*/
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <mmsystem.h> // for timeGetTime()
|
|
#include <win32.h>
|
|
#include "msvidc.h"
|
|
|
|
#include <memory.h> // for _fmemcmp
|
|
|
|
//#include <limits.h>
|
|
//#include <aviffmt.h>
|
|
|
|
DWORD numberOfBlocks;
|
|
DWORD numberOfSolids;
|
|
DWORD numberOfSolid4;
|
|
DWORD numberOfSolid2;
|
|
DWORD numberOfEdges;
|
|
DWORD numberOfSkips;
|
|
DWORD numberOfExtraSkips;
|
|
DWORD numberOfSkipCodes;
|
|
DWORD numberOfEvilRed;
|
|
|
|
/*******************************************************************
|
|
*******************************************************************/
|
|
|
|
#define SWAP(x,y) ( (x)^=(y), (y)^=(x), (x)^=(y) )
|
|
|
|
#define SWAPRGB(x,y)( SWAP((x).rgbRed, (y).rgbRed), \
|
|
SWAP((x).rgbGreen, (y).rgbGreen),\
|
|
SWAP((x).rgbBlue, (y).rgbBlue) )
|
|
|
|
// Take an RGB quad value and figure out it's lumanence
|
|
// Y = 0.3*R + 0.59*G + 0.11*B
|
|
#define RgbToY(rgb) ((((WORD)((rgb).rgbRed) * 30) + \
|
|
((WORD)((rgb).rgbGreen)* 59) + \
|
|
((WORD)((rgb).rgbBlue) * 11))/100)
|
|
|
|
#define RGB16(r,g,b) ((((WORD)(r) >> 3) << 10) | \
|
|
(((WORD)(g) >> 3) << 5) | \
|
|
(((WORD)(b) >> 3) << 0) )
|
|
|
|
#define RGBQ16(rgb) RGB16((rgb).rgbRed,(rgb).rgbGreen,(rgb).rgbBlue)
|
|
|
|
// this array is used to associate each of the 16 luminance values
|
|
// with one of the sum values
|
|
BYTE meanIndex[16] = { 0, 0, 1, 1,
|
|
0, 0, 1, 1,
|
|
2, 2, 3, 3,
|
|
2, 2, 3, 3 };
|
|
|
|
|
|
/*****************************************************************************
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
//
|
|
// map Quality into our threshold
|
|
//
|
|
// Quality goes from ICQUALITY_LOW-ICQUALITY_HIGH (bad to good)
|
|
//
|
|
// threshold = (Quality/ICQUALITY_HIGH)^THRESHOLD_POW * THRESHOLD_HIGH
|
|
//
|
|
DWORD FAR QualityToThreshold(DWORD dwQuality)
|
|
{
|
|
#define THRESHOLD_HIGH ((256*256l)/2)
|
|
#define THRESHOLD_POW 4
|
|
double dw1;
|
|
|
|
dw1 = (double)(dwQuality) / ICQUALITY_HIGH;
|
|
|
|
// unbelievably enough, pow() doesn't work on alpha or mips!
|
|
// also I can't believe this will be less efficient than pow(x, 4)
|
|
dw1 = (dw1 * dw1 * dw1 * dw1);
|
|
|
|
return (DWORD) (dw1 * THRESHOLD_HIGH);
|
|
|
|
//return (DWORD)(pow((double)(dwQuality)/ICQUALITY_HIGH,THRESHOLD_POW) * THRESHOLD_HIGH);
|
|
}
|
|
|
|
/*******************************************************************
|
|
*******************************************************************/
|
|
|
|
//
|
|
// table to map a 5bit index (0-31) to a 8 bit value (0-255)
|
|
//
|
|
static BYTE aw5to8[32] = {(BYTE)-1};
|
|
|
|
//
|
|
// inverse table to map a RGB16 to a 8bit pixel
|
|
//
|
|
#define MAPRGB16(rgb16) lpITable[(rgb16)]
|
|
#define MAPRGB(rgb) lpITable[RGBQ16(rgb)]
|
|
|
|
|
|
/*******************************************************************
|
|
*******************************************************************/
|
|
|
|
DWORD FAR CompressFrameBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut,
|
|
LPBYTE *lplpITable, RGBQUAD *prgbqOut)
|
|
{
|
|
int i;
|
|
|
|
//
|
|
// init the 5bit to 8bit conversion table.
|
|
//
|
|
if (aw5to8[0] != 0)
|
|
for (i=0; i<32; i++)
|
|
aw5to8[i] = (BYTE)(i * 255 / 31);
|
|
|
|
//
|
|
// copy the color table to local storage
|
|
//
|
|
if (lpbiIn->biBitCount == 8)
|
|
{
|
|
if (lpbiIn->biClrUsed == 0)
|
|
lpbiIn->biClrUsed = 256;
|
|
}
|
|
|
|
//
|
|
// if we are compressing to 8bit, then build a inverse table
|
|
//
|
|
if (lpbiOut->biBitCount == 8)
|
|
{
|
|
if (lpbiOut->biClrUsed == 0)
|
|
lpbiOut->biClrUsed = 256;
|
|
|
|
if (_fmemcmp((LPVOID)prgbqOut, (LPVOID)(lpbiOut+1),
|
|
(int)lpbiOut->biClrUsed * sizeof(RGBQUAD)))
|
|
{
|
|
for (i=0; i<(int)lpbiOut->biClrUsed; i++)
|
|
prgbqOut[i] = ((LPRGBQUAD)(lpbiOut+1))[i];
|
|
|
|
if (*lplpITable)
|
|
GlobalFreePtr(*lplpITable);
|
|
|
|
*lplpITable = NULL;
|
|
}
|
|
|
|
if (*lplpITable == NULL)
|
|
{
|
|
// !!! Need a critical section around this code!
|
|
DPF(("Building ITable.... (%d colors)", (int)lpbiOut->biClrUsed));
|
|
*lplpITable = MakeITable(prgbqOut, (int)lpbiOut->biClrUsed);
|
|
// !!! Critical section can end here....
|
|
}
|
|
|
|
if (*lplpITable == NULL)
|
|
return (DWORD)ICERR_MEMORY;
|
|
}
|
|
|
|
return ICERR_OK;
|
|
}
|
|
|
|
/*******************************************************************
|
|
*******************************************************************/
|
|
|
|
DWORD FAR CompressFrameEnd(LPBYTE *lplpITable)
|
|
{
|
|
if (*lplpITable)
|
|
GlobalFreePtr(*lplpITable);
|
|
|
|
*lplpITable = NULL;
|
|
|
|
return ICERR_OK;
|
|
}
|
|
|
|
/*******************************************************************
|
|
*******************************************************************/
|
|
|
|
void FAR CompressFrameFree(void)
|
|
{
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
GetCell - get a 4x4 cell from a image.
|
|
|
|
returns pointer to next cell.
|
|
|
|
*******************************************************************/
|
|
|
|
static LPVOID _FASTCALL
|
|
GetCell(LPBITMAPINFOHEADER lpbi, LPVOID lpBits, PCELL pCell)
|
|
{
|
|
UINT WidthBytes;
|
|
int bits;
|
|
int i;
|
|
int x;
|
|
int y;
|
|
BYTE b;
|
|
HPBYTE pb;
|
|
RGBQUAD FAR *prgbqIn;
|
|
|
|
RGB555 rgb555;
|
|
|
|
pb = lpBits;
|
|
|
|
bits = (int)lpbi->biBitCount;
|
|
WidthBytes = DIBWIDTHBYTES(*lpbi);
|
|
WidthBytes-= (WIDTH_CBLOCK * bits/8);
|
|
|
|
((HPBYTE)lpBits) += (WIDTH_CBLOCK * bits/8); // "next" cell
|
|
|
|
i = 0;
|
|
|
|
switch (bits)
|
|
{
|
|
case 8:
|
|
prgbqIn = (RGBQUAD FAR *) (lpbi + 1);
|
|
for( y = 0; y < HEIGHT_CBLOCK; y++ )
|
|
{
|
|
for( x = 0; x < WIDTH_CBLOCK; x++ )
|
|
{
|
|
b = *pb++;
|
|
pCell[i++] = prgbqIn[b];
|
|
}
|
|
pb += WidthBytes; // next row in this block
|
|
}
|
|
break;
|
|
|
|
case 16:
|
|
for( y = 0; y < HEIGHT_CBLOCK; y++ )
|
|
{
|
|
for( x = 0; x < WIDTH_CBLOCK; x++ )
|
|
{
|
|
rgb555 = *((HPRGB555)pb)++;
|
|
pCell[i].rgbRed = aw5to8[(rgb555 >> 10) & 0x1F];
|
|
pCell[i].rgbGreen = aw5to8[(rgb555 >> 5) & 0x1F];
|
|
pCell[i].rgbBlue = aw5to8[(rgb555 >> 0) & 0x1F];
|
|
i++;
|
|
}
|
|
pb += WidthBytes; // next row in this block
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
for( y = 0; y < HEIGHT_CBLOCK; y++ )
|
|
{
|
|
for( x = 0; x < WIDTH_CBLOCK; x++ )
|
|
{
|
|
pCell[i].rgbBlue = *pb++;
|
|
pCell[i].rgbGreen = *pb++;
|
|
pCell[i].rgbRed = *pb++;
|
|
i++;
|
|
}
|
|
pb += WidthBytes; // next row in this block
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
for( y = 0; y < HEIGHT_CBLOCK; y++ )
|
|
{
|
|
for( x = 0; x < WIDTH_CBLOCK; x++ )
|
|
{
|
|
pCell[i].rgbBlue = *pb++;
|
|
pCell[i].rgbGreen = *pb++;
|
|
pCell[i].rgbRed = *pb++;
|
|
pb++;
|
|
i++;
|
|
}
|
|
pb += WidthBytes; // next row in this block
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// return the pointer to the "next" cell
|
|
//
|
|
return lpBits;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
CmpCell - compares two 4x4 cells and returns an error value.
|
|
|
|
the error value is a sum of squares error.
|
|
|
|
the error value ranges from
|
|
|
|
0 = exact
|
|
3*256^2 = way off
|
|
|
|
*******************************************************************/
|
|
|
|
static DWORD _FASTCALL
|
|
CmpCell(PCELL cellA, PCELL cellB)
|
|
{
|
|
#if 0
|
|
int i;
|
|
long l;
|
|
int dr,dg,db;
|
|
|
|
for (l=0,i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
{
|
|
dr = (int)cellA[i].rgbRed - (int)cellB[i].rgbRed;
|
|
dg = (int)cellA[i].rgbGreen - (int)cellB[i].rgbGreen;
|
|
db = (int)cellA[i].rgbBlue - (int)cellB[i].rgbBlue;
|
|
|
|
l += ((long)dr * dr) + ((long)dg * dg) + ((long)db * db);
|
|
}
|
|
|
|
return l / (HEIGHT_CBLOCK*WIDTH_CBLOCK);
|
|
#else
|
|
int i;
|
|
DWORD dw;
|
|
|
|
//
|
|
//
|
|
#define SUMSQ(a,b) \
|
|
if (a > b) \
|
|
dw += (UINT)(a-b) * (UINT)(a-b); \
|
|
else \
|
|
dw += (UINT)(b-a) * (UINT)(b-a);
|
|
|
|
for (dw=0,i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
{
|
|
SUMSQ(cellA[i].rgbRed, cellB[i].rgbRed);
|
|
SUMSQ(cellA[i].rgbGreen, cellB[i].rgbGreen);
|
|
SUMSQ(cellA[i].rgbBlue, cellB[i].rgbBlue);
|
|
}
|
|
|
|
return dw / (HEIGHT_CBLOCK*WIDTH_CBLOCK);
|
|
#endif
|
|
}
|
|
|
|
#if 0 // This routine is unused
|
|
/*******************************************************************
|
|
|
|
MapCell - map a CELL full of 24bit values down to thier nearest
|
|
colors in the 8bit palette
|
|
|
|
*******************************************************************/
|
|
|
|
static void _FASTCALL
|
|
MapCell(PCELL pCell)
|
|
{
|
|
int i;
|
|
int n;
|
|
|
|
for (i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
{
|
|
n = MAPRGB(pCell[i]); // map to nearest palette index
|
|
pCell[i] = prgbqOut[n]; // ...and map back to a RGB
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// take care of any outstanding skips
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#define FlushSkips() \
|
|
\
|
|
while (SkipCount > 0) \
|
|
{ \
|
|
WORD w; \
|
|
\
|
|
w = min(SkipCount, SKIP_MAX); \
|
|
SkipCount -= w; \
|
|
w |= SKIP_MAGIC; \
|
|
*dst++ = w; \
|
|
numberOfSkipCodes++; \
|
|
actualSize += 2; \
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
routine: CompressFrame16
|
|
purp: compress a frame, outputing 16 bit compressed data.
|
|
returns: number of bytes in compressed buffer
|
|
*******************************************************************/
|
|
|
|
DWORD FAR CompressFrame16(LPBITMAPINFOHEADER lpbi, // DIB header to compress
|
|
LPVOID lpBits, // DIB bits to compress
|
|
LPVOID lpData, // put compressed data here
|
|
DWORD threshold, // edge threshold
|
|
DWORD thresholdInter, // inter-frame threshold
|
|
LPBITMAPINFOHEADER lpbiPrev, // previous frame
|
|
LPVOID lpPrev, // previous frame
|
|
LONG (CALLBACK *Status) (LPARAM lParam, UINT message, LONG l),
|
|
LPARAM lParam,
|
|
PCELLS pCells)
|
|
{
|
|
UINT bix;
|
|
UINT biy;
|
|
UINT WidthBytes;
|
|
UINT WidthBytesPrev;
|
|
|
|
WORD SkipCount;
|
|
|
|
WORD luminance[16], luminanceMean[4];
|
|
DWORD luminanceSum;
|
|
WORD sumR,sumG,sumB;
|
|
WORD sumR0[4],sumG0[4],sumB0[4];
|
|
WORD sumR1[4],sumG1[4],sumB1[4];
|
|
WORD meanR0[4],meanG0[4],meanB0[4];
|
|
WORD meanR1[4],meanG1[4],meanB1[4];
|
|
WORD zeros[4], ones[4];
|
|
UINT x,y;
|
|
WORD mask;
|
|
HPBYTE srcPtr;
|
|
HPBYTE prvPtr;
|
|
DWORD actualSize;
|
|
UINT i;
|
|
UINT mi;
|
|
HPWORD dst;
|
|
RGBQUAD rgb;
|
|
int iStatusEvery;
|
|
|
|
#ifdef DEBUG
|
|
DWORD time = timeGetTime();
|
|
#endif
|
|
|
|
WidthBytes = DIBWIDTHBYTES(*lpbi);
|
|
|
|
if (lpbiPrev)
|
|
WidthBytesPrev = DIBWIDTHBYTES(*lpbiPrev);
|
|
|
|
bix = (int)lpbi->biWidth/WIDTH_CBLOCK;
|
|
biy = (int)lpbi->biHeight/HEIGHT_CBLOCK;
|
|
|
|
if (bix < 100)
|
|
iStatusEvery = 4;
|
|
else if (bix < 200)
|
|
iStatusEvery = 2;
|
|
else
|
|
iStatusEvery = 1;
|
|
|
|
actualSize = 0;
|
|
numberOfSkipCodes = 0;
|
|
numberOfSkips = 0;
|
|
numberOfExtraSkips = 0;
|
|
numberOfEdges = 0;
|
|
numberOfBlocks = 0;
|
|
numberOfSolids = 0;
|
|
numberOfSolid4 = 0;
|
|
numberOfSolid2 = 0;
|
|
numberOfEvilRed = 0;
|
|
|
|
dst = (HPWORD)lpData;
|
|
SkipCount = 0;
|
|
|
|
for( y = 0; y < biy; y++ )
|
|
{
|
|
if (Status && ((y % iStatusEvery) == 0)) {
|
|
if (Status(lParam, ICSTATUS_STATUS, (y * 100) / biy) != 0)
|
|
return (DWORD) -1;
|
|
}
|
|
|
|
srcPtr = lpBits;
|
|
prvPtr = lpPrev;
|
|
|
|
for( x = 0; x < bix; x++ )
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// get the cell to compress from the image.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
srcPtr = GetCell(lpbi, srcPtr, pCells->cell);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// see if it matches the cell in the previous frame.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
if (lpbiPrev)
|
|
{
|
|
prvPtr = GetCell(lpbiPrev, prvPtr, pCells->cellPrev);
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellPrev) <= thresholdInter)
|
|
{
|
|
skip_cell:
|
|
numberOfSkips++;
|
|
SkipCount++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// compute luminance of each pixel in the compression block
|
|
// sum the total luminance in the block
|
|
// find the pixels with the largest and smallest luminance
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
luminanceSum = 0;
|
|
sumR = 0;
|
|
sumG = 0;
|
|
sumB = 0;
|
|
|
|
for (i = 0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
{
|
|
sumR += pCells->cell[i].rgbRed;
|
|
sumG += pCells->cell[i].rgbGreen;
|
|
sumB += pCells->cell[i].rgbBlue;
|
|
|
|
luminance[i] = RgbToY(pCells->cell[i]);
|
|
luminanceSum += luminance[i];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// see if we make the cell a single color, and get away with it
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
sumR /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
|
|
sumG /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
|
|
sumB /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
|
|
|
|
rgb.rgbRed = (BYTE)sumR;
|
|
rgb.rgbGreen = (BYTE)sumG;
|
|
rgb.rgbBlue = (BYTE)sumB;
|
|
|
|
for (i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
pCells->cellT[i] = rgb;
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
|
|
{
|
|
if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
|
|
{
|
|
numberOfExtraSkips++;
|
|
goto skip_cell;
|
|
}
|
|
|
|
FlushSkips();
|
|
|
|
// single color!!
|
|
solid_color:
|
|
mask = RGB16(sumR, sumG, sumB) | 0x8000;
|
|
|
|
if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
|
|
{
|
|
numberOfEvilRed++;
|
|
mask ^= SKIP_MAGIC;
|
|
mask |= 0x8000;
|
|
}
|
|
|
|
*dst++ = mask;
|
|
numberOfSolids++;
|
|
actualSize += 2;
|
|
continue;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// make a 4x4 block
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
luminanceMean[0] = (WORD)(luminanceSum >> 4);
|
|
|
|
// zero summing arrays
|
|
zeros[0]=0;
|
|
ones[0] =0;
|
|
sumR0[0]=0;
|
|
sumR1[0]=0;
|
|
sumG0[0]=0;
|
|
sumG1[0]=0;
|
|
sumB0[0]=0;
|
|
sumB1[0]=0;
|
|
|
|
// define which of the two colors to choose
|
|
// for each pixel by creating the mask
|
|
mask = 0;
|
|
for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
|
|
{
|
|
if( luminance[i] < luminanceMean[0] )
|
|
{
|
|
// mask &= ~(1 << i); // already clear
|
|
zeros[0]++;
|
|
sumR0[0] += pCells->cell[i].rgbRed;
|
|
sumG0[0] += pCells->cell[i].rgbGreen;
|
|
sumB0[0] += pCells->cell[i].rgbBlue;
|
|
}
|
|
else
|
|
{
|
|
mask |= (1 << i);
|
|
ones[0]++;
|
|
sumR1[0] += pCells->cell[i].rgbRed;
|
|
sumG1[0] += pCells->cell[i].rgbGreen;
|
|
sumB1[0] += pCells->cell[i].rgbBlue;
|
|
}
|
|
}
|
|
|
|
// define the "one" color as the mean of each element
|
|
if( ones[0] != 0 )
|
|
{
|
|
meanR1[0] = sumR1[0] / ones[0];
|
|
meanG1[0] = sumG1[0] / ones[0];
|
|
meanB1[0] = sumB1[0] / ones[0];
|
|
}
|
|
else
|
|
{
|
|
meanR1[0] = meanG1[0] = meanB1[0] = 0;
|
|
}
|
|
|
|
if( zeros[0] != 0 )
|
|
{
|
|
meanR0[0] = sumR0[0] / zeros[0];
|
|
meanG0[0] = sumG0[0] / zeros[0];
|
|
meanB0[0] = sumB0[0] / zeros[0];
|
|
}
|
|
else
|
|
{
|
|
meanR0[0] = meanG0[0] = meanB0[0] = 0;
|
|
}
|
|
|
|
//
|
|
// build the block and make sure, it is within error.
|
|
//
|
|
for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
|
|
{
|
|
if( luminance[i] < luminanceMean[0] )
|
|
{
|
|
pCells->cellT[i].rgbRed = (BYTE)meanR0[0];
|
|
pCells->cellT[i].rgbGreen = (BYTE)meanG0[0];
|
|
pCells->cellT[i].rgbBlue = (BYTE)meanB0[0];
|
|
}
|
|
else
|
|
{
|
|
pCells->cellT[i].rgbRed = (BYTE)meanR1[0];
|
|
pCells->cellT[i].rgbGreen = (BYTE)meanG1[0];
|
|
pCells->cellT[i].rgbBlue = (BYTE)meanB1[0];
|
|
}
|
|
}
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
|
|
{
|
|
if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
|
|
{
|
|
numberOfExtraSkips++;
|
|
goto skip_cell;
|
|
}
|
|
|
|
//
|
|
// handle any outstanding skip codes
|
|
//
|
|
FlushSkips();
|
|
|
|
//
|
|
// we should never, ever generate a mask of all ones or
|
|
// zeros!
|
|
//
|
|
if (mask == 0x0000)
|
|
{
|
|
DPF(("4x4 generated a zero mask!"));
|
|
sumR = meanR0[0]; sumG = meanG0[0]; sumB = meanB0[0];
|
|
goto solid_color;
|
|
}
|
|
|
|
if (mask == 0xFFFF)
|
|
{
|
|
DPF(("4x4 generated a FFFF mask!"));
|
|
sumR = meanR1[0]; sumG = meanG1[0]; sumB = meanB1[0];
|
|
goto solid_color;
|
|
}
|
|
|
|
|
|
//
|
|
// remember the high bit of the mask is used to mark
|
|
// a skip or solid color, so make sure the high bit
|
|
// is zero.
|
|
//
|
|
if (mask & 0x8000)
|
|
{
|
|
*dst++ = ~mask;
|
|
*dst++ = RGB16(meanR0[0],meanG0[0],meanB0[0]);
|
|
*dst++ = RGB16(meanR1[0],meanG1[0],meanB1[0]);
|
|
}
|
|
else
|
|
{
|
|
*dst++ = mask;
|
|
*dst++ = RGB16(meanR1[0],meanG1[0],meanB1[0]);
|
|
*dst++ = RGB16(meanR0[0],meanG0[0],meanB0[0]);
|
|
}
|
|
actualSize += 6;
|
|
numberOfBlocks++;
|
|
|
|
continue;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// see if we make the cell four solid colorls, and get away with it
|
|
//
|
|
// C D E F
|
|
// 8 9 A C
|
|
// 4 5 6 7
|
|
// 0 1 2 3
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef DEBUG
|
|
for (i=0; i <= 10; i == 2 ? (i += 6) : (i += 2))
|
|
{
|
|
pCells->cellT[i].rgbRed = (BYTE)(((WORD)pCells->cell[i].rgbRed
|
|
+ pCells->cell[i+1].rgbRed
|
|
+ pCells->cell[i+4].rgbRed
|
|
+ pCells->cell[i+5].rgbRed ) / 4);
|
|
pCells->cellT[i].rgbGreen = (BYTE)(((WORD)pCells->cell[i].rgbGreen
|
|
+ pCells->cell[i+1].rgbGreen
|
|
+ pCells->cell[i+4].rgbGreen
|
|
+ pCells->cell[i+5].rgbGreen ) / 4);
|
|
pCells->cellT[i].rgbBlue = (BYTE)(((WORD)pCells->cell[i].rgbBlue
|
|
+ pCells->cell[i+1].rgbBlue
|
|
+ pCells->cell[i+4].rgbBlue
|
|
+ pCells->cell[i+5].rgbBlue ) / 4);
|
|
pCells->cellT[i+1] = pCells->cellT[i+4]
|
|
= pCells->cellT[i+5]
|
|
= pCells->cellT[i];
|
|
}
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
|
|
{
|
|
// four colors
|
|
numberOfSolid4++;
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// make a 2x2 block
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
FlushSkips();
|
|
|
|
numberOfEdges++;
|
|
|
|
luminanceMean[0] = (luminance[0] + luminance[1] + luminance[4] + luminance[5]) / 4;
|
|
luminanceMean[1] = (luminance[2] + luminance[3] + luminance[6] + luminance[7]) / 4;
|
|
luminanceMean[2] = (luminance[8] + luminance[9] + luminance[12] + luminance[13]) / 4;
|
|
luminanceMean[3] = (luminance[10] + luminance[11] + luminance[14] + luminance[15]) / 4;
|
|
|
|
// zero summing arrays
|
|
zeros[0]=zeros[1]=zeros[2]=zeros[3]=0;
|
|
ones[0]=ones[1]=ones[2]=ones[3]=0;
|
|
sumR0[0]=sumR0[1]=sumR0[2]=sumR0[3]=0;
|
|
sumR1[0]=sumR1[1]=sumR1[2]=sumR1[3]=0;
|
|
sumG0[0]=sumG0[1]=sumG0[2]=sumG0[3]=0;
|
|
sumG1[0]=sumG1[1]=sumG1[2]=sumG1[3]=0;
|
|
sumB0[0]=sumB0[1]=sumB0[2]=sumB0[3]=0;
|
|
sumB1[0]=sumB1[1]=sumB1[2]=sumB1[3]=0;
|
|
|
|
// define which of the two colors to choose
|
|
// for each pixel by creating the mask
|
|
mask = 0;
|
|
for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
|
|
{
|
|
mi = meanIndex[i];
|
|
if( luminance[i] < luminanceMean[mi] )
|
|
{
|
|
// mask &= ~(1 << i); // already clear
|
|
zeros[mi]++;
|
|
sumR0[mi] += pCells->cell[i].rgbRed;
|
|
sumG0[mi] += pCells->cell[i].rgbGreen;
|
|
sumB0[mi] += pCells->cell[i].rgbBlue;
|
|
}
|
|
else
|
|
{
|
|
mask |= (1 << i);
|
|
ones[mi]++;
|
|
sumR1[mi] += pCells->cell[i].rgbRed;
|
|
sumG1[mi] += pCells->cell[i].rgbGreen;
|
|
sumB1[mi] += pCells->cell[i].rgbBlue;
|
|
}
|
|
}
|
|
|
|
// store the mask
|
|
|
|
if (mask & 0x8000)
|
|
*dst++ = ~mask;
|
|
else
|
|
*dst++ = mask;
|
|
|
|
actualSize += 2;
|
|
|
|
// make the colors
|
|
for( i=0; i < 4; i++ )
|
|
{
|
|
// define the "one" color as the mean of each element
|
|
if( ones[i] != 0 )
|
|
{
|
|
meanR1[i] = sumR1[i] / ones[i];
|
|
meanG1[i] = sumG1[i] / ones[i];
|
|
meanB1[i] = sumB1[i] / ones[i];
|
|
}
|
|
else
|
|
{
|
|
meanR1[i] = meanG1[i] = meanB1[i] = 0;
|
|
}
|
|
|
|
if( zeros[i] != 0 )
|
|
{
|
|
meanR0[i] = sumR0[i] / zeros[i];
|
|
meanG0[i] = sumG0[i] / zeros[i];
|
|
meanB0[i] = sumB0[i] / zeros[i];
|
|
}
|
|
else
|
|
{
|
|
meanR0[i] = meanG0[i] = meanB0[i] = 0;
|
|
}
|
|
|
|
// convert to 555 and set bit 15 if this is an edge and
|
|
// this is the first color
|
|
|
|
if (mask & 0x8000)
|
|
{
|
|
*dst++ = RGB16(meanR0[i],meanG0[i],meanB0[i]) | (i==0 ? 0x8000 : 0);
|
|
*dst++ = RGB16(meanR1[i],meanG1[i],meanB1[i]);
|
|
}
|
|
else
|
|
{
|
|
*dst++ = RGB16(meanR1[i],meanG1[i],meanB1[i]) | (i==0 ? 0x8000 : 0);
|
|
*dst++ = RGB16(meanR0[i],meanG0[i],meanB0[i]);
|
|
}
|
|
actualSize += 4;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// next scan.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
((HPBYTE)lpBits) += WidthBytes * HEIGHT_CBLOCK;
|
|
|
|
if (lpPrev)
|
|
((HPBYTE)lpPrev) += WidthBytesPrev * HEIGHT_CBLOCK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// take care of any outstanding skips, !!! note we dont need this if we
|
|
// assume a EOF!
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
FlushSkips();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// all done generate a EOF zero mask
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
*dst++ = 0;
|
|
actualSize += 2;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
DPF(("CompressFrame16:"));
|
|
DPF((" time: %ld", timeGetTime() - time));
|
|
DPF((" tol: %ld/%ld", threshold, thresholdInter));
|
|
DPF((" Size: %ld", actualSize));
|
|
DPF((" Skips: %ld (%ld)", numberOfSkips, numberOfSkipCodes));
|
|
DPF((" Extra Skips: %ld", numberOfExtraSkips));
|
|
DPF((" Solid: %ld", numberOfSolids));
|
|
DPF((" 4x4: %ld", numberOfBlocks));
|
|
DPF((" 2x2: %ld", numberOfEdges));
|
|
DPF((" EvilRed: %ld", numberOfEvilRed));
|
|
DPF((" 4Solid: %ld", numberOfSolid4));
|
|
|
|
return( actualSize );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
routine: CompressFrame8
|
|
purp: compress a frame, outputing 8 bit compressed data.
|
|
returns: number of bytes in compressed buffer
|
|
|
|
!!! this is almost a 1:1 copy of the above routine help!
|
|
*******************************************************************/
|
|
|
|
DWORD FAR CompressFrame8(LPBITMAPINFOHEADER lpbi, // DIB header to compress
|
|
LPVOID lpBits, // DIB bits to compress
|
|
LPVOID lpData, // put compressed data here
|
|
DWORD threshold, // edge threshold
|
|
DWORD thresholdInter, // inter-frame threshold
|
|
LPBITMAPINFOHEADER lpbiPrev, // previous frame
|
|
LPVOID lpPrev, // previous frame
|
|
LONG (CALLBACK *Status) (LPARAM lParam, UINT message, LONG l),
|
|
LPARAM lParam,
|
|
PCELLS pCells,
|
|
LPBYTE lpITable,
|
|
RGBQUAD * prgbqOut)
|
|
|
|
{
|
|
UINT bix;
|
|
UINT biy;
|
|
UINT WidthBytes;
|
|
UINT WidthBytesPrev;
|
|
|
|
WORD SkipCount;
|
|
|
|
WORD luminance[16], luminanceMean[4];
|
|
DWORD luminanceSum;
|
|
WORD sumR,sumG,sumB;
|
|
WORD sumR0[4],sumG0[4],sumB0[4];
|
|
WORD sumR1[4],sumG1[4],sumB1[4];
|
|
WORD meanR0[4],meanG0[4],meanB0[4];
|
|
WORD meanR1[4],meanG1[4],meanB1[4];
|
|
WORD zeros[4], ones[4];
|
|
UINT x,y;
|
|
WORD mask;
|
|
HPBYTE srcPtr;
|
|
HPBYTE prvPtr;
|
|
DWORD actualSize;
|
|
UINT i;
|
|
WORD mi;
|
|
HPWORD dst;
|
|
RGBQUAD rgb,rgb0,rgb1;
|
|
BYTE b, b0, b1;
|
|
WORD w;
|
|
int iStatusEvery;
|
|
|
|
#ifdef DEBUG
|
|
DWORD time = timeGetTime();
|
|
#endif
|
|
|
|
WidthBytes = DIBWIDTHBYTES(*lpbi);
|
|
|
|
if (lpbiPrev)
|
|
WidthBytesPrev = DIBWIDTHBYTES(*lpbiPrev);
|
|
|
|
bix = (int)lpbi->biWidth/WIDTH_CBLOCK;
|
|
biy = (int)lpbi->biHeight/HEIGHT_CBLOCK;
|
|
|
|
if (bix < 100)
|
|
iStatusEvery = 4;
|
|
else if (bix < 200)
|
|
iStatusEvery = 2;
|
|
else
|
|
iStatusEvery = 1;
|
|
|
|
actualSize = 0;
|
|
numberOfSkipCodes = 0;
|
|
numberOfSkips = 0;
|
|
numberOfExtraSkips = 0;
|
|
numberOfEdges = 0;
|
|
numberOfBlocks = 0;
|
|
numberOfSolids = 0;
|
|
numberOfSolid4 = 0;
|
|
numberOfSolid2 = 0;
|
|
numberOfEvilRed = 0;
|
|
|
|
dst = (HPWORD)lpData;
|
|
SkipCount = 0;
|
|
|
|
if (lpITable == NULL)
|
|
{
|
|
DPF(("ICM_COMPRESS_BEGIN not recieved!"));
|
|
return 0;
|
|
}
|
|
|
|
for( y = 0; y < biy; y++ )
|
|
{
|
|
srcPtr = lpBits;
|
|
prvPtr = lpPrev;
|
|
|
|
if (Status && ((y % iStatusEvery) == 0)) {
|
|
if (Status(lParam, ICSTATUS_STATUS, (y * 100) / biy) != 0)
|
|
return (DWORD) -1;
|
|
}
|
|
|
|
for( x = 0; x < bix; x++ )
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// get the cell to compress from the image.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
srcPtr = GetCell(lpbi, srcPtr, pCells->cell);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// see if it matches the cell in the previous frame.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
if (lpbiPrev)
|
|
{
|
|
prvPtr = GetCell(lpbiPrev, prvPtr, pCells->cellPrev);
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellPrev) <= thresholdInter)
|
|
{
|
|
skip_cell:
|
|
numberOfSkips++;
|
|
SkipCount++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// compute luminance of each pixel in the compression block
|
|
// sum the total luminance in the block
|
|
// find the pixels with the largest and smallest luminance
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
luminanceSum = 0;
|
|
sumR = 0;
|
|
sumG = 0;
|
|
sumB = 0;
|
|
|
|
for (i = 0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
{
|
|
sumR += pCells->cell[i].rgbRed;
|
|
sumG += pCells->cell[i].rgbGreen;
|
|
sumB += pCells->cell[i].rgbBlue;
|
|
|
|
luminance[i] = RgbToY(pCells->cell[i]);
|
|
luminanceSum += luminance[i];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// see if we make the cell a single color, and get away with it
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
sumR /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
|
|
sumG /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
|
|
sumB /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
|
|
|
|
rgb.rgbRed = (BYTE)sumR;
|
|
rgb.rgbGreen = (BYTE)sumG;
|
|
rgb.rgbBlue = (BYTE)sumB;
|
|
|
|
b = MAPRGB(rgb); // map color to 8bit
|
|
rgb = prgbqOut[b];
|
|
|
|
for (i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
|
|
pCells->cellT[i] = rgb;
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
|
|
{
|
|
if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
|
|
{
|
|
numberOfExtraSkips++;
|
|
goto skip_cell;
|
|
}
|
|
|
|
FlushSkips();
|
|
|
|
solid_color:
|
|
// single color!!
|
|
mask = SOLID_MAGIC | b;
|
|
|
|
*dst++ = mask;
|
|
numberOfSolids++;
|
|
actualSize += 2;
|
|
continue;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// make a 4x4 block
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
luminanceMean[0] = (WORD)(luminanceSum >> 4);
|
|
|
|
// zero summing arrays
|
|
zeros[0]=0;
|
|
ones[0] =0;
|
|
sumR0[0]=0;
|
|
sumR1[0]=0;
|
|
sumG0[0]=0;
|
|
sumG1[0]=0;
|
|
sumB0[0]=0;
|
|
sumB1[0]=0;
|
|
|
|
// define which of the two colors to choose
|
|
// for each pixel by creating the mask
|
|
mask = 0;
|
|
for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
|
|
{
|
|
if( luminance[i] < luminanceMean[0] )
|
|
{
|
|
// mask &= ~(1 << i); // already clear
|
|
zeros[0]++;
|
|
sumR0[0] += pCells->cell[i].rgbRed;
|
|
sumG0[0] += pCells->cell[i].rgbGreen;
|
|
sumB0[0] += pCells->cell[i].rgbBlue;
|
|
}
|
|
else
|
|
{
|
|
mask |= (1 << i);
|
|
ones[0]++;
|
|
sumR1[0] += pCells->cell[i].rgbRed;
|
|
sumG1[0] += pCells->cell[i].rgbGreen;
|
|
sumB1[0] += pCells->cell[i].rgbBlue;
|
|
}
|
|
}
|
|
|
|
// define the "one" color as the mean of each element
|
|
if( ones[0] != 0 )
|
|
{
|
|
meanR1[0] = sumR1[0] / ones[0];
|
|
meanG1[0] = sumG1[0] / ones[0];
|
|
meanB1[0] = sumB1[0] / ones[0];
|
|
}
|
|
else
|
|
{
|
|
meanR1[0] = meanG1[0] = meanB1[0] = 0;
|
|
}
|
|
|
|
if( zeros[0] != 0 )
|
|
{
|
|
meanR0[0] = sumR0[0] / zeros[0];
|
|
meanG0[0] = sumG0[0] / zeros[0];
|
|
meanB0[0] = sumB0[0] / zeros[0];
|
|
}
|
|
else
|
|
{
|
|
meanR0[0] = meanG0[0] = meanB0[0] = 0;
|
|
}
|
|
|
|
//
|
|
// map colors to 8-bit
|
|
//
|
|
rgb0.rgbRed = (BYTE)meanR0[0];
|
|
rgb0.rgbGreen = (BYTE)meanG0[0];
|
|
rgb0.rgbBlue = (BYTE)meanB0[0];
|
|
b0 = MAPRGB(rgb0);
|
|
rgb0 = prgbqOut[b0];
|
|
|
|
rgb1.rgbRed = (BYTE)meanR1[0];
|
|
rgb1.rgbGreen = (BYTE)meanG1[0];
|
|
rgb1.rgbBlue = (BYTE)meanB1[0];
|
|
b1 = MAPRGB(rgb1);
|
|
rgb1 = prgbqOut[b1];
|
|
|
|
//
|
|
// build the block and make sure, it is within error.
|
|
//
|
|
for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
|
|
{
|
|
if( luminance[i] < luminanceMean[0] )
|
|
pCells->cellT[i] = rgb0;
|
|
else
|
|
pCells->cellT[i] = rgb1;
|
|
}
|
|
|
|
if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
|
|
{
|
|
if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
|
|
{
|
|
numberOfExtraSkips++;
|
|
goto skip_cell;
|
|
}
|
|
|
|
FlushSkips();
|
|
|
|
// store the mask
|
|
|
|
//
|
|
// we should never, ever generate a mask of all ones or
|
|
// zeros!
|
|
//
|
|
if (mask == 0x0000)
|
|
{
|
|
DPF(("4x4 generated a zero mask!"));
|
|
b = b0;
|
|
goto solid_color;
|
|
}
|
|
|
|
if (mask == 0xFFFF)
|
|
{
|
|
DPF(("4x4 generated a FFFF mask!"));
|
|
b = b1;
|
|
goto solid_color;
|
|
}
|
|
|
|
if (b0 == b1)
|
|
{
|
|
DPF(("4x4 generated two colors the same!"));
|
|
b = b1;
|
|
goto solid_color;
|
|
}
|
|
|
|
//
|
|
// remember the high bit of the mask is used to mark
|
|
// a skip or solid color, so make sure the high bit
|
|
// is zero.
|
|
//
|
|
if (mask & 0x8000)
|
|
{
|
|
mask = ~mask;
|
|
SWAP(b0,b1);
|
|
}
|
|
|
|
*dst++ = mask;
|
|
*dst++ = (WORD)b1 | ((WORD)b0 << 8);
|
|
|
|
actualSize += 4;
|
|
numberOfBlocks++;
|
|
|
|
continue;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// make a 2x2 block
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
FlushSkips();
|
|
|
|
numberOfEdges++;
|
|
|
|
luminanceMean[0] = (luminance[0] + luminance[1] + luminance[4] + luminance[5]) / 4;
|
|
luminanceMean[1] = (luminance[2] + luminance[3] + luminance[6] + luminance[7]) / 4;
|
|
luminanceMean[2] = (luminance[8] + luminance[9] + luminance[12] + luminance[13]) / 4;
|
|
luminanceMean[3] = (luminance[10] + luminance[11] + luminance[14] + luminance[15]) / 4;
|
|
|
|
// zero summing arrays
|
|
zeros[0]=zeros[1]=zeros[2]=zeros[3]=0;
|
|
ones[0]=ones[1]=ones[2]=ones[3]=0;
|
|
sumR0[0]=sumR0[1]=sumR0[2]=sumR0[3]=0;
|
|
sumR1[0]=sumR1[1]=sumR1[2]=sumR1[3]=0;
|
|
sumG0[0]=sumG0[1]=sumG0[2]=sumG0[3]=0;
|
|
sumG1[0]=sumG1[1]=sumG1[2]=sumG1[3]=0;
|
|
sumB0[0]=sumB0[1]=sumB0[2]=sumB0[3]=0;
|
|
sumB1[0]=sumB1[1]=sumB1[2]=sumB1[3]=0;
|
|
|
|
// define which of the two colors to choose
|
|
// for each pixel by creating the mask
|
|
mask = 0;
|
|
for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
|
|
{
|
|
mi = meanIndex[i];
|
|
if( luminance[i] < luminanceMean[mi] )
|
|
{
|
|
// mask &= ~(1 << i); // already clear
|
|
zeros[mi]++;
|
|
sumR0[mi] += pCells->cell[i].rgbRed;
|
|
sumG0[mi] += pCells->cell[i].rgbGreen;
|
|
sumB0[mi] += pCells->cell[i].rgbBlue;
|
|
}
|
|
else
|
|
{
|
|
mask |= (1 << i);
|
|
ones[mi]++;
|
|
sumR1[mi] += pCells->cell[i].rgbRed;
|
|
sumG1[mi] += pCells->cell[i].rgbGreen;
|
|
sumB1[mi] += pCells->cell[i].rgbBlue;
|
|
}
|
|
}
|
|
|
|
// store the mask
|
|
//
|
|
// in the 8-bit case the mask must have the following form:
|
|
//
|
|
// 1X1XXXXXXXXXXXXX
|
|
//
|
|
// these bits can be forces high by exchanging the colors for
|
|
// the top two cells.
|
|
//
|
|
|
|
w = mask;
|
|
|
|
if (!(mask & 0x8000))
|
|
w ^= 0xCC00;
|
|
|
|
if (!(mask & 0x2000))
|
|
w ^= 0x3300;
|
|
|
|
*dst++ = w;
|
|
actualSize += 2;
|
|
|
|
// make the colors
|
|
for( i=0; i < 4; i++ )
|
|
{
|
|
// define the "one" color as the mean of each element
|
|
if( ones[i] != 0 )
|
|
{
|
|
meanR1[i] = sumR1[i] / ones[i];
|
|
meanG1[i] = sumG1[i] / ones[i];
|
|
meanB1[i] = sumB1[i] / ones[i];
|
|
}
|
|
else
|
|
{
|
|
meanR1[i] = meanG1[i] = meanB1[i] = 0;
|
|
}
|
|
|
|
if( zeros[i] != 0 )
|
|
{
|
|
meanR0[i] = sumR0[i] / zeros[i];
|
|
meanG0[i] = sumG0[i] / zeros[i];
|
|
meanB0[i] = sumB0[i] / zeros[i];
|
|
}
|
|
else
|
|
{
|
|
meanR0[i] = meanG0[i] = meanB0[i] = 0;
|
|
}
|
|
|
|
// convert to the 8bit palette, and write out the colors
|
|
// make sure to exchange the colors if we hade to invert
|
|
// the mask to normalize it.
|
|
|
|
rgb0.rgbRed = (BYTE)meanR0[i];
|
|
rgb0.rgbGreen = (BYTE)meanG0[i];
|
|
rgb0.rgbBlue = (BYTE)meanB0[i];
|
|
b0 = MAPRGB(rgb0);
|
|
|
|
rgb1.rgbRed = (BYTE)meanR1[i];
|
|
rgb1.rgbGreen = (BYTE)meanG1[i];
|
|
rgb1.rgbBlue = (BYTE)meanB1[i];
|
|
b1 = MAPRGB(rgb1);
|
|
|
|
if (i==3 && !(mask & 0x8000))
|
|
SWAP(b0,b1);
|
|
|
|
if (i==2 && !(mask & 0x2000))
|
|
SWAP(b0,b1);
|
|
|
|
if (b0 == b0)
|
|
{
|
|
numberOfSolid2++;
|
|
}
|
|
|
|
*dst++ = (WORD)b1 | ((WORD)b0 << 8);
|
|
actualSize += 2;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// next scan.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
((HPBYTE)lpBits) += WidthBytes * HEIGHT_CBLOCK;
|
|
|
|
if (lpPrev)
|
|
((HPBYTE)lpPrev) += WidthBytesPrev * HEIGHT_CBLOCK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// take care of any outstanding skips, !!! note we dont need this if we
|
|
// assume a EOF!
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
FlushSkips();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// all done generate a EOF zero mask
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
*dst++ = 0;
|
|
actualSize += 2;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
DPF(("CompressFrame8:"));
|
|
DPF((" time: %ld", timeGetTime() - time));
|
|
DPF((" tol: %ld/%ld", threshold, thresholdInter));
|
|
DPF((" Size: %ld", actualSize));
|
|
DPF((" Skips: %ld (%ld)", numberOfSkips, numberOfSkipCodes));
|
|
DPF((" Extra Skips: %ld", numberOfExtraSkips));
|
|
DPF((" Solid: %ld", numberOfSolids));
|
|
DPF((" 4x4: %ld", numberOfBlocks));
|
|
DPF((" 2x2: %ld", numberOfEdges));
|
|
|
|
return( actualSize );
|
|
}
|
|
|
|
|