Browse Source

Merge branch 'master' into af/memory-diagnostics

pull/1969/head
James Jackson-South 4 years ago
parent
commit
35ed52a74f
  1. 133
      src/ImageSharp/Formats/Webp/AlphaEncoder.cs
  2. 52
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  3. 45
      src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
  4. 1
      src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs
  5. 41
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  6. 48
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  7. 11
      src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
  8. 2
      src/ImageSharp/Formats/Webp/WebpEncoder.cs
  9. 7
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
  10. 1
      src/ImageSharp/ImageSharp.csproj
  11. BIN
      src/ImageSharp/Metadata/Profiles/Exif/DC-X008-Translation-2019-E.pdf
  12. 4
      src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
  13. 121
      src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
  14. 19
      src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
  15. 21
      src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs
  16. 39
      src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
  17. 25
      src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
  18. 24
      src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs
  19. 34
      src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs
  20. 15
      src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs
  21. 94
      src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs
  22. 55
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs
  23. 47
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs
  24. 806
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs
  25. 3
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
  26. 68
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  27. 4
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  28. 2
      tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs
  29. 40
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
  30. 5
      tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
  31. 65
      tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs
  32. 2
      tests/ImageSharp.Tests/TestImages.cs
  33. 3
      tests/Images/Input/Jpg/baseline/Calliphora_encoded_strings.jpg
  34. 3
      tests/Images/Input/Png/transparency.png

133
src/ImageSharp/Formats/Webp/AlphaEncoder.cs

@ -0,0 +1,133 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Webp
{
/// <summary>
/// Methods for encoding the alpha data of a VP8 image.
/// </summary>
internal class AlphaEncoder : IDisposable
{
private IMemoryOwner<byte> alphaData;
/// <summary>
/// Encodes the alpha channel data.
/// Data is either compressed as lossless webp image or uncompressed.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="configuration">The global configuration.</param>
/// <param name="memoryAllocator">The memory manager.</param>
/// <param name="compress">Indicates, if the data should be compressed with the lossless webp compression.</param>
/// <param name="size">The size in bytes of the alpha data.</param>
/// <returns>The encoded alpha data.</returns>
public IMemoryOwner<byte> EncodeAlpha<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress, out int size)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
this.alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
if (compress)
{
WebpEncodingMethod effort = WebpEncodingMethod.Default;
int quality = 8 * (int)effort;
using var lossLessEncoder = new Vp8LEncoder(
memoryAllocator,
configuration,
width,
height,
quality,
effort,
WebpTransparentColorMode.Preserve,
false,
0);
// The transparency information will be stored in the green channel of the ARGB quadruplet.
// The green channel is allowed extra transformation steps in the specification -- unlike the other channels,
// that can improve compression.
using Image<Rgba32> alphaAsImage = DispatchAlphaToGreen(image, this.alphaData.GetSpan());
size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, this.alphaData);
return this.alphaData;
}
size = width * height;
return this.alphaData;
}
/// <summary>
/// Store the transparency in the green channel.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="alphaData">A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</param>
/// <returns>The transparency image.</returns>
private static Image<Rgba32> DispatchAlphaToGreen<TPixel>(Image<TPixel> image, Span<byte> alphaData)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
var alphaAsImage = new Image<Rgba32>(width, height);
for (int y = 0; y < height; y++)
{
Memory<Rgba32> rowBuffer = alphaAsImage.DangerousGetPixelRowMemory(y);
Span<Rgba32> pixelRow = rowBuffer.Span;
Span<byte> alphaRow = alphaData.Slice(y * width, width);
for (int x = 0; x < width; x++)
{
// Leave A/R/B channels zero'd.
pixelRow[x] = new Rgba32(0, alphaRow[x], 0, 0);
}
}
return alphaAsImage;
}
/// <summary>
/// Extract the alpha data of the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="configuration">The global configuration.</param>
/// <param name="memoryAllocator">The memory manager.</param>
/// <returns>A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</returns>
private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
int height = image.Height;
int width = image.Width;
IMemoryOwner<byte> alphaDataBuffer = memoryAllocator.Allocate<byte>(width * height);
Span<byte> alphaData = alphaDataBuffer.GetSpan();
using IMemoryOwner<Rgba32> rowBuffer = memoryAllocator.Allocate<Rgba32>(width);
Span<Rgba32> rgbaRow = rowBuffer.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgba32(configuration, rowSpan, rgbaRow);
int offset = y * width;
for (int x = 0; x < width; x++)
{
alphaData[offset + x] = rgbaRow[x].A;
}
}
return alphaDataBuffer;
}
/// <inheritdoc/>
public void Dispose() => this.alphaData?.Dispose();
}
}

52
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -47,6 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <param name="stream">The stream to write to.</param>
public void WriteToStream(Stream stream) => stream.Write(this.Buffer.AsSpan(0, this.NumBytes()));
/// <summary>
/// Writes the encoded bytes of the image to the given buffer. Call Finish() before this.
/// </summary>
/// <param name="dest">The destination buffer.</param>
public void WriteToBuffer(Span<byte> dest) => this.Buffer.AsSpan(0, this.NumBytes()).CopyTo(dest);
/// <summary>
/// Resizes the buffer to write to.
/// </summary>
@ -94,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// Calculates the chunk size of EXIF or XMP metadata.
/// </summary>
/// <param name="metadataBytes">The metadata profile bytes.</param>
/// <returns>The exif chunk size in bytes.</returns>
/// <returns>The metadata chunk size in bytes.</returns>
protected uint MetadataChunkSize(byte[] metadataBytes)
{
uint metaSize = (uint)metadataBytes.Length;
@ -103,6 +109,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
return metaChunkSize;
}
/// <summary>
/// Calculates the chunk size of a alpha chunk.
/// </summary>
/// <param name="alphaBytes">The alpha chunk bytes.</param>
/// <returns>The alpha data chunk size in bytes.</returns>
protected uint AlphaChunkSize(Span<byte> alphaBytes)
{
uint alphaSize = (uint)alphaBytes.Length + 1;
uint alphaChunkSize = WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
return alphaChunkSize;
}
/// <summary>
/// Writes a metadata profile (EXIF or XMP) to the stream.
/// </summary>
@ -128,6 +147,37 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
}
}
/// <summary>
/// Writes the alpha chunk to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="dataBytes">The alpha channel data bytes.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha channel data is compressed.</param>
protected void WriteAlphaChunk(Stream stream, Span<byte> dataBytes, bool alphaDataIsCompressed)
{
uint size = (uint)dataBytes.Length + 1;
Span<byte> buf = this.scratchBuffer.AsSpan(0, 4);
BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.Alpha);
stream.Write(buf);
BinaryPrimitives.WriteUInt32LittleEndian(buf, size);
stream.Write(buf);
byte flags = 0;
if (alphaDataIsCompressed)
{
flags |= 1;
}
stream.WriteByte(flags);
stream.Write(dataBytes);
// Add padding byte if needed.
if ((size & 1) == 1)
{
stream.WriteByte(0);
}
}
/// <summary>
/// Writes a VP8X header to the stream.
/// </summary>

45
src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs

@ -409,7 +409,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param>
public void WriteEncodedImageToStream(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, uint width, uint height, bool hasAlpha)
/// <param name="alphaData">The alpha channel data.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha data is compressed.</param>
public void WriteEncodedImageToStream(
Stream stream,
ExifProfile exifProfile,
XmpProfile xmpProfile,
uint width,
uint height,
bool hasAlpha,
Span<byte> alphaData,
bool alphaDataIsCompressed)
{
bool isVp8X = false;
byte[] exifBytes = null;
@ -418,7 +428,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
if (exifProfile != null)
{
isVp8X = true;
riffSize += ExtendedFileChunkSize;
exifBytes = exifProfile.ToByteArray();
riffSize += this.MetadataChunkSize(exifBytes);
}
@ -426,11 +435,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
if (xmpProfile != null)
{
isVp8X = true;
riffSize += ExtendedFileChunkSize;
xmpBytes = xmpProfile.Data;
riffSize += this.MetadataChunkSize(xmpBytes);
}
if (hasAlpha)
{
isVp8X = true;
riffSize += this.AlphaChunkSize(alphaData);
}
if (isVp8X)
{
riffSize += ExtendedFileChunkSize;
}
this.Finish();
uint numBytes = (uint)this.NumBytes();
int mbSize = this.enc.Mbw * this.enc.Mbh;
@ -451,7 +470,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
riffSize += WebpConstants.TagSize + WebpConstants.ChunkHeaderSize + vp8Size;
// Emit headers and partition #0
this.WriteWebpHeaders(stream, size0, vp8Size, riffSize, isVp8X, width, height, exifProfile, xmpProfile, hasAlpha);
this.WriteWebpHeaders(stream, size0, vp8Size, riffSize, isVp8X, width, height, exifProfile, xmpProfile, hasAlpha, alphaData, alphaDataIsCompressed);
bitWriterPartZero.WriteToStream(stream);
// Write the encoded image to the stream.
@ -639,7 +658,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
while (it.Next());
}
private void WriteWebpHeaders(Stream stream, uint size0, uint vp8Size, uint riffSize, bool isVp8X, uint width, uint height, ExifProfile exifProfile, XmpProfile xmpProfile, bool hasAlpha)
private void WriteWebpHeaders(
Stream stream,
uint size0,
uint vp8Size,
uint riffSize,
bool isVp8X,
uint width,
uint height,
ExifProfile exifProfile,
XmpProfile xmpProfile,
bool hasAlpha,
Span<byte> alphaData,
bool alphaDataIsCompressed)
{
this.WriteRiffHeader(stream, riffSize);
@ -647,6 +678,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
if (isVp8X)
{
this.WriteVp8XHeader(stream, exifProfile, xmpProfile, width, height, hasAlpha);
if (hasAlpha)
{
this.WriteAlphaChunk(stream, alphaData, alphaDataIsCompressed);
}
}
this.WriteVp8Header(stream, vp8Size);

1
src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs

@ -31,6 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <summary>
/// Gets a value indicating whether the alpha plane should be compressed with Webp lossless format.
/// Defaults to true.
/// </summary>
bool UseAlphaCompression { get; }

41
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public Vp8LHashChain HashChain { get; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// Encodes the image as lossless webp to the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
@ -236,10 +236,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
image.Metadata.SyncProfiles();
int width = image.Width;
int height = image.Height;
ImageMetadata metadata = image.Metadata;
metadata.SyncProfiles();
// Convert image pixels to bgra array.
bool hasAlpha = this.ConvertPixelsToBgra(image, width, height);
@ -253,11 +255,42 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.EncodeStream(image);
// Write bytes from the bitwriter buffer to the stream.
ImageMetadata metadata = image.Metadata;
metadata.SyncProfiles();
this.bitWriter.WriteEncodedImageToStream(stream, metadata.ExifProfile, metadata.XmpProfile, (uint)width, (uint)height, hasAlpha);
}
/// <summary>
/// Encodes the alpha image data using the webp lossless compression.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="alphaData">The destination buffer to write the encoded alpha data to.</param>
/// <returns>The size of the compressed data in bytes.
/// If the size of the data is the same as the pixel count, the compression would not yield in smaller data and is left uncompressed.
/// </returns>
public int EncodeAlphaImageData<TPixel>(Image<TPixel> image, IMemoryOwner<byte> alphaData)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
int pixelCount = width * height;
// Convert image pixels to bgra array.
this.ConvertPixelsToBgra(image, width, height);
// The image-stream will NOT contain any headers describing the image dimension, the dimension is already known.
this.EncodeStream(image);
this.bitWriter.Finish();
int size = this.bitWriter.NumBytes();
if (size >= pixelCount)
{
// Compressing would not yield in smaller data -> leave the data uncompressed.
return pixelCount;
}
this.bitWriter.WriteToBuffer(alphaData.GetSpan());
return size;
}
/// <summary>
/// Writes the image size to the bitwriter buffer.
/// </summary>

