Skip to content

Commit 4832d8f

Browse files
committed
Add Icon Support
- Ico Decoder - Ico Detector - Ico Detector UnitTest - Cur Decoder - Cur Detector - Cur Detector UnitTest Signed-off-by: 舰队的偶像-岛风酱! <frg2089@outlook.com>
1 parent 1129382 commit 4832d8f

32 files changed

Lines changed: 1015 additions & 2 deletions

ImageSharp.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493
661661
tests\Images\Input\Qoi\wikipedia_008.qoi = tests\Images\Input\Qoi\wikipedia_008.qoi
662662
EndProjectSection
663663
EndProject
664+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Icon", "Icon", "{95E45DDE-A67D-48AD-BBA8-5FAA151B860D}"
665+
ProjectSection(SolutionItems) = preProject
666+
tests\Images\Input\Icon\aero_arrow.cur = tests\Images\Input\Icon\aero_arrow.cur
667+
tests\Images\Input\Icon\flutter.ico = tests\Images\Input\Icon\flutter.ico
668+
EndProjectSection
669+
EndProject
664670
Global
665671
GlobalSection(SolutionConfigurationPlatforms) = preSolution
666672
Debug|Any CPU = Debug|Any CPU
@@ -714,6 +720,7 @@ Global
714720
{670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254}
715721
{5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
716722
{E801B508-4935-41CD-BA85-CF11BFF55A45} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
723+
{95E45DDE-A67D-48AD-BBA8-5FAA151B860D} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
717724
EndGlobalSection
718725
GlobalSection(ExtensibilityGlobals) = postSolution
719726
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}

