VC++图像存取总结

584 篇文章 3 订阅 ¥99.90 ¥99.00
282 篇文章 5 订阅

使用GDI+的MFC应用程序的文件打开和保存对话框



文章概要:CGdiplusFileOpenDialog和CGdiplusFileSaveAsDialog类提供了一个


在GDI+的MFC应用程序轻松实现文件打开和保存对话框的方法。 主要特点是: 


它们可以动态地装入关于支持图像的解码器/编码器和构建文件过滤清单。 它们


可以获得当前图像编码器标识码(CLSID)以供需要将其作为一个参数的GDI+函


数使用。 它们可以检查和验证文件名和扩展名。
(译注:原文代码的Demo_VC7没有在同Demo_VC6一样显示滚动条视图,修改之)
开发环境:Windows XP/Server 2003/2000/NT4.0-SP6/98/Me, MS Visual Sudio 


6.0/2003, GDI+
介绍
CGdiplusFileOpenDialog和CGdiplusFileSaveAsDialog类提供了一个在GDI+的


MFC应用程序轻松实现文件打开和保存对话框的方法。
主要特点是:
它们可以动态地装入关于支持图像的解码器/编码器和构建文件过滤清单。
它们可以获得当前图像编码器标识码(CLSID)以供需要将其作为一个参数的


GDI+函数使用。
它们可以检查和验证文件名和扩展名。
你可以在类的描述和demo应用程序中找到更多的详情。
(类的)层次表


CGdiplusFileDialog类
CGdiplusFileDialog继承于MFC CFileDialog类,同时也是


CGdiplusFileOpenDialog和CGdiplusFileSaveAsDialog的抽象基类。
封装一个包含所支持的解码器/编码器的数组(m_arrCodecInfo)
实现基函数TranslateFilter:转换一个MFC-like文件过滤器(使用一个''|''作


为分隔符)到一个OPENFILENAME结构文件过滤器(使用''\0''作为分隔符)
声明两个纯虚函数:FillCodecInfoArray 和ConstructMFCStyleFilte;在派生


类中,覆写函数实现特定操作。
覆写CFileDialog::DoModal;在调用基类函数之前调用,它
o 检查GDI+库是否被初始化
o 调用FillCodecInfoArray、 ConstructMFCStyleFilter 和 TranslateFilter


函数
CGdiplusFileOpenDialog类
CGdiplusFileOpenDialog实现文件打开命令对话框。
覆写CGdiplusFileDialog::FillCodecInfoArray并调用


Gdiplus::GetImageDecoders填充关于可用译码器的信息数组。
o 文件格式的名称(如:"JPEG")
o 已用扩展名(如:"*.JPG、*.JPEG、*.JPE、*.JFIF")
o 在打开对话框(译注:原文为Save As dialog,应该是Open dialog,笔误了


)中使用的缺省扩展名(如:"JPG")
o 解码器标识码
覆写CGdiplusFileDialog::ConstructMFCStyleFilter构建一个"MFC 样式"的过


滤器(使用一个''|''作为分隔符);一个附加的"All GDI+ supported"(所有


GDI+支持)的过滤项被添入


举例
#include "GdiplusFileOpenDialog.h"
// ...
bool CFoo::LoadImage(Image*& pImage)
{
bool bLoaded = false;
CGdiplusFileOpenDialog dlgFile;
if(IDOK == dlgFile.DoModal())
{
CString strPathName = dlgFile.GetPathName();
pImage = Image::FromFile(strPathName.AllocSysString());
Status status = pImage->GetLastStatus();
if(Ok == status)
{
bLoaded = true;
}
}
return bLoaded;
}
CGdiplusFileSaveAsDialog类
CGdiplusFileSaveAsDialog实现文件保存的通用型对话框。
覆写CGdiplusFileDialog::FillCodecInfoArray并调用


Gdiplus::GetImageEncoders来填充关于可用编码器的信息数组。
 文件格式的名称(如:" BMP ")
 已用扩展名(如:"*. BMP、*. DIB、*. RLE ")
 在保存对话框中使用的缺省扩展名(如:" BMP ")
覆写CGdiplusFileDialog::ConstructMFCStyleFilter构建"MFC 样式"的过滤器


(使用一个''|''作为分隔符)
覆写CFileDialog::OnInitDone 和 CFileDialog::OnTypeChange来设置缺省扩展


名并装入缺省编码器标识码(m_clsid成员变量)
实现public方法GetCodecCLSID;codec(编码器/译码器)CLSID进一步被传递到


Gdiplis::Image::Save函数
覆写CFileDialog::OnFileNameOK以实现文件扩展名检查:
 如果文件名没有扩展名,使用缺省扩展名和CLSID
 如果文件名有一个扩展名并且可以被codec信息数组找到,扩展名和相应的


CLSID将用来代替缺省值
 如果文件名有一个扩展名同时不能被codec信息数组找到,它返回TRUE同时对话


框保持显示以便用户输入另一个文件名


举例


#include "GdiplusFileSaveAsDialog.h"


// ...


bool CFoo::SaveImage(Image* pImage)


{


bool bSaved = false;


CGdiplusFileSaveAsDialog dlgFile;


if(IDOK == dlgFile.DoModal())


{


CLSID clsid = dlgFile.GetCodecCLSID();


CString strPathName = dlgFile.GetPathName();


Status status = pImage->Save(strPathName.AllocSysString(),


&clsid);


if(Ok == status)


{


bSaved = true;


}


}


return bSaved;


}
Demo应用程序
Demo应用程序是一个建立在MDI框架上的简单的图像浏览器。它演示了如何使用


CGdiplusFileOpenDialog 和CGdiplusFileSaveAsDialog.


StdAfx.h 中的包含和声明


typedef unsigned __int32 ULONG_PTR;


#include < afxtempl.h >


#include < gdiplus.h >


#pragma comment(lib, "gdiplus.lib")