48
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -71,12 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
private int uvAlpha;
/// <summary>
/// Scratch buffer to reduce allocations.
/// </summary>
private readonly int[] scratch = new int[16];
private readonly byte[] averageBytesPerMb = { 50, 24, 16, 9, 7, 5, 3, 2 };
private readonly bool alphaCompression;
private const int NumMbSegments = 4;
@ -105,6 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <param name="entropyPasses">Number of entropy-analysis passes (in [1..10]).</param>
/// <param name="filterStrength">The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).</param>
/// <param name="spatialNoiseShaping">The spatial noise shaping. 0=off, 100=maximum.</param>
/// <param name="alphaCompression">If true, the alpha channel will be compressed with the lossless compression.</param>
public Vp8Encoder(
MemoryAllocator memoryAllocator,
Configuration configuration,
@ -114,7 +110,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
WebpEncodingMethod method,
int entropyPasses,
int filterStrength,
int spatialNoiseShaping)
int spatialNoiseShaping,
bool alphaCompression)
{
this.memoryAllocator = memoryAllocator;
this.configuration = configuration;
@ -125,6 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10);
this.filterStrength = Numerics.Clamp(filterStrength, 0, 100);
this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100);
this.alphaCompression = alphaCompression;
this.rdOptLevel = method is WebpEncodingMethod.BestQuality ? Vp8RdLevel.RdOptTrellisAll
: method >= WebpEncodingMethod.Level5 ? Vp8RdLevel.RdOptTrellis
: method >= WebpEncodingMethod.Level3 ? Vp8RdLevel.RdOptBasic
@ -174,6 +172,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.ResetBoundaryPredictions();
}
// This uses C#'s optimization to refer to the static data segment of the assembly, no allocation occurs.
private static ReadOnlySpan<byte> AverageBytesPerMb => new byte[] { 50, 24, 16, 9, 7, 5, 3, 2 };
public int BaseQuant { get; set; }
/// <summary>
@ -297,10 +298,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
int width = image.Width;
int height = image.Height;
int pixelCount = width * height;
Span<byte> y = this.Y.GetSpan();
Span<byte> u = this.U.GetSpan();
Span<byte> v = this.V.GetSpan();
YuvConversion.ConvertRgbToYuv(image, this.configuration, this.memoryAllocator, y, u, v);
bool hasAlpha = YuvConversion.ConvertRgbToYuv(image, this.configuration, this.memoryAllocator, y, u, v);
int yStride = width;
int uvStride = (yStride + 1) >> 1;
@ -318,12 +320,26 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.SetLoopParams(this.quality);
// Initialize the bitwriter.
int averageBytesPerMacroBlock = this.averageBytesPerMb[this.BaseQuant >> 4];
int averageBytesPerMacroBlock = AverageBytesPerMb[this.BaseQuant >> 4];
int expectedSize = this.Mbw * this.Mbh * averageBytesPerMacroBlock;
this.bitWriter = new Vp8BitWriter(expectedSize, this);
// TODO: EncodeAlpha();
bool hasAlpha = false;
// Extract and encode alpha channel data, if present.
int alphaDataSize = 0;
bool alphaCompressionSucceeded = false;
using var alphaEncoder = new AlphaEncoder();
Span<byte> alphaData = Span<byte>.Empty;
if (hasAlpha)
{
// TODO: This can potentially run in an separate task.
IMemoryOwner<byte> encodedAlphaData = alphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.alphaCompression, out alphaDataSize);
alphaData = encodedAlphaData.GetSpan();
if (alphaDataSize < pixelCount)
{
// Only use compressed data, if the compressed data is actually smaller then the uncompressed data.
alphaCompressionSucceeded = true;
}
}
// Stats-collection loop.
this.StatLoop(width, height, yStride, uvStride);
@ -358,7 +374,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Write bytes from the bitwriter buffer to the stream.
ImageMetadata metadata = image.Metadata;
metadata.SyncProfiles();
this.bitWriter.WriteEncodedImageToStream(stream, metadata.ExifProfile, metadata.XmpProfile, (uint)width, (uint)height, hasAlpha);
this.bitWriter.WriteEncodedImageToStream(
stream,
metadata.ExifProfile,
metadata.XmpProfile,
(uint)width,
(uint)height,
hasAlpha,
alphaData,
this.alphaCompression && alphaCompressionSucceeded);
}
/// <inheritdoc/>

11
src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs

@ -318,7 +318,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <param name="y">Span to store the luma component of the image.</param>
/// <param name="u">Span to store the u component of the image.</param>
/// <param name="v">Span to store the v component of the image.</param>
public static void ConvertRgbToYuv<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, Span<byte> y, Span<byte> u, Span<byte> v)
/// <returns>true, if the image contains alpha data.</returns>
public static bool ConvertRgbToYuv<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, Span<byte> y, Span<byte> u, Span<byte> v)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
@ -335,6 +336,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<Bgra32> bgraRow1 = bgraRow1Buffer.GetSpan();
int uvRowIndex = 0;
int rowIndex;
bool hasAlpha = false;
for (rowIndex = 0; rowIndex < height - 1; rowIndex += 2)
{
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
@ -343,6 +345,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
PixelOperations<TPixel>.Instance.ToBgra32(configuration, nextRowSpan, bgraRow1);
bool rowsHaveAlpha = WebpCommonUtils.CheckNonOpaque(bgraRow0) && WebpCommonUtils.CheckNonOpaque(bgraRow1);
if (rowsHaveAlpha)
{
hasAlpha = true;
}
// Downsample U/V planes, two rows at a time.
if (!rowsHaveAlpha)
@ -375,10 +381,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
else
{
AccumulateRgba(bgraRow0, bgraRow0, tmpRgbSpan, width);
hasAlpha = true;
}
ConvertRgbaToUv(tmpRgbSpan, u.Slice(uvRowIndex * uvWidth), v.Slice(uvRowIndex * uvWidth), uvWidth);
}
return hasAlpha;
}
/// <summary>

2
src/ImageSharp/Formats/Webp/WebpEncoder.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
public WebpEncodingMethod Method { get; set; } = WebpEncodingMethod.Default;
/// <inheritdoc/>
public bool UseAlphaCompression { get; set; }
public bool UseAlphaCompression { get; set; } = true;
/// <inheritdoc/>
public int EntropyPasses { get; set; } = 1;

7
src/ImageSharp/Formats/Webp/WebpEncoderCore.cs

@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// TODO: not used at the moment.
/// Indicating whether the alpha plane should be compressed with Webp lossless format.
/// Defaults to true.
/// </summary>
private readonly bool alphaCompression;
@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
/// Encodes the image as webp to the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
@ -149,7 +149,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
this.method,
this.entropyPasses,
this.filterStrength,
this.spatialNoiseShaping);
this.spatialNoiseShaping,
this.alphaCompression);
enc.Encode(image, stream);
}
}

1
src/ImageSharp/ImageSharp.csproj

@ -49,6 +49,7 @@
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) OR '$(TargetFramework)' == 'net472'">

BIN
src/ImageSharp/Metadata/Profiles/Exif/DC-X008-Translation-2019-E.pdf

Binary file not shown.

4
src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Text;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
@ -22,5 +23,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
0x00,
0x2A
};
// UTF-8 is better than ASCII, UTF-8 encodes the ASCII codes the same way
public static Encoding DefaultEncoding => Encoding.UTF8;
}
}

121
src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs

@ -0,0 +1,121 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Text;
using static SixLabors.ImageSharp.Metadata.Profiles.Exif.EncodedString;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal static class ExifEncodedStringHelpers
{
public const int CharacterCodeBytesLength = 8;
private const ulong AsciiCode = 0x_00_00_00_49_49_43_53_41;
private const ulong JISCode = 0x_00_00_00_00_00_53_49_4A;
private const ulong UnicodeCode = 0x_45_44_4F_43_49_4E_55;
private const ulong UndefinedCode = 0x_00_00_00_00_00_00_00_00;
private static ReadOnlySpan<byte> AsciiCodeBytes => new byte[] { 0x41, 0x53, 0x43, 0x49, 0x49, 0, 0, 0 };
private static ReadOnlySpan<byte> JISCodeBytes => new byte[] { 0x4A, 0x49, 0x53, 0, 0, 0, 0, 0 };
private static ReadOnlySpan<byte> UnicodeCodeBytes => new byte[] { 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0 };
private static ReadOnlySpan<byte> UndefinedCodeBytes => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
// 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990)
// https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-6.0
private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(20932);
public static bool IsEncodedString(ExifTagValue tag) => tag switch
{
ExifTagValue.UserComment or ExifTagValue.GPSProcessingMethod or ExifTagValue.GPSAreaInformation => true,
_ => false
};
public static ReadOnlySpan<byte> GetCodeBytes(CharacterCode code) => code switch
{
CharacterCode.ASCII => AsciiCodeBytes,
CharacterCode.JIS => JISCodeBytes,
CharacterCode.Unicode => UnicodeCodeBytes,
CharacterCode.Undefined => UndefinedCodeBytes,
_ => UndefinedCodeBytes
};
public static Encoding GetEncoding(CharacterCode code) => code switch
{
CharacterCode.ASCII => Encoding.ASCII,
CharacterCode.JIS => JIS0208Encoding,
CharacterCode.Unicode => Encoding.Unicode,
CharacterCode.Undefined => Encoding.UTF8,
_ => Encoding.UTF8
};
public static bool TryParse(ReadOnlySpan<byte> buffer, out EncodedString encodedString)
{
if (TryDetect(buffer, out CharacterCode code))
{
string text = GetEncoding(code).GetString(buffer.Slice(CharacterCodeBytesLength));
encodedString = new EncodedString(code, text);
return true;
}
encodedString = default;
return false;
}
public static uint GetDataLength(EncodedString encodedString) =>
(uint)GetEncoding(encodedString.Code).GetByteCount(encodedString.Text) + CharacterCodeBytesLength;
public static int Write(EncodedString encodedString, Span<byte> destination)
{
GetCodeBytes(encodedString.Code).CopyTo(destination);
string text = encodedString.Text;
int count = Write(GetEncoding(encodedString.Code), text, destination.Slice(CharacterCodeBytesLength));
return CharacterCodeBytesLength + count;
}
public static unsafe int Write(Encoding encoding, string value, Span<byte> destination)
{
fixed (char* c = value)
{
fixed (byte* b = destination)
{
return encoding.GetBytes(c, value.Length, b, destination.Length);
}
}
}
private static bool TryDetect(ReadOnlySpan<byte> buffer, out CharacterCode code)
{
if (buffer.Length >= CharacterCodeBytesLength)
{
ulong test = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
switch (test)
{
case AsciiCode:
code = CharacterCode.ASCII;
return true;
case JISCode:
code = CharacterCode.JIS;
return true;
case UnicodeCode:
code = CharacterCode.Unicode;
return true;
case UndefinedCode:
code = CharacterCode.Undefined;
return true;
default:
break;
}
}
code = default;
return false;
}
}
}

19
src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs

