Skip to content

Commit dbd1783

Browse files
committed
Add encoders that cannot be used.
1 parent 746e742 commit dbd1783

15 files changed

Lines changed: 316 additions & 6 deletions

src/ImageSharp/Formats/Bmp/BmpEncoder.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public sealed class BmpEncoder : QuantizingImageEncoder
2929
/// </summary>
3030
public bool SupportTransparency { get; init; }
3131

32+
/// <inheritdoc cref="BmpDecoderOptions.ProcessedAlphaMask"/>
33+
internal bool ProcessedAlphaMask { get; init; }
34+
35+
/// <inheritdoc cref="BmpDecoderOptions.SkipFileHeader"/>
36+
internal bool SkipFileHeader { get; init; }
37+
38+
/// <inheritdoc cref="BmpDecoderOptions.UseDoubleHeight"/>
39+
internal bool UseDoubleHeight { get; init; }
40+
3241
/// <inheritdoc/>
3342
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
3443
{

src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Buffers;
55
using System.Buffers.Binary;
6+
using System.IO;
67
using System.Runtime.InteropServices;
78
using SixLabors.ImageSharp.Advanced;
89
using SixLabors.ImageSharp.Common.Helpers;
@@ -11,6 +12,7 @@
1112
using SixLabors.ImageSharp.PixelFormats;
1213
using SixLabors.ImageSharp.Processing;
1314
using SixLabors.ImageSharp.Processing.Processors.Quantization;
15+
using static System.Net.Mime.MediaTypeNames;
1416

1517
namespace SixLabors.ImageSharp.Formats.Bmp;
1618

@@ -92,6 +94,15 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
9294
/// </summary>
9395
private readonly IPixelSamplingStrategy pixelSamplingStrategy;
9496

97+
/// <inheritdoc cref="BmpDecoderOptions.ProcessedAlphaMask"/>
98+
private readonly bool processedAlphaMask;
99+
100+
/// <inheritdoc cref="BmpDecoderOptions.SkipFileHeader"/>
101+
private readonly bool skipFileHeader;
102+
103+
/// <inheritdoc cref="BmpDecoderOptions.UseDoubleHeight"/>
104+
private readonly bool isDoubleHeight;
105+
95106
/// <summary>
96107
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
97108
/// </summary>
@@ -104,6 +115,9 @@ public BmpEncoderCore(BmpEncoder encoder, MemoryAllocator memoryAllocator)
104115
this.quantizer = encoder.Quantizer ?? KnownQuantizers.Octree;
105116
this.pixelSamplingStrategy = encoder.PixelSamplingStrategy;
106117
this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3;
118+
this.processedAlphaMask = encoder.ProcessedAlphaMask;
119+
this.skipFileHeader = encoder.SkipFileHeader;
120+
this.isDoubleHeight = encoder.UseDoubleHeight;
107121
}
108122

109123
/// <summary>
@@ -154,11 +168,23 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
154168
_ => BmpInfoHeader.SizeV3
155169
};
156170

157-
BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, image.Height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData);
171+
// for ico/cur encoder.
172+
int height = image.Height;
173+
if (this.isDoubleHeight)
174+
{
175+
height <<= 1;
176+
}
177+
178+
BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData);
158179

159180
Span<byte> buffer = stackalloc byte[infoHeaderSize];
160181

161-
WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
182+
// for ico/cur encoder.
183+
if (!this.skipFileHeader)
184+
{
185+
WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
186+
}
187+
162188
this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
163189
this.WriteImage(configuration, stream, image);
164190
WriteColorProfile(stream, iccProfileData, buffer);
@@ -455,6 +481,11 @@ private void Write8BitPixelData<TPixel>(Configuration configuration, Stream stre
455481
{
456482
this.Write8BitColor(configuration, stream, image, colorPalette);
457483
}
484+
485+
if (this.processedAlphaMask)
486+
{
487+
ProcessedAlphaMask(stream, image);
488+
}
458489
}
459490

460491
/// <summary>
@@ -572,6 +603,11 @@ private void Write4BitPixelData<TPixel>(Configuration configuration, Stream stre
572603
stream.WriteByte(0);
573604
}
574605
}
606+
607+
if (this.processedAlphaMask)
608+
{
609+
ProcessedAlphaMask(stream, image);
610+
}
575611
}
576612

577613
/// <summary>
@@ -629,6 +665,11 @@ private void Write2BitPixelData<TPixel>(Configuration configuration, Stream stre
629665
stream.WriteByte(0);
630666
}
631667
}
668+
669+
if (this.processedAlphaMask)
670+
{
671+
ProcessedAlphaMask(stream, image);
672+
}
632673
}
633674

634675
/// <summary>
@@ -679,6 +720,11 @@ private void Write1BitPixelData<TPixel>(Configuration configuration, Stream stre
679720
stream.WriteByte(0);
680721
}
681722
}
723+
724+
if (this.processedAlphaMask)
725+
{
726+
ProcessedAlphaMask(stream, image);
727+
}
682728
}
683729

684730
/// <summary>
@@ -722,4 +768,35 @@ private static void Write1BitPalette(Stream stream, int startIdx, int endIdx, Re
722768

723769
stream.WriteByte(indices);
724770
}
771+
772+
private static void ProcessedAlphaMask<TPixel>(Stream stream, Image<TPixel> image)
773+
where TPixel : unmanaged, IPixel<TPixel>
774+
{
775+
Rgba32 rgba = default;
776+
int arrayWidth = image.Width / 8;
777+
int padding = arrayWidth % 4;
778+
if (padding is not 0)
779+
{
780+
padding = 4 - padding;
781+
}
782+
783+
Span<byte> mask = stackalloc byte[arrayWidth];
784+
for (int y = image.Height - 1; y >= 0; y--)
785+
{
786+
mask.Clear();
787+
for (int x = 0; x < image.Width; x++)
788+
{
789+
int bit = x % 8;
790+
int i = x / 8;
791+
TPixel pixel = image[x, y];
792+
pixel.ToRgba32(ref rgba);
793+
if (rgba.A is not 0)
794+
{
795+
mask[i] &= unchecked((byte)(0b10000000 >> bit));
796+
}
797+
}
798+
799+
stream.Write(mask);
800+
}
801+
}
725802
}

src/ImageSharp/Formats/Cur/CurConfigurationModule.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ public sealed class CurConfigurationModule : IImageFormatConfigurationModule
1313
/// <inheritdoc/>
1414
public void Configure(Configuration configuration)
1515
{
16-
// TODO: CurEncoder
17-
// configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder());
16+
configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder());
1817
configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance);
1918
configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector());
2019
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Cur;
5+
6+
/// <summary>
7+
/// Image encoder for writing an image to a stream as a Windows Cursor.
8+
/// </summary>
9+
public sealed class CurEncoder : QuantizingImageEncoder
10+
{
11+
/// <inheritdoc/>
12+
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
13+
{
14+
CurEncoderCore encoderCore = new();
15+
encoderCore.Encode(image, stream, cancellationToken);
16+
}
17+
}
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+
using SixLabors.ImageSharp.Formats.Icon;
5+
6+
namespace SixLabors.ImageSharp.Formats.Cur;
7+
8+
internal sealed class CurEncoderCore : IconEncoderCore
9+
{
10+
protected override void GetHeader(in Image image)
11+
{
12+
this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count);
13+
this.Entries = image.Frames.Select(i =>
14+
{
15+
CurFrameMetadata metadata = i.Metadata.GetCurMetadata();
16+
return metadata.ToIconDirEntry();
17+
}).ToArray();
18+
}
19+
}

src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ public sealed class IcoConfigurationModule : IImageFormatConfigurationModule
1313
/// <inheritdoc/>
1414
public void Configure(Configuration configuration)
1515
{
16-
// TODO: IcoEncoder
17-
// configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder());
16+
configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder());
1817
configuration.ImageFormatsManager.SetDecoder(IcoFormat.Instance, IcoDecoder.Instance);
1918
configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector());
2019
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Ico;
5+
6+
/// <summary>
7+
/// Image encoder for writing an image to a stream as a Windows Icon.
8+
/// </summary>
9+
public sealed class IcoEncoder : QuantizingImageEncoder
10+
{
11+
/// <inheritdoc/>
12+
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
13+
{
14+
IcoEncoderCore encoderCore = new();
15+
encoderCore.Encode(image, stream, cancellationToken);
16+
}
17+
}
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+
using SixLabors.ImageSharp.Formats.Icon;
5+
6+
namespace SixLabors.ImageSharp.Formats.Ico;
7+
8+
internal sealed class IcoEncoderCore : IconEncoderCore
9+
{
10+
protected override void GetHeader(in Image image)
11+
{
12+
this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count);
13+
this.Entries = image.Frames.Select(i =>
14+
{
15+
IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata();
16+
return metadata.ToIconDirEntry();
17+
}).ToArray();
18+
}
19+
}

src/ImageSharp/Formats/Icon/IconAssert.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,18 @@ internal static long EndOfStream(long v, long length)
3232

3333
return v;
3434
}
35+
36+
internal static byte Is1ByteSize(int i)
37+
{
38+
if (i is 256)
39+
{
40+
return 0;
41+
}
42+
else if (i > byte.MaxValue)
43+
{
44+
throw new FormatException("Image size Too Large.");
45+
}
46+
47+
return (byte)i;
48+
}
3549
}

src/ImageSharp/Formats/Icon/IconDir.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ public IconDir(IconFileType type, ushort count)
2525

2626
public static IconDir Parse(in ReadOnlySpan<byte> data)
2727
=> MemoryMarshal.Cast<byte, IconDir>(data)[0];
28+
29+
public unsafe void WriteTo(in Stream stream)
30+
=> stream.Write(MemoryMarshal.Cast<IconDir, byte>([this]));
2831
}

0 commit comments

Comments
 (0)