using namespace Gdiplus;
注意
对于比VC++6.0更新的版本,或如果你已安装了platform SDK更新,请移去


ULONG_PTR的定义。
(仅在VC++ 6.0下)确保gdiplus.lib 的位置应在


Tools/Option/Directories/Library文件夹下;抑或你可以在pragma comment指


示中提供其完全路径和文件名称。
初始化和发布GDI+
你必须在做如何GDI+调用前调用GdiplusStartup,同时在你结束使用GDI+时调用


GdiplusShutdown。


class CDemoApp : public CWinApp


{


// ...


// Attributes


protected:


ULONG_PTR m_gdiplusToken;


// ...


// Implementation


protected:


bool InitGdiplus();


void TermGdiplus();


// ...


};


// Called from CDemoApp::InitInstance


bool CDemoApp::InitGdiplus()


{


GdiplusStartupInput gdiplusStartupInput;


Status status = GdiplusStartup(&m_gdiplusToken,


&gdiplusStartupInput,


NULL);


return (Ok == status);


}


// Called from CDemoApp::ExitInstance


void CDemoApp::TermGdiplus()


{


GdiplusShutdown(m_gdiplusToken);


}
使用CGdiplusFileOpenDialog
去掉调用基类信息处理器的ID_FILE_OPEN命令的缺省映射并将该命令映射到在一


个CWinApp派生类中处理(译注:即Demo程序中的CDemoApp类)。


void CDemoApp::OnFileOpen()


{


CGdiplusFileOpenDialog dlgFile;


if(IDOK == dlgFile.DoModal())


{


OpenDocumentFile(dlgFile.GetPathName());


}


}
覆写CDocument::OnOpenDocument虚函数。


class CDemoDoc : public CDocument


{


// ...


// Attributes


protected:


Image* m_pImage;


// Operations


public:


Image* GetImage() {return m_pImage;}


// ...


// Overrides


// ClassWizard generated virtual function overrides


//{{AFX_VIRTUAL(CDemoDoc)


public:


virtual BOOL OnOpenDocument(LPCTSTR pszPathName);


//}}AFX_VIRTUAL


// ...


};


BOOL CDemoDoc::OnOpenDocument(LPCTSTR pszPathName)


{


SafeDeleteImage();


m_pImage = Image::FromFile(CString(pszPathName).AllocSysString());


return (Ok == m_pImage->GetLastStatus());


}
使用CGdiplusFileSaveAsDialog
在CDocument派生类(译注:即Demo程序中的CDemoDoc类)中映射


ID_FILE_SAVE_AS命令。在CGdiplusFileSaveAsDialog::DoModal返回后,调用


GetPathName和GetCodecCLSID以传递(路径、文件名、Codec CLSID)到


Image::Save方法。


void CDemoDoc::OnFileSaveAs()


{


CGdiplusFileSaveAsDialog dlgFile;


if(IDOK == dlgFile.DoModal())


{


CLSID clsid = dlgFile.GetCodecCLSID();


CString strPathName = dlgFile.GetPathName();


Status status = m_pImage->Save(strPathName.AllocSysString(),


&clsid);


if(Ok != status)


{


TRACE2(_T("\nFailed to save image in ''%s'' file\n")


_T("GDI+ Error: %u"),


strPathName,


status);


}


}


}
绘图
在CDemoView::OnDraw中,使用一个Gdiplus::Graphics对象来绘图。
注意这只是一个例子并且设计得尽可能的简单。


void CDemoView::OnDraw(CDC* pDC)


{


CDemoDoc* pDoc = GetDocument();


ASSERT_VALID(pDoc);


Image* pImage = pDoc->GetImage();


if(NULL != pImage)


{


Graphics graphics(*pDC);


Status status = graphics.GetLastStatus();


if(Ok == status)


{


graphics.DrawImage(pImage, 0, 0,


pImage->GetWidth(), pImage->GetHeight());


}


}


}
结束提示:
gdiplus.dll已经包含在Windows XP 和 Windows Server 2003中
对于运行在Microsoft Windows NT 4.0-SP6/2000/98/Me之上应用程序的可再分


发,gdiplus.dll是必需的。
如果你再分发gdiplus.dll,推荐你将之放入应用程序的文件夹。
下载
Platform SDK Update:包含GDI+头文件,gdiplus.lib, 和 gdiplus.dll(VC++ 


6.0所需)
Platform SDK Redistributable: GDI+:可再分发gdiplus.dll,对于Windows 


NT 4.0-SP6/2000/98/Me
========


在MFC程序中显示JPG/GIF图像



文章概要:如果你是一个使用VB编程的程序员,要在程序中显示JPG或者GIF图像


简直易如反掌,将图像控件拖到Form中,分分钟即可搞掂。但是C++程序员要显


示同样的图形却没有那么轻松,那么是不是要自己编写JPG解压缩代码呢?当然


不用那么复杂啦!本文将针对这个问题讨论如何在MFC中显示JPG或者GIF图像。
如果你是一个使用VB编程的程序员,要在程序中显示JPG或者GIF图像简直易如反


掌,将图像控件拖到Form中,分分钟即可搞掂。但是C++程序员要显示同样的图


形却没有那么轻松,那么是不是要自己编写JPG解压缩代码呢?当然不用那么复


杂啦!本文将针对这个问题讨论如何在MFC中显示JPG或者GIF图像。
用VB写图像显示程序之所以如此轻松,完全是利用了琳琅满目的图像处理控件,


把你想要做的事情都一一搞掂。而C++程序员为了实现相同的功能必须忙乎半天


。其实,C/C++程序员也能使用那些VB程序员所用的(或者说几乎一样的)图像


控件。VB用的图像控件实际上都基于一个系统级COM类――IPicture。下面是有


