libjpeg-turbo这个库比libjpeg强大一些,libjpeg解析完bmp以后,数据格式是RGB,如果再保存为BGR就比较麻烦,而libjpeg-turbo保存为BGR格式的时候有JCS_EXT_BGR这个参数,所以保存bmp图片就方便很多了。
解析为bmp时,bmp需要4字节对齐,这里我的做法是,假如一个jpg的tup宽度是157,我这边是将图片扩展为160个像素。这里支持从本地文件加载jpg后解析为bmp,也可以从内存解析为bmp,同时支持将bmp压缩为jpg
封装头文件:
#ifndef __PARSE_JPEG__
#define __PARSE_JPEG__
#include <stdio.h>
#include <string.h>
#include<sys/types.h>
extern "C" {
#include "include/jpeglib.h"
}
class ParseJpeg
{//不使用单例是为了支持多线程操作
public:
ParseJpeg();
~ParseJpeg();
public:
unsigned char* Parse(const char* pstrImageName, bool bHasHeader/*是否只要bmp数据区*/, long& nLen/*返回bmp图像的长度*/, long& nWidth, long& nHeight);
unsigned char* Parse(const char* pstrImageData, long nImageSize, bool bHasHeader/*是否只要bmp数据区*/, long& nLen/*返回bmp图像的长度*/, long& nWidth, long& nHeight);
bool CompressBMPToJPG(const char *pstrFilename, unsigned char *bits, int nWidth, int nHeight, int nDepth, int nQuality);
private:
void analyse_jpeg(const char* pstrImageName, long& nWidth, long& nHeight);//从本地文件夹加载JPG并解析为BMP
void analyse_jpeg(const char* pstrImageData, long nImageSize, long& nWidth, long& nHeight);//从内存直接解析JPG文件
void write_bmp_header(long nWidth, long nHeight, long nDepth);
void write_bmp_data(j_decompress_ptr cinfo, unsigned char *src_buff);
private:
bool m_bHasHeader;//是否输出bmp图片文件头
long m_nLenHeader;
unsigned char* m_pJpgBuffer;
unsigned char* m_pLineBuffer;
unsigned char* m_pImageBuffer;
};
#endif
#include "ParseJpeg.h"
#define MAX_IMAGE_SIZE 20 * 1024 * 1024 //针对1920*1080的图像
#pragma pack(2)
typedef struct BITMAPFILEHEADER {
u_int16_t bfType;
u_int32_t bfSize;
u_int16_t bfReserved1;
u_int16_t bfReserved2;
u_int32_t bfOffBits;
} BITMAPFILEHEADER;
typedef struct BITMAPINFOHEADER {
u_int32_t biSize;
u_int32_t biWidth;
u_int32_t biHeight;
u_int16_t biPlanes;
u_int16_t biBitCount;
u_int32_t biCompression;
u_int32_t biSizeImage;
u_int32_t biXPelsPerMeter;
u_int32_t biYPelsPerMeter;
u_int32_t biClrUsed;
u_int32_t biClrImportant;
} BITMAPINFODEADER;
ParseJpeg::ParseJpeg()
{//主要是为了防止频繁分配内存
m_bHasHeader = false;
m_nLenHeader = 0;
m_pJpgBuffer = NULL;
while(!m_pJpgBuffer){
try{
m_pJpgBuffer = new unsigned char[MAX_IMAGE_SIZE];
}
catch(...){}
}
m_pLineBuffer = NULL;
while(!m_pLineBuffer){
try{
m_pLineBuffer = new unsigned char[MAX_IMAGE_SIZE];
}
catch(...){}
}
m_pImageBuffer = NULL;
while(!m_pImageBuffer){
try{
m_pImageBuffer = new unsigned char[MAX_IMAGE_SIZE];
}
catch(...){}
}
}
ParseJpeg::~ParseJpeg()
{
if(m_pJpgBuffer){
delete[] m_pJpgBuffer;
m_pJpgBuffer = NULL;
}
if(m_pLineBuffer){
delete[] m_pLineBuffer;
m_pLineBuffer = NULL;
}
if(m_pImageBuffer){
delete[] m_pImageBuffer;
m_pImageBuffer = NULL;
}
}
void ParseJpeg::analyse_jpeg(const char* pstrImageName, long& nWidth, long& nHeight)
{
FILE* pFile = fopen(pstrImageName, "rb+");
if(pFile){
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr); //一下为libjpeg函数,具体参看相关文档
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, pFile);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
unsigned long width = cinfo.output_width;
unsigned long height = cinfo.output_height;
unsigned short depth = cinfo.output_components;
if(width % 4 != 0){
nWidth = width + 4 - width % 4;
}
else{
nWidth = width;
}
nHeight = height;
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, width * depth, 1);
unsigned char *point = m_pJpgBuffer;
while (cinfo.output_scanline < height) {
jpeg_read_scanlines(&cinfo, buffer, 1);//读取一行jpg图像数据到buffer
memcpy(point, *buffer, width * depth);//将buffer中的数据逐行给src_buff
point += width*depth;//一次改变一行
}
if(m_bHasHeader){
write_bmp_header(nWidth, nHeight, depth);//写bmp文件头
}
write_bmp_data(&cinfo, m_pJpgBuffer);//写bmp像素数据
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(pFile);
}
}
void ParseJpeg::analyse_jpeg(const char* pstrImageData, long nImageSize, long& nWidth, long& nHeight)
{//从内存直接解析JPG文件
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr); //一下为libjpeg函数,具体参看相关文档
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, (unsigned char*)pstrImageData, nImageSize);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
unsigned long width = cinfo.output_width;
unsigned long height = cinfo.output_height;
unsigned short depth = cinfo.output_components;
if(width % 4 != 0){
nWidth = width + 4 - width % 4;
}
else{
nWidth = width;
}
nHeight = height;
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, width * depth, 1);
unsigned char *point = m_pJpgBuffer;
while (cinfo.output_scanline < height) {
jpeg_read_scanlines(&cinfo, buffer, 1);//读取一行jpg图像数据到buffer
memcpy(point, *buffer, width * depth);//将buffer中的数据逐行给src_buff
point += width*depth;//一次改变一行
}
if(m_bHasHeader){
write_bmp_header(nWidth, nHeight, depth);//写bmp文件头
}
write_bmp_data(&cinfo, m_pJpgBuffer);//写bmp像素数据
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
}
void ParseJpeg::write_bmp_header(long nWidth, long nHeight, long nDepth)
{
struct BITMAPFILEHEADER bfh;
struct BITMAPINFOHEADER bih;
unsigned long headersize = 0;
unsigned long filesize = 0;
if (nDepth == 1) {
headersize = 14 + 40 + 256 * 4;
filesize = headersize + nWidth * nHeight;
}
if (nDepth == 3) {
headersize = 14 + 40;
filesize = headersize + nWidth * nHeight * nDepth;
}
memset(&bfh, 0, sizeof(struct BITMAPFILEHEADER));
memset(&bih, 0, sizeof(struct BITMAPINFOHEADER));
//写入比较关键的几个bmp头参数
bfh.bfType = 0x4D42;
bfh.bfSize = filesize;
bfh.bfOffBits = headersize;
bih.biSize = 40;
bih.biWidth = nWidth;
bih.biHeight = nHeight;
bih.biPlanes = 1;
bih.biBitCount = (unsigned short)nDepth * 8;
bih.biSizeImage = nWidth * nHeight * nDepth;
memcpy(m_pImageBuffer, &bfh, sizeof(struct BITMAPFILEHEADER));
memcpy(m_pImageBuffer + sizeof(struct BITMAPFILEHEADER), &bih, sizeof(struct BITMAPINFOHEADER));
if (nDepth == 1) {//灰度图像要添加调色板
unsigned char *platte = NULL;
while(!platte){
try{
platte = new unsigned char[256*4];
}
catch(...){}
}
unsigned char j = 0;
for (int i = 0; i < 1024; i += 4) {
platte[i] = j;
platte[i+1] = j;
platte[i+2] = j;
platte[i+3] = 0;
j++;
}
memcpy(m_pImageBuffer + sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER), platte, sizeof(unsigned char) * 1024);
m_nLenHeader = sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER) + sizeof(unsigned char) * 1024;
delete[] platte;
}
else{
m_nLenHeader = sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER);
}
}
void ParseJpeg::write_bmp_data(j_decompress_ptr cinfo, unsigned char *src_buff)
{
unsigned long width = cinfo->output_width;
unsigned long height = cinfo->output_height;
unsigned short depth = cinfo->output_components;
unsigned char *point = src_buff + width * depth * (height - 1); //倒着写数据,bmp格式是倒的,jpg是正的
for (unsigned long i = 0; i < height; i++) {
for (unsigned long j = 0; j < width * depth; j += depth) {
if (depth == 1) {//处理灰度图
m_pLineBuffer[j] = point[j];
}
if (depth == 3) {//处理彩色图
m_pLineBuffer[j + 2] = point[j + 0];
m_pLineBuffer[j + 1] = point[j + 1];
m_pLineBuffer[j + 0] = point[j + 2];
}
}
point -= width * depth;
memcpy(m_pImageBuffer + m_nLenHeader, m_pLineBuffer, sizeof(unsigned char) * width * depth);
m_nLenHeader += sizeof(unsigned char) * width * depth;
if(width % 4 != 0){
long nOffset = 4 - width % 4;//本人添加,当bmp图片宽度不以4字节对齐的时候,会出现图像倾斜、模糊等状况
for(int i = 0; i < nOffset * depth; i++){
m_pImageBuffer[m_nLenHeader++] = 0x00;
}
}
}
}
unsigned char* ParseJpeg::Parse(const char* pstrImageName, bool bHasHeader, long& nLen, long& nWidth, long& nHeight)
{
m_bHasHeader = bHasHeader;
m_nLenHeader = 0;
memset(m_pJpgBuffer, 0, MAX_IMAGE_SIZE);
memset(m_pLineBuffer, 0, MAX_IMAGE_SIZE);
memset(m_pImageBuffer, 0, MAX_IMAGE_SIZE);
analyse_jpeg(pstrImageName, nWidth, nHeight);
nLen = m_nLenHeader;
return m_pImageBuffer;
}
unsigned char* ParseJpeg::Parse(const char* pstrImageData, long nImageSize, bool bHasHeader, long& nLen/*返回bmp图像的长度*/, long& nWidth, long& nHeight)
{
m_bHasHeader = bHasHeader;
m_nLenHeader = 0;
memset(m_pJpgBuffer, 0, MAX_IMAGE_SIZE);
memset(m_pLineBuffer, 0, MAX_IMAGE_SIZE);
memset(m_pImageBuffer, 0, MAX_IMAGE_SIZE);
analyse_jpeg(pstrImageData, nImageSize, nWidth, nHeight);
nLen = m_nLenHeader;
return m_pImageBuffer;
}
bool ParseJpeg::CompressBMPToJPG(const char *pstrFilename, unsigned char *bits, int nWidth, int nHeight, int nDepth, int nQuality)
{
FILE * pFile = fopen(pstrFilename, "wb+");
if (pFile){
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1]; //pointer to JSAMPLE row[s]
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, pFile);
cinfo.image_width = nWidth; //image width and height, in pixels
cinfo.image_height = nHeight;
cinfo.input_components = 3; //# of color components per pixel
cinfo.in_color_space = JCS_EXT_BGR; //colorspace of input image
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, nQuality, TRUE);//limit to baseline-JPEG values
jpeg_start_compress(&cinfo, TRUE);
int row_stride = nWidth * nDepth; // JSAMPLEs per row in image_buffer
while (cinfo.next_scanline < cinfo.image_height)
{
//这里我做过修改,由于jpg文件的图像是倒的,所以改了一下读的顺序
row_pointer[0] = &bits[(cinfo.image_height - cinfo.next_scanline - 1) * row_stride];
(void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(pFile);
jpeg_destroy_compress(&cinfo);
return true;
}
return false;
}
makefile:
INCLUDE_Jpeg=./include
LIBRARY_Jpeg=./lib/libjpeg.a
libChlFaceSdk.so:test.o ParseJpeg.o
g++ -fPIC -lz -lm -lc -pthread test.o ParseJpeg.o -I$(INCLUDE_Jpeg) $(LIBRARY_Jpeg) -o ./test
ParseJpeg.o:ParseJpeg.h ParseJpeg.cpp
g++ -c ParseJpeg.cpp -fPIC -o ParseJpeg.o
test.o:test.cpp
g++ -c test.cpp -fPIC -o test.o
clean:
rm test.o ParseJpeg.o