讀取和寫入中繼資料
某些影像檔案包含您可以讀取以判斷影像功能的中繼資料。 例如,數位相片可能包含中繼資料,您可以讀取以判斷用來擷取影像之相機的製作和模型。 使用 Windows GDI+,您可以讀取現有的中繼資料,也可以將新的中繼資料寫入影像檔。
GDI+ 提供統一的方式,以各種格式儲存和擷取影像檔案的中繼資料。 在 GDI+中,中繼資料片段稱為 屬性專案。 您可以呼叫Image類別的SetPropertyItem和GetPropertyItem方法來儲存和擷取中繼資料,而且您不需要擔心特定檔案格式如何儲存該中繼資料的詳細資料。
GDI+ 目前支援 TIFF、JPEG、Exif 和 PNG 檔案格式的中繼資料。 Exif 格式會指定如何儲存數位相機所擷取的影像,是以 TIFF 和 JPEG 格式為基礎。 Exif 會針對未壓縮的圖元資料使用 TIFF 格式,以及壓縮圖元資料的 JPEG 格式。
GDI+ 會定義一組可識別屬性專案的屬性標記。 某些標記是一般用途;也就是說,上述段落中所述的所有檔案格式都支援它們。 其他標籤是特殊用途,僅適用于特定格式。 如果您嘗試將屬性專案儲存到不支援該屬性專案的檔案,GDI+ 會忽略要求。 更具體來說, Image::SetPropertyItem 方法會傳回 PropertyNotSupported。
您可以呼叫 Image::GetPropertyIdList來判斷儲存在圖像檔中的屬性專案。 如果您嘗試擷取不在檔案中的屬性專案,GDI+ 會忽略要求。 更具體來說, Image::GetPropertyItem 方法會傳回 PropertyNotFound。
從檔案讀取中繼資料
下列主控台應用程式會呼叫Image物件的GetPropertySize方法,以判斷檔案中有多少個中繼資料片段FakePhoto.jpg。
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
// Initialize <tla rid="tla_gdiplus"/>.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
UINT size = 0;
UINT count = 0;
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
bitmap->GetPropertySize(&size, &count);
printf("There are %d pieces of metadata in the file.\n", count);
printf("The total size of the metadata is %d bytes.\n", size);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
}
上述程式碼以及特定檔案FakePhoto.jpg會產生下列輸出:
There are 7 pieces of metadata in the file.
The total size of the metadata is 436 bytes.
GDI+ 會將個別的中繼資料片段儲存在 PropertyItem 物件中。 您可以呼叫Image類別的GetAllPropertyItems方法,以從檔案擷取所有中繼資料。 GetAllPropertyItems方法會傳回PropertyItem物件的陣列。 呼叫 GetAllPropertyItems之前,您必須配置足以接收該陣列的緩衝區。 您可以呼叫Image類別的GetPropertySize方法來取得所需緩衝區) 位元組大小 (。
PropertyItem物件具有下列四個公用成員:
描述 | |
---|---|
id | 識別中繼資料專案的標記。 可以指派給 id (PropertyTagImageTitle、PropertyTagEquipMake、PropertyTagExifExposureTime 等) 的值定義在 Gdiplusimaging.h 中。 |
length (長度) | 值 資料成員 所指向值陣列的長度,以位元組為單位。 請注意,如果 類型 資料成員設定為 PropertyTagTypeASCII,則長度資料成員是 Null 終止字元字串的 長度 ,包括 Null 結束字元。 |
type | 值資料成員所指向之陣列中值的資料類型。 代表各種資料類型的常數 (PropertyTagTypeByte、PropertyTagTypeASCII 等) 都會在 Image 屬性標記類型常數中說明。 |
value | 值的陣列指標。 |
下列主控台應用程式會讀取並顯示檔案中FakePhoto.jpg的七個中繼資料片段。 main 函式依賴 Helper 函式 PropertyTypeFromWORD,如 main 函式所示。
#include <windows.h>
#include <gdiplus.h>
#include <strsafe.h>
using namespace Gdiplus;
INT main()
{
// Initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
UINT size = 0;
UINT count = 0;
#define MAX_PROPTYPE_SIZE 30
WCHAR strPropertyType[MAX_PROPTYPE_SIZE] = L"";
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
bitmap->GetPropertySize(&size, &count);
printf("There are %d pieces of metadata in the file.\n\n", count);
// GetAllPropertyItems returns an array of PropertyItem objects.
// Allocate a buffer large enough to receive that array.
PropertyItem* pPropBuffer =(PropertyItem*)malloc(size);
// Get the array of PropertyItem objects.
bitmap->GetAllPropertyItems(size, count, pPropBuffer);
// For each PropertyItem in the array, display the id, type, and length.
for(UINT j = 0; j < count; ++j)
{
// Convert the property type from a WORD to a string.
PropertyTypeFromWORD(
pPropBuffer[j].type, strPropertyType, MAX_PROPTYPE_SIZE);
printf("Property Item %d\n", j);
printf(" id: 0x%x\n", pPropBuffer[j].id);
wprintf(L" type: %s\n", strPropertyType);
printf(" length: %d bytes\n\n", pPropBuffer[j].length);
}
free(pPropBuffer);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
} // main
// Helper function
HRESULT PropertyTypeFromWORD(WORD index, WCHAR* string, UINT maxChars)
{
HRESULT hr = E_FAIL;
WCHAR* propertyTypes[] = {
L"Nothing", // 0
L"PropertyTagTypeByte", // 1
L"PropertyTagTypeASCII", // 2
L"PropertyTagTypeShort", // 3
L"PropertyTagTypeLong", // 4
L"PropertyTagTypeRational", // 5
L"Nothing", // 6
L"PropertyTagTypeUndefined", // 7
L"Nothing", // 8
L"PropertyTagTypeSLONG", // 9
L"PropertyTagTypeSRational"}; // 10
hr = StringCchCopyW(string, maxChars, propertyTypes[index]);
return hr;
}
上述主控台應用程式會產生下列輸出:
Property Item 0
id: 0x320
type: PropertyTagTypeASCII
length: 16 bytes
Property Item 1
id: 0x10f
type: PropertyTagTypeASCII
length: 17 bytes
Property Item 2
id: 0x110
type: PropertyTagTypeASCII
length: 7 bytes
Property Item 3
id: 0x9003
type: PropertyTagTypeASCII
length: 20 bytes
Property Item 4
id: 0x829a
type: PropertyTagTypeRational
length: 8 bytes
Property Item 5
id: 0x5090
type: PropertyTagTypeShort
length: 128 bytes
Property Item 6
id: 0x5091
type: PropertyTagTypeShort
length: 128 bytes
上述輸出會顯示每個屬性專案的十六進位識別碼。 您可以在 Image Property Tag 常數 中查閱這些識別碼,並找出它們代表下列屬性標記。
十六進位值 | 屬性標籤 |
---|---|
0x0320 0x010f 0x0110 0x9003 0x829a 0x5090 0x5091 |
PropertyTagImageTitle PropertyTagEquipMake PropertyTagEquipModel PropertyTagExifDTOriginal PropertyTagExifExposureTime PropertyTagLuminanceTable PropertyTagChrominanceTable |
清單中的第二個 (索引 1) 屬性專案具有 id PropertyTagEquipMake,並 輸入 PropertyTagTypeASCII。 下列範例是先前主控台應用程式的接續,會顯示該屬性專案的值:
printf("The equipment make is %s.\n", pPropBuffer[1].value);
上述程式程式碼會產生下列輸出:
The equipment make is Northwind Traders.
清單中的第五個 (索引 4) 屬性項具有 id PropertyTagExifExposureTime,並 輸入 PropertyTagTypeRational。 該資料類型 (PropertyTagTypeRational) 是一對 LONG。 下列範例是先前主控台應用程式的接續,會將這兩個 LONG 值顯示為分數。 該分數 1/125 是以秒為單位測量的曝光時間。
long* ptrLong = (long*)(pPropBuffer[4].value);
printf("The exposure time is %d/%d.\n", ptrLong[0], ptrLong[1]);
前一個程式碼會產生下列輸出:
The exposure time is 1/125.
將中繼資料寫入檔案
若要將中繼資料的專案寫入Image物件,請初始化PropertyItem物件,然後將該PropertyItem物件的位址傳遞至Image物件的SetPropertyItem方法。
下列主控台應用程式會將一個專案 (中繼資料的影像標題) 寫入 Image 物件,然後將映射儲存在磁片檔案FakePhoto2.jpg中。 main 函式依賴 Helper 函式 GetEncoderClsid,如擷 取編碼器的類別識別碼主題所示。
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
// Initialize <tla rid="tla_gdiplus"/>.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Status stat;
CLSID clsid;
char propertyValue[] = "Fake Photograph";
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
PropertyItem* propertyItem = new PropertyItem;
// Get the CLSID of the JPEG encoder.
GetEncoderClsid(L"image/jpeg", &clsid);
propertyItem->id = PropertyTagImageTitle;
propertyItem->length = 16; // string length including NULL terminator
propertyItem->type = PropertyTagTypeASCII;
propertyItem->value = propertyValue;
bitmap->SetPropertyItem(propertyItem);
stat = bitmap->Save(L"FakePhoto2.jpg", &clsid, NULL);
if(stat == Ok)
printf("FakePhoto2.jpg saved successfully.\n");
delete propertyItem;
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
}