479 lines
14 KiB
C
479 lines
14 KiB
C
|
/*****************************************************************************
|
||
|
*
|
||
|
* CrunchDIB - shrink a DIB down by 2 with color averaging
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define _WINDOWS
|
||
|
#include <windows.h>
|
||
|
#include <windowsx.h>
|
||
|
#include <win32.h>
|
||
|
#include <memory.h>
|
||
|
|
||
|
//
|
||
|
// support huge >64k DIBs?
|
||
|
//
|
||
|
#ifndef WIN32
|
||
|
#define HUGE_DIBS
|
||
|
#endif
|
||
|
#ifdef HUGE_DIBS
|
||
|
#define LPBYTE BYTE _huge *
|
||
|
#define LPWORD WORD _huge *
|
||
|
#define FAR _huge
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* These are the standard VGA colors, we will be stuck with until the
|
||
|
* end of time!
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
static DWORD VGAColors[16] = {
|
||
|
0x00000000 // 0000 black
|
||
|
,0x00800000 // 0001 dark red
|
||
|
,0x00008000 // 0010 dark green
|
||
|
,0x00808000 // 0011 mustard
|
||
|
,0x00000080 // 0100 dark blue
|
||
|
,0x00800080 // 0101 purple
|
||
|
,0x00008080 // 0110 dark turquoise
|
||
|
,0x00C0C0C0 // 1000 gray
|
||
|
,0x00808080 // 0111 dark gray
|
||
|
,0x00FF0000 // 1001 red
|
||
|
,0x0000FF00 // 1010 green
|
||
|
,0x00FFFF00 // 1011 yellow
|
||
|
,0x000000FF // 1100 blue
|
||
|
,0x00FF00FF // 1101 pink (magenta)
|
||
|
,0x0000FFFF // 1110 cyan
|
||
|
,0x00FFFFFF // 1111 white
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* bit(b,n) - get the Nth bit of BYTE b
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define bit(b,n) (BYTE)(((b) & (1 << (n))) >> (n))
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* SumMono
|
||
|
*
|
||
|
* this routine taks four "mono" values and returns the average value.
|
||
|
*
|
||
|
* ((b0) + (b1) + (b2) + (b3) >= 2)
|
||
|
*
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define SumMono(b0,b1,b2,b3) (BYTE)(((b0) + (b1) + (b2) + (b3) + 2) / 4)
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* MapVGA
|
||
|
*
|
||
|
* map a rgb value to a VGA index
|
||
|
*
|
||
|
* 0000 black
|
||
|
* 0001 dark red
|
||
|
* 0010 dark green
|
||
|
* 0011 mustard
|
||
|
* 0100 dark blue
|
||
|
* 0101 purple
|
||
|
* 0110 dark turquoise
|
||
|
* 1000 gray
|
||
|
* 0111 dark gray
|
||
|
* 1001 red
|
||
|
* 1010 green
|
||
|
* 1011 yellow
|
||
|
* 1100 blue
|
||
|
* 1101 pink (magenta)
|
||
|
* 1110 cyan
|
||
|
* 1111 white
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define MapVGA(r,g,b) (((r&~3) == (g&~3) && (g&~3) == (b&~3)) ? \
|
||
|
((r < 64) ? 0 : (r <= 128) ? 8 : (r <= 192) ? 7 : 15) : \
|
||
|
(((r>192) || (g>192) || (b>192)) ? \
|
||
|
(((r>191) ? 1:0) | ((g>191) ? 2:0) | ((b>191) ? 4:0) | 8) : \
|
||
|
(((r>64) ? 1:0) | ((g>64) ? 2:0) | ((b>64) ? 4:0))) )
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* SumRGB
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define SumRGB(b0,b1,b2,b3) RGB(\
|
||
|
((int)pal.palPalEntry[b0].peRed + \
|
||
|
(int)pal.palPalEntry[b1].peRed + \
|
||
|
(int)pal.palPalEntry[b2].peRed + \
|
||
|
(int)pal.palPalEntry[b3].peRed) >> 2, \
|
||
|
\
|
||
|
((int)pal.palPalEntry[b0].peGreen + \
|
||
|
(int)pal.palPalEntry[b1].peGreen + \
|
||
|
(int)pal.palPalEntry[b2].peGreen + \
|
||
|
(int)pal.palPalEntry[b3].peGreen) >> 2, \
|
||
|
\
|
||
|
((int)pal.palPalEntry[b0].peBlue + \
|
||
|
(int)pal.palPalEntry[b1].peBlue + \
|
||
|
(int)pal.palPalEntry[b2].peBlue + \
|
||
|
(int)pal.palPalEntry[b3].peBlue) >> 2)
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* RGB16
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
typedef struct { BYTE b,g,r; } RGB24;
|
||
|
|
||
|
#define RGB16(r,g,b) (\
|
||
|
(((UINT)(r) >> 3) << 10) | \
|
||
|
(((UINT)(g) >> 3) << 5) | \
|
||
|
(((UINT)(b) >> 3) << 0) )
|
||
|
|
||
|
#define rgb16(r,g,b) (\
|
||
|
((UINT)(r) << 10) | \
|
||
|
((UINT)(g) << 5) | \
|
||
|
((UINT)(b) << 0) )
|
||
|
|
||
|
//#define RGB16R(rgb) ((((UINT)(rgb) >> 10) & 0x1F) * 255u / 31u)
|
||
|
//#define RGB16G(rgb) ((((UINT)(rgb) >> 5) & 0x1F) * 255u / 31u)
|
||
|
//#define RGB16B(rgb) ((((UINT)(rgb) >> 0) & 0x1F) * 255u / 31u)
|
||
|
|
||
|
#define RGB16R(rgb) aw5to8[((UINT)(rgb) >> 10) & 0x1F]
|
||
|
#define RGB16G(rgb) aw5to8[((UINT)(rgb) >> 5) & 0x1F]
|
||
|
#define RGB16B(rgb) aw5to8[((UINT)(rgb) >> 0) & 0x1F]
|
||
|
#define RGB16r(rgb) ((BYTE)((UINT)(rgb) >> 10) & 0x1F)
|
||
|
#define RGB16g(rgb) ((BYTE)((UINT)(rgb) >> 5) & 0x1F)
|
||
|
#define RGB16b(rgb) ((BYTE)((UINT)(rgb) >> 0) & 0x1F)
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Pel() used for 24bit Crunch
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define Pel(p,x) (BYTE)(BitCount == 1 ? Pel1(p,x) : \
|
||
|
BitCount == 4 ? Pel4(p,x) : Pel8(p,x))
|
||
|
|
||
|
#define Pel1(p,x) (BYTE)bit(((LPBYTE)(p))[(x)/8],7-((x)%8))
|
||
|
#define Pel4(p,x) (BYTE)((x & 1) ? (((LPBYTE)(p))[(x)/2] & 15) : (((LPBYTE)(p))[(x)/2] >> 4))
|
||
|
#define Pel8(p,x) (BYTE)(((LPBYTE)(p))[(x)])
|
||
|
#define Pel16(p,x) (((LPWORD)(p))[(x)])
|
||
|
#define Pel24(p,x) (((RGB24 FAR *)(p))[(x)])
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* CrunchDIB - shrink a DIB down by 2 with color averaging
|
||
|
*
|
||
|
* this routine works on 1,4 bpp DIBs
|
||
|
*
|
||
|
* for mono DIBs it is assumed they are black and white
|
||
|
*
|
||
|
* for 4bpp DIBs it is assumed they use the VGA colors
|
||
|
*
|
||
|
* this routine can't be used "in place"
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
BOOL CrunchDIB(
|
||
|
LPBITMAPINFOHEADER lpbiSrc, // BITMAPINFO of source
|
||
|
LPVOID lpSrc, // input bits to crunch
|
||
|
LPBITMAPINFOHEADER lpbiDst, // BITMAPINFO of dest
|
||
|
LPVOID lpDst) // output bits to crunch
|
||
|
{
|
||
|
LPBYTE pbSrc;
|
||
|
LPBYTE pbDst;
|
||
|
LPBYTE pb;
|
||
|
LPWORD pw;
|
||
|
BYTE r,g,b,b0,b1,b2,b3;
|
||
|
WORD w0,w1,w2,w3;
|
||
|
RGB24 rgb0,rgb1,rgb2,rgb3;
|
||
|
int WidthBytesSrc;
|
||
|
int WidthBytesDst;
|
||
|
UINT x;
|
||
|
UINT y;
|
||
|
UINT dx;
|
||
|
UINT dy;
|
||
|
int i;
|
||
|
COLORREF rgb;
|
||
|
int BitCount;
|
||
|
UINT aw5to8[32];
|
||
|
|
||
|
struct {
|
||
|
WORD palVersion;
|
||
|
WORD palNumEntries;
|
||
|
PALETTEENTRY palPalEntry[256];
|
||
|
} pal;
|
||
|
|
||
|
if (lpbiSrc->biCompression != BI_RGB)
|
||
|
return FALSE;
|
||
|
|
||
|
BitCount = (int)lpbiSrc->biBitCount;
|
||
|
|
||
|
if (BitCount == 16)
|
||
|
for (i=0; i<32; i++)
|
||
|
aw5to8[i] = (UINT)i * 255u / 31u;
|
||
|
|
||
|
dx = (int)lpbiDst->biWidth;
|
||
|
WidthBytesDst = (((UINT)lpbiDst->biBitCount * dx + 31)&~31) / 8;
|
||
|
|
||
|
dy = (int)lpbiSrc->biHeight;
|
||
|
dx = (int)lpbiSrc->biWidth;
|
||
|
WidthBytesSrc = (((UINT)lpbiSrc->biBitCount * dx + 31)&~31) / 8;
|
||
|
|
||
|
dx &= ~1;
|
||
|
dy &= ~1;
|
||
|
|
||
|
pbSrc = lpSrc;
|
||
|
pbDst = lpDst;
|
||
|
|
||
|
if (lpbiSrc->biClrUsed == 0 && lpbiSrc->biBitCount <= 8)
|
||
|
lpbiSrc->biClrUsed = (1 << (int)lpbiSrc->biBitCount);
|
||
|
|
||
|
pal.palVersion = 0x300;
|
||
|
pal.palNumEntries = (int)lpbiSrc->biClrUsed;
|
||
|
|
||
|
for (i=0; i<(int)pal.palNumEntries; i++)
|
||
|
{
|
||
|
pal.palPalEntry[i].peRed = ((LPRGBQUAD)(lpbiSrc+1))[i].rgbRed;
|
||
|
pal.palPalEntry[i].peGreen = ((LPRGBQUAD)(lpbiSrc+1))[i].rgbGreen;
|
||
|
pal.palPalEntry[i].peBlue = ((LPRGBQUAD)(lpbiSrc+1))[i].rgbBlue;
|
||
|
pal.palPalEntry[i].peFlags = 0;
|
||
|
}
|
||
|
|
||
|
if (lpbiDst->biBitCount == 8)
|
||
|
_fmemcpy(lpbiDst+1,lpbiSrc+1,(int)lpbiSrc->biClrUsed*sizeof(RGBQUAD));
|
||
|
|
||
|
if (lpbiDst->biBitCount == 4)
|
||
|
_fmemcpy(lpbiDst+1,VGAColors,sizeof(VGAColors));
|
||
|
|
||
|
if ((int)lpbiDst->biBitCount == (int)lpbiSrc->biBitCount)
|
||
|
{
|
||
|
switch((int)lpbiSrc->biBitCount)
|
||
|
{
|
||
|
case 1:
|
||
|
dx = dx / 8; // dx is byte count
|
||
|
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pb = pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x += 2)
|
||
|
{
|
||
|
b0 = pbSrc[x];
|
||
|
b1 = pbSrc[x + WidthBytesSrc];
|
||
|
|
||
|
b = (BYTE)(
|
||
|
(SumMono(bit(b0,7), bit(b0,6), bit(b1,7), bit(b1,6)) << 7) |
|
||
|
(SumMono(bit(b0,5), bit(b0,4), bit(b1,5), bit(b1,4)) << 6) |
|
||
|
(SumMono(bit(b0,3), bit(b0,2), bit(b1,3), bit(b1,2)) << 5) |
|
||
|
(SumMono(bit(b0,1), bit(b0,0), bit(b1,1), bit(b1,0)) << 4));
|
||
|
|
||
|
b0 = pbSrc[x + 1];
|
||
|
b1 = pbSrc[x + 1 + WidthBytesSrc];
|
||
|
|
||
|
b |=(SumMono(bit(b0,7), bit(b0,6), bit(b1,7), bit(b1,6)) << 3) |
|
||
|
(SumMono(bit(b0,5), bit(b0,4), bit(b1,5), bit(b1,4)) << 2) |
|
||
|
(SumMono(bit(b0,3), bit(b0,2), bit(b1,3), bit(b1,2)) << 1) |
|
||
|
(SumMono(bit(b0,1), bit(b0,0), bit(b1,1), bit(b1,0)) << 0);
|
||
|
|
||
|
*pb++ = b;
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
dx = dx / 2; // dx is byte count
|
||
|
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pb = pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x+=2)
|
||
|
{
|
||
|
b0 = pbSrc[x];
|
||
|
b1 = pbSrc[x + WidthBytesSrc];
|
||
|
|
||
|
rgb = SumRGB((b0 >> 4),(b0 & 0x0F),
|
||
|
(b1 >> 4),(b1 & 0x0F));
|
||
|
|
||
|
b = (BYTE)(MapVGA(GetRValue(rgb),GetGValue(rgb),GetBValue(rgb)) << 4);
|
||
|
|
||
|
b0 = pbSrc[x + 1];
|
||
|
b1 = pbSrc[x + 1 + WidthBytesSrc];
|
||
|
|
||
|
rgb = SumRGB((b0 >> 4),(b0 & 0x0F),
|
||
|
(b1 >> 4),(b1 & 0x0F));
|
||
|
|
||
|
b |= MapVGA(GetRValue(rgb),GetGValue(rgb),GetBValue(rgb));
|
||
|
|
||
|
*pb++ = b;
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
break;
|
||
|
#if 0
|
||
|
case 8:
|
||
|
{
|
||
|
HPALETTE hpal;
|
||
|
|
||
|
hpal = CreatePalette((LPLOGPALETTE)&pal);
|
||
|
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pb = pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x+=2)
|
||
|
{
|
||
|
b0 = Pel8(pbSrc,x);
|
||
|
b1 = Pel8(pbSrc+WidthBytesSrc, x);
|
||
|
b2 = Pel8(pbSrc,x+1);
|
||
|
b3 = Pel8(pbSrc+WidthBytesSrc,x+1);
|
||
|
|
||
|
*pb++ = (BYTE)GetNearestPaletteIndex(hpal,
|
||
|
SumRGB(b0,b1,b2,b3));
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
|
||
|
DeleteObject(hpal);
|
||
|
}
|
||
|
break;
|
||
|
#endif
|
||
|
case 16:
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pw = (LPWORD)pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x += 2)
|
||
|
{
|
||
|
w0 = Pel16(pbSrc,x);
|
||
|
w1 = Pel16(pbSrc,x+1);
|
||
|
w2 = Pel16(pbSrc+WidthBytesSrc,x);
|
||
|
w3 = Pel16(pbSrc+WidthBytesSrc,x+1);
|
||
|
|
||
|
r = ((BYTE)RGB16r(w0) + RGB16r(w1) + RGB16r(w2) + RGB16r(w3)) >> 2;
|
||
|
g = ((BYTE)RGB16g(w0) + RGB16g(w1) + RGB16g(w2) + RGB16g(w3)) >> 2;
|
||
|
b = ((BYTE)RGB16b(w0) + RGB16b(w1) + RGB16b(w2) + RGB16b(w3)) >> 2;
|
||
|
|
||
|
*pw++ = rgb16(r,g,b);
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 24:
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pb = pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x += 2)
|
||
|
{
|
||
|
rgb0 = Pel24(pbSrc,x);
|
||
|
rgb1 = Pel24(pbSrc,x+1);
|
||
|
rgb2 = Pel24(pbSrc+WidthBytesSrc,x);
|
||
|
rgb3 = Pel24(pbSrc+WidthBytesSrc,x+1);
|
||
|
|
||
|
rgb = RGB(
|
||
|
((UINT)rgb0.r + rgb1.r + rgb2.r + rgb3.r)/4,
|
||
|
((UINT)rgb0.g + rgb1.g + rgb2.g + rgb3.g)/4,
|
||
|
((UINT)rgb0.b + rgb1.b + rgb2.b + rgb3.b)/4);
|
||
|
|
||
|
*pb++ = GetBValue(rgb);
|
||
|
*pb++ = GetGValue(rgb);
|
||
|
*pb++ = GetRValue(rgb);
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else if ((int)lpbiDst->biBitCount == 24)
|
||
|
{
|
||
|
switch((int)lpbiSrc->biBitCount)
|
||
|
{
|
||
|
case 1:
|
||
|
case 4:
|
||
|
case 8:
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pb = pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x += 2)
|
||
|
{
|
||
|
b0 = Pel(pbSrc,x);
|
||
|
b1 = Pel(pbSrc,x+1);
|
||
|
b2 = Pel(pbSrc+WidthBytesSrc,x);
|
||
|
b3 = Pel(pbSrc+WidthBytesSrc,x+1);
|
||
|
|
||
|
rgb = SumRGB(b0,b1,b2,b3);
|
||
|
|
||
|
*pb++ = GetBValue(rgb);
|
||
|
*pb++ = GetGValue(rgb);
|
||
|
*pb++ = GetRValue(rgb);
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 16:
|
||
|
for (y=0; y<dy; y+=2)
|
||
|
{
|
||
|
pb = pbDst;
|
||
|
|
||
|
for (x=0; x<dx; x += 2)
|
||
|
{
|
||
|
w0 = Pel16(pbSrc,x);
|
||
|
w1 = Pel16(pbSrc,x+1);
|
||
|
w2 = Pel16(pbSrc+WidthBytesSrc,x);
|
||
|
w3 = Pel16(pbSrc+WidthBytesSrc,x+1);
|
||
|
|
||
|
r = (RGB16R(w0) + RGB16R(w1) + RGB16R(w2) + RGB16R(w3)) / 4;
|
||
|
g = (RGB16G(w0) + RGB16G(w1) + RGB16G(w2) + RGB16G(w3)) / 4;
|
||
|
b = (RGB16B(w0) + RGB16B(w1) + RGB16B(w2) + RGB16B(w3)) / 4;
|
||
|
|
||
|
rgb = RGB(r,g,b);
|
||
|
|
||
|
*pb++ = GetBValue(rgb);
|
||
|
*pb++ = GetGValue(rgb);
|
||
|
*pb++ = GetRValue(rgb);
|
||
|
}
|
||
|
|
||
|
pbSrc += WidthBytesSrc*2;
|
||
|
pbDst += WidthBytesDst;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|