|
|
|
@ -68,11 +68,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// </summary>
|
|
|
|
private readonly MemoryAllocator memoryAllocator; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The global configuration.
|
|
|
|
/// </summary>
|
|
|
|
private Configuration? configuration; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The color depth, in number of bits per pixel.
|
|
|
|
/// </summary>
|
|
|
|
@ -123,7 +118,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
Guard.NotNull(image, nameof(image)); |
|
|
|
Guard.NotNull(stream, nameof(stream)); |
|
|
|
|
|
|
|
this.configuration = image.GetConfiguration(); |
|
|
|
Configuration configuration = image.GetConfiguration(); |
|
|
|
ImageMetadata metadata = image.Metadata; |
|
|
|
BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); |
|
|
|
this.bitsPerPixel ??= bmpMetadata.BitsPerPixel; |
|
|
|
@ -164,7 +159,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
|
|
|
|
WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); |
|
|
|
this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); |
|
|
|
this.WriteImage(stream, image); |
|
|
|
this.WriteImage(stream, image, configuration); |
|
|
|
WriteColorProfile(stream, iccProfileData, buffer); |
|
|
|
|
|
|
|
stream.Flush(); |
|
|
|
@ -316,38 +311,39 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <param name="image">
|
|
|
|
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
|
|
|
|
/// </param>
|
|
|
|
private void WriteImage<TPixel>(Stream stream, Image<TPixel> image) |
|
|
|
/// <param name="configuration">The global configuration.</param>
|
|
|
|
private void WriteImage<TPixel>(Stream stream, Image<TPixel> image, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer; |
|
|
|
switch (this.bitsPerPixel) |
|
|
|
{ |
|
|
|
case BmpBitsPerPixel.Pixel32: |
|
|
|
this.Write32BitPixelData(stream, pixels); |
|
|
|
this.Write32BitPixelData(stream, pixels, configuration); |
|
|
|
break; |
|
|
|
|
|
|
|
case BmpBitsPerPixel.Pixel24: |
|
|
|
this.Write24BitPixelData(stream, pixels); |
|
|
|
this.Write24BitPixelData(stream, pixels, configuration); |
|
|
|
break; |
|
|
|
|
|
|
|
case BmpBitsPerPixel.Pixel16: |
|
|
|
this.Write16BitPixelData(stream, pixels); |
|
|
|
this.Write16BitPixelData(stream, pixels, configuration); |
|
|
|
break; |
|
|
|
|
|
|
|
case BmpBitsPerPixel.Pixel8: |
|
|
|
this.Write8BitPixelData(stream, image); |
|
|
|
this.Write8BitPixelData(stream, image, configuration); |
|
|
|
break; |
|
|
|
|
|
|
|
case BmpBitsPerPixel.Pixel4: |
|
|
|
this.Write4BitPixelData(stream, image); |
|
|
|
this.Write4BitPixelData(stream, image, configuration); |
|
|
|
break; |
|
|
|
|
|
|
|
case BmpBitsPerPixel.Pixel2: |
|
|
|
this.Write2BitPixelData(stream, image); |
|
|
|
this.Write2BitPixelData(stream, image, configuration); |
|
|
|
break; |
|
|
|
|
|
|
|
case BmpBitsPerPixel.Pixel1: |
|
|
|
this.Write1BitPixelData(stream, image); |
|
|
|
this.Write1BitPixelData(stream, image, configuration); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -361,7 +357,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write32BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels) |
|
|
|
/// <param name="configuration">The global configuration.</param>
|
|
|
|
private void Write32BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4); |
|
|
|
@ -371,7 +368,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
{ |
|
|
|
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.ToBgra32Bytes( |
|
|
|
this.configuration, |
|
|
|
configuration, |
|
|
|
pixelSpan, |
|
|
|
rowSpan, |
|
|
|
pixelSpan.Length); |
|
|
|
@ -385,7 +382,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write24BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels) |
|
|
|
/// <param name="configuration">The global configuration.</param>
|
|
|
|
private void Write24BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
int width = pixels.Width; |
|
|
|
@ -397,7 +395,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
{ |
|
|
|
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.ToBgr24Bytes( |
|
|
|
this.configuration, |
|
|
|
configuration, |
|
|
|
pixelSpan, |
|
|
|
row.Slice(0, rowBytesWithoutPadding), |
|
|
|
width); |
|
|
|
@ -411,7 +409,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write16BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels) |
|
|
|
/// <param name="configuration">The global configuration.</param>
|
|
|
|
private void Write16BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
int width = pixels.Width; |
|
|
|
@ -424,7 +423,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.ToBgra5551Bytes( |
|
|
|
this.configuration, |
|
|
|
configuration, |
|
|
|
pixelSpan, |
|
|
|
row.Slice(0, rowBytesWithoutPadding), |
|
|
|
pixelSpan.Length); |
|
|
|
@ -439,7 +438,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="image"> The <see cref="Image{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write8BitPixelData<TPixel>(Stream stream, Image<TPixel> image) |
|
|
|
/// <param name="configuration">The global configuration.</param>
|
|
|
|
private void Write8BitPixelData<TPixel>(Stream stream, Image<TPixel> image, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
bool isL8 = typeof(TPixel) == typeof(L8); |
|
|
|
@ -452,7 +452,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
this.Write8BitColor(stream, image, colorPalette); |
|
|
|
this.Write8BitColor(stream, image, colorPalette, configuration); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -463,16 +463,17 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="image"> The <see cref="Image{TPixel}"/> containing pixel data.</param>
|
|
|
|
/// <param name="colorPalette">A byte span of size 1024 for the color palette.</param>
|
|
|
|
private void Write8BitColor<TPixel>(Stream stream, Image<TPixel> image, Span<byte> colorPalette) |
|
|
|
/// <param name="configuration">The global configuration</param>
|
|
|
|
private void Write8BitColor<TPixel>(Stream stream, Image<TPixel> image, Span<byte> colorPalette, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration); |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration); |
|
|
|
|
|
|
|
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image); |
|
|
|
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds); |
|
|
|
|
|
|
|
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); |
|
|
|
|
|
|
|
for (int y = image.Height - 1; y >= 0; y--) |
|
|
|
{ |
|
|
|
@ -530,10 +531,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write4BitPixelData<TPixel>(Stream stream, Image<TPixel> image) |
|
|
|
/// <param name="configuration">The global configuration.</param>
|
|
|
|
private void Write4BitPixelData<TPixel>(Stream stream, Image<TPixel> image, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, new QuantizerOptions() |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration, new QuantizerOptions() |
|
|
|
{ |
|
|
|
MaxColors = 16 |
|
|
|
}); |
|
|
|
@ -545,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
|
|
|
|
Span<byte> colorPalette = colorPaletteBuffer.GetSpan(); |
|
|
|
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); |
|
|
|
|
|
|
|
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0); |
|
|
|
int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; |
|
|
|
@ -577,10 +579,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write2BitPixelData<TPixel>(Stream stream, Image<TPixel> image) |
|
|
|
/// <param name="configuration">The global configuration</param>
|
|
|
|
private void Write2BitPixelData<TPixel>(Stream stream, Image<TPixel> image, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, new QuantizerOptions() |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration, new QuantizerOptions() |
|
|
|
{ |
|
|
|
MaxColors = 4 |
|
|
|
}); |
|
|
|
@ -592,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
|
|
|
|
Span<byte> colorPalette = colorPaletteBuffer.GetSpan(); |
|
|
|
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); |
|
|
|
|
|
|
|
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0); |
|
|
|
int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; |
|
|
|
@ -633,10 +636,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
|
|
|
|
private void Write1BitPixelData<TPixel>(Stream stream, Image<TPixel> image) |
|
|
|
/// <param name="configuration">The global configuration</param>
|
|
|
|
private void Write1BitPixelData<TPixel>(Stream stream, Image<TPixel> image, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, new QuantizerOptions() |
|
|
|
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration, new QuantizerOptions() |
|
|
|
{ |
|
|
|
MaxColors = 2 |
|
|
|
}); |
|
|
|
@ -648,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
|
|
|
|
Span<byte> colorPalette = colorPaletteBuffer.GetSpan(); |
|
|
|
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); |
|
|
|
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); |
|
|
|
|
|
|
|
ReadOnlySpan<byte> quantizedPixelRow = quantized.DangerousGetRowSpan(0); |
|
|
|
int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; |
|
|
|
@ -683,11 +687,12 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals |
|
|
|
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
|
|
|
|
/// <param name="quantizedColorPalette">The color palette from the quantized image.</param>
|
|
|
|
/// <param name="colorPalette">A temporary byte span to write the color palette to.</param>
|
|
|
|
private void WriteColorPalette<TPixel>(Stream stream, ReadOnlySpan<TPixel> quantizedColorPalette, Span<byte> colorPalette) |
|
|
|
/// <param name="configuration">The global configuration</param>
|
|
|
|
private void WriteColorPalette<TPixel>(Stream stream, ReadOnlySpan<TPixel> quantizedColorPalette, Span<byte> colorPalette, Configuration configuration) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
int quantizedColorBytes = quantizedColorPalette.Length * 4; |
|
|
|
PixelOperations<TPixel>.Instance.ToBgra32(this.configuration, quantizedColorPalette, MemoryMarshal.Cast<byte, Bgra32>(colorPalette[..quantizedColorBytes])); |
|
|
|
PixelOperations<TPixel>.Instance.ToBgra32(configuration, quantizedColorPalette, MemoryMarshal.Cast<byte, Bgra32>(colorPalette[..quantizedColorBytes])); |
|
|
|
Span<uint> colorPaletteAsUInt = MemoryMarshal.Cast<byte, uint>(colorPalette); |
|
|
|
for (int i = 0; i < colorPaletteAsUInt.Length; i++) |
|
|
|
{ |
|
|
|
|