Condividi tramite


Lettura e scrittura di metadati

Alcuni file di immagine contengono metadati che è possibile leggere per determinare le funzionalità dell'immagine. Ad esempio, una fotografia digitale può contenere metadati che è possibile leggere per determinare la creazione e il modello della fotocamera usata per acquisire l'immagine. Con Windows GDI+, è possibile leggere i metadati esistenti ed è anche possibile scrivere nuovi metadati nei file di immagine.

GDI+ offre un modo uniforme per archiviare e recuperare metadati dai file di immagine in vari formati. In GDI+, una parte di metadati viene chiamata elemento della proprietà . È possibile archiviare e recuperare i metadati chiamando i metodi SetPropertyItem e GetPropertyItem della classe Image e non è necessario preoccuparsi dei dettagli dell'archiviazione di tali metadati in un particolare formato di file.

GDI+ supporta attualmente i metadati per i formati di file TIFF, JPEG, Exif e PNG. Il formato Exif, che specifica come archiviare immagini acquisite da fotocamere digitali, è basato sui formati TIFF e JPEG. Exif usa il formato TIFF per i dati pixel non compressi e il formato JPEG per i dati pixel compressi.

GDI+ definisce un set di tag di proprietà che identificano gli elementi delle proprietà. Alcuni tag sono generici; ovvero sono supportati da tutti i formati di file indicati nel paragrafo precedente. Altri tag sono speciali e si applicano solo a determinati formati. Se si tenta di salvare un elemento di proprietà in un file che non supporta tale elemento della proprietà, GDI+ ignora la richiesta. In particolare, il metodo Image::SetPropertyItem restituisce PropertyNotSupported.

È possibile determinare gli elementi delle proprietà archiviati in un file di immagine chiamando Image::GetPropertyIdList. Se si tenta di recuperare un elemento di proprietà che non si trova nel file, GDI+ ignora la richiesta. In particolare, il metodo Image::GetPropertyItem restituisce PropertyNotFound.

Lettura dei metadati da un file

L'applicazione console seguente chiama il metodo GetPropertySize di un oggettoImageper determinare il numero di parti di metadati nel file 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;
}

Il codice precedente, insieme a un particolare file, FakePhoto.jpg, ha prodotto l'output seguente:

There are 7 pieces of metadata in the file.
The total size of the metadata is 436 bytes.

GDI+ archivia una singola parte di metadati in un oggetto PropertyItem. È possibile chiamare il metodo GetAllPropertyItems della classeImageper recuperare tutti i metadati da un file. Il metodo GetAllPropertyItems restituisce una matrice di oggetti PropertyItem . Prima di chiamare GetAllPropertyItems, è necessario allocare un buffer sufficientemente grande per ricevere tale matrice. È possibile chiamare il metodo GetPropertySize della classe Image per ottenere le dimensioni (in byte) del buffer richiesto.

Un oggetto PropertyItem dispone dei quattro membri pubblici seguenti:

Descrizione
ID Tag che identifica l'elemento di metadati. I valori che possono essere assegnati a ID (PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExifExposureTime e simili) sono definiti in Gdiplusimaging.h.
lunghezza La lunghezza, in byte, dell'array di valori a cui punta il membro dati del valore . Si noti che se il tipo di membro dati è impostato su PropertyTagTypeASCII, allora il membro dati della lunghezza è la lunghezza di una stringa di caratteri con terminazione NULL, incluso il carattere di terminazione NULL.
tipo Tipo di dati dei valori nell'array a cui punta il campo dati di valore. Le costanti (PropertyTagTypeByte, PropertyTagTypeASCII e simili) che rappresentano vari tipi di dati sono descritte in Costanti del tipo di tag della proprietà dell'immagine.
valore Puntatore a una matrice di valori.

 

L'applicazione console seguente legge e visualizza i sette metadati nel file FakePhoto.jpg. La funzione main si basa sulla funzione helper PropertyTypeFromWORD, illustrata dopo la funzione 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;
}

L'applicazione console precedente genera l'output seguente:

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

L'output precedente mostra un numero ID esadecimale per ogni elemento della proprietà. È possibile cercare i numeri ID nelle costanti tag delle proprietà immagine e scoprire che rappresentano i tag di proprietà seguenti.

Valore esadecimale Tag proprietà
0x0320 0x010f
  0x0110
  0x9003
  0x829a
  0x5090
  0x5091
PropertyTagImageTitle PropertyTagEquipMake
  PropertyTagEquipModel
  PropertyTagExifDTOriginal
  TagDellaProprietàExifTempoDiEsposizione
  PropertyTagLuminanceTable
  TabellaDiCrominanzaDelTagDellaProprietà

 

Il secondo elemento della proprietà (indice 1) nell'elenco ha ID PropertyTagEquipMake e tipo PropertyTagTypeASCII. Nell'esempio seguente, che è una continuazione dell'applicazione console precedente, viene visualizzato il valore dell'elemento della proprietà:

printf("The equipment make is %s.\n", pPropBuffer[1].value);

La riga di codice precedente produce l'output seguente:

The equipment make is Northwind Traders.

Il quinto elemento della proprietà (indice 4) nell'elenco ha ID PropertyTagExifExposureTime e tipo PropertyTagTypeRational. Il tipo di dato (PropertyTagTypeRational) è una coppia di LONG . Nell'esempio seguente, che è una continuazione dell'applicazione console precedente, vengono visualizzati i due valori LONG come frazione. Tale frazione, 1/125, è il tempo di esposizione misurato in secondi.

long* ptrLong = (long*)(pPropBuffer[4].value);
printf("The exposure time is %d/%d.\n", ptrLong[0], ptrLong[1]);

Il codice precedente produce l'output seguente:

The exposure time is 1/125.

Scrittura di metadati in un file

Per scrivere un elemento di metadati in un oggetto Image , inizializzare un oggetto PropertyItem e quindi passare l'indirizzo di tale oggetto PropertyItem al metodo SetPropertyItem dell'oggetto Image .

L'applicazione console seguente scrive un elemento (il titolo dell'immagine) dei metadati in un oggetto Image e quindi salva l'immagine nel file del disco FakePhoto2.jpg. La funzione principale si basa sulla funzione helper GetEncoderClsid, illustrata nell'argomento Recupero dell'identificatore di classe per un codificatore.

#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;
}