下图是几种在.NET Framework中的已知颜色和其RGB值(下图当然也是使用代码绘制的,代码略)。
16种颜色是位于RGB立方体中的16个点,相当于寻找一个最接近指定颜色的点。为了简化计算,计算出两点距离的平方即可。
为了加快搜索,我们可以用下面的类似绘制“金刚石”的代码提前求出最短距离的平方,这个数据将应用到GetColor函数中。原因是我们已知最近的两个颜色点的距离,如果某点与某颜色的距离小于最短距离的一半,则此颜色就是我们要找的结果。
Compute Minimum Dist^2 Code
int COLORS[16][3]={.};
/* i,j - color index */
/* ret - distance^2 */
long GetDist(int i,int j)
{
long dist=0;
dist+=(COLORS[i][0]-COLORS[j][0])*(COLORS[i][0]-COLORS[j][0]);
dist+=(COLORS[i][1]-COLORS[j][1])*(COLORS[i][1]-COLORS[j][1]);
dist+=(COLORS[i][2]-COLORS[j][2])*(COLORS[i][2]-COLORS[j][2]);
return dist;
}
void main(void)
{
int i, j;
long dist,mindist=195075;
for(i=0;i<15;i++)
{
for(j=i+1;j<16;j++)
{
dist=GetDist(i,j);
if(dist<mindist)
mindist=dist;
}
}
printf("mindis^2=%d",mindist);
}
在下面的代码里为了简化处理,我们仅考虑了一种比较常见的BMP文件,即bpp=24,未经压缩的文件。int COLORS[16][3]={.};
/* i,j - color index */
/* ret - distance^2 */
long GetDist(int i,int j)
{
long dist=0;
dist+=(COLORS[i][0]-COLORS[j][0])*(COLORS[i][0]-COLORS[j][0]);
dist+=(COLORS[i][1]-COLORS[j][1])*(COLORS[i][1]-COLORS[j][1]);
dist+=(COLORS[i][2]-COLORS[j][2])*(COLORS[i][2]-COLORS[j][2]);
return dist;
}
void main(void)
{
int i, j;
long dist,mindist=195075;
for(i=0;i<15;i++)
{
for(j=i+1;j<16;j++)
{
dist=GetDist(i,j);
if(dist<mindist)
mindist=dist;
}
}
printf("mindis^2=%d",mindist);
}
Code
#include <graphics.h>
#include <string.h>
#include <stdio.h>
#include <alloc.h>
int MAX_Y=480;
int MAX_X=640;
int COLORS[16][3]=
{
/* R G B Index ColorName */
{ 0, 0, 0}, /* 00 Black */
{ 0, 0,255}, /* 01 Blue */
{ 0,128, 0}, /* 02 Green */
{ 0,255,255}, /* 03 Cyan */
{255, 0, 0}, /* 04 Red */
{255, 0,255}, /* 05 Magenta */
{165, 42, 42}, /* 06 Brown */
{211,211,211}, /* 07 LightGray */
{169,169,169}, /* 08 DarkGray */
{173,216,230}, /* 09 LightBlue */
{144,238,144}, /* 10 LightGreen */
{144,238,238}, /* 11 LightCyan */
{238,144,144}, /* 12 LightRed */
{238,144,238}, /* 13 LightMegenta */
{255,255, 0}, /* 14 Yellow */
{255,255,255}, /* 15 White */
};
/* pixel : keep channel order with readfile order */
typedef struct _PIXEL
{
unsigned char b;
unsigned char g;
unsigned char r;
} PIXEL;
/* color item in palette */
typedef struct _RGBQUAD
{
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
/* bitmap file header */
typedef struct _BITMAPFILEHEADER
{
unsigned int type;
long fileSize;
long reserved;
long offbits;
} BITMAPFILEHEADER,*PBITMAPFILEHEADER;
/* bitmap info header */
typedef struct _BITMAPINFOHEADER
{
long dwSize;
long width;
long height;
int planes;
int bpp;
long compression;
long sizeImage;
long hResolution;
long vResolution;
long colors;
long importantColors;
} BITMAPINFOHEADER,*PBITMAPINFOHEADER;
/* Functions Declare List */
int GetColor();
void ReadImage();
void DrawAxes();
void CopyScreen();
/* Entry Point Function */
void main()
{
int driver,mode;
char filename[255];
printf("input the filename of bitmap file:\n");
scanf("%s",filename);
driver=DETECT;
initgraph(&driver,&mode,"c:\\tc\\");
/* draw a bitmap */
ReadImage(filename);
getch();
closegraph();
}
/* read pixels from imagefile and display */
void ReadImage(char* filename)
{
FILE* stream;
char string[255];
int i,j,width,height,color;
size_t bytesRead,stride,itemSize=1;
long offset;
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
unsigned char *pPixels,red,green,blue;
stream=fopen(filename,"rb");
if(stream==NULL)
{
printf("open file error!\n");
exit(0);
}
fseek(stream,0,0);
fread(&fileHeader,1,sizeof(fileHeader),stream);
fread(&infoHeader,1,sizeof(infoHeader),stream);
width=infoHeader.width;
height=infoHeader.height;
/* stride: scan line bytes count. padding for 4 bytes */
stride=(infoHeader.bpp*width+31)/32*4;
pPixels=malloc(stride);
for(j=height-1;j>=0;j--)
{
/* !!! stride (2 bytes) must be convert to long (4 bytes) */
offset=fileHeader.offbits+j*((long)stride);
fseek(stream,offset,0);
bytesRead=fread(pPixels,itemSize,stride,stream);
for(i=0;i<width;i++)
{
red = pPixels[i*3+2];
green = pPixels[i*3+1];
blue = pPixels[i*3];
color=GetColor(red,green,blue);
putpixel(100+i,height+100-j,color);
}
}
fclose(stream); /* close the bitmap file */
free(pPixels);
setcolor(63);
/* draw the bitmap border rect */
rectangle(100,100,100+width,100+height);
/* Draw X and Y axes */
DrawAxes(width,height);
setcolor(3);
sprintf(string,"FileName:\"%s\" bpp=%d",filename,infoHeader.bpp);
outtextxy(100,30,string);
outtextxy(200,460,"press any key to save screen to a bitmap.");
getch();
/* copy the screen and save to bmp file */
CopyScreen("c:\\tc\\SCREEN.BMP",0,0,199+width,199+height);
}
/* compute a color by RGB */
int GetColor(unsigned int red,unsigned int green,unsigned int blue)
{
int i,index=0;
unsigned long dist,temp;
temp=195075;
for(i=0;i<16;i++)
{
dist=0;
dist+=(COLORS[i][0]-red)*(COLORS[i][0]-red);
dist+=(COLORS[i][1]-green)*(COLORS[i][1]-green);
dist+=(COLORS[i][2]-blue)*(COLORS[i][2]-blue);
/* minimum dist^2=2492, 623=[minimum dist^2]/4 */
if(dist<=623)
return i;
if(dist<temp)
{
index=i;
temp=dist;
}
}
return index;
}
/* draw X and Y axes */
void DrawAxes(int width,int height)
{
int i,j;
char text[50];
setcolor(15);
line(100,100,150+width,100); /* X axis */
line(150+width,100,145+width,97);
line(150+width,100,145+width,103);
outtextxy(154+width,95,"X");
line(100,100,100,150+height); /* Y axis */
line(100,150+height,97,145+height);
line(100,150+height,103,145+height);
outtextxy(95,154+height,"Y");
for(i=0;i<=width+20;i+=10)
{
sprintf(text,"%d",i);
line(100+i,97,100+i,100);
if(i%50==0)
{
line(100+i,94,100+i,97); /* make longer */
sprintf(text,"%d",i);
outtextxy(95+i,85,text);
}
}
for(j=0;j<=height+20;j+=10)
{
line(97,j+100,100,j+100);
if(j%50==0)
{
line(94,j+100,97,j+100); /* make longer */
sprintf(text,"%d",j);
outtextxy(70,j+98,text);
}
}
sprintf(text,"ImageSize: %d * %d pixels",width,height);
setcolor(2);
outtextxy(100,50,text);
}
/* copy the screen and save to a bmp file */
void CopyScreen(char* filename,int left,int top,int right,int bottom)
{
int i,j,width,height,bpp=4,colorUsed=16,stride,index0,index1;
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
RGBQUAD *palette;
unsigned char *pPixels;
FILE *stream;
width=right-left+1;
height=bottom-top+1;
stride=(width*bpp+31)/32*4;
/* fill file header */
fileHeader.type=0x4D42; /* filetype:"BM" ascii code */
fileHeader.fileSize=sizeof(infoHeader);
fileHeader.fileSize+=sizeof(RGBQUAD)*colorUsed;
fileHeader.fileSize+=((long)stride)*height;
fileHeader.reserved=0;
fileHeader.offbits=sizeof(fileHeader)+sizeof(infoHeader)+4*16;
/* fill info header */
infoHeader.dwSize=sizeof(BITMAPINFOHEADER);
infoHeader.width=width;
infoHeader.height=height;
infoHeader.planes=1; /* must set to 1 */
infoHeader.bpp=bpp;
infoHeader.compression=0;
infoHeader.sizeImage=((long)stride)*height;
infoHeader.hResolution=0x27;
infoHeader.vResolution=0x27;
infoHeader.colors=16;
infoHeader.importantColors=0;
/* fill RGBQUAD items */
palette=malloc(sizeof(RGBQUAD)*16);
if(palette==NULL)
{
printf("malloc for palette fail!");
return;
}
for(i=0;i<16;i++)
{
palette[i].rgbRed = COLORS[i][0];
palette[i].rgbGreen = COLORS[i][1];
palette[i].rgbBlue = COLORS[i][2];
palette[i].rgbReserved = 0;
}
/* open the file in wb mode */
stream=fopen(filename,"wb");
if(stream==NULL)
{
printf("open file fail!");
return;
}
fwrite(&fileHeader,sizeof(fileHeader),1,stream);
fwrite(&infoHeader,sizeof(infoHeader),1,stream);
fwrite(palette,sizeof(RGBQUAD),16,stream);
free(palette);
/* write the bitmap data: color indexes ,bpp=4 */
pPixels=malloc(stride);
if(pPixels==NULL)
{
printf("malloc for pPixels fail!");
return;
}
for(j=bottom;j>=top;j--)
{
memset(pPixels,0,stride);
for(i=left;i<=right;i+=2)
{
/* 2 pixels to 1 byte */
index0=getpixel(i,j);
index1=(i==right)? 0: getpixel(i+1,j);
pPixels[i/2]=(index0<<4)+index1;
}
fwrite(pPixels,1,stride,stream);
}
fclose(stream);
} /* ---------END------------ */
#include <graphics.h>
#include <string.h>
#include <stdio.h>
#include <alloc.h>
int MAX_Y=480;
int MAX_X=640;
int COLORS[16][3]=
{
/* R G B Index ColorName */
{ 0, 0, 0}, /* 00 Black */
{ 0, 0,255}, /* 01 Blue */
{ 0,128, 0}, /* 02 Green */
{ 0,255,255}, /* 03 Cyan */
{255, 0, 0}, /* 04 Red */
{255, 0,255}, /* 05 Magenta */
{165, 42, 42}, /* 06 Brown */
{211,211,211}, /* 07 LightGray */
{169,169,169}, /* 08 DarkGray */
{173,216,230}, /* 09 LightBlue */
{144,238,144}, /* 10 LightGreen */
{144,238,238}, /* 11 LightCyan */
{238,144,144}, /* 12 LightRed */
{238,144,238}, /* 13 LightMegenta */
{255,255, 0}, /* 14 Yellow */
{255,255,255}, /* 15 White */
};
/* pixel : keep channel order with readfile order */
typedef struct _PIXEL
{
unsigned char b;
unsigned char g;
unsigned char r;
} PIXEL;
/* color item in palette */
typedef struct _RGBQUAD
{
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
/* bitmap file header */
typedef struct _BITMAPFILEHEADER
{
unsigned int type;
long fileSize;
long reserved;
long offbits;
} BITMAPFILEHEADER,*PBITMAPFILEHEADER;
/* bitmap info header */
typedef struct _BITMAPINFOHEADER
{
long dwSize;
long width;
long height;
int planes;
int bpp;
long compression;
long sizeImage;
long hResolution;
long vResolution;
long colors;
long importantColors;
} BITMAPINFOHEADER,*PBITMAPINFOHEADER;
/* Functions Declare List */
int GetColor();
void ReadImage();
void DrawAxes();
void CopyScreen();
/* Entry Point Function */
void main()
{
int driver,mode;
char filename[255];
printf("input the filename of bitmap file:\n");
scanf("%s",filename);
driver=DETECT;
initgraph(&driver,&mode,"c:\\tc\\");
/* draw a bitmap */
ReadImage(filename);
getch();
closegraph();
}
/* read pixels from imagefile and display */
void ReadImage(char* filename)
{
FILE* stream;
char string[255];
int i,j,width,height,color;
size_t bytesRead,stride,itemSize=1;
long offset;
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
unsigned char *pPixels,red,green,blue;
stream=fopen(filename,"rb");
if(stream==NULL)
{
printf("open file error!\n");
exit(0);
}
fseek(stream,0,0);
fread(&fileHeader,1,sizeof(fileHeader),stream);
fread(&infoHeader,1,sizeof(infoHeader),stream);
width=infoHeader.width;
height=infoHeader.height;
/* stride: scan line bytes count. padding for 4 bytes */
stride=(infoHeader.bpp*width+31)/32*4;
pPixels=malloc(stride);
for(j=height-1;j>=0;j--)
{
/* !!! stride (2 bytes) must be convert to long (4 bytes) */
offset=fileHeader.offbits+j*((long)stride);
fseek(stream,offset,0);
bytesRead=fread(pPixels,itemSize,stride,stream);
for(i=0;i<width;i++)
{
red = pPixels[i*3+2];
green = pPixels[i*3+1];
blue = pPixels[i*3];
color=GetColor(red,green,blue);
putpixel(100+i,height+100-j,color);
}
}
fclose(stream); /* close the bitmap file */
free(pPixels);
setcolor(63);
/* draw the bitmap border rect */
rectangle(100,100,100+width,100+height);
/* Draw X and Y axes */
DrawAxes(width,height);
setcolor(3);
sprintf(string,"FileName:\"%s\" bpp=%d",filename,infoHeader.bpp);
outtextxy(100,30,string);
outtextxy(200,460,"press any key to save screen to a bitmap.");
getch();
/* copy the screen and save to bmp file */
CopyScreen("c:\\tc\\SCREEN.BMP",0,0,199+width,199+height);
}
/* compute a color by RGB */
int GetColor(unsigned int red,unsigned int green,unsigned int blue)
{
int i,index=0;
unsigned long dist,temp;
temp=195075;
for(i=0;i<16;i++)
{
dist=0;
dist+=(COLORS[i][0]-red)*(COLORS[i][0]-red);
dist+=(COLORS[i][1]-green)*(COLORS[i][1]-green);
dist+=(COLORS[i][2]-blue)*(COLORS[i][2]-blue);
/* minimum dist^2=2492, 623=[minimum dist^2]/4 */
if(dist<=623)
return i;
if(dist<temp)
{
index=i;
temp=dist;
}
}
return index;
}
/* draw X and Y axes */
void DrawAxes(int width,int height)
{
int i,j;
char text[50];
setcolor(15);
line(100,100,150+width,100); /* X axis */
line(150+width,100,145+width,97);
line(150+width,100,145+width,103);
outtextxy(154+width,95,"X");
line(100,100,100,150+height); /* Y axis */
line(100,150+height,97,145+height);
line(100,150+height,103,145+height);
outtextxy(95,154+height,"Y");
for(i=0;i<=width+20;i+=10)
{
sprintf(text,"%d",i);
line(100+i,97,100+i,100);
if(i%50==0)
{
line(100+i,94,100+i,97); /* make longer */
sprintf(text,"%d",i);
outtextxy(95+i,85,text);
}
}
for(j=0;j<=height+20;j+=10)
{
line(97,j+100,100,j+100);
if(j%50==0)
{
line(94,j+100,97,j+100); /* make longer */
sprintf(text,"%d",j);
outtextxy(70,j+98,text);
}
}
sprintf(text,"ImageSize: %d * %d pixels",width,height);
setcolor(2);
outtextxy(100,50,text);
}
/* copy the screen and save to a bmp file */
void CopyScreen(char* filename,int left,int top,int right,int bottom)
{
int i,j,width,height,bpp=4,colorUsed=16,stride,index0,index1;
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
RGBQUAD *palette;
unsigned char *pPixels;
FILE *stream;
width=right-left+1;
height=bottom-top+1;
stride=(width*bpp+31)/32*4;
/* fill file header */
fileHeader.type=0x4D42; /* filetype:"BM" ascii code */
fileHeader.fileSize=sizeof(infoHeader);
fileHeader.fileSize+=sizeof(RGBQUAD)*colorUsed;
fileHeader.fileSize+=((long)stride)*height;
fileHeader.reserved=0;
fileHeader.offbits=sizeof(fileHeader)+sizeof(infoHeader)+4*16;
/* fill info header */
infoHeader.dwSize=sizeof(BITMAPINFOHEADER);
infoHeader.width=width;
infoHeader.height=height;
infoHeader.planes=1; /* must set to 1 */
infoHeader.bpp=bpp;
infoHeader.compression=0;
infoHeader.sizeImage=((long)stride)*height;
infoHeader.hResolution=0x27;
infoHeader.vResolution=0x27;
infoHeader.colors=16;
infoHeader.importantColors=0;
/* fill RGBQUAD items */
palette=malloc(sizeof(RGBQUAD)*16);
if(palette==NULL)
{
printf("malloc for palette fail!");
return;
}
for(i=0;i<16;i++)
{
palette[i].rgbRed = COLORS[i][0];
palette[i].rgbGreen = COLORS[i][1];
palette[i].rgbBlue = COLORS[i][2];
palette[i].rgbReserved = 0;
}
/* open the file in wb mode */
stream=fopen(filename,"wb");
if(stream==NULL)
{
printf("open file fail!");
return;
}
fwrite(&fileHeader,sizeof(fileHeader),1,stream);
fwrite(&infoHeader,sizeof(infoHeader),1,stream);
fwrite(palette,sizeof(RGBQUAD),16,stream);
free(palette);
/* write the bitmap data: color indexes ,bpp=4 */
pPixels=malloc(stride);
if(pPixels==NULL)
{
printf("malloc for pPixels fail!");
return;
}
for(j=bottom;j>=top;j--)
{
memset(pPixels,0,stride);
for(i=left;i<=right;i+=2)
{
/* 2 pixels to 1 byte */
index0=getpixel(i,j);
index1=(i==right)? 0: getpixel(i+1,j);
pPixels[i/2]=(index0<<4)+index1;
}
fwrite(pPixels,1,stride,stream);
}
fclose(stream);
} /* ---------END------------ */
首先我们要知道的几个概念:
bpp:位深度,单位是位/像素(bits per pixel),bpp决定了所能表示的颜色数量。例如bpp=1,则说明图像只有黑白两色(二值图像)。bpp=8,为普通的灰度图像。bpp=24,是最常见的RGB三通道彩色图片。
stride:扫描行宽度,单位是字节。这是一个在图像数据块(文件或内存中的)中进行定位非常重要的概念,指一行像素占据的内存大小。它必须是4bytes整数倍。因此stride从下面表达式的计算:
stride=(bm.Width*bpp+31)/32*4;
在这里我们必须注意,读取BMP文件时,文件地址是long型(4bytes),即32位的地址,当计算偏移地址时,我们必须把16位的size_t或者int类型首先转化为long型,以免高位地址丢失,导致不能正确定位文件。例如下面这句代码中的类型显示转换是不可缺少的。
offset=fileHeader.offbits+j*((long)stride);
由于在TC VGA绘图模式下无法截屏(没有DC),所以通过另存为一个4bpp的bmp图片来做示范。
(24 bpp Win32 bmp)->(TC VGA Graph Mode“截屏”)