关 IPicture 的方法描述:
方法 描述
get_Handle 返回图像对象的Windows GDI句柄 
get_Hpal 返回图像对象当前使用的调色板拷贝
get_Type 返回当前图像对象的的图像类型
get_Width 返回当前图像对象的图像宽度
get_Height 返回当前图像对象的图像高度
Render 在指定的位置、指定的设备上下文上绘制指定的图像部分
set_Hpal 设置当前图像的调色板
get_CurDC 返回当前选中这个图像的设备上下文
SelectPicture 将一个位图图像选入给定的设备上下文,返回选中图像的设备


上下文和图像的GDI句柄
get_KeepOriginalForma 返回图像对象KeepOriginalFormat 属性的当前值
put_KeepOriginalFormat 设置图像对象的KeepOriginalFormat 属性
PictureChanged 通知图像对象它的图像资源改变了
SaveAsFile 将图像数据存储到流中,格式与存成文件格式相同
get_Attributes 返回图像位属性当前的设置
从上面这个表可以看出,IPicture操纵着图像对象及其属性。图像对象提供对位


图的抽象,而Windows负责BMP、JPG和GIF位图的标准实现。程序员要做的只是实


例化IPicture,然后调用其Render函数。与通常使用接口的方式不同,这里实例


的创建我们不用CoCreateInstance函数,而是用一个专门的函数OleLoadPicture



1.
IStream* pstm = // 需要一个流(stream)
2.
IPicture* pIPicture;
3.
hr = OleLoadPicture(pstm, 0, FALSE, IID_IPicture, (void**)&pIPicture);
OleLoadPicture从流中加载图像并创建一个可用来显示图像的新IPicture对象。
1.
rc = // 显示图像的矩形
2.
// 将rc 转换为 HIMETRIC
3.
spIPicture->Render(pDC, rc);
 IPicture 负责处理所有琐事,以便确定图形之格式,如 Windows 位图、JPEG


或者GIF文件――甚至是图标和元文件(metafiles)。当然啦,所有这些的实现


细节是需要技巧的,为此我写了一个Demo程序Myimgapp(如图二)来示范这些


IPicture的使用方法。
 
图一 Myimgapp的运行画面 
Myimgapp是个典型的MFC文档/视图程序,在编写这个程序之前,我首先对 


IPicture COM接口进行封装,之所以要这么做,主要是考虑到并不是每一个程序


员都能熟练运用COM接口进行编程,另外将IPicture的主要功能封装在C++类中可


以使我们的问题更容易解决,我封装的这个C++类名字叫做CPicture。它的定义


和实现细节请参考本文提供的源代码。 
我在这个类中将复杂而陌生的COM风格的参数映射成MFC程序员更为熟悉的类型。


例如,CPicture可以让你直接从文件名加载一幅图像,CFile或者CArchive,而


不用去处理流,CPicture::Render替你完成了IPicture中所有令人讨厌的但又是


必须的HIMETRIC平滑转换工作。CPicture甚至具备了一个Load函数,它可以从资


源数据中加载图像,所以你只要用下面的代码就可以显示资源中的图像:
1.
CPicture pic(ID_MYPIC); // 加载图像
2.
CRect rc(0,0,0,0);      // 使用缺省的rc
3.
pic.Render(pDC, rc);    // 显示图像
CPicture::Render提供一个显示图片的矩形。IPicture 对图像进行延伸处理。


如果传递一个空矩形,则CPicture用图像本身的大小--不进行延伸处理。对于图


像本身而言,CPicture查找"IMAGE"类型的资源,所以在资源文件中你必须要加


入下面的代码:
1.
IDR_MYPIC IMAGE MOVEABLE PURE "res\\MyPic.jpg"
 CPicture是个很棒的傻瓜类,它具备一个 ATL 智能指针CComQIPtr指向


IPicture接口,通过调用OleLoadPicture来初始化不同的Load函数。CPicture提


供了常用的打包函数来调用底层的IPicture。CPicture只封装了那些在Demo例子


程序中要用到的方法。如果你需要调用IPicture::get_Handle或其它一些很少用


到的IPicture方法,你可以自己尝试编写相应的打包代码。 另外,在编写完


CPicture之后,我发现了一个现成的MFC类――CPictureHolder,这个类的功能


几乎与CPicture完全一样,你可以在afxctl.h文件中找到它的定义。 前面说过


,Demo例子是个典型的MFC文档/视图应用程序,因此它肯定少不了与文档和视图


类相对应的CPictureDoc 和CPictureView:
CPictureDoc类没有什么特别的处理代码,它用CPicture对象存储图像:
1.
class CPictureDoc : public CDocument {
2.
protected:
3.
CPicture m_pict; // the picture
4.
};
并且CPictureDoc::Serialize 调用CPicture::Load 从MFC存档的数据中读取图


像。
1.
void CPictureDoc::Serialize(CArchive& ar)
2.
{
3.
if (ar.IsLoading()) {
4.
m_pict.Load(ar);
5.
}
6.
}
为了使Myimgapp程序更实用,CPictureDoc::OnNewDocument从程序资源数据加载


了一幅图像。为了显示这幅图像,CPictureView::OnDraw要调用


CPicture::Render。这样程序一启动便会显示一幅默认的图像。
1.
void CPictureView::OnDraw(CDC* pDC)
2.
{
3.
CPictureDoc* pDoc = GetDocument();
4.
CPicture* ppic = pDoc->GetPicture();
5.
CRect rc;
6.
GetImageRect(rc);
7.
ppic->Render(pDC,rc);
8.
}
GetImageRect是CPictureView类的一个成员函数,作用是根据当前Myimgapp的缩


放比率(可用25%、33%、50%、75%、100%或自适应方式)获取图像矩形。


GetImageRect调用CPicture::GetImageSize来获得真正的图像大小,然后根据比


率显示。 CPictureView其余的部分完全和CScrollView的做法差不多,初始化视


图并设置滚动大小,处理命令等等。唯一让人操心的是IPicture::Render中


HIMETRIC的处理问题,因为标准的MFC应用程序都使用MM_TEXT映射模型。不用担


