Browse Source

Handle frames exceeding 256 pixels

pull/2579/head
James Jackson-South 3 years ago
committed by 舰队的偶像-岛风酱!
parent
commit
79f538723f
No known key found for this signature in database GPG Key ID: 71F5B3A2B181950C
  1. 60
      src/ImageSharp/Formats/Icon/IconDecoderCore.cs

60
src/ImageSharp/Formats/Icon/IconDecoderCore.cs

@ -32,7 +32,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals
this.ReadHeader(stream); this.ReadHeader(stream);
Span<byte> flag = stackalloc byte[PngConstants.HeaderBytes.Length]; Span<byte> flag = stackalloc byte[PngConstants.HeaderBytes.Length];
Image<TPixel> result = new(this.Dimensions.Width, this.Dimensions.Height);
List<(Image<TPixel> Image, bool IsPng, int Index)> decodedEntries = new(this.Entries.Length);
for (int i = 0; i < this.Entries.Length; i++) for (int i = 0; i < this.Entries.Length; i++)
{ {
@ -54,37 +55,62 @@ internal abstract class IconDecoderCore : IImageDecoderInternals
stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current);
bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes);
using Image<TPixel> temp = this.GetDecoder(isPng).Decode<TPixel>(stream, cancellationToken);
ImageFrame<TPixel> source = temp.Frames.RootFrameUnsafe; // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result.
ImageFrame<TPixel> target = i == 0 ? result.Frames.RootFrameUnsafe : result.Frames.CreateFrame(); Image<TPixel> temp = this.GetDecoder(isPng).Decode<TPixel>(stream, cancellationToken);
decodedEntries.Add((temp, isPng, 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.
this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height));
}
// Draw the new frame at position 0,0. We capture the dimensions for cropping during encoding ImageMetadata metadata = new();
// via the icon entry. BmpMetadata? bmpMetadata = null;
for (int h = 0; h < source.Height; h++) PngMetadata? pngMetadata = null;
Image<TPixel> result = new(this.Options.Configuration, metadata, decodedEntries.Select(x =>
{
ImageFrame<TPixel> target = new(this.Options.Configuration, this.Dimensions);
ImageFrame<TPixel> source = x.Image.Frames.RootFrameUnsafe;
for (int y = 0; y < source.Height; y++)
{ {
source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); source.PixelBuffer.DangerousGetRowSpan(y).CopyTo(target.PixelBuffer.DangerousGetRowSpan(y));
} }
// Copy the format specific metadata to the image. // Copy the format specific frame metadata to the image.
if (isPng) if (x.IsPng)
{ {
if (i == 0) if (x.Index == 0)
{ {
result.Metadata.SetFormatMetadata(PngFormat.Instance, temp.Metadata.GetPngMetadata()); pngMetadata = x.Image.Metadata.GetPngMetadata();
} }
// Bmp does not contain frame specific metadata.
target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata());
} }
else if (i == 0) else if (x.Index == 0)
{ {
// Bmp does not contain frame specific metadata. bmpMetadata = x.Image.Metadata.GetBmpMetadata();
result.Metadata.SetFormatMetadata(BmpFormat.Instance, temp.Metadata.GetBmpMetadata());
} }
// TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry)
// so we can avoid the protected Field1 and Field2 properties and use strong typing. // so we can avoid the protected Field1 and Field2 properties and use strong typing.
this.GetFrameMetadata(target.Metadata).FromIconDirEntry(entry); this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[x.Index]);
x.Image.Dispose();
return target;
}).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);
} }
return result; return result;
@ -125,6 +151,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals
int height = 0; int height = 0;
foreach (IconDirEntry entry in this.Entries) foreach (IconDirEntry entry in this.Entries)
{ {
// Since Windows 95 size of an image in the ICONDIRENTRY structure might
// be set to zero, which means 256 pixels.
if (entry.Width == 0) if (entry.Width == 0)
{ {
width = 256; width = 256;

Loading…
Cancel
Save