You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
994 lines
29 KiB
994 lines
29 KiB
#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--;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|