@ -241,9 +241,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return result;
}
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private string ConvertToString(ReadOnlySpan<byte> buffer)
private static string ConvertToString(Encoding encoding, ReadOnlySpan<byte> buffer)
{
int nullCharIndex = buffer.IndexOf((byte)0);
@ -252,9 +250,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
buffer = buffer.Slice(0, nullCharIndex);
}
return Encoding.UTF8.GetString(buffer);
return encoding.GetString(buffer);
}
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private object ConvertValue(ExifDataType dataType, ReadOnlySpan<byte> buffer, bool isArray)
{
if (buffer.Length == 0)
@ -267,8 +267,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
case ExifDataType.Unknown:
return null;
case ExifDataType.Ascii:
return this.ConvertToString(buffer);
return ConvertToString(ExifConstants.DefaultEncoding, buffer);
case ExifDataType.Byte:
case ExifDataType.Undefined:
if (!isArray)
{
return this.ConvertToByte(buffer);
@ -354,13 +355,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
return ToArray(dataType, buffer, this.ConvertToUInt64);
case ExifDataType.Undefined:
if (!isArray)
{
return this.ConvertToByte(buffer);
}
return buffer.ToArray();
default:
throw new NotSupportedException($"Data type {dataType} is not supported.");
}
@ -453,7 +448,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
// Likewise, tags that point to other IFDs, like e.g. the SubIFDs tag, are now allowed to have the datatype TIFF_IFD8 in BigTIFF.
// Again, the old datatypes TIFF_IFD, and the hardly recommendable TIFF_LONG, are still valid, too.
// https://www.awaresystems.be/imaging/tiff/bigtiff.html
ExifValue exifValue = null;
ExifValue exifValue;
switch (tag)
{
case ExifTagValue.StripOffsets:

21
src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Text;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal static class ExifUcs2StringHelpers
{
public static Encoding Ucs2Encoding => Encoding.GetEncoding("UCS-2");
public static bool IsUcs2Tag(ExifTagValue tag) => tag switch
{
ExifTagValue.XPAuthor or ExifTagValue.XPComment or ExifTagValue.XPKeywords or ExifTagValue.XPSubject or ExifTagValue.XPTitle => true,
_ => false,
};
public static int Write(string value, Span<byte> destination) => ExifEncodedStringHelpers.Write(Ucs2Encoding, value, destination);
}
}

39
src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs

@ -4,7 +4,6 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Text;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
@ -274,9 +273,19 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
object value = exifValue.GetValue();
if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag))
{
return (uint)ExifUcs2StringHelpers.Ucs2Encoding.GetByteCount((string)value);
}
if (value is EncodedString encodedString)
{
return ExifEncodedStringHelpers.GetDataLength(encodedString);
}
if (exifValue.DataType == ExifDataType.Ascii)
{
return (uint)Encoding.UTF8.GetBytes((string)value).Length + 1;
return (uint)ExifConstants.DefaultEncoding.GetByteCount((string)value) + 1;
}
if (value is Array arrayValue)
@ -289,11 +298,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private static int WriteArray(IExifValue value, Span<byte> destination, int offset)
{
if (value.DataType == ExifDataType.Ascii)
{
return WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset);
}
int newOffset = offset;
foreach (object obj in (Array)value.GetValue())
{
@ -378,7 +382,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
switch (dataType)
{
case ExifDataType.Ascii:
offset = Write(Encoding.UTF8.GetBytes((string)value), destination, offset);
offset = Write(ExifConstants.DefaultEncoding.GetBytes((string)value), destination, offset);
destination[offset] = 0;
return offset + 1;
case ExifDataType.Byte:
@ -425,14 +429,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
}
internal static int WriteValue(IExifValue value, Span<byte> destination, int offset)
internal static int WriteValue(IExifValue exifValue, Span<byte> destination, int offset)
{
if (value.IsArray && value.DataType != ExifDataType.Ascii)
object value = exifValue.GetValue();
if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag))
{
return offset + ExifUcs2StringHelpers.Write((string)value, destination.Slice(offset));
}
else if (value is EncodedString encodedString)
{
return offset + ExifEncodedStringHelpers.Write(encodedString, destination.Slice(offset));
}
if (exifValue.IsArray)
{
return WriteArray(value, destination, offset);
return WriteArray(exifValue, destination, offset);
}
return WriteValue(value.DataType, value.GetValue(), destination, offset);
return WriteValue(exifValue.DataType, value, destination, offset);
}
}
}

25
src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs

@ -41,31 +41,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// </summary>
public static ExifTag<byte[]> TIFFEPStandardID => new ExifTag<byte[]>(ExifTagValue.TIFFEPStandardID);
/// <summary>
/// Gets the XPTitle exif tag.
/// </summary>
public static ExifTag<byte[]> XPTitle => new ExifTag<byte[]>(ExifTagValue.XPTitle);
/// <summary>
/// Gets the XPComment exif tag.
/// </summary>
public static ExifTag<byte[]> XPComment => new ExifTag<byte[]>(ExifTagValue.XPComment);
/// <summary>
/// Gets the XPAuthor exif tag.
/// </summary>
public static ExifTag<byte[]> XPAuthor => new ExifTag<byte[]>(ExifTagValue.XPAuthor);
/// <summary>
/// Gets the XPKeywords exif tag.
/// </summary>
public static ExifTag<byte[]> XPKeywords => new ExifTag<byte[]>(ExifTagValue.XPKeywords);
/// <summary>
/// Gets the XPSubject exif tag.
/// </summary>
public static ExifTag<byte[]> XPSubject => new ExifTag<byte[]>(ExifTagValue.XPSubject);
/// <summary>
/// Gets the GPSVersionID exif tag.
/// </summary>

24
src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
/// <content/>
public abstract partial class ExifTag
{
/// <summary>
/// Gets the UserComment exif tag.
/// </summary>
public static ExifTag<EncodedString> UserComment { get; } = new ExifTag<EncodedString>(ExifTagValue.UserComment);
/// <summary>
/// Gets the GPSProcessingMethod exif tag.
/// </summary>
public static ExifTag<EncodedString> GPSProcessingMethod { get; } = new ExifTag<EncodedString>(ExifTagValue.GPSProcessingMethod);
/// <summary>
/// Gets the GPSAreaInformation exif tag.
/// </summary>
public static ExifTag<EncodedString> GPSAreaInformation { get; } = new ExifTag<EncodedString>(ExifTagValue.GPSAreaInformation);
}
}

34
src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
/// <content/>
public abstract partial class ExifTag
{
/// <summary>
/// Gets the title tag used by Windows (encoded in UCS2).
/// </summary>
public static ExifTag<string> XPTitle => new ExifTag<string>(ExifTagValue.XPTitle);
/// <summary>
/// Gets the comment tag used by Windows (encoded in UCS2).
/// </summary>
public static ExifTag<string> XPComment => new ExifTag<string>(ExifTagValue.XPComment);
/// <summary>
/// Gets the author tag used by Windows (encoded in UCS2).
/// </summary>
public static ExifTag<string> XPAuthor => new ExifTag<string>(ExifTagValue.XPAuthor);
/// <summary>
/// Gets the keywords tag used by Windows (encoded in UCS2).
/// </summary>
public static ExifTag<string> XPKeywords => new ExifTag<string>(ExifTagValue.XPKeywords);
/// <summary>
/// Gets the subject tag used by Windows (encoded in UCS2).
/// </summary>
public static ExifTag<string> XPSubject => new ExifTag<string>(ExifTagValue.XPSubject);
}
}

15
src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs

@ -31,11 +31,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// </summary>
public static ExifTag<byte[]> MakerNote { get; } = new ExifTag<byte[]>(ExifTagValue.MakerNote);
/// <summary>
/// Gets the UserComment exif tag.
/// </summary>
public static ExifTag<byte[]> UserComment { get; } = new ExifTag<byte[]>(ExifTagValue.UserComment);
/// <summary>
/// Gets the FlashpixVersion exif tag.
/// </summary>
@ -71,16 +66,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// </summary>
public static ExifTag<byte[]> ImageSourceData { get; } = new ExifTag<byte[]>(ExifTagValue.ImageSourceData);
/// <summary>
/// Gets the GPSProcessingMethod exif tag.
/// </summary>
public static ExifTag<byte[]> GPSProcessingMethod { get; } = new ExifTag<byte[]>(ExifTagValue.GPSProcessingMethod);
/// <summary>
/// Gets the GPSAreaInformation exif tag.
/// </summary>
public static ExifTag<byte[]> GPSAreaInformation { get; } = new ExifTag<byte[]>(ExifTagValue.GPSAreaInformation);
/// <summary>
/// Gets the FileSource exif tag.
/// </summary>

94
src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs

@ -0,0 +1,94 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
/// <summary>
/// The EXIF encoded string structure.
/// </summary>
public readonly struct EncodedString : IEquatable<EncodedString>
{
/// <summary>
/// Initializes a new instance of the <see cref="EncodedString" /> struct.
/// Default use Unicode character code.
/// </summary>
/// <param name="text">The text value.</param>
public EncodedString(string text)
: this(CharacterCode.Unicode, text)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EncodedString" /> struct.
/// </summary>
/// <param name="code">The character code.</param>
/// <param name="text">The text value.</param>
public EncodedString(CharacterCode code, string text)
{
this.Text = text;
this.Code = code;
}
/// <summary>
/// The 8-byte character code enum.
/// </summary>
public enum CharacterCode
{
/// <summary>
/// The ASCII (ITU-T T.50 IA5) character code.
/// </summary>
ASCII,
/// <summary>
/// The JIS (X208-1990) character code.
/// </summary>
JIS,
/// <summary>
/// The Unicode character code.
/// </summary>
Unicode,
/// <summary>
/// The undefined character code.
/// </summary>
Undefined
}
/// <summary>
/// Gets the character ode.
/// </summary>
public CharacterCode Code { get; }
/// <summary>
/// Gets the text.
/// </summary>
public string Text { get; }
/// <summary>
/// Converts the specified <see cref="string"/> to an instance of this type.
/// </summary>
/// <param name="text">The text value.</param>
public static implicit operator EncodedString(string text) => new(text);
/// <summary>
/// Converts the specified <see cref="EncodedString"/> to a <see cref="string"/>.
/// </summary>
/// <param name="encodedString">The <see cref="EncodedString"/> to convert.</param>
public static explicit operator string(EncodedString encodedString) => encodedString.Text;
/// <inheritdoc/>
public override bool Equals(object obj) => obj is EncodedString other && this.Equals(other);
/// <inheritdoc/>
public bool Equals(EncodedString other) => this.Text == other.Text && this.Code == other.Code;
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.Text, this.Code);
/// <inheritdoc/>
public override string ToString() => this.Text;
}
}

55
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Globalization;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal sealed class ExifEncodedString : ExifValue<EncodedString>
{
public ExifEncodedString(ExifTag<EncodedString> tag)
: base(tag)
{
}
public ExifEncodedString(ExifTagValue tag)
: base(tag)
{
}
private ExifEncodedString(ExifEncodedString value)
: base(value)
{
}
public override ExifDataType DataType => ExifDataType.Undefined;
protected override string StringValue => this.Value.Text;
public override bool TrySetValue(object value)
{
if (base.TrySetValue(value))
{
return true;
}
if (value is string stringValue)
{
this.Value = new EncodedString(stringValue);
return true;
}
else if (value is byte[] buffer)
{
if (ExifEncodedStringHelpers.TryParse(buffer, out EncodedString encodedString))
{
this.Value = encodedString;
return true;
}
}
return false;
}
public override IExifValue DeepClone() => new ExifEncodedString(this);
}
}

47
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs

@ -0,0 +1,47 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal sealed class ExifUcs2String : ExifValue<string>
{
public ExifUcs2String(ExifTag<string> tag)
: base(tag)
{
}
public ExifUcs2String(ExifTagValue tag)
: base(tag)
{
}
private ExifUcs2String(ExifUcs2String value)
: base(value)
{
}
public override ExifDataType DataType => ExifDataType.Byte;
protected override string StringValue => this.Value;
public override object GetValue() => this.Value;
public override bool TrySetValue(object value)
{
if (base.TrySetValue(value))
{
return true;
}
if (value is byte[] buffer)
{
this.Value = ExifUcs2StringHelpers.Ucs2Encoding.GetString(buffer);
return true;
}
return false;
}
public override IExifValue DeepClone() => new ExifUcs2String(this);
}
}

806
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs

@ -15,21 +15,36 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
switch (dataType)
{
case ExifDataType.Byte: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
case ExifDataType.DoubleFloat: return isArray ? (ExifValue)new ExifDoubleArray(tag) : new ExifDouble(tag);
case ExifDataType.SingleFloat: return isArray ? (ExifValue)new ExifFloatArray(tag) : new ExifFloat(tag);
case ExifDataType.Long: return isArray ? (ExifValue)new ExifLongArray(tag) : new ExifLong(tag);
case ExifDataType.Long8: return isArray ? (ExifValue)new ExifLong8Array(tag) : new ExifLong8(tag);
case ExifDataType.Rational: return isArray ? (ExifValue)new ExifRationalArray(tag) : new ExifRational(tag);
case ExifDataType.Short: return isArray ? (ExifValue)new ExifShortArray(tag) : new ExifShort(tag);
case ExifDataType.SignedByte: return isArray ? (ExifValue)new ExifSignedByteArray(tag) : new ExifSignedByte(tag);
case ExifDataType.SignedLong: return isArray ? (ExifValue)new ExifSignedLongArray(tag) : new ExifSignedLong(tag);
case ExifDataType.SignedLong8: return isArray ? (ExifValue)new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag);
case ExifDataType.SignedRational: return isArray ? (ExifValue)new ExifSignedRationalArray(tag) : new ExifSignedRational(tag);
case ExifDataType.SignedShort: return isArray ? (ExifValue)new ExifSignedShortArray(tag) : new ExifSignedShort(tag);
case ExifDataType.Ascii: return new ExifString(tag);
case ExifDataType.Undefined: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
default: return null;
case ExifDataType.Byte:
return isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
case ExifDataType.DoubleFloat:
return isArray ? new ExifDoubleArray(tag) : new ExifDouble(tag);
case ExifDataType.SingleFloat:
return isArray ? new ExifFloatArray(tag) : new ExifFloat(tag);
case ExifDataType.Long:
return isArray ? new ExifLongArray(tag) : new ExifLong(tag);
case ExifDataType.Long8:
return isArray ? new ExifLong8Array(tag) : new ExifLong8(tag);
case ExifDataType.Rational:
return isArray ? new ExifRationalArray(tag) : new ExifRational(tag);
case ExifDataType.Short:
return isArray ? new ExifShortArray(tag) : new ExifShort(tag);
case ExifDataType.SignedByte:
return isArray ? new ExifSignedByteArray(tag) : new ExifSignedByte(tag);
case ExifDataType.SignedLong:
return isArray ? new ExifSignedLongArray(tag) : new ExifSignedLong(tag);
case ExifDataType.SignedLong8:
return isArray ? new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag);
case ExifDataType.SignedRational:
return isArray ? new ExifSignedRationalArray(tag) : new ExifSignedRational(tag);
case ExifDataType.SignedShort:
return isArray ? new ExifSignedShortArray(tag) : new ExifSignedShort(tag);
case ExifDataType.Ascii:
return new ExifString(tag);
case ExifDataType.Undefined:
return isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
default:
return null;
}
}
@ -37,275 +52,530 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
switch (tag)
{
case ExifTagValue.FaxProfile: return new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte);
case ExifTagValue.ModeNumber: return new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte);
case ExifTagValue.GPSAltitudeRef: return new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte);
case ExifTagValue.FaxProfile:
return new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte);
case ExifTagValue.ModeNumber:
return new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte);
case ExifTagValue.GPSAltitudeRef:
return new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte);
case ExifTagValue.ClipPath: return new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte);
case ExifTagValue.VersionYear: return new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte);
case ExifTagValue.XMP: return new ExifByteArray(ExifTag.XMP, ExifDataType.Byte);
case ExifTagValue.CFAPattern2: return new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte);
case ExifTagValue.TIFFEPStandardID: return new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte);
case ExifTagValue.XPTitle: return new ExifByteArray(ExifTag.XPTitle, ExifDataType.Byte);
case ExifTagValue.XPComment: return new ExifByteArray(ExifTag.XPComment, ExifDataType.Byte);
case ExifTagValue.XPAuthor: return new ExifByteArray(ExifTag.XPAuthor, ExifDataType.Byte);
case ExifTagValue.XPKeywords: return new ExifByteArray(ExifTag.XPKeywords, ExifDataType.Byte);
case ExifTagValue.XPSubject: return new ExifByteArray(ExifTag.XPSubject, ExifDataType.Byte);
case ExifTagValue.GPSVersionID: return new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte);
case ExifTagValue.ClipPath:
return new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte);
case ExifTagValue.VersionYear:
return new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte);
case ExifTagValue.XMP:
return new ExifByteArray(ExifTag.XMP, ExifDataType.Byte);
case ExifTagValue.CFAPattern2:
return new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte);
case ExifTagValue.TIFFEPStandardID:
return new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte);
case ExifTagValue.GPSVersionID:
return new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte);
case ExifTagValue.PixelScale: return new ExifDoubleArray(ExifTag.PixelScale);
case ExifTagValue.IntergraphMatrix: return new ExifDoubleArray(ExifTag.IntergraphMatrix);
case ExifTagValue.ModelTiePoint: return new ExifDoubleArray(ExifTag.ModelTiePoint);
case ExifTagValue.ModelTransform: return new ExifDoubleArray(ExifTag.ModelTransform);
case ExifTagValue.PixelScale:
return new ExifDoubleArray(ExifTag.PixelScale);
case ExifTagValue.IntergraphMatrix:
return new ExifDoubleArray(ExifTag.IntergraphMatrix);
case ExifTagValue.ModelTiePoint:
return new ExifDoubleArray(ExifTag.ModelTiePoint);
case ExifTagValue.ModelTransform:
return new ExifDoubleArray(ExifTag.ModelTransform);
case ExifTagValue.SubfileType: return new ExifLong(ExifTag.SubfileType);
case ExifTagValue.SubIFDOffset: return new ExifLong(ExifTag.SubIFDOffset);
case ExifTagValue.GPSIFDOffset: return new ExifLong(ExifTag.GPSIFDOffset);
case ExifTagValue.T4Options: return new ExifLong(ExifTag.T4Options);
case ExifTagValue.T6Options: return new ExifLong(ExifTag.T6Options);
case ExifTagValue.XClipPathUnits: return new ExifLong(ExifTag.XClipPathUnits);
case ExifTagValue.YClipPathUnits: return new ExifLong(ExifTag.YClipPathUnits);
case ExifTagValue.ProfileType: return new ExifLong(ExifTag.ProfileType);
case ExifTagValue.CodingMethods: return new ExifLong(ExifTag.CodingMethods);
case ExifTagValue.T82ptions: return new ExifLong(ExifTag.T82ptions);
case ExifTagValue.JPEGInterchangeFormat: return new ExifLong(ExifTag.JPEGInterchangeFormat);
case ExifTagValue.JPEGInterchangeFormatLength: return new ExifLong(ExifTag.JPEGInterchangeFormatLength);
case ExifTagValue.MDFileTag: return new ExifLong(ExifTag.MDFileTag);
case ExifTagValue.StandardOutputSensitivity: return new ExifLong(ExifTag.StandardOutputSensitivity);
case ExifTagValue.RecommendedExposureIndex: return new ExifLong(ExifTag.RecommendedExposureIndex);
case ExifTagValue.ISOSpeed: return new ExifLong(ExifTag.ISOSpeed);
case ExifTagValue.ISOSpeedLatitudeyyy: return new ExifLong(ExifTag.ISOSpeedLatitudeyyy);
case ExifTagValue.ISOSpeedLatitudezzz: return new ExifLong(ExifTag.ISOSpeedLatitudezzz);
case ExifTagValue.FaxRecvParams: return new ExifLong(ExifTag.FaxRecvParams);
case ExifTagValue.FaxRecvTime: return new ExifLong(ExifTag.FaxRecvTime);
case ExifTagValue.ImageNumber: return new ExifLong(ExifTag.ImageNumber);
case ExifTagValue.SubfileType:
return new ExifLong(ExifTag.SubfileType);
case ExifTagValue.SubIFDOffset:
return new ExifLong(ExifTag.SubIFDOffset);
case ExifTagValue.GPSIFDOffset:
return new ExifLong(ExifTag.GPSIFDOffset);
case ExifTagValue.T4Options:
return new ExifLong(ExifTag.T4Options);
case ExifTagValue.T6Options:
return new ExifLong(ExifTag.T6Options);
case ExifTagValue.XClipPathUnits:
return new ExifLong(ExifTag.XClipPathUnits);
case ExifTagValue.YClipPathUnits:
return new ExifLong(ExifTag.YClipPathUnits);
case ExifTagValue.ProfileType:
return new ExifLong(ExifTag.ProfileType);
case ExifTagValue.CodingMethods:
return new ExifLong(ExifTag.CodingMethods);
case ExifTagValue.T82ptions:
return new ExifLong(ExifTag.T82ptions);
case ExifTagValue.JPEGInterchangeFormat:
return new ExifLong(ExifTag.JPEGInterchangeFormat);
case ExifTagValue.JPEGInterchangeFormatLength:
return new ExifLong(ExifTag.JPEGInterchangeFormatLength);
case ExifTagValue.MDFileTag:
return new ExifLong(ExifTag.MDFileTag);
case ExifTagValue.StandardOutputSensitivity:
return new ExifLong(ExifTag.StandardOutputSensitivity);
case ExifTagValue.RecommendedExposureIndex:
return new ExifLong(ExifTag.RecommendedExposureIndex);
case ExifTagValue.ISOSpeed:
return new ExifLong(ExifTag.ISOSpeed);
case ExifTagValue.ISOSpeedLatitudeyyy:
return new ExifLong(ExifTag.ISOSpeedLatitudeyyy);
case ExifTagValue.ISOSpeedLatitudezzz:
return new ExifLong(ExifTag.ISOSpeedLatitudezzz);
case ExifTagValue.FaxRecvParams:
return new ExifLong(ExifTag.FaxRecvParams);
case ExifTagValue.FaxRecvTime:
return new ExifLong(ExifTag.FaxRecvTime);
case ExifTagValue.ImageNumber:
return new ExifLong(ExifTag.ImageNumber);
case ExifTagValue.FreeOffsets: return new ExifLongArray(ExifTag.FreeOffsets);
case ExifTagValue.FreeByteCounts: return new ExifLongArray(ExifTag.FreeByteCounts);
case ExifTagValue.ColorResponseUnit: return new ExifLongArray(ExifTag.ColorResponseUnit);
case ExifTagValue.TileOffsets: return new ExifLongArray(ExifTag.TileOffsets);
case ExifTagValue.SMinSampleValue: return new ExifLongArray(ExifTag.SMinSampleValue);
case ExifTagValue.SMaxSampleValue: return new ExifLongArray(ExifTag.SMaxSampleValue);
case ExifTagValue.JPEGQTables: return new ExifLongArray(ExifTag.JPEGQTables);
case ExifTagValue.JPEGDCTables: return new ExifLongArray(ExifTag.JPEGDCTables);
case ExifTagValue.JPEGACTables: return new ExifLongArray(ExifTag.JPEGACTables);
case ExifTagValue.StripRowCounts: return new ExifLongArray(ExifTag.StripRowCounts);
case ExifTagValue.IntergraphRegisters: return new ExifLongArray(ExifTag.IntergraphRegisters);
case ExifTagValue.TimeZoneOffset: return new ExifLongArray(ExifTag.TimeZoneOffset);
case ExifTagValue.SubIFDs: return new ExifLongArray(ExifTag.SubIFDs);
case ExifTagValue.FreeOffsets:
return new ExifLongArray(ExifTag.FreeOffsets);
case ExifTagValue.FreeByteCounts:
return new ExifLongArray(ExifTag.FreeByteCounts);
case ExifTagValue.ColorResponseUnit:
return new ExifLongArray(ExifTag.ColorResponseUnit);
case ExifTagValue.TileOffsets:
return new ExifLongArray(ExifTag.TileOffsets);
case ExifTagValue.SMinSampleValue:
return new ExifLongArray(ExifTag.SMinSampleValue);
case ExifTagValue.SMaxSampleValue:
return new ExifLongArray(ExifTag.SMaxSampleValue);
case ExifTagValue.JPEGQTables:
return new ExifLongArray(ExifTag.JPEGQTables);
case ExifTagValue.JPEGDCTables:
return new ExifLongArray(ExifTag.JPEGDCTables);
case ExifTagValue.JPEGACTables:
return new ExifLongArray(ExifTag.JPEGACTables);
case ExifTagValue.StripRowCounts:
return new ExifLongArray(ExifTag.StripRowCounts);
case ExifTagValue.IntergraphRegisters:
return new ExifLongArray(ExifTag.IntergraphRegisters);
case ExifTagValue.TimeZoneOffset:
return new ExifLongArray(ExifTag.TimeZoneOffset);
case ExifTagValue.SubIFDs:
return new ExifLongArray(ExifTag.SubIFDs);
case ExifTagValue.ImageWidth: return new ExifNumber(ExifTag.ImageWidth);
case ExifTagValue.ImageLength: return new ExifNumber(ExifTag.ImageLength);
case ExifTagValue.RowsPerStrip: return new ExifNumber(ExifTag.RowsPerStrip);
case ExifTagValue.TileWidth: return new ExifNumber(ExifTag.TileWidth);
case ExifTagValue.TileLength: return new ExifNumber(ExifTag.TileLength);
case ExifTagValue.BadFaxLines: return new ExifNumber(ExifTag.BadFaxLines);
case ExifTagValue.ConsecutiveBadFaxLines: return new ExifNumber(ExifTag.ConsecutiveBadFaxLines);
case ExifTagValue.PixelXDimension: return new ExifNumber(ExifTag.PixelXDimension);
case ExifTagValue.PixelYDimension: return new ExifNumber(ExifTag.PixelYDimension);
case ExifTagValue.ImageWidth:
return new ExifNumber(ExifTag.ImageWidth);
case ExifTagValue.ImageLength:
return new ExifNumber(ExifTag.ImageLength);
case ExifTagValue.RowsPerStrip:
return new ExifNumber(ExifTag.RowsPerStrip);
case ExifTagValue.TileWidth:
return new ExifNumber(ExifTag.TileWidth);
case ExifTagValue.TileLength:
return new ExifNumber(ExifTag.TileLength);
case ExifTagValue.BadFaxLines:
return new ExifNumber(ExifTag.BadFaxLines);
case ExifTagValue.ConsecutiveBadFaxLines:
return new ExifNumber(ExifTag.ConsecutiveBadFaxLines);
case ExifTagValue.PixelXDimension:
return new ExifNumber(ExifTag.PixelXDimension);
case ExifTagValue.PixelYDimension:
return new ExifNumber(ExifTag.PixelYDimension);
case ExifTagValue.StripByteCounts: return new ExifNumberArray(ExifTag.StripByteCounts);
case ExifTagValue.StripOffsets: return new ExifNumberArray(ExifTag.StripOffsets);
case ExifTagValue.TileByteCounts: return new ExifNumberArray(ExifTag.TileByteCounts);
case ExifTagValue.ImageLayer: return new ExifNumberArray(ExifTag.ImageLayer);
case ExifTagValue.StripByteCounts:
return new ExifNumberArray(ExifTag.StripByteCounts);
case ExifTagValue.StripOffsets:
return new ExifNumberArray(ExifTag.StripOffsets);
case ExifTagValue.TileByteCounts:
return new ExifNumberArray(ExifTag.TileByteCounts);
case ExifTagValue.ImageLayer:
return new ExifNumberArray(ExifTag.ImageLayer);
case ExifTagValue.XPosition: return new ExifRational(ExifTag.XPosition);
case ExifTagValue.YPosition: return new ExifRational(ExifTag.YPosition);
case ExifTagValue.XResolution: return new ExifRational(ExifTag.XResolution);
case ExifTagValue.YResolution: return new ExifRational(ExifTag.YResolution);
case ExifTagValue.BatteryLevel: return new ExifRational(ExifTag.BatteryLevel);
case ExifTagValue.ExposureTime: return new ExifRational(ExifTag.ExposureTime);
case ExifTagValue.FNumber: return new ExifRational(ExifTag.FNumber);
case ExifTagValue.MDScalePixel: return new ExifRational(ExifTag.MDScalePixel);
case ExifTagValue.CompressedBitsPerPixel: return new ExifRational(ExifTag.CompressedBitsPerPixel);
case ExifTagValue.ApertureValue: return new ExifRational(ExifTag.ApertureValue);
case ExifTagValue.MaxApertureValue: return new ExifRational(ExifTag.MaxApertureValue);
case ExifTagValue.SubjectDistance: return new ExifRational(ExifTag.SubjectDistance);
case ExifTagValue.FocalLength: return new ExifRational(ExifTag.FocalLength);
case ExifTagValue.FlashEnergy2: return new ExifRational(ExifTag.FlashEnergy2);
case ExifTagValue.FocalPlaneXResolution2: return new ExifRational(ExifTag.FocalPlaneXResolution2);
case ExifTagValue.FocalPlaneYResolution2: return new ExifRational(ExifTag.FocalPlaneYResolution2);
case ExifTagValue.ExposureIndex2: return new ExifRational(ExifTag.ExposureIndex2);
case ExifTagValue.Humidity: return new ExifRational(ExifTag.Humidity);
case ExifTagValue.Pressure: return new ExifRational(ExifTag.Pressure);
case ExifTagValue.Acceleration: return new ExifRational(ExifTag.Acceleration);
case ExifTagValue.FlashEnergy: return new ExifRational(ExifTag.FlashEnergy);
case ExifTagValue.FocalPlaneXResolution: return new ExifRational(ExifTag.FocalPlaneXResolution);
case ExifTagValue.FocalPlaneYResolution: return new ExifRational(ExifTag.FocalPlaneYResolution);
case ExifTagValue.ExposureIndex: return new ExifRational(ExifTag.ExposureIndex);
case ExifTagValue.DigitalZoomRatio: return new ExifRational(ExifTag.DigitalZoomRatio);
case ExifTagValue.GPSAltitude: return new ExifRational(ExifTag.GPSAltitude);
case ExifTagValue.GPSDOP: return new ExifRational(ExifTag.GPSDOP);
case ExifTagValue.GPSSpeed: return new ExifRational(ExifTag.GPSSpeed);
case ExifTagValue.GPSTrack: return new ExifRational(ExifTag.GPSTrack);
case ExifTagValue.GPSImgDirection: return new ExifRational(ExifTag.GPSImgDirection);
case ExifTagValue.GPSDestBearing: return new ExifRational(ExifTag.GPSDestBearing);
case ExifTagValue.GPSDestDistance: return new ExifRational(ExifTag.GPSDestDistance);
case ExifTagValue.XPosition:
return new ExifRational(ExifTag.XPosition);
case ExifTagValue.YPosition:
return new ExifRational(ExifTag.YPosition);
case ExifTagValue.XResolution:
return new ExifRational(ExifTag.XResolution);
case ExifTagValue.YResolution:
return new ExifRational(ExifTag.YResolution);
case ExifTagValue.BatteryLevel:
return new ExifRational(ExifTag.BatteryLevel);
case ExifTagValue.ExposureTime:
return new ExifRational(ExifTag.ExposureTime);
case ExifTagValue.FNumber:
return new ExifRational(ExifTag.FNumber);
case ExifTagValue.MDScalePixel:
return new ExifRational(ExifTag.MDScalePixel);
case ExifTagValue.CompressedBitsPerPixel:
return new ExifRational(ExifTag.CompressedBitsPerPixel);
case ExifTagValue.ApertureValue:
return new ExifRational(ExifTag.ApertureValue);
case ExifTagValue.MaxApertureValue:
return new ExifRational(ExifTag.MaxApertureValue);
case ExifTagValue.SubjectDistance:
return new ExifRational(ExifTag.SubjectDistance);
case ExifTagValue.FocalLength:
return new ExifRational(ExifTag.FocalLength);
case ExifTagValue.FlashEnergy2:
return new ExifRational(ExifTag.FlashEnergy2);
case ExifTagValue.FocalPlaneXResolution2:
return new ExifRational(ExifTag.FocalPlaneXResolution2);
case ExifTagValue.FocalPlaneYResolution2:
return new ExifRational(ExifTag.FocalPlaneYResolution2);
case ExifTagValue.ExposureIndex2:
return new ExifRational(ExifTag.ExposureIndex2);
case ExifTagValue.Humidity:
return new ExifRational(ExifTag.Humidity);
case ExifTagValue.Pressure:
return new ExifRational(ExifTag.Pressure);
case ExifTagValue.Acceleration:
return new ExifRational(ExifTag.Acceleration);
case ExifTagValue.FlashEnergy:
return new ExifRational(ExifTag.FlashEnergy);
case ExifTagValue.FocalPlaneXResolution:
return new ExifRational(ExifTag.FocalPlaneXResolution);
case ExifTagValue.FocalPlaneYResolution:
return new ExifRational(ExifTag.FocalPlaneYResolution);
case ExifTagValue.ExposureIndex:
return new ExifRational(ExifTag.ExposureIndex);
case ExifTagValue.DigitalZoomRatio:
return new ExifRational(ExifTag.DigitalZoomRatio);
case ExifTagValue.GPSAltitude:
return new ExifRational(ExifTag.GPSAltitude);
case ExifTagValue.GPSDOP:
return new ExifRational(ExifTag.GPSDOP);
case ExifTagValue.GPSSpeed:
return new ExifRational(ExifTag.GPSSpeed);
case ExifTagValue.GPSTrack:
return new ExifRational(ExifTag.GPSTrack);
case ExifTagValue.GPSImgDirection:
return new ExifRational(ExifTag.GPSImgDirection);
case ExifTagValue.GPSDestBearing:
return new ExifRational(ExifTag.GPSDestBearing);
case ExifTagValue.GPSDestDistance:
return new ExifRational(ExifTag.GPSDestDistance);
case ExifTagValue.WhitePoint: return new ExifRationalArray(ExifTag.WhitePoint);
case ExifTagValue.PrimaryChromaticities: return new ExifRationalArray(ExifTag.PrimaryChromaticities);
case ExifTagValue.YCbCrCoefficients: return new ExifRationalArray(ExifTag.YCbCrCoefficients);
case ExifTagValue.ReferenceBlackWhite: return new ExifRationalArray(ExifTag.ReferenceBlackWhite);
case ExifTagValue.GPSLatitude: return new ExifRationalArray(ExifTag.GPSLatitude);
case ExifTagValue.GPSLongitude: return new ExifRationalArray(ExifTag.GPSLongitude);
case ExifTagValue.GPSTimestamp: return new ExifRationalArray(ExifTag.GPSTimestamp);
case ExifTagValue.GPSDestLatitude: return new ExifRationalArray(ExifTag.GPSDestLatitude);
case ExifTagValue.GPSDestLongitude: return new ExifRationalArray(ExifTag.GPSDestLongitude);
case ExifTagValue.LensSpecification: return new ExifRationalArray(ExifTag.LensSpecification);
case ExifTagValue.WhitePoint:
return new ExifRationalArray(ExifTag.WhitePoint);
case ExifTagValue.PrimaryChromaticities:
return new ExifRationalArray(ExifTag.PrimaryChromaticities);
case ExifTagValue.YCbCrCoefficients:
return new ExifRationalArray(ExifTag.YCbCrCoefficients);
case ExifTagValue.ReferenceBlackWhite:
return new ExifRationalArray(ExifTag.ReferenceBlackWhite);
case ExifTagValue.GPSLatitude:
return new ExifRationalArray(ExifTag.GPSLatitude);
case ExifTagValue.GPSLongitude:
return new ExifRationalArray(ExifTag.GPSLongitude);
case ExifTagValue.GPSTimestamp:
return new ExifRationalArray(ExifTag.GPSTimestamp);
case ExifTagValue.GPSDestLatitude:
return new ExifRationalArray(ExifTag.GPSDestLatitude);
case ExifTagValue.GPSDestLongitude:
return new ExifRationalArray(ExifTag.GPSDestLongitude);
case ExifTagValue.LensSpecification:
return new ExifRationalArray(ExifTag.LensSpecification);
case ExifTagValue.OldSubfileType: return new ExifShort(ExifTag.OldSubfileType);
case ExifTagValue.Compression: return new ExifShort(ExifTag.Compression);
case ExifTagValue.PhotometricInterpretation: return new ExifShort(ExifTag.PhotometricInterpretation);
case ExifTagValue.Thresholding: return new ExifShort(ExifTag.Thresholding);
case ExifTagValue.CellWidth: return new ExifShort(ExifTag.CellWidth);
case ExifTagValue.CellLength: return new ExifShort(ExifTag.CellLength);
case ExifTagValue.FillOrder: return new ExifShort(ExifTag.FillOrder);
case ExifTagValue.Orientation: return new ExifShort(ExifTag.Orientation);
case ExifTagValue.SamplesPerPixel: return new ExifShort(ExifTag.SamplesPerPixel);
case ExifTagValue.PlanarConfiguration: return new ExifShort(ExifTag.PlanarConfiguration);
case ExifTagValue.Predictor: return new ExifShort(ExifTag.Predictor);
case ExifTagValue.GrayResponseUnit: return new ExifShort(ExifTag.GrayResponseUnit);
case ExifTagValue.ResolutionUnit: return new ExifShort(ExifTag.ResolutionUnit);
case ExifTagValue.CleanFaxData: return new ExifShort(ExifTag.CleanFaxData);
case ExifTagValue.InkSet: return new ExifShort(ExifTag.InkSet);
case ExifTagValue.NumberOfInks: return new ExifShort(ExifTag.NumberOfInks);
case ExifTagValue.DotRange: return new ExifShort(ExifTag.DotRange);
case ExifTagValue.Indexed: return new ExifShort(ExifTag.Indexed);
case ExifTagValue.OPIProxy: return new ExifShort(ExifTag.OPIProxy);
case ExifTagValue.JPEGProc: return new ExifShort(ExifTag.JPEGProc);
case ExifTagValue.JPEGRestartInterval: return new ExifShort(ExifTag.JPEGRestartInterval);
case ExifTagValue.YCbCrPositioning: return new ExifShort(ExifTag.YCbCrPositioning);
case ExifTagValue.Rating: return new ExifShort(ExifTag.Rating);
case ExifTagValue.RatingPercent: return new ExifShort(ExifTag.RatingPercent);
case ExifTagValue.ExposureProgram: return new ExifShort(ExifTag.ExposureProgram);
case ExifTagValue.Interlace: return new ExifShort(ExifTag.Interlace);
case ExifTagValue.SelfTimerMode: return new ExifShort(ExifTag.SelfTimerMode);
case ExifTagValue.SensitivityType: return new ExifShort(ExifTag.SensitivityType);
case ExifTagValue.MeteringMode: return new ExifShort(ExifTag.MeteringMode);
case ExifTagValue.LightSource: return new ExifShort(ExifTag.LightSource);
case ExifTagValue.FocalPlaneResolutionUnit2: return new ExifShort(ExifTag.FocalPlaneResolutionUnit2);
case ExifTagValue.SensingMethod2: return new ExifShort(ExifTag.SensingMethod2);
case ExifTagValue.Flash: return new ExifShort(ExifTag.Flash);
case ExifTagValue.ColorSpace: return new ExifShort(ExifTag.ColorSpace);
case ExifTagValue.FocalPlaneResolutionUnit: return new ExifShort(ExifTag.FocalPlaneResolutionUnit);
case ExifTagValue.SensingMethod: return new ExifShort(ExifTag.SensingMethod);
case ExifTagValue.CustomRendered: return new ExifShort(ExifTag.CustomRendered);
case ExifTagValue.ExposureMode: return new ExifShort(ExifTag.ExposureMode);
case ExifTagValue.WhiteBalance: return new ExifShort(ExifTag.WhiteBalance);
case ExifTagValue.FocalLengthIn35mmFilm: return new ExifShort(ExifTag.FocalLengthIn35mmFilm);
case ExifTagValue.SceneCaptureType: return new ExifShort(ExifTag.SceneCaptureType);
case ExifTagValue.GainControl: return new ExifShort(ExifTag.GainControl);
case ExifTagValue.Contrast: return new ExifShort(ExifTag.Contrast);
case ExifTagValue.Saturation: return new ExifShort(ExifTag.Saturation);
case ExifTagValue.Sharpness: return new ExifShort(ExifTag.Sharpness);
case ExifTagValue.SubjectDistanceRange: return new ExifShort(ExifTag.SubjectDistanceRange);
case ExifTagValue.GPSDifferential: return new ExifShort(ExifTag.GPSDifferential);
case ExifTagValue.OldSubfileType:
return new ExifShort(ExifTag.OldSubfileType);
case ExifTagValue.Compression:
return new ExifShort(ExifTag.Compression);
case ExifTagValue.PhotometricInterpretation:
return new ExifShort(ExifTag.PhotometricInterpretation);
case ExifTagValue.Thresholding:
return new ExifShort(ExifTag.Thresholding);
case ExifTagValue.CellWidth:
return new ExifShort(ExifTag.CellWidth);
case ExifTagValue.CellLength:
return new ExifShort(ExifTag.CellLength);
case ExifTagValue.FillOrder:
return new ExifShort(ExifTag.FillOrder);
case ExifTagValue.Orientation:
return new ExifShort(ExifTag.Orientation);
case ExifTagValue.SamplesPerPixel:
return new ExifShort(ExifTag.SamplesPerPixel);
case ExifTagValue.PlanarConfiguration:
return new ExifShort(ExifTag.PlanarConfiguration);
case ExifTagValue.Predictor:
return new ExifShort(ExifTag.Predictor);
case ExifTagValue.GrayResponseUnit:
return new ExifShort(ExifTag.GrayResponseUnit);
case ExifTagValue.ResolutionUnit:
return new ExifShort(ExifTag.ResolutionUnit);
case ExifTagValue.CleanFaxData:
return new ExifShort(ExifTag.CleanFaxData);
case ExifTagValue.InkSet:
return new ExifShort(ExifTag.InkSet);
case ExifTagValue.NumberOfInks:
return new ExifShort(ExifTag.NumberOfInks);
case ExifTagValue.DotRange:
return new ExifShort(ExifTag.DotRange);
case ExifTagValue.Indexed:
return new ExifShort(ExifTag.Indexed);
case ExifTagValue.OPIProxy:
return new ExifShort(ExifTag.OPIProxy);
case ExifTagValue.JPEGProc:
return new ExifShort(ExifTag.JPEGProc);
case ExifTagValue.JPEGRestartInterval:
return new ExifShort(ExifTag.JPEGRestartInterval);
case ExifTagValue.YCbCrPositioning:
return new ExifShort(ExifTag.YCbCrPositioning);
case ExifTagValue.Rating:
return new ExifShort(ExifTag.Rating);
case ExifTagValue.RatingPercent:
return new ExifShort(ExifTag.RatingPercent);
case ExifTagValue.ExposureProgram:
return new ExifShort(ExifTag.ExposureProgram);
case ExifTagValue.Interlace:
return new ExifShort(ExifTag.Interlace);
case ExifTagValue.SelfTimerMode:
return new ExifShort(ExifTag.SelfTimerMode);
case ExifTagValue.SensitivityType:
return new ExifShort(ExifTag.SensitivityType);
case ExifTagValue.MeteringMode:
return new ExifShort(ExifTag.MeteringMode);
case ExifTagValue.LightSource:
return new ExifShort(ExifTag.LightSource);
case ExifTagValue.FocalPlaneResolutionUnit2:
return new ExifShort(ExifTag.FocalPlaneResolutionUnit2);
case ExifTagValue.SensingMethod2:
return new ExifShort(ExifTag.SensingMethod2);
case ExifTagValue.Flash:
return new ExifShort(ExifTag.Flash);
case ExifTagValue.ColorSpace:
return new ExifShort(ExifTag.ColorSpace);
case ExifTagValue.FocalPlaneResolutionUnit:
return new ExifShort(ExifTag.FocalPlaneResolutionUnit);
case ExifTagValue.SensingMethod:
return new ExifShort(ExifTag.SensingMethod);
case ExifTagValue.CustomRendered:
return new ExifShort(ExifTag.CustomRendered);
case ExifTagValue.ExposureMode:
return new ExifShort(ExifTag.ExposureMode);
case ExifTagValue.WhiteBalance:
return new ExifShort(ExifTag.WhiteBalance);
case ExifTagValue.FocalLengthIn35mmFilm:
return new ExifShort(ExifTag.FocalLengthIn35mmFilm);
case ExifTagValue.SceneCaptureType:
return new ExifShort(ExifTag.SceneCaptureType);
case ExifTagValue.GainControl:
return new ExifShort(ExifTag.GainControl);
case ExifTagValue.Contrast:
return new ExifShort(ExifTag.Contrast);
case ExifTagValue.Saturation:
return new ExifShort(ExifTag.Saturation);
case ExifTagValue.Sharpness:
return new ExifShort(ExifTag.Sharpness);
case ExifTagValue.SubjectDistanceRange:
return new ExifShort(ExifTag.SubjectDistanceRange);
case ExifTagValue.GPSDifferential:
return new ExifShort(ExifTag.GPSDifferential);
case ExifTagValue.BitsPerSample: return new ExifShortArray(ExifTag.BitsPerSample);
case ExifTagValue.MinSampleValue: return new ExifShortArray(ExifTag.MinSampleValue);
case ExifTagValue.MaxSampleValue: return new ExifShortArray(ExifTag.MaxSampleValue);
case ExifTagValue.GrayResponseCurve: return new ExifShortArray(ExifTag.GrayResponseCurve);
case ExifTagValue.ColorMap: return new ExifShortArray(ExifTag.ColorMap);
case ExifTagValue.ExtraSamples: return new ExifShortArray(ExifTag.ExtraSamples);
case ExifTagValue.PageNumber: return new ExifShortArray(ExifTag.PageNumber);
case ExifTagValue.TransferFunction: return new ExifShortArray(ExifTag.TransferFunction);
case ExifTagValue.HalftoneHints: return new ExifShortArray(ExifTag.HalftoneHints);
case ExifTagValue.SampleFormat: return new ExifShortArray(ExifTag.SampleFormat);
case ExifTagValue.TransferRange: return new ExifShortArray(ExifTag.TransferRange);
case ExifTagValue.DefaultImageColor: return new ExifShortArray(ExifTag.DefaultImageColor);
case ExifTagValue.JPEGLosslessPredictors: return new ExifShortArray(ExifTag.JPEGLosslessPredictors);
case ExifTagValue.JPEGPointTransforms: return new ExifShortArray(ExifTag.JPEGPointTransforms);
case ExifTagValue.YCbCrSubsampling: return new ExifShortArray(ExifTag.YCbCrSubsampling);
case ExifTagValue.CFARepeatPatternDim: return new ExifShortArray(ExifTag.CFARepeatPatternDim);
case ExifTagValue.IntergraphPacketData: return new ExifShortArray(ExifTag.IntergraphPacketData);
case ExifTagValue.ISOSpeedRatings: return new ExifShortArray(ExifTag.ISOSpeedRatings);
case ExifTagValue.SubjectArea: return new ExifShortArray(ExifTag.SubjectArea);
case ExifTagValue.SubjectLocation: return new ExifShortArray(ExifTag.SubjectLocation);
case ExifTagValue.BitsPerSample:
return new ExifShortArray(ExifTag.BitsPerSample);
case ExifTagValue.MinSampleValue:
return new ExifShortArray(ExifTag.MinSampleValue);
case ExifTagValue.MaxSampleValue:
return new ExifShortArray(ExifTag.MaxSampleValue);
case ExifTagValue.GrayResponseCurve:
return new ExifShortArray(ExifTag.GrayResponseCurve);
case ExifTagValue.ColorMap:
return new ExifShortArray(ExifTag.ColorMap);
case ExifTagValue.ExtraSamples:
return new ExifShortArray(ExifTag.ExtraSamples);
case ExifTagValue.PageNumber:
return new ExifShortArray(ExifTag.PageNumber);
case ExifTagValue.TransferFunction:
return new ExifShortArray(ExifTag.TransferFunction);
case ExifTagValue.HalftoneHints:
return new ExifShortArray(ExifTag.HalftoneHints);
case ExifTagValue.SampleFormat:
return new ExifShortArray(ExifTag.SampleFormat);
case ExifTagValue.TransferRange:
return new ExifShortArray(ExifTag.TransferRange);
case ExifTagValue.DefaultImageColor:
return new ExifShortArray(ExifTag.DefaultImageColor);
case ExifTagValue.JPEGLosslessPredictors:
return new ExifShortArray(ExifTag.JPEGLosslessPredictors);
case ExifTagValue.JPEGPointTransforms:
return new ExifShortArray(ExifTag.JPEGPointTransforms);
case ExifTagValue.YCbCrSubsampling:
return new ExifShortArray(ExifTag.YCbCrSubsampling);
case ExifTagValue.CFARepeatPatternDim:
return new ExifShortArray(ExifTag.CFARepeatPatternDim);
case ExifTagValue.IntergraphPacketData:
return new ExifShortArray(ExifTag.IntergraphPacketData);
case ExifTagValue.ISOSpeedRatings:
return new ExifShortArray(ExifTag.ISOSpeedRatings);
case ExifTagValue.SubjectArea:
return new ExifShortArray(ExifTag.SubjectArea);
case ExifTagValue.SubjectLocation:
return new ExifShortArray(ExifTag.SubjectLocation);
case ExifTagValue.ShutterSpeedValue: return new ExifSignedRational(ExifTag.ShutterSpeedValue);
case ExifTagValue.BrightnessValue: return new ExifSignedRational(ExifTag.BrightnessValue);
case ExifTagValue.ExposureBiasValue: return new ExifSignedRational(ExifTag.ExposureBiasValue);
case ExifTagValue.AmbientTemperature: return new ExifSignedRational(ExifTag.AmbientTemperature);
case ExifTagValue.WaterDepth: return new ExifSignedRational(ExifTag.WaterDepth);
case ExifTagValue.CameraElevationAngle: return new ExifSignedRational(ExifTag.CameraElevationAngle);
case ExifTagValue.ShutterSpeedValue:
return new ExifSignedRational(ExifTag.ShutterSpeedValue);
case ExifTagValue.BrightnessValue:
return new ExifSignedRational(ExifTag.BrightnessValue);
case ExifTagValue.ExposureBiasValue:
return new ExifSignedRational(ExifTag.ExposureBiasValue);
case ExifTagValue.AmbientTemperature:
return new ExifSignedRational(ExifTag.AmbientTemperature);
case ExifTagValue.WaterDepth:
return new ExifSignedRational(ExifTag.WaterDepth);
case ExifTagValue.CameraElevationAngle:
return new ExifSignedRational(ExifTag.CameraElevationAngle);
case ExifTagValue.Decode: return new ExifSignedRationalArray(ExifTag.Decode);
case ExifTagValue.Decode:
return new ExifSignedRationalArray(ExifTag.Decode);
case ExifTagValue.ImageDescription: return new ExifString(ExifTag.ImageDescription);
case ExifTagValue.Make: return new ExifString(ExifTag.Make);
case ExifTagValue.Model: return new ExifString(ExifTag.Model);
case ExifTagValue.Software: return new ExifString(ExifTag.Software);
case ExifTagValue.DateTime: return new ExifString(ExifTag.DateTime);
case ExifTagValue.Artist: return new ExifString(ExifTag.Artist);
case ExifTagValue.HostComputer: return new ExifString(ExifTag.HostComputer);
case ExifTagValue.Copyright: return new ExifString(ExifTag.Copyright);
case ExifTagValue.DocumentName: return new ExifString(ExifTag.DocumentName);
case ExifTagValue.PageName: return new ExifString(ExifTag.PageName);
case ExifTagValue.InkNames: return new ExifString(ExifTag.InkNames);
case ExifTagValue.TargetPrinter: return new ExifString(ExifTag.TargetPrinter);
case ExifTagValue.ImageID: return new ExifString(ExifTag.ImageID);
case ExifTagValue.MDLabName: return new ExifString(ExifTag.MDLabName);
case ExifTagValue.MDSampleInfo: return new ExifString(ExifTag.MDSampleInfo);
case ExifTagValue.MDPrepDate: return new ExifString(ExifTag.MDPrepDate);
case ExifTagValue.MDPrepTime: return new ExifString(ExifTag.MDPrepTime);
case ExifTagValue.MDFileUnits: return new ExifString(ExifTag.MDFileUnits);
case ExifTagValue.SEMInfo: return new ExifString(ExifTag.SEMInfo);
case ExifTagValue.SpectralSensitivity: return new ExifString(ExifTag.SpectralSensitivity);
case ExifTagValue.DateTimeOriginal: return new ExifString(ExifTag.DateTimeOriginal);
case ExifTagValue.DateTimeDigitized: return new ExifString(ExifTag.DateTimeDigitized);
case ExifTagValue.SubsecTime: return new ExifString(ExifTag.SubsecTime);
case ExifTagValue.SubsecTimeOriginal: return new ExifString(ExifTag.SubsecTimeOriginal);
case ExifTagValue.SubsecTimeDigitized: return new ExifString(ExifTag.SubsecTimeDigitized);
case ExifTagValue.RelatedSoundFile: return new ExifString(ExifTag.RelatedSoundFile);
case ExifTagValue.FaxSubaddress: return new ExifString(ExifTag.FaxSubaddress);
case ExifTagValue.OffsetTime: return new ExifString(ExifTag.OffsetTime);
case ExifTagValue.OffsetTimeOriginal: return new ExifString(ExifTag.OffsetTimeOriginal);
case ExifTagValue.OffsetTimeDigitized: return new ExifString(ExifTag.OffsetTimeDigitized);
case ExifTagValue.SecurityClassification: return new ExifString(ExifTag.SecurityClassification);
case ExifTagValue.ImageHistory: return new ExifString(ExifTag.ImageHistory);
case ExifTagValue.ImageUniqueID: return new ExifString(ExifTag.ImageUniqueID);
case ExifTagValue.OwnerName: return new ExifString(ExifTag.OwnerName);
case ExifTagValue.SerialNumber: return new ExifString(ExifTag.SerialNumber);
case ExifTagValue.LensMake: return new ExifString(ExifTag.LensMake);
case ExifTagValue.LensModel: return new ExifString(ExifTag.LensModel);
case ExifTagValue.LensSerialNumber: return new ExifString(ExifTag.LensSerialNumber);
case ExifTagValue.GDALMetadata: return new ExifString(ExifTag.GDALMetadata);
case ExifTagValue.GDALNoData: return new ExifString(ExifTag.GDALNoData);
case ExifTagValue.GPSLatitudeRef: return new ExifString(ExifTag.GPSLatitudeRef);
case ExifTagValue.GPSLongitudeRef: return new ExifString(ExifTag.GPSLongitudeRef);
case ExifTagValue.GPSSatellites: return new ExifString(ExifTag.GPSSatellites);
case ExifTagValue.GPSStatus: return new ExifString(ExifTag.GPSStatus);
case ExifTagValue.GPSMeasureMode: return new ExifString(ExifTag.GPSMeasureMode);
case ExifTagValue.GPSSpeedRef: return new ExifString(ExifTag.GPSSpeedRef);
case ExifTagValue.GPSTrackRef: return new ExifString(ExifTag.GPSTrackRef);
case ExifTagValue.GPSImgDirectionRef: return new ExifString(ExifTag.GPSImgDirectionRef);
case ExifTagValue.GPSMapDatum: return new ExifString(ExifTag.GPSMapDatum);
case ExifTagValue.GPSDestLatitudeRef: return new ExifString(ExifTag.GPSDestLatitudeRef);
case ExifTagValue.GPSDestLongitudeRef: return new ExifString(ExifTag.GPSDestLongitudeRef);
case ExifTagValue.GPSDestBearingRef: return new ExifString(ExifTag.GPSDestBearingRef);
case ExifTagValue.GPSDestDistanceRef: return new ExifString(ExifTag.GPSDestDistanceRef);
case ExifTagValue.GPSDateStamp: return new ExifString(ExifTag.GPSDateStamp);
case ExifTagValue.ImageDescription:
return new ExifString(ExifTag.ImageDescription);
case ExifTagValue.Make:
return new ExifString(ExifTag.Make);
case ExifTagValue.Model:
return new ExifString(ExifTag.Model);
case ExifTagValue.Software:
return new ExifString(ExifTag.Software);
case ExifTagValue.DateTime:
return new ExifString(ExifTag.DateTime);
case ExifTagValue.Artist:
return new ExifString(ExifTag.Artist);
case ExifTagValue.HostComputer:
return new ExifString(ExifTag.HostComputer);
case ExifTagValue.Copyright:
return new ExifString(ExifTag.Copyright);
case ExifTagValue.DocumentName:
return new ExifString(ExifTag.DocumentName);
case ExifTagValue.PageName:
return new ExifString(ExifTag.PageName);
case ExifTagValue.InkNames:
return new ExifString(ExifTag.InkNames);
case ExifTagValue.TargetPrinter:
return new ExifString(ExifTag.TargetPrinter);
case ExifTagValue.ImageID:
return new ExifString(ExifTag.ImageID);
case ExifTagValue.MDLabName:
return new ExifString(ExifTag.MDLabName);
case ExifTagValue.MDSampleInfo:
return new ExifString(ExifTag.MDSampleInfo);
case ExifTagValue.MDPrepDate:
return new ExifString(ExifTag.MDPrepDate);
case ExifTagValue.MDPrepTime:
return new ExifString(ExifTag.MDPrepTime);
case ExifTagValue.MDFileUnits:
return new ExifString(ExifTag.MDFileUnits);
case ExifTagValue.SEMInfo:
return new ExifString(ExifTag.SEMInfo);
case ExifTagValue.SpectralSensitivity:
return new ExifString(ExifTag.SpectralSensitivity);
case ExifTagValue.DateTimeOriginal:
return new ExifString(ExifTag.DateTimeOriginal);
case ExifTagValue.DateTimeDigitized:
return new ExifString(ExifTag.DateTimeDigitized);
case ExifTagValue.SubsecTime:
return new ExifString(ExifTag.SubsecTime);
case ExifTagValue.SubsecTimeOriginal:
return new ExifString(ExifTag.SubsecTimeOriginal);
case ExifTagValue.SubsecTimeDigitized:
return new ExifString(ExifTag.SubsecTimeDigitized);
case ExifTagValue.RelatedSoundFile:
return new ExifString(ExifTag.RelatedSoundFile);
case ExifTagValue.FaxSubaddress:
return new ExifString(ExifTag.FaxSubaddress);
case ExifTagValue.OffsetTime:
return new ExifString(ExifTag.OffsetTime);
case ExifTagValue.OffsetTimeOriginal:
return new ExifString(ExifTag.OffsetTimeOriginal);
case ExifTagValue.OffsetTimeDigitized:
return new ExifString(ExifTag.OffsetTimeDigitized);
case ExifTagValue.SecurityClassification:
return new ExifString(ExifTag.SecurityClassification);
case ExifTagValue.ImageHistory:
return new ExifString(ExifTag.ImageHistory);
case ExifTagValue.ImageUniqueID:
return new ExifString(ExifTag.ImageUniqueID);
case ExifTagValue.OwnerName:
return new ExifString(ExifTag.OwnerName);
case ExifTagValue.SerialNumber:
return new ExifString(ExifTag.SerialNumber);
case ExifTagValue.LensMake:
return new ExifString(ExifTag.LensMake);
case ExifTagValue.LensModel:
return new ExifString(ExifTag.LensModel);
case ExifTagValue.LensSerialNumber:
return new ExifString(ExifTag.LensSerialNumber);
case ExifTagValue.GDALMetadata:
return new ExifString(ExifTag.GDALMetadata);
case ExifTagValue.GDALNoData:
return new ExifString(ExifTag.GDALNoData);
case ExifTagValue.GPSLatitudeRef:
return new ExifString(ExifTag.GPSLatitudeRef);
case ExifTagValue.GPSLongitudeRef:
return new ExifString(ExifTag.GPSLongitudeRef);
case ExifTagValue.GPSSatellites:
return new ExifString(ExifTag.GPSSatellites);
case ExifTagValue.GPSStatus:
return new ExifString(ExifTag.GPSStatus);
case ExifTagValue.GPSMeasureMode:
return new ExifString(ExifTag.GPSMeasureMode);
case ExifTagValue.GPSSpeedRef:
return new ExifString(ExifTag.GPSSpeedRef);
case ExifTagValue.GPSTrackRef:
return new ExifString(ExifTag.GPSTrackRef);
case ExifTagValue.GPSImgDirectionRef:
return new ExifString(ExifTag.GPSImgDirectionRef);
case ExifTagValue.GPSMapDatum:
return new ExifString(ExifTag.GPSMapDatum);
case ExifTagValue.GPSDestLatitudeRef:
return new ExifString(ExifTag.GPSDestLatitudeRef);
case ExifTagValue.GPSDestLongitudeRef:
return new ExifString(ExifTag.GPSDestLongitudeRef);
case ExifTagValue.GPSDestBearingRef:
return new ExifString(ExifTag.GPSDestBearingRef);
case ExifTagValue.GPSDestDistanceRef:
return new ExifString(ExifTag.GPSDestDistanceRef);
case ExifTagValue.GPSDateStamp:
return new ExifString(ExifTag.GPSDateStamp);
case ExifTagValue.FileSource: return new ExifByte(ExifTag.FileSource, ExifDataType.Undefined);
case ExifTagValue.SceneType: return new ExifByte(ExifTag.SceneType, ExifDataType.Undefined);
case ExifTagValue.FileSource:
return new ExifByte(ExifTag.FileSource, ExifDataType.Undefined);
case ExifTagValue.SceneType:
return new ExifByte(ExifTag.SceneType, ExifDataType.Undefined);
case ExifTagValue.JPEGTables: return new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined);
case ExifTagValue.OECF: return new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined);
case ExifTagValue.ExifVersion: return new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined);
case ExifTagValue.ComponentsConfiguration: return new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined);
case ExifTagValue.MakerNote: return new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined);
case ExifTagValue.UserComment: return new ExifByteArray(ExifTag.UserComment, ExifDataType.Undefined);
case ExifTagValue.FlashpixVersion: return new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined);
case ExifTagValue.SpatialFrequencyResponse: return new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined);
case ExifTagValue.SpatialFrequencyResponse2: return new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined);
case ExifTagValue.Noise: return new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined);
case ExifTagValue.CFAPattern: return new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined);
case ExifTagValue.DeviceSettingDescription: return new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined);
case ExifTagValue.ImageSourceData: return new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined);
case ExifTagValue.GPSProcessingMethod: return new ExifByteArray(ExifTag.GPSProcessingMethod, ExifDataType.Undefined);
case ExifTagValue.GPSAreaInformation: return new ExifByteArray(ExifTag.GPSAreaInformation, ExifDataType.Undefined);
case ExifTagValue.JPEGTables:
return new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined);
case ExifTagValue.OECF:
return new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined);
case ExifTagValue.ExifVersion:
return new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined);
case ExifTagValue.ComponentsConfiguration:
return new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined);
case ExifTagValue.MakerNote:
return new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined);
case ExifTagValue.FlashpixVersion:
return new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined);
case ExifTagValue.SpatialFrequencyResponse:
return new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined);
case ExifTagValue.SpatialFrequencyResponse2:
return new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined);
case ExifTagValue.Noise:
return new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined);
case ExifTagValue.CFAPattern:
return new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined);
case ExifTagValue.DeviceSettingDescription:
return new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined);
case ExifTagValue.ImageSourceData:
return new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined);
default: return null;
case ExifTagValue.XPTitle:
return new ExifUcs2String(ExifTag.XPTitle);
case ExifTagValue.XPComment:
return new ExifUcs2String(ExifTag.XPComment);
case ExifTagValue.XPAuthor:
return new ExifUcs2String(ExifTag.XPAuthor);
case ExifTagValue.XPKeywords:
return new ExifUcs2String(ExifTag.XPKeywords);
case ExifTagValue.XPSubject:
return new ExifUcs2String(ExifTag.XPSubject);
case ExifTagValue.UserComment:
return new ExifEncodedString(ExifTag.UserComment);
case ExifTagValue.GPSProcessingMethod:
return new ExifEncodedString(ExifTag.GPSProcessingMethod);
case ExifTagValue.GPSAreaInformation:
return new ExifEncodedString(ExifTag.GPSAreaInformation);
default:
return null;
}
}
}

