mirror of https://github.com/SixLabors/ImageSharp
15 changed files with 316 additions and 6 deletions
@ -0,0 +1,17 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Cur; |
|||
|
|||
/// <summary>
|
|||
/// Image encoder for writing an image to a stream as a Windows Cursor.
|
|||
/// </summary>
|
|||
public sealed class CurEncoder : QuantizingImageEncoder |
|||
{ |
|||
/// <inheritdoc/>
|
|||
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) |
|||
{ |
|||
CurEncoderCore encoderCore = new(); |
|||
encoderCore.Encode(image, stream, cancellationToken); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Icon; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Cur; |
|||
|
|||
internal sealed class CurEncoderCore : IconEncoderCore |
|||
{ |
|||
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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Ico; |
|||
|
|||
/// <summary>
|
|||
/// Image encoder for writing an image to a stream as a Windows Icon.
|
|||
/// </summary>
|
|||
public sealed class IcoEncoder : QuantizingImageEncoder |
|||
{ |
|||
/// <inheritdoc/>
|
|||
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) |
|||
{ |
|||
IcoEncoderCore encoderCore = new(); |
|||
encoderCore.Encode(image, stream, cancellationToken); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Icon; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Ico; |
|||
|
|||
internal sealed class IcoEncoderCore : IconEncoderCore |
|||
{ |
|||
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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Diagnostics.CodeAnalysis; |
|||
using SixLabors.ImageSharp.Formats.Bmp; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Icon; |
|||
|
|||
internal abstract class IconEncoderCore : IImageEncoderInternals |
|||
{ |
|||
protected IconDir FileHeader { get; set; } |
|||
|
|||
protected IconDirEntry[]? Entries { get; set; } |
|||
|
|||
public void Encode<TPixel>( |
|||
Image<TPixel> image, |
|||
Stream stream, |
|||
CancellationToken cancellationToken) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(image, nameof(image)); |
|||
Guard.NotNull(stream, nameof(stream)); |
|||
|
|||
IconAssert.CanSeek(stream); |
|||
|
|||
// Stream may not at 0.
|
|||
long basePosition = stream.Position; |
|||
this.GetHeader(image); |
|||
|
|||
int dataOffset = IconDir.Size + (IconDirEntry.Size * this.Entries.Length); |
|||
_ = stream.Seek(dataOffset, SeekOrigin.Current); |
|||
|
|||
for (int i = 0; i < image.Frames.Count; i++) |
|||
{ |
|||
ImageFrame<TPixel> frame = image.Frames[i]; |
|||
this.Entries[i].ImageOffset = (uint)stream.Position; |
|||
Image<TPixel> img = new(Configuration.Default, frame.PixelBuffer, new()); |
|||
|
|||
// Note: this encoder are not supported PNG Data.
|
|||
BmpEncoder encoder = new() |
|||
{ |
|||
ProcessedAlphaMask = true, |
|||
UseDoubleHeight = true, |
|||
SkipFileHeader = true, |
|||
SupportTransparency = false, |
|||
BitsPerPixel = this.Entries[i].BitCount is 0 |
|||
? BmpBitsPerPixel.Pixel8 |
|||
: (BmpBitsPerPixel?)this.Entries[i].BitCount |
|||
}; |
|||
|
|||
encoder.Encode(img, stream); |
|||
this.Entries[i].BytesInRes = this.Entries[i].ImageOffset - (uint)stream.Position; |
|||
} |
|||
|
|||
long endPosition = stream.Position; |
|||
_ = stream.Seek(basePosition, SeekOrigin.Begin); |
|||
this.FileHeader.WriteTo(stream); |
|||
foreach (IconDirEntry entry in this.Entries) |
|||
{ |
|||
entry.WriteTo(stream); |
|||
} |
|||
|
|||
_ = stream.Seek(endPosition, SeekOrigin.Begin); |
|||
} |
|||
|
|||
[MemberNotNull(nameof(Entries))] |
|||
protected abstract void GetHeader(in Image image); |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Cur; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using static SixLabors.ImageSharp.Tests.TestImages.Cur; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; |
|||
|
|||
[Trait("Format", "Cur")] |
|||
public class CurEncoderTests |
|||
{ |
|||
private static CurEncoder CurEncoder => new(); |
|||
|
|||
public static readonly TheoryData<string> Files = new() |
|||
{ |
|||
{ WindowsMouse }, |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Files))] |
|||
public void Encode(string imagePath) |
|||
{ |
|||
TestFile testFile = TestFile.Create(imagePath); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using MemoryStream memStream = new(); |
|||
input.Save(memStream, CurEncoder); |
|||
|
|||
memStream.Seek(0, SeekOrigin.Begin); |
|||
CurDecoder.Instance.Decode(new(), memStream); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Ico; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using static SixLabors.ImageSharp.Tests.TestImages.Ico; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; |
|||
|
|||
[Trait("Format", "Icon")] |
|||
public class IcoEncoderTests |
|||
{ |
|||
private static IcoEncoder CurEncoder => new(); |
|||
|
|||
public static readonly TheoryData<string> Files = new() |
|||
{ |
|||
{ Flutter }, |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Files))] |
|||
public void Encode(string imagePath) |
|||
{ |
|||
TestFile testFile = TestFile.Create(imagePath); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using MemoryStream memStream = new(); |
|||
input.Save(memStream, CurEncoder); |
|||
|
|||
memStream.Seek(0, SeekOrigin.Begin); |
|||
IcoDecoder.Instance.Decode(new(), memStream); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue