Add custom image metadata to System.Drawing
- Dominant language
- C#
- Stars
- 4.9k
- Forks
- 1.1k
- Avg merge
- 20h 23m
- Merged PRs (30d)
- 103
Description
### Background
GDI+ 1.1 added support for custom image metadata. JPEG, PNG, and GIF images can have multiple `ImageItemData` objects serialized inside of them, each of which consist of a fixed-size description and any amount of arbitrary data. This functionality is accessed by adding the data to standard `EncodingParameter`s, which are configured with the new `ImageItems` encoder.
This proposal is one of many to add missing GDI+ 1.1 functionality to `System.Drawing`.
### Usage Example
Writing metadata:
```cs
Image image = Image.FromFile("river.jpg");
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().Single(e => e.FormatID == ImageFormat.Jpeg.Guid);
var data = new ImageItemData(ItemDataPosition.AfterHeader,
new byte[] { 0xE4 },
Encoding.ASCII.GetBytes("Byte sequence of your choice"));
var parameters = new EncoderParameters
{
Param =
{
[0] = new EncoderParameter(Encoder.ImageItems, data)
}
};
image.Save("river2.jpg", codec, parameters);
```
Reading metadata:
```cs
Image image = Image.FromFile("river2.jpg");
foreach (ImageItemData item in image.EnumerateImageItems())
{
Console.WriteLine(Convert.ToHexString(item.Data));
}
```
### API Proposal
See the documentation for:
- [ImageItemData](https://docs.microsoft.com/en-us/windows/win32/api/gdiplusimaging/nl-gdiplusimaging-imageitemdata)
- [ItemDataPosition](https://docs.microsoft.com/en-us/windows/win32/api/gdiplusimaging/ne-gdiplusimaging-itemdataposition)
- [Image::GetItemData](https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nf-gdiplusheaders-image-getitemdata)
- [Image::FindFirstItem](https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nf-gdiplusheaders-image-findfirstitem)
- [Image::FindNextItem](https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nf-gdiplusheaders-image-findnextitem)
```diff
namespace System.Drawing.Imaging
{
+ public sealed class ImageItemData
+ {
+ public ImageItemData(ItemDataPosition position, byte[] description, byte[] data);
+
+ public ItemDataPosition Position { get; set; }
+ public byte[] Description { get; set; }
+ public byte[] Data { get; set; }
+ }
+ public enum ItemDataPosition
+ {
+ AfterHeader,
+ AfterPalette,
+ AfterBits
+ }
public sealed class EncoderParameter : IDisposable
{
+ public EncoderParameter(Encoder encoder, ImageItemData value);
}
}
namespace System.Drawing
{
public abstract class Image : MarshalByRefObject, ICloneable, IDisposable, ISerializable
{
+ public IEnumerable EnumerateImageItems();
}
}
```
The native API exposes an enumerator to read this custom metadata. Firstly, `FindFirstItem` is called to initialize the enumerator. After calling this method, calling `FindNextItem` advances the iterator. Both of these methods populate data in a structure which is passed to `GetItemData`, which ultimately returns the complete metadata. I projected this into an IEnumerable for the managed definitions, as it seems to fit naturally.
This requires changes to libgdiplus in order to support it on non-Windows platforms.
Contributor guide
Assessment
This issue has not been assessed yet.