心,CPicture::Render和CPicture::GetImageSize会将这一切转换过来,所以你


不必为这些事情伤神。 CPictureView有一个消息处理器值得一提:它就是


OnEraseBkgnd,当要显示的图像比客户区小的时候,这个函数必须绘制空白区域


,如图二,OnEraseBkgnd创建一个与图像大小相等的切边(clip)矩形,然后将


客户区填成黑色。之所以要创建切边矩形,主要是避免当改变窗口大小时出现的


抖动――FillRect不绘制切边矩形内的区域,此乃Windows图形处理的常识。
 
图二 OnEraseBkgnd 填充修剪的图像 
IPicture/CPicture简化了图像的显示。它甚至可以实现调色板的识别这样复杂


的处理。你完全可以抛开老式DIB 图像绘制方法,如加载调色板、BitBlts、


StretchBlts等等――这一切IPicture全都可以搞掂。如果你未曾用IPicture显


示过图像,那么现在试试吧。 CPictureView完成图像浏览的任务看来不是什么


难事了。但是如果要把一幅图像添加到一个对话框或者其它的什么窗口中怎么办


呢?为此我创建了另外一个类――CPictureCtrl。
CPictureCtrl 使你可以在任何对话框或窗口中把图像作为子窗口显示。例如:
01.
class CAboutDialog : public CDialog {
02.
protected:
03.
CPictureCtrl m_wndPict;
04.
virtual BOOL OnInitDialog();
05.
};
06.
BOOL CAboutDialog::OnInitDialog()
07.
{
08.
m_wndPict.SubclassDlgItem(IDC_MYIMAGE,this);
09.
return CDialog::OnInitDialog();
10.
}
 假设你的对话框中有一个静态控制,它的ID=IDC_IMAGE,并且有一幅IMAGE资源


的ID与之相同。则从CStaticLink派生出的CPictureCtrl还可以指定一个URL超链


接(或者创建一个ID与此控制或图像的ID相同的串资源)。如果你指定了一个


URL,则在图像上单击鼠标将启动默认浏览器访问URL。真是酷呆了。CPicture控


制着CPicture对象并改写WM_PAINT消息处理例程,调用CPicture::Render代替通


常的静态控制处理例程。处理细节请参见代码。打开Myimgapp程序的“关于”对


话框就知道了。
========

利用IJG JPEG Library压缩图像为jpg格式



文章概要:本文介绍了使用IJG JPEG库将图像转换为JPG格式
前一段时间做了一个项目,项目中有一部分是关于图像采集的,相关硬件有:高


速数字相机(我们采用Basler A312f),工控机,服务器。其中相机通过139


4视频线连接到工控机的1394视频卡上,工控机与服务器通过百兆网连接。


具体工作流程为:工控机控制相机采集图像,帧率一般为40HZ左右,采集的


图像为位图数据,需要自己在程序中封装为bmp格式,然后压缩成jpg格式,压缩


后的图像一方面保存到本地硬盘,同时要传输到服务器上。
本文只介绍jpeg压缩的相关内容,刚开始我贪图省事,采用微软 的GDI Plus提


供的功能,很是方便,先获取jpg图像的编码,然后根据图像的位图数据创建一


个Bitmap类的对象,再保存图像为jpg格式就行了。但是我对GDI+还是很信任,


总感觉不踏实(没有理由的),我们的系统一旦安装就要长期运行,我就是担心


长期运行一段时间后GDI+会出问题,结果也证实了我的担心,我们实际使用的1


0套系统中有一套,连续运行3-5天后,GDI+就会崩溃,于是我决心采用IJG 


JPEG Library。您可以到www.ijg.org网站下载libjpeg的源码, IJG JPEG 


Library就是jpeg压缩库,是以源码的形式提供给软件开发人员的,当然在软件


包里也有编译好的库文件,我们这里就只用到其中的libjpeg.lib,jconfig.h,


jmorecfg.h,jpeglib.h这几个文件,下面我就介绍一下怎样在自己的程序里嵌


入图像压缩功能。
一、建立编译环境
所谓建立编译环境,其实非常简单,就是把上面提到的4个文件拷贝到你的项目


文件夹下,把libjpeg.lib添加到你的项目中,然后在你完成压缩功能的那个文


件里加入#include "jpeglib.h",需要注意的是,libjpeg.lib是用c语言开发的


,如果要用在你的C++程序里,需要用到extern "C",如下:
1.
// TestLibjpeg.cpp : Defines the entry point for the console 


application.
2.
//
3.
 
4.
#include "stdafx.h"
5.
#include "memory.h"
6.
extern "C" {
7.
#include "jpeglib.h"
8.
}
二、压缩步骤
1、申请并初始化jpeg压缩对象,同时要指定错误处理器
1.
struct jpeg_compress_struct jcs;
2.
 
3.
// 声明错误处理器,并赋值给jcs.err域
4.
struct jpeg_error_mgr jem;
5.
jcs.err = jpeg_std_error(&jem);
6.
 
7.
jpeg_create_compress(&jcs);
2、指定压缩后的图像所存放的目标文件,注意,目标文件应以二进制模式打开
1.
f=fopen("03.jpg","wb");
2.
if (f==NULL)
3.
{
4.
delete [] data;
5.
delete [] pDataConv;
6.
return 0;
7.
}
8.
jpeg_stdio_dest(&jcs, f);
3、设置压缩参数,主要参数有图像宽、高、色彩通道数(1:索引图像,3:


其他),色彩空间(JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像),压缩


质量等,如下:
1.
jcs.image_width = nWidth;    // 为图的宽和高,单位为像素
2.
jcs.image_height = nHeight;
3.
jcs.input_components = 1;   // 在此为1,表示灰度图, 如果是彩色位图,则


为3
4.
jcs.in_color_space = JCS_GRAYSCALE; //JCS_GRAYSCALE表示灰度图,JCS_RGB


表示彩色图像
5.
 
