Skip to content

Commit

Permalink
Add Icon Support
Browse files Browse the repository at this point in the history
- Ico Decoder
- Ico Detector
- Ico Detector UnitTest
- Cur Decoder
- Cur Detector
- Cur Detector UnitTest

Signed-off-by: 舰队的偶像-岛风酱! <frg2089@outlook.com>
  • Loading branch information
frg2089 committed Nov 13, 2023
1 parent b6d900a commit c71a9a9
Show file tree
Hide file tree
Showing 33 changed files with 1,018 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,6 @@
*.pnm filter=lfs diff=lfs merge=lfs -text
*.wbmp filter=lfs diff=lfs merge=lfs -text
*.exr filter=lfs diff=lfs merge=lfs -text
*.ico filter=lfs diff=lfs merge=lfs -text
*.cur filter=lfs diff=lfs merge=lfs -text
*.ani filter=lfs diff=lfs merge=lfs -text
7 changes: 7 additions & 0 deletions ImageSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493
tests\Images\Input\Qoi\wikipedia_008.qoi = tests\Images\Input\Qoi\wikipedia_008.qoi
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Icon", "Icon", "{95E45DDE-A67D-48AD-BBA8-5FAA151B860D}"
ProjectSection(SolutionItems) = preProject
tests\Images\Input\Icon\aero_arrow.cur = tests\Images\Input\Icon\aero_arrow.cur
tests\Images\Input\Icon\flutter.ico = tests\Images\Input\Icon\flutter.ico
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -712,6 +718,7 @@ Global
{670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254}
{5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
{E801B508-4935-41CD-BA85-CF11BFF55A45} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
{95E45DDE-A67D-48AD-BBA8-5FAA151B860D} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}
Expand Down
6 changes: 5 additions & 1 deletion src/ImageSharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Icon.Cur;
using SixLabors.ImageSharp.Formats.Icon.Ico;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
Expand Down Expand Up @@ -225,5 +227,7 @@ public void Configure(IImageFormatConfigurationModule configuration)
new TgaConfigurationModule(),
new TiffConfigurationModule(),
new WebpConfigurationModule(),
new QoiConfigurationModule());
new QoiConfigurationModule(),
new IcoConfigurationModule(),
new CurConfigurationModule());
}
19 changes: 19 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the Ico format.
/// </summary>
public sealed class CurConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
// TODO: CurEncoder
// configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder());
configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector());
}
}
28 changes: 28 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// Defines constants relating to ICOs
/// </summary>
internal static class CurConstants
{
/// <summary>
/// The list of mimetypes that equate to a ico.
/// </summary>
/// <remarks>
/// See <see href="https://en.wikipedia.org/wiki/ICO_(file_format)#MIME_type"/>
/// </remarks>
public static readonly IEnumerable<string> MimeTypes = new[]
{
"application/octet-stream",
};

/// <summary>
/// The list of file extensions that equate to a ico.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "cur" };

public const uint FileHeader = 0x00_02_00_00;
}
47 changes: 47 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.ImageSharp.PixelFormats;

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// Decoder for generating an image out of a ico encoded stream.
/// </summary>
public sealed class CurDecoder : ImageDecoder
{
private CurDecoder()
{
}

/// <summary>
/// Gets the shared instance.
/// </summary>
public static CurDecoder Instance { get; } = new();

/// <inheritdoc/>
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

Image<TPixel> image = new CurDecoderCore(options).Decode<TPixel>(options.Configuration, stream, cancellationToken);

ScaleToTargetSize(options, image);

return image;
}

/// <inheritdoc/>
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);

/// <inheritdoc/>
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

return new CurDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
}
16 changes: 16 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.ImageSharp.Metadata;

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

