From 057edd8ddd010f3f83446568c54c6565a9c4a171 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 5 Jan 2024 20:05:06 +1000 Subject: [PATCH] Use the new metadata --- src/ImageSharp/Color/Color.cs | 4 +- src/ImageSharp/Formats/Png/PngBitDepth.cs | 2 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 104 ++++++++++-------- .../PixelFormats/PixelComponentBitDepth.cs | 50 +++++++++ .../PixelFormats/PixelComponentPrecision.cs | 70 ------------ src/ImageSharp/PixelFormats/PixelTypeInfo.cs | 3 +- .../Formats/Png/PngEncoderTests.cs | 20 ++-- 7 files changed, 125 insertions(+), 128 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelComponentPrecision.cs diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index ec19a86eb9..e61abf86fc 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -94,7 +94,7 @@ public readonly partial struct Color : IEquatable { // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentPrecision.Float) + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { return new(pixel.ToScaledVector4()); } @@ -118,7 +118,7 @@ public readonly partial struct Color : IEquatable // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentPrecision.Float) + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { for (int i = 0; i < destination.Length; i++) { diff --git a/src/ImageSharp/Formats/Png/PngBitDepth.cs b/src/ImageSharp/Formats/Png/PngBitDepth.cs index 452839d1d4..a5cd2026b2 100644 --- a/src/ImageSharp/Formats/Png/PngBitDepth.cs +++ b/src/ImageSharp/Formats/Png/PngBitDepth.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. // Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it. diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 8bf8be2adc..aa3603cfbb 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1466,23 +1466,48 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Use options, then check metadata, if nothing set there then we suggest // a sensible default based upon the pixel format. - this.colorType = encoder.ColorType ?? pngMetadata.ColorType ?? SuggestColorType(); - if (!encoder.FilterMethod.HasValue) + PngColorType? colorType = encoder.ColorType ?? pngMetadata.ColorType; + byte? bits = (byte?)(encoder.BitDepth ?? pngMetadata.BitDepth); + + if (colorType is null || bits is null) { - // Specification recommends default filter method None for paletted images and Paeth for others. - this.filterMethod = this.colorType is PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth; + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + PixelComponentInfo? componentInfo = info.ComponentInfo; + + colorType ??= SuggestColorType(in info); + + if (bits is null) + { + // TODO: Update once we stop abusing PixelTypeInfo in decoders. + if (componentInfo.HasValue) + { + PixelComponentInfo c = componentInfo.Value; + bits = (byte)SuggestBitDepth(in c); + } + else + { + bits = (byte)PngBitDepth.Bit8; + } + } } // Ensure bit depth and color type are a supported combination. // Bit8 is the only bit depth supported by all color types. - byte bits = (byte)(encoder.BitDepth ?? pngMetadata.BitDepth ?? SuggestBitDepth()); - byte[] validBitDepths = PngConstants.ColorTypes[this.colorType]; + byte[] validBitDepths = PngConstants.ColorTypes[colorType.Value]; if (Array.IndexOf(validBitDepths, bits) == -1) { bits = (byte)PngBitDepth.Bit8; } - this.bitDepth = bits; + this.colorType = colorType.Value; + this.bitDepth = bits.Value; + + if (!encoder.FilterMethod.HasValue) + { + // Specification recommends default filter method None for paletted images and Paeth for others. + this.filterMethod = this.colorType is PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth; + } + use16Bit = bits == (byte)PngBitDepth.Bit16; bytesPerPixel = CalculateBytesPerPixel(this.colorType, use16Bit); @@ -1611,53 +1636,44 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable /// /// Returns a suggested for the given - /// This is not exhaustive but covers many common pixel formats. /// + /// The pixel type info. /// The type of pixel format. - private static PngColorType SuggestColorType() + private static PngColorType SuggestColorType(in PixelTypeInfo info) where TPixel : unmanaged, IPixel - => default(TPixel) switch - { - A8 => PngColorType.GrayscaleWithAlpha, - Argb32 => PngColorType.RgbWithAlpha, - Bgr24 => PngColorType.Rgb, - Bgra32 => PngColorType.RgbWithAlpha, - L8 => PngColorType.Grayscale, - L16 => PngColorType.Grayscale, - La16 => PngColorType.GrayscaleWithAlpha, - La32 => PngColorType.GrayscaleWithAlpha, - Rgb24 => PngColorType.Rgb, - Rgba32 => PngColorType.RgbWithAlpha, - Rgb48 => PngColorType.Rgb, - Rgba64 => PngColorType.RgbWithAlpha, - RgbaVector => PngColorType.RgbWithAlpha, - _ => PngColorType.RgbWithAlpha + { + if (info.AlphaRepresentation == PixelAlphaRepresentation.None) + { + return info.ColorType switch + { + PixelColorType.Grayscale => PngColorType.Grayscale, + _ => PngColorType.Rgb, + }; + } + + return info.ColorType switch + { + PixelColorType.Grayscale | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha, + _ => PngColorType.RgbWithAlpha, }; + } /// /// Returns a suggested for the given - /// This is not exhaustive but covers many common pixel formats. /// + /// The pixel type info. /// The type of pixel format. - private static PngBitDepth SuggestBitDepth() + private static PngBitDepth SuggestBitDepth(in PixelComponentInfo info) where TPixel : unmanaged, IPixel - => default(TPixel) switch - { - A8 => PngBitDepth.Bit8, - Argb32 => PngBitDepth.Bit8, - Bgr24 => PngBitDepth.Bit8, - Bgra32 => PngBitDepth.Bit8, - L8 => PngBitDepth.Bit8, - L16 => PngBitDepth.Bit16, - La16 => PngBitDepth.Bit8, - La32 => PngBitDepth.Bit16, - Rgb24 => PngBitDepth.Bit8, - Rgba32 => PngBitDepth.Bit8, - Rgb48 => PngBitDepth.Bit16, - Rgba64 => PngBitDepth.Bit16, - RgbaVector => PngBitDepth.Bit16, - _ => PngBitDepth.Bit8 - }; + { + int bits = info.GetMaximumComponentPrecision(); + if (bits > (int)PixelComponentBitDepth.Bit8) + { + return PngBitDepth.Bit16; + } + + return PngBitDepth.Bit8; + } private unsafe struct ScratchBuffer { diff --git a/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs b/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs new file mode 100644 index 0000000000..674c9363b5 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides enumeration of the precision in bits of individual components within a pixel format. +/// +public enum PixelComponentBitDepth +{ + /// + /// 1 bit per component. + /// + Bit1 = 1, + + /// + /// 2 bits per component. + /// + Bit2 = 2, + + /// + /// 4 bits per component. + /// + Bit4 = 4, + + /// + /// 8 bits per component. + /// + Bit8 = 8, + + /// + /// 16 bits per component. + /// + Bit16 = 16, + + /// + /// 32 bits per component. + /// + Bit32 = 32, + + /// + /// 64 bits per component. + /// + Bit64 = 64, + + /// + /// 128 bits per component. + /// + Bit128 = 128 +} diff --git a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs deleted file mode 100644 index 2f15b7fad8..0000000000 --- a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.PixelFormats; - -/// -/// Provides enumeration of the precision in bits of individual components within a pixel format. -/// -public enum PixelComponentPrecision -{ - /// - /// 8-bit signed integer. - /// - SByte = sizeof(sbyte) * 8, - - /// - /// 8-bit unsigned integer. - /// - Byte = sizeof(byte) * 8, - - /// - /// 16-bit signed integer. - /// - Short = sizeof(short) * 8, - - /// - /// 16-bit unsigned integer. - /// - UShort = sizeof(ushort) * 8, - - /// - /// 32-bit signed integer. - /// - Int = sizeof(int) * 8, - - /// - /// 32-bit unsigned integer. - /// - UInt = sizeof(uint) * 8, - - /// - /// 64-bit signed integer. - /// - Long = sizeof(long) * 8, - - /// - /// 64-bit unsigned integer. - /// - ULong = sizeof(ulong) * 8, - - /// - /// 16-bit floating point. - /// - Half = (sizeof(float) * 8) / 2, - - /// - /// 32-bit floating point. - /// - Float = sizeof(float) * 8, - - /// - /// 64-bit floating point. - /// - Double = sizeof(double) * 8, - - /// - /// 128-bit floating point. - /// - Decimal = sizeof(decimal) * 8, -} diff --git a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs index 81c70a18f7..7cd1284f48 100644 --- a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -4,8 +4,9 @@ using System.Runtime.CompilerServices; // TODO: Review this type as it's used to represent 2 different things. -// 1.The encoded image pixel format. +// 1. The encoded image pixel format. // 2. The pixel format of the decoded image. +// Only the bits per pixel is used by the decoder, we should make it a property of the image metadata. namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 477d88e9a8..825becb36a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -256,21 +256,21 @@ public partial class PngEncoderTests [Theory] [WithBlankImages(1, 1, PixelTypes.A8, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Argb32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Bgr565, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.Bgr565, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Bgra4444, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Byte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfSingle, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfVector2, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfVector4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.NormalizedByte2, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.HalfSingle, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.HalfVector2, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.HalfVector4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.NormalizedByte2, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.NormalizedByte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.NormalizedShort4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Rg32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Rgba1010102, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.NormalizedShort4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.Rg32, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.Rgba1010102, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] [WithBlankImages(1, 1, PixelTypes.Rgba32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.RgbaVector, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Short2, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Short4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.Short2, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.Short4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] [WithBlankImages(1, 1, PixelTypes.Rgb24, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Bgr24, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Bgra32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]