From 729d64ec757536c30c3f3ed5255f2460762206d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:56 +0800 Subject: [PATCH] Refactor SetFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderCore.cs | 8 ++- .../Formats/Icon/Ico/IcoDecoderCore.cs | 9 ++- .../Formats/Icon/IconDecoderCore.cs | 64 +++++++++++++------ 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index afd751279b..2ed89483a0 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -14,5 +14,11 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) => metadata.GetCurMetadata().FromIconDirEntry(entry); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + { + CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); + curFrameMetadata.FromIconDirEntry(entry); + curFrameMetadata.Compression = compression; + curFrameMetadata.BitsPerPixel = bitsPerPixel; + } } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 4884fdf9a3..c7841ce399 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -14,6 +14,11 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) - => metadata.GetIcoMetadata().FromIconDirEntry(entry); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + { + IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); + icoFrameMetadata.FromIconDirEntry(entry); + icoFrameMetadata.Compression = compression; + icoFrameMetadata.BitsPerPixel = bitsPerPixel; + } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 21c0a3bc45..6af9553cc0 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -33,7 +33,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, bool IsPng, int Index)> decodedEntries = new(this.Entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { @@ -58,7 +58,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); - decodedEntries.Add((temp, isPng, i)); + decodedEntries.Add((temp, isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, i)); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. @@ -66,10 +66,10 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } ImageMetadata metadata = new(); - BmpMetadata? bmpMetadata = null; PngMetadata? pngMetadata = null; Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { + BmpBitsPerPixel bitsPerPixel = default; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -78,7 +78,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Copy the format specific frame metadata to the image. - if (x.IsPng) + if (x.Compression is IconFrameCompression.Png) { if (x.Index == 0) { @@ -88,12 +88,12 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else if (x.Index == 0) + else { - bmpMetadata = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(target.Metadata, this.Entries[x.Index]); + this.SetFrameMetadata(target.Metadata, this.Entries[x.Index], x.Compression, bitsPerPixel); x.Image.Dispose(); @@ -101,11 +101,6 @@ internal abstract class IconDecoderCore : IImageDecoderInternals }).ToArray()); // Copy the format specific metadata to the image. - if (bmpMetadata != null) - { - result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); - } - if (pngMetadata != null) { result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); @@ -116,23 +111,56 @@ internal abstract class IconDecoderCore : IImageDecoderInternals public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { + // Stream may not at 0. + long basePosition = stream.Position; this.ReadHeader(stream); + Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; + ImageMetadata metadata = new(); ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; for (int i = 0; i < frames.Length; i++) { - // TODO: Use the Identify methods in each decoder to return the - // format specific metadata for the image and frame. + BmpBitsPerPixel bitsPerPixel = default; + ref IconDirEntry entry = ref this.Entries[i]; + + // If we hit the end of the stream we should break. + if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) + { + break; + } + + // There should always be enough bytes for this regardless of the entry type. + if (stream.Read(flag) != PngConstants.HeaderBytes.Length) + { + break; + } + + // Reset the stream position. + stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); + + // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. + ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); + frames[i] = new(); - this.SetFrameMetadata(frames[i], this.Entries[i]); + if (isPng) + { + bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; + } + + this.SetFrameMetadata(frames[i], this.Entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. + this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } - // TODO: Use real values from the metadata. - return new(new(32), new(0), metadata, frames); + return new(new(32), this.Dimensions, metadata, frames); } - protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry); + protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); protected void ReadHeader(Stream stream) {