3
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs

@ -39,10 +39,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
using var memoryStream = new MemoryStream(this.jpegBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream);
var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true });
using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true });
var scanDecoder = new HuffmanScanDecoder(bufferedStream, new NoopSpectralConverter(), cancellationToken: default);
decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default);
decoder.Dispose();
}
// We want to test only stream parsing and scan decoding, we don't need to convert spectral data to actual pixels

68
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs

@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata;
@ -300,5 +301,72 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
});
Assert.Null(ex);
}
[Fact]
public void EncodedStringTags_WriteAndRead()
{
using var memoryStream = new MemoryStream();
using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora)))
{
var exif = new ExifProfile();
exif.SetValue(ExifTag.GPSDateStamp, "2022-01-06");
exif.SetValue(ExifTag.XPTitle, "A bit of test metadata for image title");
exif.SetValue(ExifTag.XPComment, "A bit of test metadata for image comment");
exif.SetValue(ExifTag.XPAuthor, "Dan Petitt");
exif.SetValue(ExifTag.XPKeywords, "Keyword1;Keyword2");
exif.SetValue(ExifTag.XPSubject, "This is a subject");
// exif.SetValue(ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.JIS, "ビッ"));
exif.SetValue(ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.JIS, "eng comment text (JIS)"));
exif.SetValue(ExifTag.GPSProcessingMethod, new EncodedString(EncodedString.CharacterCode.ASCII, "GPS processing method (ASCII)"));
exif.SetValue(ExifTag.GPSAreaInformation, new EncodedString(EncodedString.CharacterCode.Unicode, "GPS area info (Unicode)"));
image.Metadata.ExifProfile = exif;
image.Save(memoryStream, new JpegEncoder());
}
memoryStream.Seek(0, SeekOrigin.Begin);
using (var image = Image.Load(memoryStream))
{
ExifProfile exif = image.Metadata.ExifProfile;
VerifyEncodedStrings(exif);
}
}
[Fact]
public void EncodedStringTags_Read()
{
using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora_EncodedStrings)))
{
ExifProfile exif = image.Metadata.ExifProfile;
VerifyEncodedStrings(exif);
}
}
private static void VerifyEncodedStrings(ExifProfile exif)
{
Assert.NotNull(exif);
Assert.Equal("2022-01-06", exif.GetValue(ExifTag.GPSDateStamp).Value);
Assert.Equal("A bit of test metadata for image title", exif.GetValue(ExifTag.XPTitle).Value);
Assert.Equal("A bit of test metadata for image comment", exif.GetValue(ExifTag.XPComment).Value);
Assert.Equal("Dan Petitt", exif.GetValue(ExifTag.XPAuthor).Value);
Assert.Equal("Keyword1;Keyword2", exif.GetValue(ExifTag.XPKeywords).Value);
Assert.Equal("This is a subject", exif.GetValue(ExifTag.XPSubject).Value);
Assert.Equal("eng comment text (JIS)", exif.GetValue(ExifTag.UserComment).Value.Text);
Assert.Equal(EncodedString.CharacterCode.JIS, exif.GetValue(ExifTag.UserComment).Value.Code);
Assert.Equal("GPS processing method (ASCII)", exif.GetValue(ExifTag.GPSProcessingMethod).Value.Text);
Assert.Equal(EncodedString.CharacterCode.ASCII, exif.GetValue(ExifTag.GPSProcessingMethod).Value.Code);
Assert.Equal("GPS area info (Unicode)", (string)exif.GetValue(ExifTag.GPSAreaInformation).Value);
Assert.Equal(EncodedString.CharacterCode.Unicode, exif.GetValue(ExifTag.GPSAreaInformation).Value.Code);
}
}
}

4
tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Calculating data from ImageSharp
byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var ms = new MemoryStream(sourceBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, ms);
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Calculating data from ImageSharp
byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var ms = new MemoryStream(sourceBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, ms);

2
tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Decoding
using var converter = new SpectralConverter<TPixel>(Configuration.Default);
var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
var scanDecoder = new HuffmanScanDecoder(bufferedStream, converter, cancellationToken: default);
decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default);

40
tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

@ -167,18 +167,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder);
}
[Theory]
[WithFile(TestPatternOpaque, PixelTypes.Rgba32)]
[WithFile(TestPatternOpaqueSmall, PixelTypes.Rgba32)]
public void Encode_Lossless_WorksWithTestPattern<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossless };
image.VerifyEncoder(provider, "webp", string.Empty, encoder);
}
[Fact]
public void Encode_Lossless_OneByOnePixel_Works()
{
@ -279,6 +267,34 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality));
}
[Theory]
[WithFile(TestImages.Png.Transparency, PixelTypes.Rgba32, false)]
[WithFile(TestImages.Png.Transparency, PixelTypes.Rgba32, true)]
public void Encode_Lossy_WithAlpha_Works<TPixel>(TestImageProvider<TPixel> provider, bool compressed)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebpEncoder()
{
FileFormat = WebpFileFormatType.Lossy,
UseAlphaCompression = compressed
};
using Image<TPixel> image = provider.GetImage();
image.VerifyEncoder(provider, "webp", $"with_alpha_compressed_{compressed}", encoder, ImageComparer.Tolerant(0.04f));
}
[Theory]
[WithFile(TestPatternOpaque, PixelTypes.Rgba32)]
[WithFile(TestPatternOpaqueSmall, PixelTypes.Rgba32)]
public void Encode_Lossless_WorksWithTestPattern<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossless };
image.VerifyEncoder(provider, "webp", string.Empty, encoder);
}
[Theory]
[WithFile(TestPatternOpaque, PixelTypes.Rgba32)]
[WithFile(TestPatternOpaqueSmall, PixelTypes.Rgba32)]

