|
Volume Number: | 10 | |||
Issue Number: | 7 | |||
Column Tag: | Programmers’ Challenge |
Programmers’ Challenge
By Mike Scanlin, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
Color Space Conversion
Typically, when an RGB image is compressed into JPEG data, it is first converted into separate luminance (Y) and chrominance (U and V) components. Although JPEG doesn’t specify which color space conversion to use, a commonly used one is:
Y 0.29900000 0.58700000 0.11400000 R U = -0.16873590 -0.33126410 0.50000000 * G V 0.50000000 -0.41868760 -0.08131241 B
where R, G and B are unsigned chars (0..255). For the outputs, Y is an unsigned char (0..255) while U and V are signed chars (-128..127).
The prototype of the two functions you write are:
/* 1 */ void *RGBtoYUVInit(void); void RGBtoYUV(rPtr, gPtr, bPtr, yPtr, uPtr, vPtr, numPixels,privateDataPtr) unsigned char *rPtr; unsigned char *gPtr; unsigned char *bPtr; unsigned char *yPtr; signed char *uPtr; signed char *vPtr; unsigned long numPixels; void *privateDataPtr;
This month you’re being given a chance to have a separate initialization routine that will not be timed (only the RGBtoYUV will count towards your time). It can create whatever lookup tables RGBtoYUV may need and return a pointer to that private data. The return value from RGBtoYUVInit will be passed to RGBtoYUV as the privateDataPtr parameter. You decide what it points to (if anything).
There are two key aspects to writing RGBtoYUV. The first is that it has to be fast (as always). The second, though, is that it has to be accurate (or else when someone reconstructs the image with the inverse conversion image quality will be lost). Even though the outputs are only 8 bits, the matrix coefficients require much more than that to represent. Your output values must equal what you would get if you carried out the matrix math with complete precision and then rounded the results down to 8 bits as the last step (with .5 rounding down to zero). For instance, if R = 3, G = 17 and B = 23 then: Y = 3*.299 + 17*.587 + 23*.114 which is 13.498. When rounded this becomes 13 which is what you should return as part of the buffer that yPtr points to.
Each of the pointers to the RGB input data and YUV output data point to a buffer filled with data of one component (so there are 6 buffers total). numPixels is between 1 and 1,000,000 and is the size of each buffer. If numPixels were 100 then rPtr would point to 100 red values and gPtr and bPtr would point to 100 corresponding green and blue values. Your routine would then set the 100 bytes pointed to by yPtr to the appropriate Y values (and likewise for the U and V values, too).
The RGB and YUV buffers will be allocated for you. Your initialization routine may allocate up to 1MB of lookup tables if it wants to (it will be able to get a contiguous 1MB piece if it needs it).
TWO MONTHS AGO WINNER
We have a new first-time winner this month. Congrats to Troy Anderson (Paradise Valley, AZ) for his somewhat large but definitely fast entry in the Flip Horizontal challenge. He was faster than second place winner Bob Boonstra (Westford, MA) in every case that I tested. No small feat considering that Bob is a three-time Challenge winner. Troy also beat another three-time winner, Bill Karsh (Chicago, IL), in almost every test case. Unfortunately, Bill may have been too ecstatic with his win last month to test every possible case this month and unfortunately I had to disqualify his entry for lack of correctness.
Here are the code sizes and times. The time numbers represents the sum of the times for many different inputs (different depths, different rowBytes, etc). Numbers in parens after a person’s name indicate how many times that person has finished in the top 5 places of all previous Programmer Challenges, not including this one:
Name time code+data
Troy Anderson 759 2442
Bob Boonstra (8) 818 1564
Allen Stenger (5) 1069 1318
Michael Panchenko 2952 616
The best way to do well at the Flip Horizontal problem is to write dedicated code to handle each possible depth. That’s exactly what Troy did. He then went even further by special casing certain common cases, such as when rowBytes is a multiple of four.
Troy also solved the flip-byte problem (that exists when the depth is less than 8) the same way that almost everyone else did: with a lookup table for each case (1-bit, 2-bit and 4-bit). For example, when you’re flipping a bitmap horizontally it becomes necessary to flip all 8 bits in a byte. With a 256 element lookup table you can do this in a single lookup.
The 8-bit, 16-bit and 32-bit deep cases are all very similar. Troy reuses similar code by letting the preprocessor fill in the types of his variables (he uses the #define T for this purpose).
Another way of doing this, if the code is similar enough for each case, is to make the whole routine a macro and have it take a parameter which represents the type (byte, short, etc) that you want the code generated for. For instance, Bob Boonstra created this macro:
/* 2 */ /* Macro DoFlipHoriz handles cases where a pixel is one byte, word, or longword in size. */ #define DoFlipHoriz(tp) \ { \ /* loopCount=numCols/2 has already been calculated. */ \ if (0 < loopCount) do { \ register tp *p,*q; \ p = (tp *)base; \ q = p+numCols; \ cCount = loopCount; \ do { \ register tp temp; \ temp = *p; \ *p++ = *--q; \ *q = temp; \ } while (--cCount); \ base += rowBytes; \ } while (--rCount); \ } and then uses it like this in part of his solution: register short cCount,rCount,loopCount; rCount = numRows; loopCount = numCols>>1; if (8 == pixSize) DoFlipHoriz(uchar) else if (16==pixSize) DoFlipHoriz(ushort) else /*if (32==pixSize)*/ DoFlipHoriz(ulong) You’ll get 3 copies of the macro’s code, each for a different size pixel. Here’s Troy’s winning solution: // MacTech Magazine Programmers' Challenge // May, 1994 // Submitted by Troy Anderson // // Copyright (c) 1994 Troy L. Anderson #include <QDOffscreen.h> typedef unsigned char UCHAR; prototypes void FlipPixMapHorz( PixMapHandle thePixMapHndl); static void Flip_Long( PixMapHandle theMap, short rowBytes, short depth, Rect* area); static void Flip_Word( PixMapHandle theMap, short rowBytes, short depth, Rect* area); static void ExchangeWords_Long( PixMapHandle theMap, short rowBytes, short depth, Rect* area); static void ExchangeWords_Word( PixMapHandle theMap, short rowBytes, short depth, Rect* area); static void ExchangeWords_Byte( PixMapHandle theMap, short rowBytes, short depth, Rect* area); FlipPixMapHorz // This could be made a bit faster by in-lining the functions, but this is much clearer, // and not very much slower. void FlipPixMapHorz( PixMapHandle thePixMapHndl) { short rowBytes = (**thePixMapHndl).rowBytes & 0x7fff; Boolean longAligned = rowBytes % 4 == 0; short depth = (**thePixMapHndl).pixelSize; Rect bounds = (**thePixMapHndl).bounds; switch( depth) { case 1: case 2: case 4: if (longAligned) Flip_Long( thePixMapHndl, rowBytes, depth, &bounds); else Flip_Word( thePixMapHndl, rowBytes, depth, &bounds); break; case 8: ExchangeWords_Byte( thePixMapHndl, rowBytes, depth, &bounds); break; case 16: ExchangeWords_Word( thePixMapHndl, rowBytes, depth, &bounds); break; case 32: ExchangeWords_Long( thePixMapHndl, rowBytes, depth, &bounds); break; } } ExchangeWords_Long long word alignment version static void ExchangeWords_Long( PixMapHandle theMap, short rowBytes, short depth, Rect* area) { #undef T #define T long short rowCells = rowBytes / sizeof(T); short numCells = ((area->right - area->left) * depth + sizeof(T)*8 - 1) / (sizeof(T)*8); T temp; register T *cellPtr1, *cellPtr2; T *aRow; T *firstRow = (T*)GetPixBaseAddr( theMap); T *lastRow = firstRow + rowCells * (long)(area->bottom - area->top); // Flip the words in each row for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) for ( cellPtr1 = aRow + numCells-1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) temp = *cellPtr1, // swap them *cellPtr1 = *cellPtr2, *cellPtr2 = temp; } ExchangeWords word alignment version static void ExchangeWords_Word( PixMapHandle theMap, short rowBytes, short depth, Rect* area) { #undef T #define T short short rowCells = rowBytes / sizeof(T); short numCells = ((area->right - area->left) * depth + sizeof(T)*8 - 1) / (sizeof(T)*8); T temp; register T *cellPtr1, *cellPtr2; T *aRow; T *firstRow = (T*)GetPixBaseAddr( theMap); T *lastRow = firstRow + rowCells * (long)(area->bottom - area->top); // Flip the words in each row for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) for ( cellPtr1 = aRow + numCells-1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) temp = *cellPtr1, // swap them *cellPtr1 = *cellPtr2, *cellPtr2 = temp; } ExchangeWords byte alignment version static void ExchangeWords_Byte( PixMapHandle theMap, short rowBytes, short depth, Rect* area) { #undef T #define T char short rowCells = rowBytes / sizeof(T); short numCells = ((area->right - area->left) * depth + sizeof(T)*8 - 1) / (sizeof(T)*8); T temp; register T *cellPtr1, *cellPtr2; T *aRow; T *firstRow = (T*)GetPixBaseAddr( theMap); T *lastRow = firstRow + rowCells * (long)(area->bottom - area->top); // Flip the words in each row for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) for ( cellPtr1 = aRow + numCells-1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) temp = *cellPtr1, // swap them *cellPtr1 = *cellPtr2, *cellPtr2 = temp; } Inverse tables // Inverse tables used to flip the bits in a byte - // index is input, value is inverse of index // This is the 1-bit per pixel table static char byteFlips1[] ={ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff }; // This is the 2-bits per pixel table static char byteFlips2[] ={ 0x00, 0x40, 0x80, 0xc0, 0x10, 0x50, 0x90, 0xd0, 0x20, 0x60, 0xa0, 0xe0, 0x30, 0x70, 0xb0, 0xf0, 0x04, 0x44, 0x84, 0xc4, 0x14, 0x54, 0x94, 0xd4, 0x24, 0x64, 0xa4, 0xe4, 0x34, 0x74, 0xb4, 0xf4, 0x08, 0x48, 0x88, 0xc8, 0x18, 0x58, 0x98, 0xd8, 0x28, 0x68, 0xa8, 0xe8, 0x38, 0x78, 0xb8, 0xf8, 0x0c, 0x4c, 0x8c, 0xcc, 0x1c, 0x5c, 0x9c, 0xdc, 0x2c, 0x6c, 0xac, 0xec, 0x3c, 0x7c, 0xbc, 0xfc, 0x01, 0x41, 0x81, 0xc1, 0x11, 0x51, 0x91, 0xd1, 0x21, 0x61, 0xa1, 0xe1, 0x31, 0x71, 0xb1, 0xf1, 0x05, 0x45, 0x85, 0xc5, 0x15, 0x55, 0x95, 0xd5, 0x25, 0x65, 0xa5, 0xe5, 0x35, 0x75, 0xb5, 0xf5, 0x09, 0x49, 0x89, 0xc9, 0x19, 0x59, 0x99, 0xd9, 0x29, 0x69, 0xa9, 0xe9, 0x39, 0x79, 0xb9, 0xf9, 0x0d, 0x4d, 0x8d, 0xcd, 0x1d, 0x5d, 0x9d, 0xdd, 0x2d, 0x6d, 0xad, 0xed, 0x3d, 0x7d, 0xbd, 0xfd, 0x02, 0x42, 0x82, 0xc2, 0x12, 0x52, 0x92, 0xd2, 0x22, 0x62, 0xa2, 0xe2, 0x32, 0x72, 0xb2, 0xf2, 0x06, 0x46, 0x86, 0xc6, 0x16, 0x56, 0x96, 0xd6, 0x26, 0x66, 0xa6, 0xe6, 0x36, 0x76, 0xb6, 0xf6, 0x0a, 0x4a, 0x8a, 0xca, 0x1a, 0x5a, 0x9a, 0xda, 0x2a, 0x6a, 0xaa, 0xea, 0x3a, 0x7a, 0xba, 0xfa, 0x0e, 0x4e, 0x8e, 0xce, 0x1e, 0x5e, 0x9e, 0xde, 0x2e, 0x6e, 0xae, 0xee, 0x3e, 0x7e, 0xbe, 0xfe, 0x03, 0x43, 0x83, 0xc3, 0x13, 0x53, 0x93, 0xd3, 0x23, 0x63, 0xa3, 0xe3, 0x33, 0x73, 0xb3, 0xf3, 0x07, 0x47, 0x87, 0xc7, 0x17, 0x57, 0x97, 0xd7, 0x27, 0x67, 0xa7, 0xe7, 0x37, 0x77, 0xb7, 0xf7, 0x0b, 0x4b, 0x8b, 0xcb, 0x1b, 0x5b, 0x9b, 0xdb, 0x2b, 0x6b, 0xab, 0xeb, 0x3b, 0x7b, 0xbb, 0xfb, 0x0f, 0x4f, 0x8f, 0xcf, 0x1f, 0x5f, 0x9f, 0xdf, 0x2f, 0x6f, 0xaf, 0xef, 0x3f, 0x7f, 0xbf, 0xff }; // This is the 4-bits per pixel table static char byteFlips4[] ={ 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1, 0xf1, 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xa2, 0xb2, 0xc2, 0xd2, 0xe2, 0xf2, 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xa3, 0xb3, 0xc3, 0xd3, 0xe3, 0xf3, 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4, 0xc4, 0xd4, 0xe4, 0xf4, 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8, 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9, 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa, 0x0b, 0x1b, 0x2b, 0x3b, 0x4b, 0x5b, 0x6b, 0x7b, 0x8b, 0x9b, 0xab, 0xbb, 0xcb, 0xdb, 0xeb, 0xfb, 0x0c, 0x1c, 0x2c, 0x3c, 0x4c, 0x5c, 0x6c, 0x7c, 0x8c, 0x9c, 0xac, 0xbc, 0xcc, 0xdc, 0xec, 0xfc, 0x0d, 0x1d, 0x2d, 0x3d, 0x4d, 0x5d, 0x6d, 0x7d, 0x8d, 0x9d, 0xad, 0xbd, 0xcd, 0xdd, 0xed, 0xfd, 0x0e, 0x1e, 0x2e, 0x3e, 0x4e, 0x5e, 0x6e, 0x7e, 0x8e, 0x9e, 0xae, 0xbe, 0xce, 0xde, 0xee, 0xfe, 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff }; Flip_Long static void Flip_Long( PixMapHandle theMap, short rowBytes, short depth, Rect* area) { #undef T #define T long register UCHAR temp; short rowCells = rowBytes / sizeof(T); long bitsPerRow = (area->right - area->left) * (long)depth - 1; short numCells = (bitsPerRow + sizeof(T)*8) / (sizeof(T)*8); T* cellPtr; T* aRow; T* firstRow = (T*)GetPixBaseAddr( theMap); T* lastRow = firstRow + rowCells * (long)(area->bottom - area->top); register T* cellPtr1, *cellPtr2; short numBitsToShift = ((sizeof(T)*8) - (bitsPerRow % (sizeof(T)*8) + 1)); T shiftMask; T* shiftCellPtr; char* flipTable; switch(depth) { case 1: flipTable = byteFlips1; break; case 2: flipTable = byteFlips2; break; case 4: flipTable = byteFlips4; break; } if (numBitsToShift) { shiftMask = (1L << numBitsToShift) - 1; for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) { // With each pair of cells in the row (one on the left, the other on the right), // flip the pixels in the individual cells and swap the cells with one another. for ( cellPtr1 = aRow + numCells - 1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr2)[3]]; ((UCHAR*)cellPtr2)[3] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[1]; ((UCHAR*)cellPtr1)[1] = flipTable[((UCHAR*)cellPtr2)[2]]; ((UCHAR*)cellPtr2)[2] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[2]; ((UCHAR*)cellPtr1)[2] = flipTable[((UCHAR*)cellPtr2)[1]]; ((UCHAR*)cellPtr2)[1] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[3]; ((UCHAR*)cellPtr1)[3] = flipTable[((UCHAR*)cellPtr2)[0]]; ((UCHAR*)cellPtr2)[0] = flipTable[temp]; } // If there's an odd number of cells in the row, there is one cell we haven't // touched. It needs to be flipped. if (cellPtr1 == cellPtr2) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr1)[3]]; ((UCHAR*)cellPtr1)[3] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[1]; ((UCHAR*)cellPtr1)[1] = flipTable[((UCHAR*)cellPtr1)[2]]; ((UCHAR*)cellPtr1)[2] = flipTable[temp]; } // Slide the pixels to the left for ( shiftCellPtr = aRow; shiftCellPtr < aRow + rowCells; shiftCellPtr++) { // shift the bits over *shiftCellPtr <<= numBitsToShift; // bring in the bits from the next cell - garbage will be brought in during // the last iteration, but it’s put into the last cell, outside the bounds of the // image (but still in the data area) *shiftCellPtr |= shiftMask & (*(shiftCellPtr+1) >> (sizeof(T)*8 - numBitsToShift)); } } } else // no need to shift pixels, otherwise, just the same as previous loop for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) { // With each pair of cells in the row (one on the left, the other on the right), // flip the pixels in the individual cells and swap the cells with one another. for ( cellPtr1 = aRow + numCells - 1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr2)[3]]; ((UCHAR*)cellPtr2)[3] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[1]; ((UCHAR*)cellPtr1)[1] = flipTable[((UCHAR*)cellPtr2)[2]]; ((UCHAR*)cellPtr2)[2] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[2]; ((UCHAR*)cellPtr1)[2] = flipTable[((UCHAR*)cellPtr2)[1]]; ((UCHAR*)cellPtr2)[1] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[3]; ((UCHAR*)cellPtr1)[3] = flipTable[((UCHAR*)cellPtr2)[0]]; ((UCHAR*)cellPtr2)[0] = flipTable[temp]; } // If there are an odd number of cells in the row, // there is one cell we haven't touched. // It needs to be flipped. if (cellPtr1 == cellPtr2) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr1)[3]]; ((UCHAR*)cellPtr1)[3] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[1]; ((UCHAR*)cellPtr1)[1] = flipTable[((UCHAR*)cellPtr1)[2]]; ((UCHAR*)cellPtr1)[2] = flipTable[temp]; } } } } Flip_Word static void Flip_Word( PixMapHandle theMap, short rowBytes, short depth, Rect* area) { #undef T #define T short register UCHAR temp; short rowCells = rowBytes / sizeof(T); long bitsPerRow = (area->right - area->left) * (long)depth - 1; short numCells = (bitsPerRow + sizeof(T)*8) / (sizeof(T)*8); T* cellPtr; T* aRow; T* firstRow = (T*)GetPixBaseAddr( theMap); T* lastRow = firstRow + rowCells * (long)(area->bottom - area->top); register T* cellPtr1, *cellPtr2; short numBitsToShift = ((sizeof(T)*8) - (bitsPerRow % (sizeof(T)*8) + 1)); T shiftMask; T* shiftCellPtr; char* flipTable; switch(depth) { case 1: flipTable = byteFlips1; break; case 2: flipTable = byteFlips2; break; case 4: flipTable = byteFlips4; break; } if (numBitsToShift) { shiftMask = (1L << numBitsToShift) - 1; for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) { // With each pair of cells in the row (one on the left, the other on the right), // flip the pixels in the individual cells and swap the cells with one another. for ( cellPtr1 = aRow + numCells - 1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr2)[1]]; ((UCHAR*)cellPtr2)[1] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[1]; ((UCHAR*)cellPtr1)[1] = flipTable[((UCHAR*)cellPtr2)[0]]; ((UCHAR*)cellPtr2)[0] = flipTable[temp]; } // If there's an odd number of cells in the row, there is one cell we haven't // touched. It needs to be flipped. if (cellPtr1 == cellPtr2) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr1)[1]]; ((UCHAR*)cellPtr1)[1] = flipTable[temp]; } // Slide the pixels to the left for ( shiftCellPtr = aRow; shiftCellPtr < aRow + rowCells; shiftCellPtr++) { // shift the bits over *shiftCellPtr <<= numBitsToShift; // bring in the bits from the next cell - garbage will be brought in during last // iteration, but it’s put into the last // cell, outside the bounds of the image (but still in the data area) *shiftCellPtr |= shiftMask & (*(shiftCellPtr+1) >> (sizeof(T)*8 - numBitsToShift)); } } } else // no need to shift pixels, otherwise, just the same as previous loop for ( aRow = firstRow; aRow < lastRow; aRow += rowCells) { // With each pair of cells in the row (one on the // left, the other on the right), flip the pixels // in the individual cells and swap the cells with // one another. for ( cellPtr1 = aRow + numCells - 1, cellPtr2 = aRow; cellPtr1 > cellPtr2; cellPtr1--, cellPtr2++) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr2)[1]]; ((UCHAR*)cellPtr2)[1] = flipTable[temp]; temp = ((UCHAR*)cellPtr1)[1]; ((UCHAR*)cellPtr1)[1] = flipTable[((UCHAR*)cellPtr2)[0]]; ((UCHAR*)cellPtr2)[0] = flipTable[temp]; } // If there are an odd number of cells in the row, // there is one cell we haven't touched. // It needs to be flipped. if (cellPtr1 == cellPtr2) { temp = ((UCHAR*)cellPtr1)[0]; ((UCHAR*)cellPtr1)[0] = flipTable[((UCHAR*)cellPtr1)[1]]; ((UCHAR*)cellPtr1)[1] = flipTable[temp]; } } } }
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine