/*****************************************************************************
*
* CrunchDIB - shrink a DIB down by 2 with color averaging
*
*****************************************************************************/
/**************************************************************************
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1993 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.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 *
#undef FAR
#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;
}