5
tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

@ -51,6 +51,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
{ ExifTag.ImageDescription, "ImageDescription" },
{ ExifTag.ExposureTime, new Rational(1.0 / 1600.0) },
{ ExifTag.Model, "Model" },
{ ExifTag.XPAuthor, "The XPAuthor text" },
{ ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.Unicode, "The Unicode text") },
{ ExifTag.GPSAreaInformation, new EncodedString("Default constructor text (GPSAreaInformation)") },
};
[Theory]
@ -504,7 +507,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
public void IfdStructure()
{
var exif = new ExifProfile();
exif.SetValue(ExifTag.XPAuthor, Encoding.GetEncoding("UCS-2").GetBytes("Dan Petitt"));
exif.SetValue(ExifTag.XPAuthor, "Dan Petitt");
Span<byte> actualBytes = exif.ToByteArray();

65
tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Text;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using Xunit;
@ -23,11 +25,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.XMP },
{ ExifTag.CFAPattern2 },
{ ExifTag.TIFFEPStandardID },
{ ExifTag.XPTitle },
{ ExifTag.XPComment },
{ ExifTag.XPAuthor },
{ ExifTag.XPKeywords },
{ ExifTag.XPSubject },
{ ExifTag.GPSVersionID },
};
@ -295,7 +292,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.GPSDestLongitudeRef },
{ ExifTag.GPSDestBearingRef },
{ ExifTag.GPSDestDistanceRef },
{ ExifTag.GPSDateStamp }
{ ExifTag.GPSDateStamp },
};
public static TheoryData<ExifTag> UndefinedTags => new TheoryData<ExifTag>
@ -311,7 +308,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.ExifVersion },
{ ExifTag.ComponentsConfiguration },
{ ExifTag.MakerNote },
{ ExifTag.UserComment },
{ ExifTag.FlashpixVersion },
{ ExifTag.SpatialFrequencyResponse },
{ ExifTag.SpatialFrequencyResponse2 },
@ -319,10 +315,24 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.CFAPattern },
{ ExifTag.DeviceSettingDescription },
{ ExifTag.ImageSourceData },
};
public static TheoryData<ExifTag> EncodedStringTags => new TheoryData<ExifTag>
{
{ ExifTag.UserComment },
{ ExifTag.GPSProcessingMethod },
{ ExifTag.GPSAreaInformation }
};
public static TheoryData<ExifTag> Ucs2StringTags => new TheoryData<ExifTag>
{
{ ExifTag.XPTitle },
{ ExifTag.XPComment },
{ ExifTag.XPAuthor },
{ ExifTag.XPKeywords },
{ ExifTag.XPSubject },
};
[Theory]
[MemberData(nameof(ByteTags))]
public void ExifByteTests(ExifTag tag)
@ -593,5 +603,46 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
var typed = (ExifByteArray)value;
Assert.Equal(expected, typed.Value);
}
[Theory]
[MemberData(nameof(EncodedStringTags))]
public void ExifEncodedStringTests(ExifTag tag)
{
foreach (object code in Enum.GetValues(typeof(EncodedString.CharacterCode)))
{
var charCode = (EncodedString.CharacterCode)code;
Assert.Equal(ExifEncodedStringHelpers.CharacterCodeBytesLength, ExifEncodedStringHelpers.GetCodeBytes(charCode).Length);
const string expectedText = "test string";
var expected = new EncodedString(charCode, expectedText);
ExifValue value = ExifValues.Create(tag);
Assert.False(value.TrySetValue(123));
Assert.True(value.TrySetValue(expected));
var typed = (ExifEncodedString)value;
Assert.Equal(expected, typed.Value);
Assert.Equal(expectedText, (string)typed.Value);
Assert.Equal(charCode, typed.Value.Code);
}
}
[Theory]
[MemberData(nameof(Ucs2StringTags))]
public void ExifUcs2StringTests(ExifTag tag)
{
const string expected = "Dan Petitt";
ExifValue value = ExifValues.Create(tag);
Assert.False(value.TrySetValue(123));
Assert.True(value.TrySetValue(expected));
var typed = (ExifUcs2String)value;
Assert.Equal(expected, typed.Value);
Assert.True(value.TrySetValue(Encoding.GetEncoding("UCS-2").GetBytes(expected)));
Assert.Equal(expected, typed.Value);
}
}
}

