您的位置:首页 > 编程语言 > C语言/C++

C语言实现的BMP和JPEG图片的解码

2013-08-05 18:09 337 查看
/*

这是read_picture.c 文件的代码 ,主要是对输入的图片的文件(BMP和JPEG类型)进行解码,转换成在LCD中显示的数据;

使用该程序时,要先把jpeglib库文件和头文件放到对应的标谁库和标准头文件中;否则编译出错的

*/

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <string.h>

#include <linux/fb.h>

#include <sys/mman.h>

#include <sys/ioctl.h>

#include <arpa/inet.h>

#include "jpeglib.h"

#include <setjmp.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

unsigned char* read_bmp(char* filename);

unsigned char* read_jpeg(char *path);

//14byte文件头

typedef struct

{
char cfType[2];//文件类型,"BM"(0x4D42)
long cfSize;//文件大小(字节)
long cfReserved;//保留,值为0
long cfoffBits;//数据区相对于文件头的偏移量(字节)

}__attribute__((packed)) BITMAPFILEHEADER;

//__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐

//40byte信息头

typedef struct

{
char ciSize[4];//BITMAPFILEHEADER所占的字节数
long ciWidth;//宽度
long ciHeight;//高度
char ciPlanes[2];//目标设备的位平面数,值为1
int ciBitCount;//每个像素的位数
char ciCompress[4];//压缩说明
char ciSizeImage[4];//用字节表示的图像大小,该数据必须是4的倍数
char ciXPelsPerMeter[4];//目标设备的水平像素数/米
char ciYPelsPerMeter[4];//目标设备的垂直像素数/米
char ciClrUsed[4]; //位图使用调色板的颜色数
char ciClrImportant[4]; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要

}__attribute__((packed)) BITMAPINFOHEADER;

typedef struct

{
unsigned char blue;
unsigned char green;
unsigned char red;
//unsigned char reserved;

}__attribute__((packed)) PIXEL;//颜色模式RGB

BITMAPFILEHEADER FileHead;

BITMAPINFOHEADER InfoHead;

int width_bmp;

int height_bmp;

/*

第1 个参数: 图片所在的路径名

第2 个参数: 没有任何作用,先将他设置为空

返回值: 返回一个指向堆的指针,是解码后的图片数据

*/

int read_picture_type(char *pic_name)//根据文析名得到图片的类型

{
char *pic_tail;
int name_len = strlen(pic_name);
pic_tail = pic_name + name_len-1;
while(pic_name != pic_tail)
{
if(*pic_tail == '.')
{

if(strcmp(pic_tail,".bmp") == 0 || strcmp(pic_tail,".BMP") == 0)
{
return 2;
}
if(strcmp(pic_tail,".jpg") == 0 || strcmp(pic_tail,".JPG") == 0)
{
return 1;

}

}
pic_tail--;
}
printf("This is not bmp or jpg file\n");
return 0;

}

/*

参数1 pic_name: 是要解码图片的路径

返回值:是返回一个指向解码后数据指针

*/

unsigned char *read_picture(char *pic_name)//根据图片的类型,调用对应的解码函数进行解码,返回一个指向解码后数据的指针

{
int pic_type = read_picture_type(pic_name);

switch(pic_type)//根据图片类型不同,调用不同函数来解码
{
case 0: 
printf("picture file ERR\n\n");
return NULL;
break;

case 1:
//如果是JPEG图片
return read_jpeg(pic_name);
break;

case 2://如果是BMP图片
return read_bmp(pic_name);
break;
}

return NULL;

}

/*

参数1:是BMP图片的路径

返回一个指向堆分配空间指针,存放图片解码后的数据

*/

unsigned char* read_bmp(char* filename)//BMP类型图片的解码,

