C语言读取和显示BMP文件

在TC2.0下,隶属于16位子系统,所以int是2字节,long是4字节,char是1字节。绘图系统模式是VGA,颜色当然也很有限,所以读取bmp像素后需要把像素颜色转换为“最近”的已有VGA颜色。用int GetColor(int r,int g,int b)实现返回一个颜色值(color code)。用putpixel(int x,int y,int color)绘制一个像素。
      下图是几种在.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,未经压缩的文件。
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            */
        {
1654242},     /* 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“截屏”)
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值