Browse Source

Added CICP metadata handling to PNG de/encoder

pull/2592/head
Tammo Hinrichs 3 years ago
parent
commit
b12badadee
  1. 6
      src/ImageSharp/Formats/Png/PngChunkType.cs
  2. 33
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 21
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  4. 1
      tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

6
src/ImageSharp/Formats/Png/PngChunkType.cs

@ -140,6 +140,12 @@ internal enum PngChunkType : uint
/// <remarks>cHRM (Single)</remarks>
Chroma = 0x6348524d,
/// <summary>
/// If this chunk is present, it specifies the color space, transfer function, matrix coefficients of the image
/// using the code points specified in [ITU-T-H.273]
/// </summary>
Cicp = 0x63494350,
/// <summary>
/// This chunk is an ancillary chunk as defined in the PNG Specification.
/// It must appear before the first IDAT chunk within a valid PNG stream.

33
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -16,6 +16,7 @@ using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.CICP;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
@ -183,6 +184,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngChunkType.Gamma:
ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Cicp:
ReadCicpChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.FrameControl:
frameCount++;
if (frameCount == this.maxFrames)
@ -352,6 +356,15 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Cicp:
if (this.colorMetadataOnly)
{
this.SkipChunkDataAndCrc(chunk);
break;
}
ReadCicpChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.FrameControl:
++frameCount;
if (frameCount == this.maxFrames)
@ -1392,6 +1405,26 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
return false;
}
/// <summary>
/// Reads the CICP color profile chunk.
/// </summary>
/// <param name="metadata">The metadata.</param>
/// <param name="data">The bytes containing the profile.</param>
private static void ReadCicpChunk(ImageMetadata metadata, ReadOnlySpan<byte> data)
{
if (data.Length < 4)
{
// Ignore invalid cICP chunks.
return;
}
byte colorPrimaries = data[0];
byte transferFunction = data[1];
byte matrixCoefficients = data[2];
bool? fullRange = data[3] == 1 ? true : data[3] == 0 ? false : null;
metadata.CicpProfile = new CicpProfile(colorPrimaries, transferFunction, matrixCoefficients, fullRange);
}
/// <summary>
/// Reads exif data encoded into a text chunk with the name "raw profile type exif".
/// This method was used by ImageMagick, exiftool, exiv2, digiKam, etc, before the

21
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -166,6 +166,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
this.WriteHeaderChunk(stream);
this.WriteGammaChunk(stream);
this.WriteCicpChunk(stream, metadata);
this.WriteColorProfileChunk(stream, metadata);
this.WritePaletteChunk(stream, quantized);
this.WriteTransparencyChunk(stream, pngMetadata);
@ -773,6 +774,26 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
this.WriteChunk(stream, PngChunkType.InternationalText, payload);
}
/// <summary>
/// Writes the CICP profile chunk
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="metaData">The image meta data.</param>
private void WriteCicpChunk(Stream stream, ImageMetadata metaData)
{
if (metaData.CicpProfile is null)
{
return;
}
Span<byte> outputBytes = this.chunkDataBuffer.Span[..4];
outputBytes[0] = (byte)metaData.CicpProfile.ColorPrimaries;
outputBytes[1] = (byte)metaData.CicpProfile.TransferCharacteristics;
outputBytes[2] = (byte)metaData.CicpProfile.MatrixCoefficients;
outputBytes[3] = (byte)(metaData.CicpProfile.FullRange ? 1 : 0);
this.WriteChunk(stream, PngChunkType.Cicp, outputBytes);
}
/// <summary>
/// Writes the color profile chunk.
/// </summary>

1
tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

@ -29,6 +29,7 @@ public class PngChunkTypeTests
Assert.Equal(PngChunkType.Background, GetType("bKGD"));
Assert.Equal(PngChunkType.EmbeddedColorProfile, GetType("iCCP"));
Assert.Equal(PngChunkType.StandardRgbColourSpace, GetType("sRGB"));
Assert.Equal(PngChunkType.Cicp, GetType("cICP"));
Assert.Equal(PngChunkType.SignificantBits, GetType("sBIT"));
Assert.Equal(PngChunkType.Histogram, GetType("hIST"));
Assert.Equal(PngChunkType.SuggestedPalette, GetType("sPLT"));

Loading…
Cancel
Save