src/ImageSharp/Configuration.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using SixLabors.ImageSharp.Formats;
66
using SixLabors.ImageSharp.Formats.Bmp;
77
using SixLabors.ImageSharp.Formats.Gif;
8+
using SixLabors.ImageSharp.Formats.Icon.Cur;
9+
using SixLabors.ImageSharp.Formats.Icon.Ico;
810
using SixLabors.ImageSharp.Formats.Jpeg;
911
using SixLabors.ImageSharp.Formats.Pbm;
1012
using SixLabors.ImageSharp.Formats.Png;
@@ -222,5 +224,7 @@ public void Configure(IImageFormatConfigurationModule configuration)
222224
new TgaConfigurationModule(),
223225
new TiffConfigurationModule(),
224226
new WebpConfigurationModule(),
225-
new QoiConfigurationModule());
227+
new QoiConfigurationModule(),
228+
new IcoConfigurationModule(),
229+
new CurConfigurationModule());
226230
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
5+
6+
/// <summary>
7+
/// Registers the image encoders, decoders and mime type detectors for the Ico format.
8+
/// </summary>
9+
public sealed class CurConfigurationModule : IImageFormatConfigurationModule
10+
{
11+
/// <inheritdoc/>
12+
public void Configure(Configuration configuration)
13+
{
14+
// TODO: CurEncoder
15+
// configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder());
16+
configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance);
17+
configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector());
18+
}
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
5+
6+
/// <summary>
7+
/// Defines constants relating to ICOs
8+
/// </summary>
9+
internal static class CurConstants
10+
{
11+
/// <summary>
12+
/// The list of mimetypes that equate to a ico.
13+
/// </summary>
14+
/// <remarks>
15+
/// See <see href="https://en.wikipedia.org/wiki/ICO_(file_format)#MIME_type"/>
16+
/// </remarks>
17+
public static readonly IEnumerable<string> MimeTypes = new[]
18+
{
19+
"application/octet-stream",
20+
};
21+
22+
/// <summary>
23+
/// The list of file extensions that equate to a ico.
24+
/// </summary>
25+
public static readonly IEnumerable<string> FileExtensions = new[] { "cur" };
26+
27+
public const uint FileHeader = 0x00_02_00_00;
28+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using SixLabors.ImageSharp.PixelFormats;
5+
6+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
7+
8+
/// <summary>
9+
/// Decoder for generating an image out of a ico encoded stream.
10+
/// </summary>
11+
public sealed class CurDecoder : ImageDecoder
12+
{
13+
private CurDecoder()
14+
{
15+
}
16+
17+
/// <summary>
18+
/// Gets the shared instance.
19+
/// </summary>
20+
public static CurDecoder Instance { get; } = new();
21+
22+
/// <inheritdoc/>
23+
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
24+
{
25+
Guard.NotNull(options, nameof(options));
26+
Guard.NotNull(stream, nameof(stream));
27+
28+
Image<TPixel> image = new CurDecoderCore(options).Decode<TPixel>(options.Configuration, stream, cancellationToken);
29+
30+
ScaleToTargetSize(options, image);
31+
32+
return image;
33+
}
34+
35+
/// <inheritdoc/>
36+
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
37+
=> this.Decode<Rgba32>(options, stream, cancellationToken);
38+
39+
/// <inheritdoc/>
40+
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
41+
{
42+
Guard.NotNull(options, nameof(options));
43+
Guard.NotNull(stream, nameof(stream));
44+
45+
return new CurDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
46+
}
47+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using SixLabors.ImageSharp.Metadata;
5+
6+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
7+
8+
internal sealed class CurDecoderCore : IconDecoderCore
9+
{
10+
public CurDecoderCore(DecoderOptions options)
11+
: base(options)
12+
{
13+
}
14+
15+
protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetCurMetadata();
16+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
5+
6+
/// <summary>
7+
/// Registers the image encoders, decoders and mime type detectors for the ICO format.
8+
/// </summary>
9+
public sealed class CurFormat : IImageFormat<CurMetadata, CurFrameMetadata>
10+
{
11+
private CurFormat()
12+
{
13+
}
14+
15+
/// <summary>
16+
/// Gets the shared instance.
17+
/// </summary>
18+
public static CurFormat Instance { get; } = new();
19+
20+
/// <inheritdoc/>
21+
public string Name => "ICO";
22+
23+
/// <inheritdoc/>
24+
public string DefaultMimeType => CurConstants.MimeTypes.First();
25+
26+
/// <inheritdoc/>
27+
public IEnumerable<string> MimeTypes => CurConstants.MimeTypes;
28+
29+
/// <inheritdoc/>
30+
public IEnumerable<string> FileExtensions => CurConstants.FileExtensions;
31+
32+
/// <inheritdoc/>
33+
public CurMetadata CreateDefaultFormatMetadata() => new();
34+
35+
/// <inheritdoc/>
36+
public CurFrameMetadata CreateDefaultFormatFrameMetadata() => new();
37+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
5+
6+
/// <summary>
7+
/// IcoFrameMetadata
8+
/// </summary>
9+
public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable<CurFrameMetadata>, IDeepCloneable
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
13+
/// </summary>
14+
public CurFrameMetadata()
15+
{
16+
}
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
20+
/// </summary>
21+
/// <param name="metadata">metadata</param>
22+
public CurFrameMetadata(IconFrameMetadata metadata)
23+
: base(metadata)
24+
{
25+
}
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
29+
/// </summary>
30+
/// <param name="width">width</param>
31+
/// <param name="height">height</param>
32+
/// <param name="colorCount">colorCount</param>
33+
/// <param name="field1">field1</param>
34+
/// <param name="field2">field2</param>
35+
public CurFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2)
36+
: base(width, height, colorCount, field1, field2)
37+
{
38+
}
39+
40+
/// <summary>
41+
/// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
42+
/// </summary>
43+
public ushort HotspotX { get => this.Field1; set => this.Field1 = value; }
44+
45+
/// <summary>
46+
/// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top.
47+
/// </summary>
48+
public ushort HotspotY { get => this.Field2; set => this.Field2 = value; }
49+
50+
/// <inheritdoc/>
51+
public override CurFrameMetadata DeepClone() => new(this);
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
5+
6+
/// <summary>
7+
/// Provides Ico specific metadata information for the image.
8+
/// </summary>
9+
public class CurMetadata : IDeepCloneable<CurMetadata>, IDeepCloneable
10+
{
11+
/// <inheritdoc/>
12+
public CurMetadata DeepClone() => new();
13+
14+
/// <inheritdoc/>
15+
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
16+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using SixLabors.ImageSharp.Metadata;
6+
7+
namespace SixLabors.ImageSharp.Formats.Icon.Cur;
8+
9+
/// <summary>
10+
/// Extension methods for the <see cref="ImageMetadata"/> type.
11+
/// </summary>
12+
public static class MetadataExtensions
13+
{
14+
/// <summary>
15+
/// Gets the Icon format specific metadata for the image.
16+
/// </summary>
17+
/// <param name="source">The metadata this method extends.</param>
18+
/// <returns>The <see cref="CurMetadata"/>.</returns>
19+
public static CurMetadata GetCurMetadata(this ImageMetadata source)
20+
=> source.GetFormatMetadata(CurFormat.Instance);
21+
22+
/// <summary>
23+
/// Gets the Icon format specific metadata for the image frame.
24+
/// </summary>
25+
/// <param name="source">The metadata this method extends.</param>
26+
/// <returns>The <see cref="CurFrameMetadata"/>.</returns>
27+
public static CurFrameMetadata GetCurMetadata(this ImageFrameMetadata source)
28+
=> source.GetFormatMetadata(CurFormat.Instance);
29+
30+
/// <summary>
31+
/// Gets the Icon format specific metadata for the image frame.
32+
/// </summary>
33+
/// <param name="source">The metadata this method extends.</param>
34+
/// <param name="metadata">
35+
/// When this method returns, contains the metadata associated with the specified frame,
36+
/// if found; otherwise, the default value for the type of the metadata parameter.
37+
/// This parameter is passed uninitialized.
38+
/// </param>
39+
/// <returns>
40+
/// <see langword="true"/> if the Icon frame metadata exists; otherwise, <see langword="false"/>.
41+
/// </returns>
42+
public static bool TryGetCurMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out CurFrameMetadata? metadata)
43+
=> source.TryGetFormatMetadata(CurFormat.Instance, out metadata);
44+
}

0 commit comments

Comments
 (0)