Win32 C Function for High-Quality Bitmap Shrinking
Introduction
The Win32 API provides a function called StretchBlt, which can be used to shrink and expand bitmaps. However, when you need the same quality as that produced by professional packages such as Adobe Photoshop or Corel Draw, this function falls woefully short. Another Win32 function, CopyImage, can be used to expand images. However, while its antialiasing is very efficient, the resulting image once again is even worse than the StrechBlt function.
Therefore, I wrote my own function (ShrinkBitmap) to give me the ability to shink and expand images in my own application, yet do so with the same quality I would expect from a graphics package. Take a look at the following table of images and you'll see first a large image (the original) and then a comparison of images that have been shrunk using the StretchBlt function, my ShrinkBitmap function and the an Adobe Photoshop conversion. As you can see, ShrinkBitmap gives you Adobo Photoshop bitmap shrinking capabilities from within your own application.
This is me in original size and I'm going to shrink myself
Resolution StretchBlt My function Adobe Photoshop
105 ¡Á 150
35 ¡Á 50
Source Code
Place these macros into the beginning of your program:
#define Alloc(p,t) (t *)malloc((p)*sizeof(t))
#define For(i,n) for ((i)=0;(i)<(n);(i)++)
#define iFor(n) For (i,n)
#define jFor(n) For (j,n)
And also this type-definition:
typedef struct {
WORD x,y; // dimensions
WORD l; // bytes per scan-line (32-bit allignment)
BYTE *b; // bits of bitmap,3 bytes/pixel, BGR
} tWorkBMP; // 24-bit working bitmap
The functions also require the header file for memory allocation, or you can rewrite them to use keywords new and delete.
#include
Here's the actual ShrinkBitmap function
HBITMAP ShrinkBitmap (HBITMAP a,WORD bx,WORD by)
// creates and returns new bitmap with dimensions of
// [bx,by] by shrinking bitmap a both [bx,by] must be less or equal
// than the dims of a, unless the result is nonsense
{
tWorkBMP in,out;
HBITMAP b=CreateEmptyBitmap(bx,by);
OpenBitmapForWork (a,&in);
ShrinkWorkingBitmap (&in,&out,bx,by);
free (in.b);
SaveWorkingBitmap (&out,b);
free (out.b);
return (b);
}
The following functions are just supporting and I recommend treating them in "black box" fashion. But you have to place them in front of the ShrinkBitmap function or forward declare them.
void CreateWorkingBitmap (WORD dx,WORD dy,tWorkBMP *w)
{
w->x=dx;
w->y=dy;
w->l=(dx+1)*3&0xfffc;
w->b=Alloc(w->l*dy,BYTE);
}
HBITMAP CreateEmptyBitmap (WORD dx,WORD dy)
{
HDC h=GetDC(NULL);
HBITMAP b=CreateCompatibleBitmap(h,dx,dy);
ReleaseDC (NULL,h);
return (b);
}
void SetBMIHeader (BITMAPINFO *b,short dx,short dy)
{
b->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
b->bmiHeader.biWidth=dx;
b->bmiHeader.biHeight=-dy;
b->bmiHeader.biPlanes=1;
b->bmiHeader.biBitCount=24;
b->bmiHeader.biCompression=BI_RGB;
b->bmiHeader.biSizeImage=0;
b->bmiHeader.biXPelsPerMeter=1;
b->bmiHeader.biYPelsPerMeter=1;
b->bmiHeader.biClrUsed=0;
b->bmiHeader.biClrImportant=0;
}
void SaveWorkingBitmap (tWorkBMP *w,HBITMAP b)
{
BITMAPINFO s;
HDC h=GetDC(NULL);
SetBMIHeader (&s,w->x,w->y);
SetDIBits (h,b,0,w->y,w->b,&s,DIB_RGB_COLORS);
ReleaseDC (NULL,h);
}
void ShrinkWorkingBitmap (tWorkBMP *a,tWorkBMP *b,WORD bx,WORD by)
{
BYTE *uy=a->b,*ux,i;
WORD x,y,nx,ny=0;
DWORD df=3*bx,nf=df*by,j;
float k,qx[2],qy[2],q[4],*f=Alloc(nf,float);
CreateWorkingBitmap (bx,by,b);
jFor (nf) f[j]=0;
j=0;
For (y,a->y) {
ux=uy;
uy+=a->l;
nx=0;
ny+=by;
if (ny>a->y) {
qy[0]=1-(qy[1]=(ny-a->y)/(float)by);
For (x,a->x) {
nx+=bx;
if (nx>a->x) {
qx[0]=1-(qx[1]=(nx-a->x)/(float)bx);
iFor (4) q[i]=qx[i&1]*qy[i>>1];
iFor (3) {
f[j]+=(*ux)*q[0];
f[j+3]+=(*ux)*q[1];
f[j+df]+=(*ux)*q[2];
f[(j++)+df+3]+=(*(ux++))*q[3];
}
}
else iFor (3) {
f[j+i]+=(*ux)*qy[0];
f[j+df+i]+=(*(ux++))*qy[1];
}
if (nx>=a->x) nx-=a->x;
if (!nx) j+=3;
}
}
else {
For (x,a->x) {
nx+=bx;
if (nx>a->x) {
qx[0]=1-(qx[1]=(nx-a->x)/(float)bx);
iFor (3) {
f[j]+=(*ux)*qx[0];
f[(j++)+3]+=(*(ux++))*qx[1];
}
}
else iFor (3) f[j+i]+=*(ux++);
if (nx>=a->x) nx-=a->x;
if (!nx) j+=3;
}
if (nyy) j-=df;
}
if (ny>=a->y) ny-=a->y;
}
nf=0;
k=bx*by/(float)(a->x*a->y);
uy=b->b;
For (y,by) {
jFor (df) uy[j]=f[nf++]*k+.5;
uy+=b->l;
}
free (f);
}