internal sealed class CurDecoderCore : IconDecoderCore
{
public CurDecoderCore(DecoderOptions options)
: base(options)
{
}

protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetCurMetadata();
}
37 changes: 37 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the ICO format.
/// </summary>
public sealed class CurFormat : IImageFormat<CurMetadata, CurFrameMetadata>
{
private CurFormat()
{
}

/// <summary>
/// Gets the shared instance.
/// </summary>
public static CurFormat Instance { get; } = new();

/// <inheritdoc/>
public string Name => "ICO";

/// <inheritdoc/>
public string DefaultMimeType => CurConstants.MimeTypes.First();

/// <inheritdoc/>
public IEnumerable<string> MimeTypes => CurConstants.MimeTypes;

/// <inheritdoc/>
public IEnumerable<string> FileExtensions => CurConstants.FileExtensions;

/// <inheritdoc/>
public CurMetadata CreateDefaultFormatMetadata() => new();

/// <inheritdoc/>
public CurFrameMetadata CreateDefaultFormatFrameMetadata() => new();
}
52 changes: 52 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// IcoFrameMetadata
/// </summary>
public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable<CurFrameMetadata>, IDeepCloneable
{
/// <summary>
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
/// </summary>
public CurFrameMetadata()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
/// </summary>
/// <param name="metadata">metadata</param>
public CurFrameMetadata(IconFrameMetadata metadata)
: base(metadata)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
/// </summary>
/// <param name="width">width</param>
/// <param name="height">height</param>
/// <param name="colorCount">colorCount</param>
/// <param name="field1">field1</param>
/// <param name="field2">field2</param>
public CurFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2)
: base(width, height, colorCount, field1, field2)
{
}

/// <summary>
/// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
/// </summary>
public ushort HotspotX { get => this.Field1; set => this.Field1 = value; }

/// <summary>
/// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top.
/// </summary>
public ushort HotspotY { get => this.Field2; set => this.Field2 = value; }

/// <inheritdoc/>
public override CurFrameMetadata DeepClone() => new(this);
}
16 changes: 16 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// Provides Ico specific metadata information for the image.
/// </summary>
public class CurMetadata : IDeepCloneable<CurMetadata>, IDeepCloneable
{
/// <inheritdoc/>
public CurMetadata DeepClone() => new();

/// <inheritdoc/>
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
}
44 changes: 44 additions & 0 deletions src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Metadata;

namespace SixLabors.ImageSharp.Formats.Icon.Cur;

/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static class MetadataExtensions
{
/// <summary>
/// Gets the Icon format specific metadata for the image.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="CurMetadata"/>.</returns>
public static CurMetadata GetCurMetadata(this ImageMetadata source)
=> source.GetFormatMetadata(CurFormat.Instance);

/// <summary>
/// Gets the Icon format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="CurFrameMetadata"/>.</returns>
public static CurFrameMetadata GetCurMetadata(this ImageFrameMetadata source)
=> source.GetFormatMetadata(CurFormat.Instance);

/// <summary>
/// Gets the Icon format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">
/// When this method returns, contains the metadata associated with the specified frame,
/// if found; otherwise, the default value for the type of the metadata parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the Icon frame metadata exists; otherwise, <see langword="false"/>.
/// </returns>
public static bool TryGetCurMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out CurFrameMetadata? metadata)
=> source.TryGetFormatMetadata(CurFormat.Instance, out metadata);
}
19 changes: 19 additions & 0 deletions src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Ico;

/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the Ico format.
/// </summary>
public sealed class IcoConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
// TODO: IcoEncoder
// configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder());
configuration.ImageFormatsManager.SetDecoder(IcoFormat.Instance, IcoDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector());
}
}
38 changes: 38 additions & 0 deletions src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Icon.Ico;

/// <summary>
/// Defines constants relating to ICOs
/// </summary>
internal static class IcoConstants
{
/// <summary>
/// The list of mimetypes that equate to a ico.
/// </summary>
/// <remarks>
/// See <see href="https://en.wikipedia.org/wiki/ICO_(file_format)#MIME_type"/>
/// </remarks>
public static readonly IEnumerable<string> MimeTypes = new[]
{
// IANA-registered
"image/vnd.microsoft.icon",

// ICO & CUR types used by Windows
"image/x-icon",

// Erroneous types but have been used
"image/ico",
"image/icon",
"text/ico",
"application/ico",
};

/// <summary>
/// The list of file extensions that equate to a ico.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "ico" };

public const uint FileHeader = 0x00_01_00_00;
}
Loading

0 comments on commit c71a9a9

Please sign in to comment.