0xAA55 发表于 2015-4-8 17:45:51

【C】libjpeg的使用方法

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;

//每行字节数
size_t cbPitch=CalcPitch(24,宽度);

//设置输出的颜色类型
cinfo.out_color_space=JCS_RGB;
jpeg_calc_output_dimensions(&cinfo);

//开始解压
jpeg_start_decompress(&cinfo);

//指向自己分配好的图像内存
row_pointer=(uint8_t*)g_pBits;

//一行一行读取
while(cinfo.output_scanline<cinfo.output_height)
{
        jpeg_read_scanlines(&cinfo,row_pointer,1);
        row_pointer+=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表示满画质。
jpeg_set_quality(&jcs,0/*渣画质*/,TRUE);

//计算每行字节数
cbPitch=CalcPitch(24,宽度);

//开始压缩
jpeg_start_compress(&jcs,TRUE);

//指向要存储的位图。
row_pointer=(uint8_t*)g_pBits;

//一行一行写入
while(jcs.next_scanline<jcs.image_height)
{
        jpeg_write_scanlines(&jcs,row_pointer,1);
        row_pointer+=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;
        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=(uint8_t*)g_pBits;

        //一行一行读取
        while(cinfo.output_scanline<cinfo.output_height)
        {
                jpeg_read_scanlines(&cinfo,row_pointer,1);
                row_pointer+=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;
        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=(uint8_t*)g_pBits;

        //一行一行写入
        while(jcs.next_scanline<jcs.image_height)
        {
                jpeg_write_scanlines(&jcs,row_pointer,1);
                row_pointer+=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;
                        pLinePtr=pLinePtr;
                        pLinePtr=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_illust.php?mode=medium&illust_id=48042573
作者:稀泥m
途中的角色:時崎 狂三

程序窗口图:

BIN:
SRC:

接下来说到另一个问题——怎么取得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遗迹其它系统,应该使用其它的内存管理源码。
页: [1]
查看完整版本: 【C】libjpeg的使用方法