2
tests/ImageSharp.Tests/TestImages.cs

@ -15,6 +15,7 @@ namespace SixLabors.ImageSharp.Tests
{
public static class Png
{
public const string Transparency = "Png/transparency.png";
public const string P1 = "Png/pl.png";
public const string Pd = "Png/pd.png";
public const string Blur = "Png/blur.png";
@ -191,6 +192,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Exif = "Jpg/baseline/exif.jpg";
public const string Floorplan = "Jpg/baseline/Floorplan.jpg";
public const string Calliphora = "Jpg/baseline/Calliphora.jpg";
public const string Calliphora_EncodedStrings = "Jpg/baseline/Calliphora_encoded_strings.jpg";
public const string Ycck = "Jpg/baseline/ycck.jpg";
public const string Turtle420 = "Jpg/baseline/turtle.jpg";
public const string GammaDalaiLamaGray = "Jpg/baseline/gamma_dalai_lama_gray.jpg";

3
tests/Images/Input/Jpg/baseline/Calliphora_encoded_strings.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:59f76d2935a619d128a63d6bfcd5ce9feec492a7f5175327e47554c90b4ec242
size 258081

3
tests/Images/Input/Png/transparency.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:843bea4db378f52935e2f19f60d289df8ebe20ddde3977c63225f1d58a10bd62
size 48119
Loading…
Cancel
Save