/*----------------------------------------------------------------------+ | 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 #include #include #endif #include #include #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 //#include //#include #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); 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); 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); 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); 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