6.
jpeg_set_defaults(&jcs);
7.
jpeg_set_quality (&jcs, 80, true);
需要注意的是,jpeg_set_defaults函数一定要等设置好图像宽、高、色彩通道


数计色彩空间四个参数后才能调用,因为这个函数要用到这四个值,调用


jpeg_set_defaults函数后,jpeglib库采用默认的设置对图像进行压缩,如果需


要改变设置,如压缩质量,调用这个函数后,可以调用其它设置函数,如


jpeg_set_quality函数。其实图像压缩时有好多参数可以设置,但大部分我们都


用不着设置,只需调用jpeg_set_defaults函数值为默认值即可。
4、上面的工作准备完成后,就可以压缩了,压缩过程非常简单,首先调用


jpeg_start_compress,然后可以对每一行进行压缩,也可以对若干行进行压缩


,甚至可以对整个的图像进行一次压缩,压缩完成后,记得要调用


jpeg_finish_compress函数,如下:
01.
jpeg_start_compress(&jcs, TRUE);
02.
 
03.
JSAMPROW row_pointer[1];   // 一行位图
04.
int row_stride;      // 每一行的字节数
05.
 
06.
row_stride = jcs.image_width;  // 如果不是索引图,此处需要乘以3
07.
 
08.
// 对每一行进行压缩
09.
while (jcs.next_scanline < jcs.image_height) {
10.
row_pointer[0] = & pDataConv[jcs.next_scanline * row_stride];
11.
jpeg_write_scanlines(&jcs, row_pointer, 1);
12.
}
13.
 
14.
jpeg_finish_compress(&jcs);
5、最后就是释放压缩工作过程中所申请的资源了,主要就是jpeg压缩对象,由


于在本例中我是直接用的局部变量,所以只需调用jpeg_destroy_compress这个


函数即可,如下:
1.
jpeg_destroy_compress(&jcs);
三、解压缩步骤
解压缩步骤与压缩步骤非常相似,只是解压缩对象为jpeg_decompress_struct类


型,步骤如下:
1、声明并初始化解压缩对象,同时制定错误信息管理器
1.
struct jpeg_decompress_struct cinfo;
2.
struct jpeg_error_mgr jerr;
3.
 
4.
cinfo.err = jpeg_std_error(&jerr);
5.
jpeg_create_decompress(&cinfo);
2、打开jpg图像文件,并指定为解压缩对象的源文件
1.
FILE *f = fopen(strSourceFileName,"rb");
2.
if (f==NULL)
3.
{
4.
printf("Open file error!\n");
5.
return;
6.
}
7.
//
8.
jpeg_stdio_src(&cinfo, f);
3、读取图像信息
1.
jpeg_read_header(&cinfo, TRUE);
4、根据图像信息申请一个图像缓冲区
1.
data = new BYTE 


cinfo.image_width*cinfo.image_height*cinfo.num_components];
5、开始解压缩
01.
jpeg_start_decompress(&cinfo);
02.
 
03.
JSAMPROW row_pointer[1];
04.
while (cinfo.output_scanline < cinfo.output_height)
05.
{
06.
row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-


1)*cinfo.image_width*cinfo.num_components];
07.
jpeg_read_scanlines(&cinfo,row_pointer ,
08.
1);
09.
}
10.
jpeg_finish_decompress(&cinfo);
6、释放资源
1.
jpeg_destroy_decompress(&cinfo);
2.
 
3.
fclose(f);
好了,利用IJG JPEG Library进行图像压缩就介绍到这里,希望对大家有所帮助


,实例代码已经实现了图像的压缩和解压缩的全部功能,命令格式


为:“TestLibjpeg.exe j|j24|b 源文件名 目标文件名”,其中,j选项将源文


件压缩为jpg格式,不改变色彩模式,j24选项将源文件压缩为24为jpg格式,b选


项将源文件解压缩为bmp格式,该实例并没有提供文件有效性的验证,如果要引


入自己的代码,请自己实现文件有效性的验证。
========

通过VB结构数组输入数据VC编写DLL实现图形文件



文章概要:太多的例子讲如何如何实现VC编写动态链接库,VB中声明后使用,但没


有一个好的文章来深入探讨这个问题,本文负责解决此类问题。
前言
太多的例子讲如何如何实现VC编写动态链接库,VB中声明后使用,但没有一个好的


文章来深入探讨这个问题,本文负责解决此类问题。
1.一般情况
VB中定义为:
1.
Private Declare Function FuncFoo Lib "MyStDll.DLL" _
2.
(ByVal lParam1 As Long, ByVal lParam2 As Long) As Long
VC中定义为:
1.
UINT WINAPI FuncFoo(UINT lParam1, UINT lParam2);
不用说大家就知道两个long参数,按值传递的
2.实现类型数组:
VB中定义为:
1.
Private Declare Function FuncFoo Lib "MyStDll.DLL" _
2.
(lParam1 As Long, ByVal lParam2 As Long) As Long
VC中定义为:
1.
UINT WINAPI FuncFoo(UINT lParam1[DATASIZE], UINT lParam2);
其中DATASIZE为定义常数ByRef 按地址传参数,当然VB缺省参数形式ByRef,将数


组的第一个数据传入,就是这个类型数组的地址了
用法如下:
1.
...
2.
Dim oData(1 to 3) as Long
3.
oData(1) = 100
4.
oData(2) = 200
5.
oData(3) = 300
6.
Call FuncFoo(oData(0),3)
7.
...
3.实现结构数组:
如果此时用上述方法
在VB中来这样写:
1.
Private Type LPDATA
2.
Row as Long
3.
Col as Long
4.
Tips as String
5.
Data as double
6.
End Type
7.
Private Declare Function FuncFoo Lib "MyStDll.DLL" _
8.
(lParam1 As LPDATA, ByVal lParam2 As Long) As Long
在VC中这样写:
1.
typedef struct tagData {
2.
UINT Row;
3.
UINT Col;
4.
BSTR Tips; //注意一般关于字符串最好用 BSTR ,后面解释为什么
5.
double Data;
6.
} * LPDATA;
7.
UINT WINAPI FuncFoo(LPDATA lParam1[DATASIZE] ,UINT lParam2);或
8.
UINT WINAPI FuncFoo(LPDATA *lParam1 ,UINT lParam2);
这样的结果大家不妨试一试,数据乱的毫无规律,我也很不解,搞不懂,网上


