Browse Source

Parse HIF metadata

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
d6dc2b1473
  1. 22
      src/ImageSharp/Formats/Heic/Heic4CharCode.cs
  2. 6
      src/ImageSharp/Formats/Heic/Heic4CharCode.tt
  3. 22
      src/ImageSharp/Formats/Heic/HeicDecoderCore.cs
  4. 8
      src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs
  5. 4
      tests/ImageSharp.Tests/Formats/Heic/HeicDecoderTests.cs
  6. 2
      tests/ImageSharp.Tests/TestImages.cs

22
src/ImageSharp/Formats/Heic/Heic4CharCode.cs

@ -124,15 +124,30 @@ public enum Heic4CharCode : uint
ipma = 0x69706D61U,
/// <summary>
/// High Efficient Image Coding.
/// High Efficient Image Coding brand.
/// </summary>
heic = 0x68656963U,
/// <summary>
/// High Efficient Image Coding brand (legacy name).
/// </summary>
heix = 0x68656978U,
/// <summary>
/// High Efficient File brand.
/// </summary>
hif1 = 0x68696631U,
/// <summary>
/// High Efficiency Coding tile.
/// </summary>
hvc1 = 0x68766331U,
/// <summary>
/// Legacy JPEG coded tile.
/// </summary>
jpeg = 0x6A706567U,
/// <summary>
/// Data Information.
/// </summary>
@ -203,4 +218,9 @@ public enum Heic4CharCode : uint
/// </summary>
pict = 0x70696374U,
/// <summary>
/// Unique Identifier.
/// </summary>
uuid = 0x75756964U,
}

6
src/ImageSharp/Formats/Heic/Heic4CharCode.tt

@ -29,8 +29,11 @@
"udes", "User Description",
"ipco", "Item Property Container",
"ipma", "Item Property Association",
"heic", "High Efficient Image Coding",
"heic", "High Efficient Image Coding brand",
"heix", "High Efficient Image Coding brand (legacy name)",
"hif1", "High Efficient File brand",
"hvc1", "High Efficiency Coding tile",
"jpeg", "Legacy JPEG coded tile",
"dinf", "Data Information",
"grpl", "Group list",
"hdlr", "Handler",
@ -45,6 +48,7 @@
"mime", "MIME type",
"uri ", "URI",
"pict", "Picture handler type",
"uuid", "Unique Identifier",
};
#>

22
src/ImageSharp/Formats/Heic/HeicDecoderCore.cs

@ -202,11 +202,12 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
case Heic4CharCode.idat:
case Heic4CharCode.grpl:
case Heic4CharCode.ipro:
case Heic4CharCode.uuid:
// Silently skip these boxes.
SkipBox(stream, length);
break;
default:
throw new ImageFormatException($"Unknown metadata box type of '{Enum.GetName(boxType)}'");
throw new ImageFormatException($"Unknown metadata box type of '{PrettyPrint(boxType)}'");
}
}
}
@ -373,8 +374,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
else
{
string enumName = Enum.GetName(containerType) ?? ((uint)containerType).ToString("X", CultureInfo.InvariantCulture);
throw new ImageFormatException($"Unknown container type in property box of '{enumName}'");
throw new ImageFormatException($"Unknown container type in property box of '{PrettyPrint(containerType)}'");
}
}
}
@ -424,8 +424,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// TODO: Implement
break;
default:
string enumName = Enum.GetName(itemType) ?? ((uint)itemType).ToString("X", CultureInfo.InvariantCulture);
throw new ImageFormatException($"Unknown item type in property box of '{enumName}'");
throw new ImageFormatException($"Unknown item type in property box of '{PrettyPrint(itemType)}'");
}
}
}
@ -601,4 +600,17 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
Span<byte> bytes = span[..span.IndexOf((byte)0)];
return Encoding.UTF8.GetString(bytes);
}
private static string PrettyPrint(Heic4CharCode code)
{
string? pretty = Enum.GetName(code);
if (string.IsNullOrEmpty(pretty))
{
Span<byte> bytes = stackalloc byte[4];
BinaryPrimitives.WriteUInt32BigEndian(bytes, (uint)code);
pretty = Encoding.ASCII.GetString(bytes);
}
return pretty;
}
}

8
src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs

@ -21,7 +21,9 @@ public sealed class HeicImageFormatDetector : IImageFormatDetector
return format != null;
}
private static bool IsSupportedFileFormat(ReadOnlySpan<byte> header) =>
BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heic4CharCode.ftyp &&
BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8)) == (uint)Heic4CharCode.heic;
private static bool IsSupportedFileFormat(ReadOnlySpan<byte> header) {
bool hasFtyp = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heic4CharCode.ftyp;
uint brand = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8));
return hasFtyp && (brand == (uint)Heic4CharCode.heic || brand == (uint)Heic4CharCode.heix);
}
}

4
tests/ImageSharp.Tests/Formats/Heic/HeicDecoderTests.cs

@ -13,6 +13,7 @@ public class HeicDecoderTests
[Theory]
[InlineData(TestImages.Heic.Image1)]
[InlineData(TestImages.Heic.Sample640x427)]
[InlineData(TestImages.Heic.FujiFilmHif)]
public void Identify(string imagePath)
{
TestFile testFile = TestFile.Create(imagePath);
@ -27,8 +28,7 @@ public class HeicDecoderTests
}
[Theory]
[WithFile(TestImages.Heic.Image1, PixelTypes.Rgba32)]
[WithFile(TestImages.Heic.Sample640x427, PixelTypes.Rgba32)]
[WithFile(TestImages.Heic.FujiFilmHif, PixelTypes.Rgba32)]
public void Decode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{

2
tests/ImageSharp.Tests/TestImages.cs

@ -1112,6 +1112,6 @@ public static class TestImages
public const string Image3 = "Heic/image3.heic";
public const string Image4 = "Heic/image4.heic";
public const string Sample640x427 = "Heic/dwsample-heic-640.heic";
public const string FujiFilm-HIF = "Heic/IMG-20230508-0053.hif";
public const string FujiFilmHif = "Heic/IMG-20230508-0053.hif";
}
}

Loading…
Cancel
Save