UID 1
精华
积分 76365
威望 点
宅币 个
贡献 次
宅之契约 份
最后登录 1970-1-1
在线时间 小时
libjpeg是一个用于压缩、解压jpg格式图像的库。这个库在1998年后就不再更新了。但是jpeg的压缩格式却一直使用至今。简直是C语言中的战斗机。
libjpeg的使用方法很简单。首先借助makefile编译官方的源码取得libjpeg.lib然后设置自己的工程引用这个库就行了,我们稍后再讲怎么编译。
首先是头文件的包含。
#include"jpeglib.h"
然后就是接下来的两个部分:如何压缩,如何解压。
先说如何解压。
解压就是,你用fopen打开一个jpg文件(第二个参数是"rb"),分配内存用于存储解压后的位图,设置好你要接收的格式是什么类型,然后使用jpg的库完成解压。
首先你需要这样两个结构体:struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jem={0}; 复制代码 其中jpeg_decompress_struct是装解压信息的结构体,jpeg_error_mgr是错误管理器,它里面有一系列的函数指针,你可以设置自己的错误回调函数。也可以不设置。如果你不设置,它遇到错误就执行exit(1),直接干掉你的程序。因此这也是一种很直观的调试方式——我的程序被干掉了就说明有错。
然后就对JPG文件进行读取。//打开文件
fp=fopen(szFileName,"rb");
if(!fp)
return;
//设置错误处理
cinfo.err=jpeg_std_error(&jem);
//创建解压结构
jpeg_create_decompress(&cinfo);
//设置stdio读取来源
jpeg_stdio_src(&cinfo,fp);
//读取文件头
jpeg_read_header(&cinfo,TRUE); 复制代码 之后用户需要自己根据需求分配一下内存,用于存储位图。搞定以后,运行如下的代码。//计算每行字节数的宏
#define CalcPitch(b,w) ((((b)*(w)-1)/32+1)*4)
//行指针
JSAMPROW row_pointer[1];
//每行字节数
size_t cbPitch=CalcPitch(24,宽度);
//设置输出的颜色类型
cinfo.out_color_space=JCS_RGB;
jpeg_calc_output_dimensions(&cinfo);
//开始解压
jpeg_start_decompress(&cinfo);
//指向自己分配好的图像内存
row_pointer[0]=(uint8_t*)g_pBits;
//一行一行读取
while(cinfo.output_scanline<cinfo.output_height)
{
jpeg_read_scanlines(&cinfo,row_pointer,1);
row_pointer[0]+=cbPitch;//转到下一行。
}
//读取结束
jpeg_finish_decompress(&cinfo);
//释放资源
jpeg_destroy_decompress(&cinfo);
//关闭文件
fclose(fp); 复制代码 然后你的图像就已经读取到g_pBits中了!
压缩,就是你提供一个位图(比如RGB像素阵列或者YUV阵列等,可以选择的),然后提供一个打开了写入文件的FILE*,设置好参数,就可以进行压缩了。
压缩要用到的结构体是:struct jpeg_compress_struct jcs;
struct jpeg_error_mgr jem={0}; 复制代码 同样的错误处理方式。
然后我们就可以打开文件进行写入操作了。//打开文件进行写入
fp=fopen(szFileName,"wb");
if(!fp)
return;
//设置错误处理
jcs.err=jpeg_std_error(&jem);
//创建压缩结构
jpeg_create_compress(&jcs);
//设置stdio写入位置
jpeg_stdio_dest(&jcs,fp);
//设置写入参数
jcs.image_width=宽度;//为图的宽和高,单位为像素
jcs.image_height=高度;
jcs.input_components=3;//图像源有红绿蓝三个组分
jcs.in_color_space=JCS_RGB;//格式是红绿蓝真彩色
jpeg_set_defaults(&jcs);//填写其它默认数值
//设置图像品质
//值的取值范围是[0,100],0表示渣画质,100表示满画质。
jpeg_set_quality(&jcs,0/*渣画质*/,TRUE);
//计算每行字节数
cbPitch=CalcPitch(24,宽度);
//开始压缩
jpeg_start_compress(&jcs,TRUE);
//指向要存储的位图。
row_pointer[0]=(uint8_t*)g_pBits;
//一行一行写入
while(jcs.next_scanline<jcs.image_height)
{
jpeg_write_scanlines(&jcs,row_pointer,1);
row_pointer[0]+=cbPitch;//转到下一行
}
//读取结束
jpeg_finish_compress(&jcs);
//释放资源
jpeg_destroy_compress(&jcs);
//关闭文件
fclose(fp); 复制代码 接下来就是完整的工程的代码了。
这个工程演示了如何读取jpg、写入jpg的方法。
它会加载工程文件夹中的test.jpg,然后以渣画质方式输出到out.jpg。最后创建一个窗口用于显示加载的jpg图像。
(PS.为什么要用渣画质输出?因为这样能明显看到它有效地设置了画质然后输出了文件。)#include<tchar.h>
#include<stdio.h>
#include<Windows.h>
#include<memory.h>
#include<stdint.h>
#undef FAR
#define XMD_H
#include"jpeglib.h"
uint32_t g_uWidth;
uint32_t g_uHeight;
void *g_pBits=NULL;
HDC g_hDCJpeg=NULL;
HBITMAP g_hBMPJpeg=NULL;
#define CalcPitch(b,w) ((((b)*(w)-1)/32+1)*4)
//=============================================================================
//函数:BuildBitmap
//描述:建立位图用于绘图
//-----------------------------------------------------------------------------
void BuildBitmap(uint32_t uWidth,uint32_t uHeight)
{
BITMAPINFOHEADER BMIF=
{
sizeof(BITMAPINFOHEADER),
(LONG)uWidth,
-(LONG)uHeight,
1,
24,
BI_RGB,
0,
0,
0,
0,
0
};
g_uWidth=uWidth;
g_uHeight=uHeight;
if(g_hBMPJpeg)
DeleteObject(&g_hBMPJpeg);
if(g_hDCJpeg)
DeleteDC(g_hDCJpeg);
g_hDCJpeg=CreateCompatibleDC(NULL);
if(!g_hDCJpeg)
return;
g_hBMPJpeg=CreateDIBSection(g_hDCJpeg,(BITMAPINFO*)&BMIF,DIB_PAL_COLORS,&g_pBits,NULL,0);
if(!g_hBMPJpeg)
return;
SelectObject(g_hDCJpeg,g_hBMPJpeg);
}
//=============================================================================
//函数:LoadJPG
//描述:从文件中加载
//-----------------------------------------------------------------------------
void LoadJPG(char*szFileName)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jem={0};
FILE*fp=NULL;
JSAMPROW row_pointer[1];
size_t cbPitch;
fp=fopen(szFileName,"rb");
if(!fp)
return;
//设置错误处理
cinfo.err=jpeg_std_error(&jem);
//创建解压结构
jpeg_create_decompress(&cinfo);
//设置stdio读取来源
jpeg_stdio_src(&cinfo,fp);
//读取文件头
jpeg_read_header(&cinfo,TRUE);
//分配内存,构建位图
BuildBitmap(cinfo.image_width,cinfo.image_height);
cbPitch=CalcPitch(24,g_uWidth);
//设置输出的颜色类型
cinfo.out_color_space=JCS_RGB;
jpeg_calc_output_dimensions(&cinfo);
//开始解压
jpeg_start_decompress(&cinfo);
row_pointer[0]=(uint8_t*)g_pBits;
//一行一行读取
while(cinfo.output_scanline<cinfo.output_height)
{
jpeg_read_scanlines(&cinfo,row_pointer,1);
row_pointer[0]+=cbPitch;
}
//读取结束
jpeg_finish_decompress(&cinfo);
//释放资源
jpeg_destroy_decompress(&cinfo);
//关闭文件
fclose(fp);
}
//=============================================================================
//函数:SaveJPG
//描述:保存到文件
//-----------------------------------------------------------------------------
void SaveJPG(char*szFileName)
{
struct jpeg_compress_struct jcs;
struct jpeg_error_mgr jem={0};
FILE*fp=NULL;
JSAMPROW row_pointer[1];
size_t cbPitch;
//打开文件进行写入
fp=fopen(szFileName,"wb");
if(!fp)
return;
//设置错误处理
jcs.err=jpeg_std_error(&jem);
//创建压缩结构
jpeg_create_compress(&jcs);
//设置stdio写入位置
jpeg_stdio_dest(&jcs,fp);
//设置写入参数
jcs.image_width=g_uWidth;//为图的宽和高,单位为像素
jcs.image_height=g_uHeight;
jcs.input_components=3;//图像源有红绿蓝三个组分
jcs.in_color_space=JCS_RGB;//格式是红绿蓝真彩色
jpeg_set_defaults(&jcs);
//设置图像品质
jpeg_set_quality(&jcs,0/*渣画质*/,TRUE);
cbPitch=CalcPitch(24,g_uWidth);
//开始压缩
jpeg_start_compress(&jcs,TRUE);
row_pointer[0]=(uint8_t*)g_pBits;
//一行一行写入
while(jcs.next_scanline<jcs.image_height)
{
jpeg_write_scanlines(&jcs,row_pointer,1);
row_pointer[0]+=cbPitch;
}
//读取结束
jpeg_finish_compress(&jcs);
//释放资源
jpeg_destroy_compress(&jcs);
//关闭文件
fclose(fp);
}
//=============================================================================
//函数:SwapRGB
//描述:将颜色从BGR到RGB之间进行转换
//-----------------------------------------------------------------------------
void SwapRGB()
{
uint8_t*pLinePtr;
uint32_t x,y;
size_t cbPitch;
pLinePtr=(uint8_t*)g_pBits;
cbPitch=CalcPitch(24,g_uWidth);
for(y=0;y<g_uHeight;y++)
{
for(x=0;x<g_uWidth;x++)
{
uint8_t t=pLinePtr[x*3];
pLinePtr[x*3]=pLinePtr[x*3+2];
pLinePtr[x*3+2]=t;
}
pLinePtr+=cbPitch;
}
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wp,LPARAM lp);
int APIENTRY _tWinMain
(
HINSTANCE hInst,
HINSTANCE hPrevInst,
LPTSTR lpCmdLine,
int nShow
)
{
MSG msg;
WNDCLASSEX WCEx=
{
sizeof(WNDCLASSEX),
0,
WndProc,
0,
0,
hInst,
LoadIcon(NULL,MAKEINTRESOURCE(IDI_APPLICATION)),
LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW)),
(HBRUSH)(COLOR_BTNFACE+1),
NULL,
TEXT("Jpeg_Demo_Window"),
LoadIcon(NULL,MAKEINTRESOURCE(IDI_APPLICATION))
};
HWND hWnd=CreateWindowEx(0,MAKEINTATOM(RegisterClassEx(&WCEx)),TEXT("JPG"),
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInst,NULL);
ShowWindow(hWnd,nShow);
UpdateWindow(hWnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wp,LPARAM lp)
{
switch(Msg)
{
case WM_CREATE:
LoadJPG("test.jpg");
SaveJPG("out.jpg");
SwapRGB();
break;
case WM_PAINT:
{
RECT rc;
PAINTSTRUCT ps;
HDC hDC=BeginPaint(hWnd,&ps);
uint32_t TargW,TargH;
SetStretchBltMode(hDC,HALFTONE);
GetClientRect(hWnd,&rc);
TargW=rc.right-rc.left;
TargH=rc.bottom-rc.top;
//如果需要缩小
if(TargW<g_uWidth||TargH<g_uHeight)
{
uint32_t DstW,DstH;
//首先宽度固定,计算高度
DstW=TargW;
DstH=DstW*g_uHeight/g_uWidth;
//如果高度超过目标,则高度固定,计算宽度
if(DstH>TargH)
{
DstH=TargH;
DstW=DstH*g_uWidth/g_uHeight;
}
StretchBlt(hDC,rc.left,rc.top,DstW,DstH,g_hDCJpeg,0,0,g_uWidth,g_uHeight,SRCCOPY);
}
else
BitBlt(hDC,rc.left,rc.top,g_uWidth,g_uHeight,g_hDCJpeg,0,0,SRCCOPY);
EndPaint(hWnd,&ps);
}
break;
case WM_SIZING:
case WM_SIZE:
{
RECT rc;
GetClientRect(hWnd,&rc);
InvalidateRect(hWnd,&rc,TRUE);
}
break;
case WM_DESTROY:
if(g_hBMPJpeg)
DeleteObject(&g_hBMPJpeg);
if(g_hDCJpeg)
DeleteDC(g_hDCJpeg);
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd,Msg,wp,lp);
} 复制代码 原图:
图像来源:http://www.pixiv.net/member_illu ... ;illust_id=48042573
作者:稀泥m
途中的角色:時崎 狂三
程序窗口图:
BIN:
jpeg.exe
(114.09 KB, 下载次数: 9)
SRC:
jpeg.7z
(255.78 KB, 下载次数: 28)
接下来说到另一个问题——怎么取得libjpeg.lib?其实我觉得你既然能得到官方的源码了你就能自己编译。你可以用makefile编译,也可以自己建立一个工程来编译。那就像我上传的源码那样,直接把libjpeg中的源码加入到工程中参与编译就行了。
你只需要将以下的这些文件加入到自己的工程中,然后编译为静态库就行了。这样你还能借助VS的IDE设置自己的优化方式呢。
jcapimin.c,jcapistd.c,jccoefct.c,jccolor.c,jcdctmgr.c,jchuff.c,jchuff.h,jcinit.c,jcmainct.c,jcmarker.c,jcmaster.c,jcomapi.c,jconfig.h,jcparam.c,jcphuff.c,jcprepct.c,jcsample.c,jctrans.c,jdapimin.c,jdapistd.c,jdatadst.c,jdatasrc.c,jdcoefct.c,jdcolor.c,jdct.h,jddctmgr.c,jdhuff.c,jdhuff.h,jdinput.c,jdmainct.c,jdmarker.c,jdmaster.c,jdmerge.c,jdphuff.c,jdpostct.c,jdsample.c,jdtrans.c,jerror.c,jerror.h,jfdctflt.c,jfdctfst.c,jfdctint.c,jidctflt.c,jidctfst.c,jidctint.c,jidctred.c,jinclude.h,jmemmgr.c,jmemnobs.c,jmemsys.h,jmorecfg.h,jpegint.h,jpeglib.h,jquant1.c,jquant2.c,jutils.c,jversion.h然后你自己的工程只需要包含jpeglib.h就行了。其中jconfig.h是我从ijg的开源库中jconfig.vs改过来的。内存管理的源码用的是jmemnobs.c。对于Mac OS X遗迹其它系统,应该使用其它的内存管理源码。