diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs
index 0cf8c97a54..3a7288b718 100644
--- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs
+++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs
@@ -5,15 +5,6 @@ using SixLabors.ImageSharp.Formats.Icon;
namespace SixLabors.ImageSharp.Formats.Cur;
-internal sealed class CurEncoderCore : IconEncoderCore
+internal sealed class CurEncoderCore() : IconEncoderCore(IconFileType.CUR)
{
- protected override void GetHeader(in Image image)
- {
- this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count);
- this.Entries = image.Frames.Select(i =>
- {
- CurFrameMetadata metadata = i.Metadata.GetCurMetadata();
- return metadata.ToIconDirEntry();
- }).ToArray();
- }
}
diff --git a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs
index 2b410a0f95..6394c564b0 100644
--- a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs
@@ -2,14 +2,15 @@
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
+using SixLabors.ImageSharp.Formats.Cur;
using SixLabors.ImageSharp.Metadata;
-namespace SixLabors.ImageSharp.Formats.Cur;
+namespace SixLabors.ImageSharp;
///
/// Extension methods for the type.
///
-public static class MetadataExtensions
+public static partial class MetadataExtensions
{
///
/// Gets the Icon format specific metadata for the image.
diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs
index 6c2f3abe4d..12ced58fd6 100644
--- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs
+++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs
@@ -5,15 +5,6 @@ using SixLabors.ImageSharp.Formats.Icon;
namespace SixLabors.ImageSharp.Formats.Ico;
-internal sealed class IcoEncoderCore : IconEncoderCore
+internal sealed class IcoEncoderCore() : IconEncoderCore(IconFileType.ICO)
{
- protected override void GetHeader(in Image image)
- {
- this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count);
- this.Entries = image.Frames.Select(i =>
- {
- IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata();
- return metadata.ToIconDirEntry();
- }).ToArray();
- }
}
diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs
index 2a275d1a1d..8d7eb17b5c 100644
--- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs
@@ -88,7 +88,8 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable
BitCount = this.Compression switch
{
IconFrameCompression.Bmp => (ushort)this.BitsPerPixel,
- _ => 0,
+ IconFrameCompression.Png => 32,
+ _ => throw new NotSupportedException($"Value: {this.Compression}"),
},
};
}
diff --git a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs
index ba21d0abc5..497375f994 100644
--- a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs
@@ -2,14 +2,15 @@
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
+using SixLabors.ImageSharp.Formats.Ico;
using SixLabors.ImageSharp.Metadata;
-namespace SixLabors.ImageSharp.Formats.Ico;
+namespace SixLabors.ImageSharp;
///
/// Extension methods for the type.
///
-public static class MetadataExtensions
+public static partial class MetadataExtensions
{
///
/// Gets the Ico format specific metadata for the image.
diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs
index 05a657b51b..b4d563d4b6 100644
--- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs
+++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs
@@ -2,16 +2,18 @@
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
-using SixLabors.ImageSharp.Formats.Bmp;
+using SixLabors.ImageSharp.Formats.Cur;
+using SixLabors.ImageSharp.Formats.Ico;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Icon;
-internal abstract class IconEncoderCore : IImageEncoderInternals
+internal abstract class IconEncoderCore(IconFileType iconFileType)
+ : IImageEncoderInternals
{
- protected IconDir FileHeader { get; set; }
+ private IconDir fileHeader;
- protected IconDirEntry[]? Entries { get; set; }
+ private IconFrameMetadata[]? entries;
public void Encode(
Image image,
@@ -26,44 +28,93 @@ internal abstract class IconEncoderCore : IImageEncoderInternals
// Stream may not at 0.
long basePosition = stream.Position;
- this.GetHeader(image);
+ this.InitHeader(image);
- int dataOffset = IconDir.Size + (IconDirEntry.Size * this.Entries.Length);
+ int dataOffset = IconDir.Size + (IconDirEntry.Size * this.entries.Length);
_ = stream.Seek(dataOffset, SeekOrigin.Current);
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame frame = image.Frames[i];
- this.Entries[i].ImageOffset = (uint)stream.Position;
- Image img = new(Configuration.Default, frame.PixelBuffer, new());
+ int width = this.entries[i].Entry.Width;
+ if (width is 0)
+ {
+ width = frame.Width;
+ }
+
+ int height = this.entries[i].Entry.Height;
+ if (height is 0)
+ {
+ height = frame.Height;
+ }
+
+ this.entries[i].Entry.ImageOffset = (uint)stream.Position;
- // Note: this encoder are not supported PNG Data.
- BmpEncoder encoder = new()
+ Image img = new(width, height);
+ for (int y = 0; y < height; y++)
{
- ProcessedAlphaMask = true,
- UseDoubleHeight = true,
- SkipFileHeader = true,
- SupportTransparency = false,
- BitsPerPixel = this.Entries[i].BitCount is 0
- ? BmpBitsPerPixel.Pixel8
- : (BmpBitsPerPixel?)this.Entries[i].BitCount
+ frame.PixelBuffer.DangerousGetRowSpan(y)[..width].CopyTo(img.GetRootFramePixelBuffer().DangerousGetRowSpan(y));
+ }
+
+ QuantizingImageEncoder encoder = this.entries[i].Compression switch
+ {
+ IconFrameCompression.Bmp => new Bmp.BmpEncoder()
+ {
+ ProcessedAlphaMask = true,
+ UseDoubleHeight = true,
+ SkipFileHeader = true,
+ SupportTransparency = false,
+ BitsPerPixel = iconFileType is IconFileType.ICO
+ ? (Bmp.BmpBitsPerPixel?)this.entries[i].Entry.BitCount
+ : Bmp.BmpBitsPerPixel.Pixel24 // TODO: Here you need to switch to selecting the corresponding value according to the size of the image
+ },
+ IconFrameCompression.Png => new Png.PngEncoder(),
+ _ => throw new NotSupportedException(),
};
encoder.Encode(img, stream);
- this.Entries[i].BytesInRes = this.Entries[i].ImageOffset - (uint)stream.Position;
+ this.entries[i].Entry.BytesInRes = (uint)stream.Position - this.entries[i].Entry.ImageOffset;
}
long endPosition = stream.Position;
_ = stream.Seek(basePosition, SeekOrigin.Begin);
- this.FileHeader.WriteTo(stream);
- foreach (IconDirEntry entry in this.Entries)
+ this.fileHeader.WriteTo(stream);
+ foreach (IconFrameMetadata frame in this.entries)
{
- entry.WriteTo(stream);
+ frame.Entry.WriteTo(stream);
}
_ = stream.Seek(endPosition, SeekOrigin.Begin);
}
- [MemberNotNull(nameof(Entries))]
- protected abstract void GetHeader(in Image image);
+ [MemberNotNull(nameof(entries))]
+ private void InitHeader(in Image image)
+ {
+ this.fileHeader = new(iconFileType, (ushort)image.Frames.Count);
+ this.entries = iconFileType switch
+ {
+ IconFileType.ICO =>
+ image.Frames.Select(i =>
+ {
+ IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata();
+ return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry());
+ }).ToArray(),
+ IconFileType.CUR =>
+ image.Frames.Select(i =>
+ {
+ CurFrameMetadata metadata = i.Metadata.GetCurMetadata();
+ return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry());
+ }).ToArray(),
+ _ => throw new NotSupportedException(),
+ };
+ }
+
+ internal sealed class IconFrameMetadata(IconFrameCompression compression, IconDirEntry iconDirEntry)
+ {
+ private IconDirEntry iconDirEntry = iconDirEntry;
+
+ public IconFrameCompression Compression { get; set; } = compression;
+
+ public ref IconDirEntry Entry => ref this.iconDirEntry;
+ }
}