{
FILE *fp;
int rc;
int line_x, line_y;
long int location = 0, BytesPerLine1 = 0,BytesPerLine2 = 0;
unsigned char* image;

fp = fopen(filename, "rb" );
if (fp == NULL)
{
printf("openerr\n");
return( NULL);
}

rc = fread( &FileHead, sizeof(BITMAPFILEHEADER), 1, fp );//读取文件头
if ( rc != 1)
{
printf("read header error!\n");
fclose( fp );
return( NULL );
}

//检测是否是bmp图像
if (memcmp( FileHead.cfType, "BM", 2) != 0)
{
printf("it's not a BMP file\n");
fclose( fp );
return( NULL );
}

rc = fread( (char *)&InfoHead, sizeof(BITMAPINFOHEADER),1, fp );//读取信息头
if ( rc != 1)
{
printf("read infoheader error!\n");
fclose( fp );
return( NULL );
}

//跳转的数据区  FileHead.cfoffBits是始止位距数据区的偏移量
fseek(fp, FileHead.cfoffBits, SEEK_SET);
//每行字节数   InfoHead,是BMP文件的信息头  ciBitCount对应图片的位深度

BytesPerLine1 = ((InfoHead.ciWidth * 16) / 32) * 4;//生成后的,16的偏移量,
BytesPerLine2 = (InfoHead.ciWidth * InfoHead.ciBitCount+31 ) / 32 * 4;//图片本身一行数据的偏移量 

//printf("BytesPerLine==%d\n",BytesPerLine);
//printf("ciWidth==%d\n",InfoHead.ciWidth);
//printf("ciHeight==%d\n",InfoHead.ciHeight);
width_bmp=InfoHead.ciWidth;
height_bmp=InfoHead.ciHeight;
image=(unsigned char*)malloc(width_bmp*height_bmp*2);
if(InfoHead.ciWidth%2 != 0)
width_bmp=InfoHead.ciWidth-1;

line_x = line_y = 0;
unsigned short short_buf;
PIXEL pix;

while(!feof(fp))
{

rc = fread( (char *)&pix, 1, sizeof(PIXEL), fp);//读一个像素点,放到pix这个结构体中
if (rc != sizeof(PIXEL))
break;
location = line_x *16 / 8 + (InfoHead.ciHeight - line_y -1) * BytesPerLine1;
//显示每一个像素
short_buf=(((pix.red&0xf8)>>3)<<11)|(((pix.green&0xfc) >> 2) << 5) | ((pix.blue&0xf8) >> 3);
//上面一个像素点已经变成16位的了,所以分成两个8位的,写入到数组中

*(image + location + 0) = (char)(short_buf&0xff);
*(image + location + 1) = (char)((short_buf&0xff00) >> 8);

line_x++;//当前是那个像素点,bits_per_pixel/8是每个像素占多少位字节
if (line_x == InfoHead.ciWidth )
{
line_x = 0;//对应一行中的那个像素
line_y++;//对应那一行
fseek(fp, BytesPerLine2*line_y+FileHead.cfoffBits, SEEK_SET);//重定位,偏移量指针
if(line_y == InfoHead.ciHeight)
break;
}
}
fclose( fp );
return (image);

}

/*

这是jpeg图片的解码

参数1:是jpeg图片的路径

返回 :返回一个解码后的数据

*/

unsigned char* read_jpeg(char *path)

{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile;
int row_stride;
unsigned char *buffer;

// 分配和初始化一个decompression结构体
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);

// 指定源文件
if ((infile = fopen(path, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", path);
return NULL;
}
jpeg_stdio_src(&cinfo, infile);
// 用jpeg_read_header获得jpg信息
jpeg_read_header(&cinfo, TRUE);
/* 源信息 */

#if 0
printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);

// 设置解压参数,比如放大、缩小
printf("enter scale M/N:\n");
//anf("%d/%d", &cinfo.scale_num, &cinfo.scale_denom);
printf("scale to : %d/%d\n", cinfo.scale_num, cinfo.scale_denom);

#endif
// 启动解压:jpeg_start_decompress

jpeg_start_decompress(&cinfo);

#if 0
/* 输出的图象的信息 */
printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);

#endif
width_bmp= cinfo.output_width;
height_bmp = cinfo.output_height;

// 一行的数据长度
row_stride = cinfo.output_width * cinfo.output_components;
buffer = malloc(row_stride);
unsigned char *image= (unsigned char*)malloc(cinfo.output_width* cinfo.output_height*2);//一个颜色点16位两个字节
unsigned short short_buf;
int curr_y = 0;
int curr_x = 0;
int jpeg_x=0;
int location;
// 循环调用jpeg_read_scanlines来一行一行地获得解压的数据
while (cinfo.output_scanline < cinfo.output_height) 
{
location =curr_x*2 + cinfo.output_width*2*curr_y;
(void) jpeg_read_scanlines(&cinfo, &buffer, 1);
while(curr_x < cinfo.output_width)
{
short_buf=(((*(buffer+jpeg_x)&0xf8)>>3)<<11)|(((*(buffer+jpeg_x+1)&0xfc) >> 2) << 5) | ((*(buffer+jpeg_x+2)&0xf8) >> 3);

*(image + location + 0) = (char)(short_buf&0xff);
*(image + location + 1) = (char)((short_buf&0xff00) >> 8);
jpeg_x = jpeg_x+3;
curr_x++;//当前是那个像素点,bits_per_pixel/8是每个像素占多少位字节
location =curr_x*2 + cinfo.output_width*2*curr_y;
}
curr_y++;
curr_x = 0;
jpeg_x = 0;
}

free(buffer);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return image;

}

//这是主函数,图片的输入路径由main函数的第一个参数决定

int main(char argv, char **argc)

{

if(argv != 2)
{
printf("input fine err!\nplease input pic file\n");

return 0;
}
/* 初始化framebuffer */
initFramebuffer();
printf("file:%s\n",argc[1]);
unsigned char *image = read_picture(argc[1]);
write_buf_image(image,0,0,width_bmp,height_bmp); /*往framebuffer里面写数据,显示到LCD上面去*/
return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: