#include "stdafx.h" #include "dithers.h" #ifndef _DEBUG #define INLINE __inline #else #define INLINE #endif //----------------------------------------------------------------------------- // helpers //----------------------------------------------------------------------------- const BYTE g_abClamp[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46, 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68, 69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89, 90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108, 109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142, 143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, 177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193, 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227, 228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244, 245,246,247,248,249,250,251,252,253,254,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255 }; INLINE UINT Clamp8(int z) { #ifdef _DEBUG UINT t = (z & 0xff00) ? (0xff & ~(z >> 16)) : z; if (t != g_abClamp[z + 128]) DebugBreak(); #endif return g_abClamp[z + 128]; } INLINE WORD rgb555(BYTE r, BYTE g, BYTE b) { return( WORD( ((((WORD)(r) << 5) | (WORD)(g)) << 5) | (WORD)(b) ) ); } //----------------------------------------------------------------------------- // Halftoning stuff //----------------------------------------------------------------------------- // // This table is used to halftone from 8 to 5 bpp. Typically, 16 bit // halftoning code will map an 8 bit value to a 5 bit value, map it back to // 8 bits, compute some error, and use a halftone table to adjust the 5 bit // value for the error. This array is a concatenation of 8 different 8-to-5 // tables that include the error factoring in their mapping. It is used with // the halftoning table below, which gives indices to each of the mapping // tables within the array. Given the correct table pointer for a pixel, // callers can perform a single lookup per color component in this table to get // a halftoned 5 bit component. // #pragma data_seg(".text", "CODE") BYTE aHT16Data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 31, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31 }; UINT aHT16Heads[4][4] = { 262, 256, 261, 0, 258, 260, 257, 259, 261, 0, 262, 256, 257, 259, 258, 260, }; #pragma data_seg() INLINE UINT * Get555HalftoneRow(UINT y) { return aHT16Heads[y % 4]; } INLINE BYTE * Get555HalftoneTable(UINT *row, UINT x) { return aHT16Data + row[x % 4]; } //----------------------------------------------------------------------------- // Rounding stuff //----------------------------------------------------------------------------- // // round an 8bit value to a 5bit value with good distribution // #if 0 // not presently used #pragma data_seg(".text", "CODE") BYTE aRound8to5[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, }; #pragma data_seg() #endif // not presently used // // complement of table above // #pragma data_seg(".text", "CODE") BYTE aRound5to8[] = { 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99,107,115,123, 132,140,148,156,165,173,181,189,197,206,214,222,230,239,247,255, }; #pragma data_seg() /////////////////////////////////////////////////////////////////////////////// // // Dithering stuff. // // This code implements error-diffusion to an arbitrary set of colors, // optionally with transparency. Since the output colors can be arbitrary, // the color picker for the dither is a 32k inverse-mapping table. 24bpp // values are whacked down to 16bpp (555) and used as indices into the table. // To compensate for posterization effects when converting 24bpp to 16bpp, an // ordered dither (16bpp halftone) is used to generate the 555 color. // /////////////////////////////////////////////////////////////////////////////// INLINE void SwapError(ERRBUF **a, ERRBUF **b) { ERRBUF *te; te = *a; *a = *b; *b = te; } INLINE void ZeroError(ERRBUF *err, size_t pels) { ZeroMemory(err - 1, ErrbufBytes(pels)); } /////////////////////////////////////////////////////////////////////////////// // // Dith8to8() 8bpp to 8bpp dither. // // Computes the 24bpp source color by combining the source pixel's color table // entry with accumulated error for the pixel. Halftones this 24bpp value to a // 16bpp 555 color. Uses the 16bpp color as a lookup into an inverse mapping // table to pick the output color for the pixel. Uses the destination color // table entry to compute and accumulates error for neighboring pixels. // /////////////////////////////////////////////////////////////////////////////// INLINE void DithScan8to8(BYTE *dst, const BYTE *src, const RGBQUAD *colorsIN, const RGBQUAD *colorsOUT, const BYTE *map, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT xl, UINT y) { UINT *row = Get555HalftoneRow(y); for (; x < xl; src++, x++) { register const RGBQUAD *pChosen; register BYTE *tbl; register int r; register int g; register int b; pChosen = colorsIN + *src; r = Clamp8((int)pChosen->rgbRed + (cur_err->r >> 4)); g = Clamp8((int)pChosen->rgbGreen + (cur_err->g >> 4)); b = Clamp8((int)pChosen->rgbBlue + (cur_err->b >> 4)); cur_err++; tbl = Get555HalftoneTable(row, x); pChosen = colorsOUT + (*dst++ = map[rgb555(tbl[r], tbl[g], tbl[b])]); r -= (int)pChosen->rgbRed; (nxt_err+1)->r += r * 1; (nxt_err-1)->r += r * 3; (nxt_err )->r += r * 5; (cur_err )->r += r * 7; g -= (int)pChosen->rgbGreen; (nxt_err+1)->g += g * 1; (nxt_err-1)->g += g * 3; (nxt_err )->g += g * 5; (cur_err )->g += g * 7; b -= (int)pChosen->rgbBlue; (nxt_err+1)->b += b * 1; (nxt_err-1)->b += b * 3; (nxt_err )->b += b * 5; (cur_err )->b += b * 7; nxt_err++; } } void Dith8to8(BYTE *dst, const BYTE *src, int dst_next_scan, int src_next_scan, const RGBQUAD *colorsIN, const RGBQUAD *colorsOUT, const BYTE *map, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT cx, UINT y, int cy) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else dy = 1; if (y & 1) SwapError(&cur_err, &nxt_err); while (cy) { DithScan8to8(dst, src, colorsIN, colorsOUT, map, cur_err, nxt_err, x, cx, y); ZeroError(cur_err, cx); SwapError(&cur_err, &nxt_err); *(BYTE **)&src += src_next_scan; *(BYTE **)&dst += dst_next_scan; y += dy; cy--; } } INLINE void ConvertScan8to8( BYTE* pbDest, const BYTE* pbSrc, const RGBQUAD* prgbColors, const BYTE* pbMap, UINT x, UINT xl, UINT y ) { UINT* pRow = Get555HalftoneRow( y ); BYTE* pbTable; BYTE r; BYTE g; BYTE b; for (; x < xl; pbSrc += 3, x++ ) { r = prgbColors[*pbSrc].rgbRed; g = prgbColors[*pbSrc].rgbGreen; b = prgbColors[*pbSrc].rgbBlue; pbTable = Get555HalftoneTable( pRow, x ); *pbDest = pbMap[rgb555( pbTable[r], pbTable[g], pbTable[b] )]; } } void Convert8to8( BYTE* pbDest, const BYTE* pbSrc, int nDestPitch, int nSrcPitch, const RGBQUAD* prgbColors, const BYTE* pbMap, UINT x, UINT nWidth, UINT y, int nHeight ) { int dy; UINT x2; x2 = x+nWidth; if( nHeight < 0 ) { dy = -1; nHeight *= -1; } else { dy = 1; } while( nHeight ) { ConvertScan8to8( pbDest, pbSrc, prgbColors, pbMap, x, x2, y ); pbSrc += nSrcPitch; pbDest += nDestPitch; y += dy; nHeight--; } } INLINE void DithScanGray8to8( BYTE* pbDest, const BYTE* pbSrc, const RGBQUAD* prgbColors, const BYTE* pbMap, ERRBUF* pCurrentError, ERRBUF* pNextError, UINT x, UINT xl, UINT y ) { BYTE bSrc; BYTE bDest; UINT* pRow = Get555HalftoneRow( y ); for(; x < xl; pbSrc++, x++ ) { const RGBQUAD* prgbChosen; BYTE* pbTable; int r; int g; int b; bSrc = *pbSrc; r = Clamp8( (int)bSrc + pCurrentError->r/16 ); g= Clamp8( (int)bSrc + pCurrentError->g/16 ); b = Clamp8( (int)bSrc + pCurrentError->b/16 ); pCurrentError++; pbTable = Get555HalftoneTable( pRow, x ); bDest = pbMap[rgb555( pbTable[r], pbTable[g], pbTable[b] )]; prgbChosen = prgbColors+bDest; *pbDest = bDest; pbDest++; r -= (int)prgbChosen->rgbRed; (pNextError+1)->r += r * 1; (pNextError-1)->r += r * 3; (pNextError)->r += r * 5; (pCurrentError)->r += r * 7; g -= (int)prgbChosen->rgbGreen; (pNextError+1)->g += g * 1; (pNextError-1)->g += g * 3; (pNextError)->g += g * 5; (pCurrentError)->g += g * 7; b -= (int)prgbChosen->rgbBlue; (pNextError+1)->b += b * 1; (pNextError-1)->b += b * 3; (pNextError)->b += b * 5; (pCurrentError)->b += b * 7; pNextError++; } } void DithGray8to8( BYTE* pbDest, const BYTE* pbSrc, int nDestPitch, int nSrcPitch, const RGBQUAD* prgbColors, const BYTE* pbMap, ERRBUF* pCurrentError, ERRBUF* pNextError, UINT x, UINT cx, UINT y, int cy ) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else { dy = 1; } if (y & 1) { SwapError( &pCurrentError, &pNextError ); } while (cy) { DithScanGray8to8( pbDest, pbSrc, prgbColors, pbMap, pCurrentError, pNextError, x, cx, y ); ZeroError( pCurrentError, cx ); SwapError( &pCurrentError, &pNextError ); *(BYTE **)&pbSrc += nSrcPitch; *(BYTE **)&pbDest += nDestPitch; y += dy; cy--; } } INLINE void ConvertScanGray8to8( BYTE* pbDest, const BYTE* pbSrc, const BYTE* pbMap, UINT x, UINT xl, UINT y ) { UINT* pRow = Get555HalftoneRow( y ); BYTE* pbTable; BYTE g; for (; x < xl; pbSrc++, x++ ) { g = *pbSrc; pbTable = Get555HalftoneTable( pRow, x ); *pbDest = pbMap[rgb555( pbTable[g], pbTable[g], pbTable[g] )]; } } void ConvertGray8to8( BYTE* pbDest, const BYTE* pbSrc, int nDestPitch, int nSrcPitch, const BYTE* pbMap, UINT x, UINT nWidth, UINT y, int nHeight ) { int dy; UINT x2; x2 = x+nWidth; if( nHeight < 0 ) { dy = -1; nHeight *= -1; } else { dy = 1; } while( nHeight ) { ConvertScanGray8to8( pbDest, pbSrc, pbMap, x, x2, y ); pbSrc += nSrcPitch; pbDest += nDestPitch; y += dy; nHeight--; } } /////////////////////////////////////////////////////////////////////////////// // // Dith8to8t() 8bpp to 8bpp dither with transparency. // // If the source pixel is the given source transparency color, this routine // picks the destination transparency color for output and zero error is // accumulated to the pixel's neighbors. // Otherwise, this routine functions identically to Dith8to8. // /////////////////////////////////////////////////////////////////////////////// INLINE void DithScan8to8t(BYTE *dst, const BYTE *src, const RGBQUAD *colorsIN, const RGBQUAD *colorsOUT, const BYTE *map, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT xl, UINT y, BYTE indexTxpOUT, BYTE indexTxpIN) { UINT *row = Get555HalftoneRow(y); for (; x < xl; src++, x++) { register const RGBQUAD *pChosen; register BYTE *tbl; register BYTE index; register int r; register int g; register int b; index = *src; if (index == indexTxpIN) { *dst++ = indexTxpOUT; cur_err++; nxt_err++; continue; } pChosen = colorsIN + index; r = Clamp8((int)pChosen->rgbRed + (cur_err->r >> 4)); g = Clamp8((int)pChosen->rgbGreen + (cur_err->g >> 4)); b = Clamp8((int)pChosen->rgbBlue + (cur_err->b >> 4)); cur_err++; tbl = Get555HalftoneTable(row, x); pChosen = colorsOUT + (*dst++ = map[rgb555(tbl[r], tbl[g], tbl[b])]); r -= (int)pChosen->rgbRed; (nxt_err+1)->r += r * 1; (nxt_err-1)->r += r * 3; (nxt_err )->r += r * 5; (cur_err )->r += r * 7; g -= (int)pChosen->rgbGreen; (nxt_err+1)->g += g * 1; (nxt_err-1)->g += g * 3; (nxt_err )->g += g * 5; (cur_err )->g += g * 7; b -= (int)pChosen->rgbBlue; (nxt_err+1)->b += b * 1; (nxt_err-1)->b += b * 3; (nxt_err )->b += b * 5; (cur_err )->b += b * 7; nxt_err++; } } void Dith8to8t(BYTE *dst, const BYTE *src, int dst_next_scan, int src_next_scan, const RGBQUAD *colorsIN, const RGBQUAD *colorsOUT, const BYTE *map, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT cx, UINT y, int cy, BYTE indexTxpOUT, BYTE indexTxpIN) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else dy = 1; if (y & 1) SwapError(&cur_err, &nxt_err); while (cy) { DithScan8to8t(dst, src, colorsIN, colorsOUT, map, cur_err, nxt_err, x, cx, y, indexTxpOUT, indexTxpIN); ZeroError(cur_err, cx); SwapError(&cur_err, &nxt_err); *(BYTE **)&src += src_next_scan; *(BYTE **)&dst += dst_next_scan; y += dy; cy--; } } /////////////////////////////////////////////////////////////////////////////// // // Dith8to16() 8bpp to 16bpp dither. // // Computes the 24bpp source color by combining the source pixel's color table // entry with accumulated error for the pixel. Halftones this 24bpp value to a // 16bpp 555 color. Remaps this color to 24bpp to compute and accumulates // error for neighboring pixels. // /////////////////////////////////////////////////////////////////////////////// INLINE void DithScan8to16(WORD *dst, const BYTE *src, const RGBQUAD *colors, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT xl, UINT y) { UINT *row = Get555HalftoneRow(y); for (; x < xl; src++, x++) { register const RGBQUAD *pChosen; register WORD wColor; register BYTE *tbl; register int r; register int g; register int b; pChosen = colors + *src; r = Clamp8((int)pChosen->rgbRed + cur_err->r / 16); g = Clamp8((int)pChosen->rgbGreen + cur_err->g / 16); b = Clamp8((int)pChosen->rgbBlue + cur_err->b / 16); cur_err++; tbl = Get555HalftoneTable(row, x); wColor = (*dst++ = rgb555(tbl[r], tbl[g], tbl[b])); b -= (int)aRound5to8[wColor & 0x1f]; (nxt_err+1)->b += b * 1; (nxt_err-1)->b += b * 3; (nxt_err )->b += b * 5; (cur_err )->b += b * 7; g -= (int)aRound5to8[(wColor >> 5) & 0x1f]; (nxt_err+1)->g += g * 1; (nxt_err-1)->g += g * 3; (nxt_err )->g += g * 5; (cur_err )->g += g * 7; r -= (int)aRound5to8[(wColor >> 10) & 0x1f]; (nxt_err+1)->r += r * 1; (nxt_err-1)->r += r * 3; (nxt_err )->r += r * 5; (cur_err )->r += r * 7; nxt_err++; } } void Dith8to16(WORD *dst, const BYTE *src, int dst_next_scan, int src_next_scan, const RGBQUAD *colors, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT cx, UINT y, int cy) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else dy = 1; if (y & 1) SwapError(&cur_err, &nxt_err); while (cy) { DithScan8to16(dst, src, colors, cur_err, nxt_err, x, cx, y); ZeroError(cur_err, cx); SwapError(&cur_err, &nxt_err); *(BYTE **)&src += src_next_scan; *(BYTE **)&dst += dst_next_scan; y += dy; cy--; } } /////////////////////////////////////////////////////////////////////////////// // // Dith8to16t() 8bpp to 16bpp dither with transparency. // // If the source pixel is the given source transparency color, this routine // picks the destination transparency color for output and zero error is // accumulated to the pixel's neighbors. // Otherwise, this routine functions identically to Dith8to16. // /////////////////////////////////////////////////////////////////////////////// INLINE void DithScan8to16t(WORD *dst, const BYTE *src, const RGBQUAD *colors, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT xl, UINT y, WORD wColorTxpOUT, BYTE indexTxpIN) { UINT *row = Get555HalftoneRow(y); for (; x < xl; src ++, x++) { register const RGBQUAD *pChosen; register WORD wColor; register BYTE *tbl; register BYTE index; register int r; register int g; register int b; index = *src; if (index == indexTxpIN) { *dst++ = wColorTxpOUT; cur_err++; nxt_err++; continue; } pChosen = colors + index; r = Clamp8((int)pChosen->rgbRed + cur_err->r / 16); g = Clamp8((int)pChosen->rgbGreen + cur_err->g / 16); b = Clamp8((int)pChosen->rgbBlue + cur_err->b / 16); cur_err++; tbl = Get555HalftoneTable(row, x); wColor = (*dst++ = rgb555(tbl[r], tbl[g], tbl[b])); b -= (int)aRound5to8[wColor & 0x1f]; (nxt_err+1)->b += b * 1; (nxt_err-1)->b += b * 3; (nxt_err )->b += b * 5; (cur_err )->b += b * 7; g -= (int)aRound5to8[(wColor >> 5) & 0x1f]; (nxt_err+1)->g += g * 1; (nxt_err-1)->g += g * 3; (nxt_err )->g += g * 5; (cur_err )->g += g * 7; r -= (int)aRound5to8[(wColor >> 10) & 0x1f]; (nxt_err+1)->r += r * 1; (nxt_err-1)->r += r * 3; (nxt_err )->r += r * 5; (cur_err )->r += r * 7; nxt_err++; } } void Dith8to16t(WORD *dst, const BYTE *src, int dst_next_scan, int src_next_scan, const RGBQUAD *colors, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT cx, UINT y, int cy, WORD wColorTxpOUT, BYTE indexTxpIN) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else dy = 1; if (y & 1) SwapError(&cur_err, &nxt_err); while (cy) { DithScan8to16t(dst, src, colors, cur_err, nxt_err, x, cx, y, wColorTxpOUT, indexTxpIN); ZeroError(cur_err, cx); SwapError(&cur_err, &nxt_err); *(BYTE **)&src += src_next_scan; *(BYTE **)&dst += dst_next_scan; y += dy; cy--; } } /////////////////////////////////////////////////////////////////////////////// // // Dith24to8() 24bpp to 8bpp dither. // // Computes the 24bpp source color by combining the source pixel's color with // accumulated error for the pixel. Halftones this 24bpp value to a 16bpp 555 // color. Uses the 16bpp color as a lookup into an inverse mapping table to // pick the output color for the pixel. Uses the destination color table entry // to compute and accumulates error for neighboring pixels. // /////////////////////////////////////////////////////////////////////////////// INLINE void DithScan24to8(BYTE *dst, const BYTE *src, const RGBQUAD *colors, const BYTE *map, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT xl, UINT y) { UINT *row = Get555HalftoneRow(y); for (; x < xl; src += 3, x++) { register const RGBQUAD *pChosen; register BYTE *tbl; register int r; register int g; register int b; r = Clamp8((int)src[2] + cur_err->r / 16); g = Clamp8((int)src[1] + cur_err->g / 16); b = Clamp8((int)src[0] + cur_err->b / 16); cur_err++; tbl = Get555HalftoneTable(row, x); pChosen = colors + (*dst++ = map[rgb555(tbl[r], tbl[g], tbl[b])]); r -= (int)pChosen->rgbRed; (nxt_err+1)->r += r * 1; (nxt_err-1)->r += r * 3; (nxt_err )->r += r * 5; (cur_err )->r += r * 7; g -= (int)pChosen->rgbGreen; (nxt_err+1)->g += g * 1; (nxt_err-1)->g += g * 3; (nxt_err )->g += g * 5; (cur_err )->g += g * 7; b -= (int)pChosen->rgbBlue; (nxt_err+1)->b += b * 1; (nxt_err-1)->b += b * 3; (nxt_err )->b += b * 5; (cur_err )->b += b * 7; nxt_err++; } } void Dith24to8(BYTE *dst, const BYTE *src, int dst_next_scan, int src_next_scan, const RGBQUAD *colors, const BYTE *map, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT cx, UINT y, int cy) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else dy = 1; if (y & 1) SwapError(&cur_err, &nxt_err); while (cy) { DithScan24to8(dst, src, colors, map, cur_err, nxt_err, x, cx, y); ZeroError(cur_err, cx); SwapError(&cur_err, &nxt_err); *(BYTE **)&src += src_next_scan; *(BYTE **)&dst += dst_next_scan; y += dy; cy--; } } INLINE void ConvertScan24to8( BYTE* pbDest, const BYTE* pbSrc, const BYTE* pbMap, UINT x, UINT xl, UINT y ) { UINT* pRow; BYTE* pbTable; BYTE r; BYTE g; BYTE b; pRow = Get555HalftoneRow( y ); for (; x < xl; pbSrc += 3, x++ ) { r = pbSrc[2]; g = pbSrc[1]; b = pbSrc[0]; pbTable = Get555HalftoneTable( pRow, x ); *pbDest = pbMap[rgb555( pbTable[r], pbTable[g], pbTable[b] )]; } } void Convert24to8( BYTE* pbDest, const BYTE* pbSrc, int nDestPitch, int nSrcPitch, const BYTE* pbMap, UINT x, UINT nWidth, UINT y, int nHeight ) { int dy; UINT x2; x2 = x+nWidth; if( nHeight < 0 ) { dy = -1; nHeight *= -1; } else { dy = 1; } while( nHeight ) { ConvertScan24to8( pbDest, pbSrc, pbMap, x, x2, y ); pbSrc += nSrcPitch; pbDest += nDestPitch; y += dy; nHeight--; } } /////////////////////////////////////////////////////////////////////////////// // // Dith24to16() 24bpp to 16bpp dither. // // Computes the 24bpp source color by combining the source pixel's color with // accumulated error for the pixel. Halftones this 24bpp value to a 16bpp 555 // color. Remaps this color to 24bpp to compute and accumulates error for // neighboring pixels. // /////////////////////////////////////////////////////////////////////////////// INLINE void DithScan24to16(WORD *dst, const BYTE *src, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT xl, UINT y) { UINT *row = Get555HalftoneRow(y); for (; x < xl; src += 3, x++) { register WORD wColor; register BYTE *tbl; register int r; register int g; register int b; r = Clamp8((int)src[2] + cur_err->r / 16); g = Clamp8((int)src[1] + cur_err->g / 16); b = Clamp8((int)src[0] + cur_err->b / 16); cur_err++; tbl = Get555HalftoneTable(row, x); wColor = (*dst++ = rgb555(tbl[r], tbl[g], tbl[b])); b -= (int)aRound5to8[wColor & 0x1f]; (nxt_err+1)->b += b * 1; (nxt_err-1)->b += b * 3; (nxt_err )->b += b * 5; (cur_err )->b += b * 7; g -= (int)aRound5to8[(wColor >> 5) & 0x1f]; (nxt_err+1)->g += g * 1; (nxt_err-1)->g += g * 3; (nxt_err )->g += g * 5; (cur_err )->g += g * 7; r -= (int)aRound5to8[(wColor >> 10) & 0x1f]; (nxt_err+1)->r += r * 1; (nxt_err-1)->r += r * 3; (nxt_err )->r += r * 5; (cur_err )->r += r * 7; nxt_err++; } } void Dith24to16(WORD *dst, const BYTE *src, int dst_next_scan, int src_next_scan, ERRBUF *cur_err, ERRBUF *nxt_err, UINT x, UINT cx, UINT y, int cy) { int dy; cx += x; if (cy < 0) { dy = -1; cy *= -1; } else dy = 1; if (y & 1) SwapError(&cur_err, &nxt_err); while (cy) { DithScan24to16(dst, src, cur_err, nxt_err, x, cx, y); ZeroError(cur_err, cx); SwapError(&cur_err, &nxt_err); *(BYTE **)&src += src_next_scan; *(BYTE **)&dst += dst_next_scan; y += dy; cy--; } } ///////////////////////////////////////////////////////////////////////////////