SOS,HELP me,目前最流行的方式,我也用上了,国内外知名的,被遗忘的角落,等等


,大家没人理我,可能看到了不解,个中高手过着深入简出的生活,没有回答,没人


解答,我从MSDN终于搞到一个东西他就是SAFEARRARY
01.
typedef struct FARSTRUCT tagSAFEARRAY {
02.
unsigned short cDims;
03.
unsigned short fFeatures;
04.
unsigned short cbElements;
05.
unsigned short cLocks;
06.
unsigned long handle;
07.
void HUGEP *pvData ;
08.
SAFEARRAYBOUND rgsabound[1];
09.
} SAFEARRAY;
下次我再单独写篇稿子讨论这个东西,解决问题先. 应该这么做
在VB中来这样写:
1.
Private Type LPDATA
2.
Row as Long
3.
Col as Long
4.
Tips as String
5.
Data as double
6.
End Type
7.
Private Declare Function FuncFoo Lib "MyStDll.DLL" _
8.
(lParam1() As LPDATA, ByVal lParam2 As Long) As Long
用法如下:
1.
...
2.
Dim oData(1 to 3) as Long
3.
oData(1) = 100
4.
oData(2) = 200
5.
oData(3) = 300
6.
Call FuncFoo(oData(),3)
7.
...
在VC中这样写:
1.
typedef struct tagData {
2.
UINT Row;
3.
UINT Col;
4.
BSTR Tips; //注意一般关于字符串最好用 BSTR ,后面解释为什么
5.
double Data;
6.
} * LPDATA;
7.
UINT WINAPI FuncFoo(LPSAFEARRAY FAR * Param1,UINT lParam2);
在VC中访问VB传入的结构数据:
1.
LPDATA lparDataSet;
2.
lparDataSet = (LPDATA)((*Param1)->pvData);
3.
for (UINT u=0;u<((*Param1)->rgsabound->cElements);u++,lparDataSet++)
4.
{
5.
UINT aa = lparDataSet->Row;
6.
CString bb = lparDataSet->Tips;
7.
double cc = lparDataSet->Data;
8.
}
注意:为什么用BSTR不用什么LPCTSTR xxx,TCHAR xxx[mmm],char xxx[mmm],我举


个例子大家试一试就知道了
1. VB送入字符 "个大12"
2. 正常VC显示数据为 B8 F6 B4 F3 31 32
3. VC结构传入显示为 2A 4E 27 59 31 00 32 00 (unicode?)
第一部分写到这里吧,剩下的东西还是等各位朋友自己慢慢去摸索,仓促难免有


