libjpeg这个库主要用于处理jpeg数据,比如将RGB压缩成JPEG,或者将JPEG解压为RGB。其实早在4年前已经接触过,但一直没写过这方面的文章。后来想想还是有必要写出来,至少可以证明自己搞过这东西。
libjpeg使用十分简单,而且源码带有例子程序,下面的代码基本上就是该例子。所以没什么技术含量。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>
// jpeg库头文件必须放到stdio.h后面
#include "libjpeg/include/jpeglib.h"
#include "libjpeg/include/jerror.h"
typedef struct my_error_mgr * my_error_ptr;
void my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
// 读取JPG图片数据,并解压到内存中,*rgb_buffer需要自行释放
int read_jpeg_file(const char* jpeg_file, unsigned char** rgb_buffer, int* size, int* width, int* height)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
FILE* fp;
JSAMPARRAY buffer;
int row_stride = 0;
unsigned char* tmp_buffer = NULL;
int rgb_size;
fp = fopen(jpeg_file, "rb");
if (fp == NULL)
{
printf("open file %s failed.\n", jpeg_file);
return -1;
}
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer))
{
jpeg_destroy_decompress(&cinfo);
fclose(fp);
return -1;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
jpeg_read_header(&cinfo, TRUE);
//cinfo.out_color_space = JCS_RGB; //JCS_YCbCr; // 设置输出格式
jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_width * cinfo.output_components;
*width = cinfo.output_width;
*height = cinfo.output_height;
rgb_size = row_stride * cinfo.output_height; // 总大小
*size = rgb_size;
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
*rgb_buffer = (unsigned char *)malloc(sizeof(char) * rgb_size); // 分配总内存
printf("debug--:\nrgb_size: %d, size: %d w: %d h: %d row_stride: %d \n", rgb_size,
cinfo.image_width*cinfo.image_height*3,
cinfo.image_width,
cinfo.image_height,
row_stride);
tmp_buffer = *rgb_buffer;
while (cinfo.output_scanline < cinfo.output_height) // 解压每一行
{
jpeg_read_scanlines(&cinfo, buffer, 1);
// 复制到内存
memcpy(tmp_buffer, buffer[0], row_stride);
tmp_buffer += row_stride;
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(fp);
return 0;
}
int write_jpeg_file(const char* jpeg_file, unsigned char* rgb_buffer, int width, int height, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
int row_stride = 0;
FILE* fp = NULL;
JSAMPROW row_pointer[1];
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
fp = fopen(jpeg_file, "wb");
if (fp == NULL)
{
printf("open file %s failed.\n", jpeg_file);
return -1;
}
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, 1); // todo 1 == true
jpeg_start_compress(&cinfo, TRUE);
row_stride = width * cinfo.input_components;
while (cinfo.next_scanline < cinfo.image_height)
{
row_pointer[0] = &rgb_buffer[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(fp);
return 0;
}
实际上我是使用保存为bmp格式图片的方式进行测试的,bmp图片是RGB分量实际上应该是BGR,所以如果要正确保存bmp,则要进行R、B分量互换,在解压时,libjpeg可以设置输出颜色,使用cinfo.out_color_space,如果不想自己调换R、B,则将其赋值为JCS_EXT_BGR。另外,bmp文件头信息中的高值为负数,否则保存的图片是上下颠倒的。
李迟 2015.6.30 晚饭后