📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

143 lines
4.9 KiB

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers.Binary;
namespace SixLabors.ImageSharp.Formats.Png.Chunks;
/// <summary>
/// Represents the png header chunk.
/// </summary>
internal readonly struct PngHeader
{
public const int Size = 13;
public PngHeader(
int width,
int height,
byte bitDepth,
PngColorType colorType,
byte compressionMethod,
byte filterMethod,
PngInterlaceMode interlaceMethod)
{
this.Width = width;
this.Height = height;
this.BitDepth = bitDepth;
this.ColorType = colorType;
this.CompressionMethod = compressionMethod;
this.FilterMethod = filterMethod;
this.InterlaceMethod = interlaceMethod;
}
/// <summary>
/// Gets the dimension in x-direction of the image in pixels.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the dimension in y-direction of the image in pixels.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets the bit depth.
/// Bit depth is a single-byte integer giving the number of bits per sample
/// or per palette index (not per pixel). Valid values are 1, 2, 4, 8, and 16,
/// although not all values are allowed for all color types.
/// </summary>
public byte BitDepth { get; }
/// <summary>
/// Gets the color type.
/// Color type is a integer that describes the interpretation of the
/// image data. Color type codes represent sums of the following values:
/// 1 (palette used), 2 (color used), and 4 (alpha channel used).
/// </summary>
public PngColorType ColorType { get; }
/// <summary>
/// Gets the compression method.
/// Indicates the method used to compress the image data. At present,
/// only compression method 0 (deflate/inflate compression with a sliding
/// window of at most 32768 bytes) is defined.
/// </summary>
public byte CompressionMethod { get; }
/// <summary>
/// Gets the preprocessing method.
/// Indicates the preprocessing method applied to the image
/// data before compression. At present, only filter method 0
/// (adaptive filtering with five basic filter types) is defined.
/// </summary>
public byte FilterMethod { get; }
/// <summary>
/// Gets the transmission order.
/// Indicates the transmission order of the image data.
/// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace).
/// </summary>
public PngInterlaceMode InterlaceMethod { get; }
/// <summary>
/// Validates the png header.
/// </summary>
/// <exception cref="NotSupportedException">
/// Thrown if the image does pass validation.
/// </exception>
public void Validate()
{
if (!PngConstants.ColorTypes.TryGetValue(this.ColorType, out byte[] supportedBitDepths))
{
throw new NotSupportedException($"Invalid or unsupported color type. Was '{this.ColorType}'.");
}
if (supportedBitDepths.AsSpan().IndexOf(this.BitDepth) == -1)
{
throw new NotSupportedException($"Invalid or unsupported bit depth. Was '{this.BitDepth}'.");
}
if (this.FilterMethod != 0)
{
throw new NotSupportedException($"Invalid filter method. Expected 0. Was '{this.FilterMethod}'.");
}
// The png specification only defines 'None' and 'Adam7' as interlaced methods.
if (this.InterlaceMethod is not PngInterlaceMode.None and not PngInterlaceMode.Adam7)
{
throw new NotSupportedException($"Invalid interlace method. Expected 'None' or 'Adam7'. Was '{this.InterlaceMethod}'.");
}
}
/// <summary>
/// Writes the header to the given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
public void WriteTo(Span<byte> buffer)
{
BinaryPrimitives.WriteInt32BigEndian(buffer[..4], this.Width);
BinaryPrimitives.WriteInt32BigEndian(buffer.Slice(4, 4), this.Height);
buffer[8] = this.BitDepth;
buffer[9] = (byte)this.ColorType;
buffer[10] = this.CompressionMethod;
buffer[11] = this.FilterMethod;
buffer[12] = (byte)this.InterlaceMethod;
}
/// <summary>
/// Parses the PngHeader from the given data buffer.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed PngHeader.</returns>
public static PngHeader Parse(ReadOnlySpan<byte> data)
=> new(
width: BinaryPrimitives.ReadInt32BigEndian(data[..4]),
height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
bitDepth: data[8],
colorType: (PngColorType)data[9],
compressionMethod: data[10],
filterMethod: data[11],
interlaceMethod: (PngInterlaceMode)data[12]);
}