From d52815bfd0730ffb4dd1aea7acc12e21a8bc8092 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 May 2024 23:05:49 +1000 Subject: [PATCH] Wire up BmpMetadata proper --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 96 +++++++++++++++++-- .../Formats/FormatConnectingMetadata.cs | 2 +- src/ImageSharp/PixelFormats/PixelColorType.cs | 7 +- .../PixelFormats/PixelComponentInfo.cs | 13 ++- .../PixelFormats/PixelColorTypeTests.cs | 37 +++++++ 5 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index f381249d8..2f03b9c6f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -1,6 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + +// TODO: Add color table information. namespace SixLabors.ImageSharp.Formats.Bmp; /// @@ -37,25 +40,106 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata public static BmpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) - => throw new NotImplementedException(); + { + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + if (bpp == 1) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel1 }; + } + + if (bpp == 2) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel2 }; + } + + if (bpp <= 4) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel4 }; + } + + if (bpp <= 8) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel8 }; + } + + if (bpp <= 16) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel16, InfoHeaderType = BmpInfoHeaderType.WinVersion3 }; + } + + if (bpp <= 24) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.WinVersion4 }; + } + + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel32, InfoHeaderType = BmpInfoHeaderType.WinVersion5 }; + } /// public static BmpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) - => throw new NotImplementedException(); + => new(); /// public FormatConnectingMetadata ToFormatConnectingMetadata() - => throw new NotImplementedException(); + { + int bpp = (int)this.BitsPerPixel; + + PixelAlphaRepresentation alpha = this.InfoHeaderType switch + { + BmpInfoHeaderType.WinVersion2 or + BmpInfoHeaderType.Os2Version2Short or + BmpInfoHeaderType.WinVersion3 or + BmpInfoHeaderType.AdobeVersion3 or + BmpInfoHeaderType.Os2Version2 => PixelAlphaRepresentation.None, + BmpInfoHeaderType.AdobeVersion3WithAlpha or + BmpInfoHeaderType.WinVersion4 or + BmpInfoHeaderType.WinVersion5 or + _ => bpp < 32 ? PixelAlphaRepresentation.None : PixelAlphaRepresentation.Unassociated + }; + + PixelComponentInfo info = this.BitsPerPixel switch + { + BmpBitsPerPixel.Pixel1 => PixelComponentInfo.Create(1, bpp, 1), + BmpBitsPerPixel.Pixel2 => PixelComponentInfo.Create(1, bpp, 2), + BmpBitsPerPixel.Pixel4 => PixelComponentInfo.Create(1, bpp, 4), + BmpBitsPerPixel.Pixel8 => PixelComponentInfo.Create(1, bpp, 8), + + // Could be 555 with padding but 565 is more common in newer bitmaps and offers + // greater accuracy due to extra green precision. + BmpBitsPerPixel.Pixel16 => PixelComponentInfo.Create(3, bpp, 5, 6, 5), + BmpBitsPerPixel.Pixel24 => PixelComponentInfo.Create(3, bpp, 8, 8, 8), + BmpBitsPerPixel.Pixel32 or _ => PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8), + }; + + PixelColorType color = this.BitsPerPixel switch + { + BmpBitsPerPixel.Pixel1 or + BmpBitsPerPixel.Pixel2 or + BmpBitsPerPixel.Pixel4 or + BmpBitsPerPixel.Pixel8 => PixelColorType.Indexed, + BmpBitsPerPixel.Pixel16 or + BmpBitsPerPixel.Pixel24 => PixelColorType.RGB, + BmpBitsPerPixel.Pixel32 or _ => PixelColorType.RGB | PixelColorType.Alpha, + }; + + return new() + { + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + } + }; + } /// public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() - => throw new NotImplementedException(); + => new(); /// public IDeepCloneable DeepClone() => ((IDeepCloneable)this).DeepClone(); /// BmpMetadata IDeepCloneable.DeepClone() => new(this); - - // TODO: Colors used once we support encoding palette bmps. } diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs index a7030b464..2acbe9699 100644 --- a/src/ImageSharp/Formats/FormatConnectingMetadata.cs +++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs @@ -34,7 +34,7 @@ public class FormatConnectingMetadata /// /// Defaults to . /// - public FrameColorTableMode ColorTableMode { get; init; } + public FrameColorTableMode ColorTableMode { get; init; } = FrameColorTableMode.Global; /// /// Gets the default background color of the canvas when animating. diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs index 86c3d51e9..52d4df73a 100644 --- a/src/ImageSharp/PixelFormats/PixelColorType.cs +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -99,8 +99,13 @@ public enum PixelColorType /// YCCK = Luminance | ChrominanceBlue | ChrominanceRed | Key, + /// + /// Indicates that the color is indexed using a palette. + /// + Indexed = 1 << 14, + /// /// Indicates that the color is of a type not specified in this enum. /// - Other = 1 << 14 + Other = 1 << 15 } diff --git a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs index fca371ca9..64b233f35 100644 --- a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs @@ -41,6 +41,17 @@ public readonly struct PixelComponentInfo /// The component precision and index cannot exceed the component range. public static PixelComponentInfo Create(int count, params int[] precision) where TPixel : unmanaged, IPixel + => Create(count, Unsafe.SizeOf() * 8, precision); + + /// + /// Creates a new instance. + /// + /// The number of components within the pixel format. + /// The number of bits per pixel. + /// The precision in bits of each component. + /// The . + /// The component precision and index cannot exceed the component range. + public static PixelComponentInfo Create(int count, int bitsPerPixel, params int[] precision) { if (precision.Length != count || precision.Length > 16) { @@ -70,7 +81,7 @@ public readonly struct PixelComponentInfo sum += p; } - return new PixelComponentInfo(count, (Unsafe.SizeOf() * 8) - sum, precisionData1, precisionData2); + return new PixelComponentInfo(count, bitsPerPixel - sum, precisionData1, precisionData2); } /// diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs index 215fe7b87..09e84d53b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs @@ -140,6 +140,13 @@ public class PixelColorTypeTests Assert.True(colorType.HasFlag(PixelColorType.Key)); } + [Fact] + public void PixelColorType_Indexed_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Indexed; + Assert.True(colorType.HasFlag(PixelColorType.Indexed)); + } + [Fact] public void PixelColorType_Other_ShouldBeSet() { @@ -166,6 +173,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -181,6 +189,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -197,6 +206,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -212,6 +222,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Luminance)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -227,6 +238,31 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_Indexed_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.Indexed; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.RGB)); + Assert.False(colorType.HasFlag(PixelColorType.BGR)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.YCbCr)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.CMYK)); + Assert.False(colorType.HasFlag(PixelColorType.YCCK)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -251,5 +287,6 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Key)); Assert.False(colorType.HasFlag(PixelColorType.CMYK)); Assert.False(colorType.HasFlag(PixelColorType.YCCK)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); } }