疏漏之处,请各位谅解。
作者信息:
阿鬼 [ http://GhostStudio.yeah.net ]
本文写于:2001/10/26 15:40
分享到:新浪微博 腾讯微博 更多 0 收藏
========

用CabLib创建DXF(绘图交换格式)文件



文章概要:本文详细介绍了DXF文件的格式以及用CabLib生成图形并存储为DXF文


件的方法,并提供了CabLib的源代码以及测试代码。


介绍
什么是DXF
绘图交换格式(DXF)文件允许在AutoCAD和其它程序进行图形交换。DXF文件即可


以是ASCII格式也可以是二进制格式。 因为ASCII格式的DXF文件比二进制格式更


加通用,所以CadLib使用ASCII的DXF格式。
什么是CadLib
CadLib不是计算机辅助设计(CAD)程序。它是创建用于CAD程序DXF文件的工具。


它由两部分组成,一部分是用于创建DXF文件的动态链接库,另一部分是编程接


口,该类封装了cadio.dll中的函数,可以在Microsoft Visual C++项目中使用


。另外,cadio.dll 也可以用于其它Win32编程中。
为什么使用CadLib
一些程序需要输出可以被AutoCad等程序使用的图形文件,例如,在一个"发动机


设计程序"中,程序需要输出图形,而最常用的交换数据的图形格式便是DXF。
 
DXF文件结构
DXF文件格式是图形文件中所有信息的标签数据描述。标签数据指的是文件中的


被称为群码的整型数据前的每个数据项。 群码的值指示了下面数据的数据类型


以及数据单元的含义。事实上图形文件中所有用户特殊信息都可以描述为DXF格


式。(参考AutoCad的DXF参考说明)
DXF文件由许多区域组成,每个区域里有许多图形数据,CadLib可以使用如下区


域:
1. 头部 HEADER
2. 表格 TABLES
3. 区块 BLOCKS
4. 实体 ENTITIES
CadLib中的DXF文件结构参考AutoCad中的DXF格式说明,具体可以在下面网址找


到参考:
http://www.autodesk.com/techpubs/autocad/acad2000/dxf/dxf_format.htm
http://www.martinreddy.net/gfx/3d/DXF12.spec

这里的类是CadIO.dll和主程序之间的接口。"Test"演示了如何用CadLib中的


CDxfFileWrite和CDrawing类创建DXF文件。
CDxfFileWrite类
CDxfFileWrite封装了直接创建DXF文件的所有命令。使用方法如下:
1. 创建DXF文件
1.
CDxfFileWrite dxffile;
2.
dxffile.Create( "d:\\test.dxf" );
2. HEADER区域的开始与结束。这里是为了兼容CAD程序。其它的不需要HEADER区


域。
1.
// Header Section ------------------------------------------
2.
dxffile.BeginSection(SEC_HEADER);
3.
dxffile.EndSection();
4.
// close HEADER section ------------------------------------
3. 开始表格区域并放置LAYER, LTYPE, STYLE, DIMSTYLE等你所需的表格类型,


然后结束该区域。
01.
// Tables Section ------------------------------------------
02.
dxffile.BeginSection(SEC_TABLES);
03.
 
04.
// LTYPE table type -------------------------
05.
dxffile.BeginTableType(TAB_LTYPE);
06.
 
07.
DXFLTYPE ltype;
08.
double elem[4];
09.
 
10.
// Continuous
11.
ZeroMemory(<ype, sizeof(ltype));
12.
ltype.Name = "Continuous";
13.
ltype.DescriptiveText = "Solid line";
14.
dxffile.AddLinetype(<ype);
15.
 
16.
// DASHDOT2
17.
ZeroMemory(<ype, sizeof(ltype));
18.
ltype.Name = "DASHDOT2";
19.
ltype.DescriptiveText = "Dash dot (.5x) 


_._._._._._._._._._._._._._._.";
20.
ltype.ElementsNumber = 4;
21.
ltype.TotalPatternLength = 0.5;
22.
ltype.Elements = elem;
23.
elem[0] = 0.25;
24.
elem[1] = -0.125;
25.
elem[2] = 0.0;
26.
elem[3] = -0.125;
27.
dxffile.AddLinetype(<ype);
28.
 
29.
dxffile.EndTableType();
30.
// close LTYPE table type -------------------
31.
 
32.
// LAYER table type -------------------------
33.
result &= dxffile.BeginTableType(TAB_LAYER);
34.
result &= dxffile.AddLayer("Layer1", 1, "Continuous");
35.
result &= dxffile.AddLayer("Layer2", 2, "Continuous");
36.
result &= dxffile.AddLayer("Layer3", 3, "Continuous");
37.
result &= dxffile.AddLayer("Layer4", 4, "Continuous");
38.
result &= dxffile.EndTableType();
39.
// close LAYER table type -------------------
40.
 
41.
// STYLE table type -------------------------
42.
dxffile.BeginTableType(TAB_STYLE);
43.
 
44.
DXFSTYLE tstyle;
45.
ZeroMemory(&tstyle, sizeof(tstyle));
46.
tstyle.Name = "Style1";
47.
tstyle.PrimaryFontFilename = "TIMES.TTF";
48.
tstyle.Height = 0.3;
49.
tstyle.WidthFactor = 1;
50.
dxffile.AddTextStyle(&tstyle);
51.
 
52.
dxffile.EndTableType();
53.
// close STYLE table type -------------------
54.
 
55.
// DIMSTYLE table type ----------------------
56.
dxffile.BeginTableType(TAB_DIMSTYLE);
57.
 
58.
DXFDIMSTYLE dimstyle;
59.
 
60.
// DIM1
61.
ZeroMemory(&dimstyle, sizeof(dimstyle));
62.
dimstyle.Name = "DIM1"; // DimStyle Name
63.
dimstyle.DIMCLRD = 2; // Dimension line & Arrow heads color
64.
dimstyle.DIMDLE = 0.0000; // Dimension line size after Extensionline
65.
dimstyle.DIMCLRE = 2; // Extension line color
66.
dimstyle.DIMEXE = 0.1800; // Extension line size after Dimline
67.
dimstyle.DIMEXO = 0.0625; // Offset from origin
68.
dimstyle.DIMBLK1 = "ClosedFilled";// 1st Arrow head
69.
dimstyle.DIMBLK2 = "ClosedFilled";// 2nd Arrow head
70.
dimstyle.DIMASZ = 0.1800; // Arrow size
71.
dimstyle.DIMTXSTY = "Style1"; // Text style
72.
dimstyle.DIMCLRT = 3; // Text color
73.
dimstyle.DIMTXT = 0.1800; // Text height
74.
dimstyle.DIMTAD = 1; // Vertical Text Placement
75.
dimstyle.DIMGAP = 0.0900; // Offset from dimension line
76.
dxffile.AddDimStyle(&dimstyle);
77.
 
78.
dxffile.EndTableType();
79.
// close DIMSTYLE table type ----------------
80.
 
81.
dxffile.EndSection();
82.
// close TABLES section ------------------------------------
4. 开始实体区域并放置LINE, CIRCLE, SOLID, TEXT, ARC, POINT, DIMLINEAR


等数据,然后结束该区域。
01.
// Entities Section ------------------------------------------
02.
dxffile.BeginSection(SEC_ENTITIES);
03.
 
04.
// set current layer to Layer2
05.
dxffile.SetCurrentLayer("Layer2");
06.
// draw a line
07.
dxffile.Line(1.2, 3.3, 7.5, 7.7);
08.
// draw a circle
09.
dxffile.Circle(7.8, 4.3, 1.75);
10.
// set current layer to Layer4
11.
dxffile.SetCurrentLayer("Layer4");
12.
 
13.
// draw a solid
14.
REALPOINT points[4];
15.
points[0].x = 10.4; points[0].y = 7.2;
16.
points[1].x = 13.6; points[1].y = 7.4;
17.
points[2].x = 13.1; points[2].y = 4.9;
18.
points[3].x = 10.9; points[3].y = 5.9;
19.
Solid(4, points);
20.
 
21.
// set current textstyle to Style1
22.
dxffile.SetCurrentTextStyle("Style1");
23.
// draw text
24.
dxffile.Text("Sample Text", 5.9, 6.7, 0.3, 35);
25.
// draw a dimension line
26.
dxffile.SetCurrentDimStyle("DIM1");
27.
dxffile.DimLinear(6.05, 3, 9.55, 3, 9.55, 2, 0, "3.50");
28.
 
29.
dxffile.EndSection();
30.
// close ENTITIES section ----------------------------------
5. 关闭DXF文件
dxffile.Close();
CDrawing 类
CDrawing封装了在内存中创建图形并保存到DXF文件中的功能。使用方法如下:
1. 创建内存绘图
CDrawing drw;
drw.Create();
2. 在内存中创建LAYER, LTYPE, STYLE, DIMSTYLE等所需要的表格类型
01.
// Tables Section ------------------------------------------
02.
// LTYPE table type -------------------------
03.
LTYPE ltype;
04.
OBJHANDLE objhandle1;
05.
 
06.
// Continuous
07.
ZeroMemory( 3. 生成实体数据(LINE, CIRCLE, SOLID, TEXT, ARC, POINT, 


DIMLINEAR, POLYLINE)
08.
// Entities Section ------------------------------------------
09.
// set current layer to Layer2
10.
drw.SetLayer("Layer2");
11.
// draw a line
12.
drw.Line(1.2, 3.3, 7.5, 7.7);
13.
// draw a circle
14.
drw.Circle(7.8, 4.3, 1.75);
15.
// set current layer to Layer1
16.
drw.SetLayer("Layer1");
17.
 
18.
// draw a solid
19.
REALPOINT points[4];
20.
points[0].x = 10.4; points[0].y = 7.2;
21.
points[1].x = 13.6; points[1].y = 7.4;
22.
points[2].x = 13.1; points[2].y = 4.9;
23.
points[3].x = 10.9; points[3].y = 5.9;
24.
drw.Solid(points[0], points[1], points[2], points[3]);
25.
 
26.
// set current textstyle to Style1
27.
drw.SetTextStyle("Style1");
28.
// draw text
29.
drw.Text("Sample Text", 5.9, 6.7, 0.3, 35);
30.
// draw a dimension line
31.
drw.SetDimStyle("DIM1");
32.
drw.DimLinear(6.05, 3, 9.55, 3, 9.55, 2, 0, "3.50");
4. 将数据保存到DXF文件中
1.
drw.SaveDXFFile(DxfFileName);
5. 删除CDrawing对象并释放内存
从DXF文件中加载数据
1. 创建内存绘图 
1.
CDrawing drw;
2.
drw.Create( );
2. 使用LoadDXFFile加载DXF文件到内存
1.
drw.LoadDXFFile("Sample.dxf");
结论
这些代码对于需要在程序中创建DXF文件的程序员来说是很有用的,CadLib虽然


不是创建DXF文件最好的商业软件,但它是开源的,可以自由修改代码。

========

(GDI+)Image类的序列化


文章概要:本文介绍如何采用GDI+ 通过IMAGE类的序列化实现对图形文件的读取、显示。
利用GDI+图形设备接口提供的Image类我们可以很方便地显示JPEG、GIF等文件格式的图片,但美中不足的是Image类未提供序列化数据的功能,如果要将从JPEG、GIF等格式的文件中读取的图片数据保存到我们自己开发的应用程序文档中,并能从文档中读取已经保存的图片数据并正确的显示出来,就必须实现Image类数据的序列化。 假如在你的文档类中有个指向Image类的成员指针变量m_pImage用于显示图片,那么在文档类的序列化函数Serialize(CArchive& ar)中加入以下的代码就能实现m_pImage所指向的图片数据的序列化:
01.
if (ar.IsStoring())
02.
{
03.
HGLOBAL m_hMem = GlobalAlloc(GMEM_MOVEABLE, 0);
04.
IStream *pstm=NULL;
05.
CreateStreamOnHGlobal(m_hMem, TRUE, &pstm);
06.
CLSID clsid;
07.
USES_CONVERSION;
08.
GetCodecClsid(A2W("image/bmp"), &clsid);
09.
m_pImage->Save(pstm,&clsid,NULL);
10.
if (pstm==NULL)
11.
return;
12.
LARGE_INTEGER liBeggining = { 0 };
13.
pstm->Seek(liBeggining, STREAM_SEEK_SET, NULL);
14.
DWORD wTemp=GlobalSize(m_hMem);
15.
LPBYTE lpData = (LPBYTE)GlobalLock(m_hMem);
16.
ar << wTemp;
17.
ar.Write(lpData,wTemp);
18.
pstm->Release();
19.
GlobalUnlock(m_hMem);
20.
}
21.
else
22.
{
23.
DWORD wTemp;
24.
ar >> wTemp;
25.
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, wTemp);
26.
if (m_hMem == NULL)
27.
return;
28.
IStream *pstm=NULL;
29.
CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
30.
if (pstm==NULL)
31.
return;
32.
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
33.
ar.Read(pmem,wTemp);
34.
if (m_pImage) {
35.
delete m_pImage;
36.
m_pImage = NULL;
37.
}
38.
using namespace Gdiplus;
39.
m_pImage = Image::FromStream(pstm, FALSE);
40.
pstm->Release();
41.
GlobalUnlock(m_hMem);
42.
//注意此处不能加GlobalFree(m_hMem);否则图片显示不出来。
其中函数GetCodecClsid的实现代码如下:
01.
int GetCodecClsid(const WCHAR* format, CLSID* pClsid)
02.
{
03.
UINT  num = 0;          // number of image encoders
04.
UINT  size = 0;         // size of the image encoder array in bytes
05.
using namespace Gdiplus;
06.
ImageCodecInfo* pImageCodecInfo = NULL;
07.
GetImageEncodersSize(&num, &size);
08.
if(size == 0)
09.
return -1;  // Failure
10.
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
11.
if(pImageCodecInfo == NULL)
12.
return -1;  // Failure
13.
GetImageEncoders(num, size, pImageCodecInfo);
14.
for(UINT j = 0; j < num; ++j)
15.
{
16.
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
17.
{
18.
*pClsid = pImageCodecInfo[j].Clsid;
19.
return j;  // Success
20.
}   
21.
} // for
22.
return -1;  // Failure
23.
} // GetCodecClsid
形参format用以说明图片数据的保存格式,可以取以下一些值:"image/bmp"(位图格式),"image/jpeg"(JPEG格式),"image/gif"(GIF格式)等。

========


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值