使用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格式)等。
========