Чтение и запись метаданных
Некоторые файлы с изображениями содержат метаданные, которые можно прочитать, чтобы определить свойства изображения. Например, цифровая фотография может содержать метаданные, которые позволяют определить модель камеры, с помощью которой была получена данная фотография. С помощью Windows GDI+ можно считывать существующие метаданные, а также записывать новые метаданные в файлы изображений.
GDI+ предоставляет единый способ хранения и извлечения метаданных из файлов изображений в различных форматах. В GDI+ элемент метаданных называется элементом свойства. Вы можете хранить и извлекать метаданные, вызывая методы SetPropertyItem и GetPropertyItem класса Image , и вам не нужно беспокоиться о том, как в определенном формате файла хранятся эти метаданные.
В настоящее время GDI+ поддерживает метаданные для форматов файлов TIFF, JPEG, Exif и PNG. Формат Exif, определяющий способ хранения изображений, захваченных цифровыми камерами, основан на форматах TIFF и JPEG. Exif использует формат TIFF для несжатой пиксельной данных и формат JPEG для сжатых пиксельных данных.
GDI+ определяет набор тегов свойств, определяющих элементы свойств. Некоторые теги являются общими; то есть они поддерживаются всеми форматами файлов, упомянутыми в предыдущем абзаце. Другие теги являются специальными и применяются только к определенным форматам. При попытке сохранить элемент свойства в файл, который не поддерживает этот элемент свойства, GDI+ игнорирует запрос. В частности, метод Image::SetPropertyItem возвращает PropertyNotSupported.
Элементы свойств, хранящиеся в файле изображения, можно определить, вызвав Image::GetPropertyIdList. При попытке получить элемент свойства, которого нет в файле, GDI+ игнорирует запрос. В частности, метод Image::GetPropertyItem возвращает propertyNotFound.
Чтение метаданных из файла
Следующее консольное приложение вызывает метод GetPropertySize объекта Image , чтобы определить, сколько фрагментов метаданных содержится в файле 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 . Чтобы получить все метаданные из файла, можно вызвать метод GetAllPropertyItems класса Image . Метод GetAllPropertyItems возвращает массив объектов PropertyItem . Перед вызовом GetAllPropertyItems необходимо выделить буфер, достаточно большой для получения этого массива. Вы можете вызвать метод GetPropertySize класса Image , чтобы получить размер (в байтах) требуемого буфера.
Объект PropertyItem имеет следующие четыре открытых члена:
Описание | |
---|---|
идентификатор | Тег, идентифицирующий блок метаданных. Значения, которые могут быть назначены id (PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExifExposureTime и т. д.), определяются в Gdiplusimaging.h. |
length | Длина (в байтах) массива значений, на который указывает элемент данных значения . Обратите внимание, что если элемент данных типа имеет значение PropertyTagTypeASCII, то элемент данных length — это длина символьной строки, завершающейся null, включая признак конца NULL. |
type | Тип данных значений в массиве, на который указывает элемент данных значения. Константы (PropertyTagTypeByte, PropertyTagTypeASCII и т. д.), представляющие различные типы данных, описаны в разделе Константы типа тега свойства Image. |
value | Указатель на массив значений. |
Следующее консольное приложение считывает и отображает семь фрагментов метаданных в файле FakePhoto.jpg. Функция main использует вспомогающую функцию 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
В предыдущих выходных данных показан шестнадцатеричный идентификатор для каждого элемента свойства. Эти идентификаторы можно найти в разделе Константы тегов свойств изображений и узнать, что они представляют следующие теги свойств.
Шестнадцатеричное значение | Тег свойства |
---|---|
0x0320 0x010f 0x0110 0x9003 0x829a 0x5090 0x5091 |
PropertyTagImageTitle PropertyTagEquipMake PropertyTagEquipModel PropertyTagExifDTOriginal PropertyTagExifExposureTime PropertyTagLuminanceTable PropertyTagChrominanceTable |
Второй элемент свойства (индекс 1) в списке имеет идентификатор PropertyTagEquipMake и тип PropertyTagTypeASCII. В следующем примере, который является продолжением предыдущего консольного приложения, отображается значение этого элемента свойства:
printf("The equipment make is %s.\n", pPropBuffer[1].value);
В предыдущей строке кода выводятся следующие выходные данные:
The equipment make is Northwind Traders.
Пятый элемент свойства (индекс 4) в списке имеет идентификатор PropertyTagExifExposureTime и тип PropertyTagTypeRational. Этот тип данных (PropertyTagTypeRational) является парой longs. В следующем примере, который является продолжением предыдущего консольного приложения, эти два значения 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 методу SetPropertyItem объекта Image .
Следующее консольное приложение записывает один элемент (заголовок изображения) метаданных в объект Image , а затем сохраняет образ в файле диска FakePhoto2.jpg. Функция main использует вспомогающую функцию 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;
}