Browse Source

Remove nullable disable from Formats.BMP

* Removed nullable disable
* refactored stream usage. It is now always passed as a parameter and not stored a a instance variable
pull/2345/head
Stefan Nikolei 3 years ago
parent
commit
3182f935dc
  1. 3
      src/ImageSharp/Color/Color.cs
  2. 179
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 5
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  4. 2
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
  5. 4
      src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs
  6. 4
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
  7. 2
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs
  8. 6
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
  9. 2
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
  10. 4
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
  11. 2
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

3
src/ImageSharp/Color/Color.cs

@ -286,12 +286,11 @@ public readonly partial struct Color : IEquatable<Color>
/// Bulk converts a span of <see cref="Color"/> to a span of a specified <typeparamref name="TPixel"/> type. /// Bulk converts a span of <see cref="Color"/> to a span of a specified <typeparamref name="TPixel"/> type.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam> /// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source color span.</param> /// <param name="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param> /// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
#pragma warning disable RCS1163 // Unused parameter. #pragma warning disable RCS1163 // Unused parameter.
public static void ToPixel<TPixel>(Configuration configuration, ReadOnlySpan<Color> source, Span<TPixel> destination) public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination)
#pragma warning restore RCS1163 // Unused parameter. #pragma warning restore RCS1163 // Unused parameter.
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {

179
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Common.Helpers;
@ -58,20 +58,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
private const int RleDelta = 0x02; private const int RleDelta = 0x02;
/// <summary>
/// The stream to decode from.
/// </summary>
private BufferedReadStream stream;
/// <summary> /// <summary>
/// The metadata. /// The metadata.
/// </summary> /// </summary>
private ImageMetadata metadata; private ImageMetadata? metadata;
/// <summary> /// <summary>
/// The bitmap specific metadata. /// The bitmap specific metadata.
/// </summary> /// </summary>
private BmpMetadata bmpMetadata; private BmpMetadata? bmpMetadata;
/// <summary> /// <summary>
/// The file header containing general information. /// The file header containing general information.
@ -126,7 +121,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Image<TPixel> image = null; Image<TPixel>? image = null;
try try
{ {
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
@ -142,24 +137,25 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3) if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
{ {
this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
else else
{ {
this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
} }
else if (this.infoHeader.BitsPerPixel == 24) else if (this.infoHeader.BitsPerPixel == 24)
{ {
this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
else if (this.infoHeader.BitsPerPixel == 16) else if (this.infoHeader.BitsPerPixel == 16)
{ {
this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
else if (this.infoHeader.BitsPerPixel <= 8) else if (this.infoHeader.BitsPerPixel <= 8)
{ {
this.ReadRgbPalette( this.ReadRgbPalette(
stream,
pixels, pixels,
palette, palette,
this.infoHeader.Width, this.infoHeader.Width,
@ -172,19 +168,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break; break;
case BmpCompression.RLE24: case BmpCompression.RLE24:
this.ReadRle24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRle24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
break; break;
case BmpCompression.RLE8: case BmpCompression.RLE8:
case BmpCompression.RLE4: case BmpCompression.RLE4:
this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRle(stream, this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break; break;
case BmpCompression.BitFields: case BmpCompression.BitFields:
case BmpCompression.BI_ALPHABITFIELDS: case BmpCompression.BI_ALPHABITFIELDS:
this.ReadBitFields(pixels, inverted); this.ReadBitFields(stream, pixels, inverted);
break; break;
@ -250,14 +246,16 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// which will be used to determine which bits belong to that channel. /// which will be used to determine which bits belong to that channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The output pixel buffer containing the decoded image.</param> /// <param name="pixels">The output pixel buffer containing the decoded image.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadBitFields<TPixel>(Buffer2D<TPixel> pixels, bool inverted) private void ReadBitFields<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
if (this.infoHeader.BitsPerPixel == 16) if (this.infoHeader.BitsPerPixel == 16)
{ {
this.ReadRgb16( this.ReadRgb16(
stream,
pixels, pixels,
this.infoHeader.Width, this.infoHeader.Width,
this.infoHeader.Height, this.infoHeader.Height,
@ -269,6 +267,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
else else
{ {
this.ReadRgb32BitFields( this.ReadRgb32BitFields(
stream,
pixels, pixels,
this.infoHeader.Width, this.infoHeader.Width,
this.infoHeader.Height, this.infoHeader.Height,
@ -282,17 +281,17 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data. /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data.
/// Compressed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte}, Span{bool}, Span{bool})"/> /// Compressed RLE4 stream is uncompressed by <see cref="UncompressRle4(BufferedReadStream, int, Span{byte}, Span{bool}, Span{bool})"/>
/// Compressed RLE4 stream is uncompressed by <see cref="UncompressRle4(int, Span{byte}, Span{bool}, Span{bool})"/>
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="compression">The compression type. Either RLE4 or RLE8.</param> /// <param name="compression">The compression type. Either RLE4 or RLE8.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param> /// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle<TPixel>(BmpCompression compression, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted) private void ReadRle<TPixel>(BufferedReadStream stream, BmpCompression compression, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
@ -305,11 +304,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
Span<byte> bufferSpan = buffer.Memory.Span; Span<byte> bufferSpan = buffer.Memory.Span;
if (compression is BmpCompression.RLE8) if (compression is BmpCompression.RLE8)
{ {
this.UncompressRle8(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
} }
else else
{ {
this.UncompressRle4(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
} }
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
@ -368,11 +367,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Looks up color values and builds the image from de-compressed RLE24. /// Looks up color values and builds the image from de-compressed RLE24.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted) private void ReadRle24<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
@ -384,7 +384,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span; Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span;
Span<byte> bufferSpan = buffer.GetSpan(); Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle24(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
@ -446,18 +446,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <br/>If first byte is 0, the second byte may have special meaning. /// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and second byte contains two color indexes. /// <br/>Otherwise, the first byte is the length of the run and second byte contains two color indexes.
/// </remarks> /// </remarks>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="w">The width of the bitmap.</param> /// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param> /// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="undefinedPixels">Keeps track over skipped and therefore undefined pixels.</param> /// <param name="undefinedPixels">Keeps track over skipped and therefore undefined pixels.</param>
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param> /// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
private void UncompressRle4(int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels) private void UncompressRle4(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
{ {
Span<byte> cmd = stackalloc byte[2]; Span<byte> cmd = stackalloc byte[2];
int count = 0; int count = 0;
while (count < buffer.Length) while (count < buffer.Length)
{ {
if (this.stream.Read(cmd, 0, cmd.Length) != 2) if (stream.Read(cmd, 0, cmd.Length) != 2)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap."); BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap.");
} }
@ -478,8 +479,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break; break;
case RleDelta: case RleDelta:
int dx = this.stream.ReadByte(); int dx = stream.ReadByte();
int dy = this.stream.ReadByte(); int dy = stream.ReadByte();
count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels);
break; break;
@ -492,7 +493,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
byte[] run = new byte[bytesToRead]; byte[] run = new byte[bytesToRead];
this.stream.Read(run, 0, run.Length); stream.Read(run, 0, run.Length);
int idx = 0; int idx = 0;
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
@ -512,7 +513,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Absolute mode data is aligned to two-byte word-boundary. // Absolute mode data is aligned to two-byte word-boundary.
int padding = bytesToRead & 1; int padding = bytesToRead & 1;
this.stream.Skip(padding); stream.Skip(padding);
break; break;
} }
@ -551,18 +552,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <br/>If first byte is 0, the second byte may have special meaning. /// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and second byte is the color for the run. /// <br/>Otherwise, the first byte is the length of the run and second byte is the color for the run.
/// </remarks> /// </remarks>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="w">The width of the bitmap.</param> /// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param> /// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="undefinedPixels">Keeps track of skipped and therefore undefined pixels.</param> /// <param name="undefinedPixels">Keeps track of skipped and therefore undefined pixels.</param>
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param> /// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
private void UncompressRle8(int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels) private void UncompressRle8(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
{ {
Span<byte> cmd = stackalloc byte[2]; Span<byte> cmd = stackalloc byte[2];
int count = 0; int count = 0;
while (count < buffer.Length) while (count < buffer.Length)
{ {
if (this.stream.Read(cmd, 0, cmd.Length) != 2) if (stream.Read(cmd, 0, cmd.Length) != 2)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap."); BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap.");
} }
@ -583,8 +585,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break; break;
case RleDelta: case RleDelta:
int dx = this.stream.ReadByte(); int dx = stream.ReadByte();
int dy = this.stream.ReadByte(); int dy = stream.ReadByte();
count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels);
break; break;
@ -596,7 +598,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
byte[] run = new byte[length]; byte[] run = new byte[length];
this.stream.Read(run, 0, run.Length); stream.Read(run, 0, run.Length);
run.AsSpan().CopyTo(buffer[count..]); run.AsSpan().CopyTo(buffer[count..]);
@ -605,7 +607,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Absolute mode data is aligned to two-byte word-boundary. // Absolute mode data is aligned to two-byte word-boundary.
int padding = length & 1; int padding = length & 1;
this.stream.Skip(padding); stream.Skip(padding);
break; break;
} }
@ -630,18 +632,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <br/>If first byte is 0, the second byte may have special meaning. /// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and following three bytes are the color for the run. /// <br/>Otherwise, the first byte is the length of the run and following three bytes are the color for the run.
/// </remarks> /// </remarks>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="w">The width of the bitmap.</param> /// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param> /// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="undefinedPixels">Keeps track of skipped and therefore undefined pixels.</param> /// <param name="undefinedPixels">Keeps track of skipped and therefore undefined pixels.</param>
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param> /// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
private void UncompressRle24(int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels) private void UncompressRle24(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
{ {
Span<byte> cmd = stackalloc byte[2]; Span<byte> cmd = stackalloc byte[2];
int uncompressedPixels = 0; int uncompressedPixels = 0;
while (uncompressedPixels < buffer.Length) while (uncompressedPixels < buffer.Length)
{ {
if (this.stream.Read(cmd, 0, cmd.Length) != 2) if (stream.Read(cmd, 0, cmd.Length) != 2)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap."); BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap.");
} }
@ -662,8 +665,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break; break;
case RleDelta: case RleDelta:
int dx = this.stream.ReadByte(); int dx = stream.ReadByte();
int dy = this.stream.ReadByte(); int dy = stream.ReadByte();
uncompressedPixels += RleSkipDelta(uncompressedPixels, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); uncompressedPixels += RleSkipDelta(uncompressedPixels, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels);
break; break;
@ -675,7 +678,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
byte[] run = new byte[length * 3]; byte[] run = new byte[length * 3];
this.stream.Read(run, 0, run.Length); stream.Read(run, 0, run.Length);
run.AsSpan().CopyTo(buffer[(uncompressedPixels * 3)..]); run.AsSpan().CopyTo(buffer[(uncompressedPixels * 3)..]);
@ -684,7 +687,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Absolute mode data is aligned to two-byte word-boundary. // Absolute mode data is aligned to two-byte word-boundary.
int padding = run.Length & 1; int padding = run.Length & 1;
this.stream.Skip(padding); stream.Skip(padding);
break; break;
} }
@ -693,8 +696,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
int max = uncompressedPixels + cmd[0]; int max = uncompressedPixels + cmd[0];
byte blueIdx = cmd[1]; byte blueIdx = cmd[1];
byte greenIdx = (byte)this.stream.ReadByte(); byte greenIdx = (byte)stream.ReadByte();
byte redIdx = (byte)this.stream.ReadByte(); byte redIdx = (byte)stream.ReadByte();
int bufferIdx = uncompressedPixels * 3; int bufferIdx = uncompressedPixels * 3;
for (; uncompressedPixels < max; uncompressedPixels++) for (; uncompressedPixels < max; uncompressedPixels++)
@ -800,6 +803,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the color palette from the stream. /// Reads the color palette from the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param> /// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
@ -808,7 +812,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="bytesPerColorMapEntry">Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps /// <param name="bytesPerColorMapEntry">Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.</param> /// the bytes per color palette entry's can be 3 bytes instead of 4.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) private void ReadRgbPalette<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// Pixels per byte (bits per pixel). // Pixels per byte (bits per pixel).
@ -833,7 +837,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
if (this.stream.Read(rowSpan) == 0) if (stream.Read(rowSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -861,6 +865,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the 16 bit color palette from the stream. /// Reads the 16 bit color palette from the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
@ -868,7 +873,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="redMask">The bitmask for the red channel.</param> /// <param name="redMask">The bitmask for the red channel.</param>
/// <param name="greenMask">The bitmask for the green channel.</param> /// <param name="greenMask">The bitmask for the green channel.</param>
/// <param name="blueMask">The bitmask for the blue channel.</param> /// <param name="blueMask">The bitmask for the blue channel.</param>
private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask) private void ReadRgb16<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int padding = CalculatePadding(width, 2); int padding = CalculatePadding(width, 2);
@ -889,7 +894,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(bufferSpan) == 0) if (stream.Read(bufferSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -935,11 +940,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the 24 bit color palette from the stream. /// Reads the 24 bit color palette from the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted) private void ReadRgb24<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int padding = CalculatePadding(width, 3); int padding = CalculatePadding(width, 3);
@ -948,7 +954,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(rowSpan) == 0) if (stream.Read(rowSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -967,11 +973,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the 32 bit color palette from the stream. /// Reads the 32 bit color palette from the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32Fast<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted) private void ReadRgb32Fast<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int padding = CalculatePadding(width, 4); int padding = CalculatePadding(width, 4);
@ -980,7 +987,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(rowSpan) == 0) if (stream.Read(rowSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -1000,11 +1007,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format. /// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted) private void ReadRgb32Slow<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int padding = CalculatePadding(width, 4); int padding = CalculatePadding(width, 4);
@ -1012,7 +1020,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
using IMemoryOwner<Bgra32> bgraRow = this.memoryAllocator.Allocate<Bgra32>(width); using IMemoryOwner<Bgra32> bgraRow = this.memoryAllocator.Allocate<Bgra32>(width);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
Span<Bgra32> bgraRowSpan = bgraRow.GetSpan(); Span<Bgra32> bgraRowSpan = bgraRow.GetSpan();
long currentPosition = this.stream.Position; long currentPosition = stream.Position;
bool hasAlpha = false; bool hasAlpha = false;
// Loop though the rows checking each pixel. We start by assuming it's // Loop though the rows checking each pixel. We start by assuming it's
@ -1020,7 +1028,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// actually a BGRA image, and change tactics accordingly. // actually a BGRA image, and change tactics accordingly.
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(rowSpan) == 0) if (stream.Read(rowSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -1049,14 +1057,14 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
} }
// Reset our stream for a second pass. // Reset our stream for a second pass.
this.stream.Position = currentPosition; stream.Position = currentPosition;
// Process the pixels in bulk taking the raw alpha component value. // Process the pixels in bulk taking the raw alpha component value.
if (hasAlpha) if (hasAlpha)
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(rowSpan) == 0) if (stream.Read(rowSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -1077,7 +1085,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Slow path. We need to set each alpha component value to fully opaque. // Slow path. We need to set each alpha component value to fully opaque.
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(rowSpan) == 0) if (stream.Read(rowSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -1105,6 +1113,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Decode an 32 Bit Bitmap containing a bitmask for each color channel. /// Decode an 32 Bit Bitmap containing a bitmask for each color channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The output pixel buffer containing the decoded image.</param> /// <param name="pixels">The output pixel buffer containing the decoded image.</param>
/// <param name="width">The width of the image.</param> /// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param> /// <param name="height">The height of the image.</param>
@ -1113,7 +1122,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="greenMask">The bitmask for the green channel.</param> /// <param name="greenMask">The bitmask for the green channel.</param>
/// <param name="blueMask">The bitmask for the blue channel.</param> /// <param name="blueMask">The bitmask for the blue channel.</param>
/// <param name="alphaMask">The bitmask for the alpha channel.</param> /// <param name="alphaMask">The bitmask for the alpha channel.</param>
private void ReadRgb32BitFields<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask) private void ReadRgb32BitFields<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
@ -1142,7 +1151,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
if (this.stream.Read(bufferSpan) == 0) if (stream.Read(bufferSpan) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
} }
@ -1228,10 +1237,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Reads the <see cref="BmpInfoHeader"/> from the stream. /// Reads the <see cref="BmpInfoHeader"/> from the stream.
/// </summary> /// </summary>
private void ReadInfoHeader() /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(bmpMetadata))]
private void ReadInfoHeader(BufferedReadStream stream)
{ {
Span<byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize]; Span<byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
long infoHeaderStart = this.stream.Position; long infoHeaderStart = stream.Position;
// Resolution is stored in PPM. // Resolution is stored in PPM.
this.metadata = new ImageMetadata this.metadata = new ImageMetadata
@ -1240,7 +1252,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
}; };
// Read the header size. // Read the header size.
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize is < BmpInfoHeader.CoreSize or > BmpInfoHeader.MaxHeaderSize) if (headerSize is < BmpInfoHeader.CoreSize or > BmpInfoHeader.MaxHeaderSize)
@ -1249,7 +1261,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
} }
// Read the rest of the header. // Read the rest of the header.
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2; BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2;
if (headerSize == BmpInfoHeader.CoreSize) if (headerSize == BmpInfoHeader.CoreSize)
@ -1275,7 +1287,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
if (this.infoHeader.Compression == BmpCompression.BitFields) if (this.infoHeader.Compression == BmpCompression.BitFields)
{ {
byte[] bitfieldsBuffer = new byte[12]; byte[] bitfieldsBuffer = new byte[12];
this.stream.Read(bitfieldsBuffer, 0, 12); stream.Read(bitfieldsBuffer, 0, 12);
Span<byte> data = bitfieldsBuffer.AsSpan(); Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
@ -1284,7 +1296,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
{ {
byte[] bitfieldsBuffer = new byte[16]; byte[] bitfieldsBuffer = new byte[16];
this.stream.Read(bitfieldsBuffer, 0, 16); stream.Read(bitfieldsBuffer, 0, 16);
Span<byte> data = bitfieldsBuffer.AsSpan(); Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
@ -1324,12 +1336,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
if (this.infoHeader.ProfileData != 0 && this.infoHeader.ProfileSize != 0) if (this.infoHeader.ProfileData != 0 && this.infoHeader.ProfileSize != 0)
{ {
// Read color profile. // Read color profile.
long streamPosition = this.stream.Position; long streamPosition = stream.Position;
byte[] iccProfileData = new byte[this.infoHeader.ProfileSize]; byte[] iccProfileData = new byte[this.infoHeader.ProfileSize];
this.stream.Position = infoHeaderStart + this.infoHeader.ProfileData; stream.Position = infoHeaderStart + this.infoHeader.ProfileData;
this.stream.Read(iccProfileData); stream.Read(iccProfileData);
this.metadata.IccProfile = new IccProfile(iccProfileData); this.metadata.IccProfile = new IccProfile(iccProfileData);
this.stream.Position = streamPosition; stream.Position = streamPosition;
} }
} }
else else
@ -1358,10 +1370,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Reads the <see cref="BmpFileHeader"/> from the stream. /// Reads the <see cref="BmpFileHeader"/> from the stream.
/// </summary> /// </summary>
private void ReadFileHeader() /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadFileHeader(BufferedReadStream stream)
{ {
Span<byte> buffer = stackalloc byte[BmpFileHeader.Size]; Span<byte> buffer = stackalloc byte[BmpFileHeader.Size];
this.stream.Read(buffer, 0, BmpFileHeader.Size); stream.Read(buffer, 0, BmpFileHeader.Size);
short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer); short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer);
switch (fileTypeMarker) switch (fileTypeMarker)
@ -1375,7 +1388,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Because we only decode the first bitmap in the array, the array header will be ignored. // Because we only decode the first bitmap in the array, the array header will be ignored.
// The bitmap file header of the first image follows the array header. // The bitmap file header of the first image follows the array header.
this.stream.Read(buffer, 0, BmpFileHeader.Size); stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer); this.fileHeader = BmpFileHeader.Parse(buffer);
if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
{ {
@ -1398,12 +1411,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="palette">The color palette.</param> /// <param name="palette">The color palette.</param>
/// <returns>Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps /// <returns>Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.</returns> /// the bytes per color palette entry's can be 3 bytes instead of 4.</returns>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(bmpMetadata))]
private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette) private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette)
{ {
this.stream = stream; this.ReadFileHeader(stream);
this.ReadInfoHeader(stream);
this.ReadFileHeader();
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin // If the height is negative, then this is a Windows bitmap whose origin
@ -1451,13 +1464,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry; colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
} }
palette = null; palette = Array.Empty<byte>();
if (colorMapSizeBytes > 0) if (colorMapSizeBytes > 0)
{ {
// Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit.
// Make sure, that we will not read pass the bitmap offset (starting position of image data). // Make sure, that we will not read pass the bitmap offset (starting position of image data).
if ((this.stream.Position + colorMapSizeBytes) > this.fileHeader.Offset) if ((stream.Position + colorMapSizeBytes) > this.fileHeader.Offset)
{ {
BmpThrowHelper.ThrowInvalidImageContentException( BmpThrowHelper.ThrowInvalidImageContentException(
$"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset."); $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset.");
@ -1465,21 +1478,21 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
palette = new byte[colorMapSizeBytes]; palette = new byte[colorMapSizeBytes];
if (this.stream.Read(palette, 0, colorMapSizeBytes) == 0) if (stream.Read(palette, 0, colorMapSizeBytes) == 0)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for the palette!"); BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for the palette!");
} }
} }
int skipAmount = this.fileHeader.Offset - (int)this.stream.Position; int skipAmount = this.fileHeader.Offset - (int)stream.Position;
if ((skipAmount + (int)this.stream.Position) > this.stream.Length) if ((skipAmount + (int)stream.Position) > stream.Length)
{ {
BmpThrowHelper.ThrowInvalidImageContentException("Invalid file header offset found. Offset is greater than the stream length."); BmpThrowHelper.ThrowInvalidImageContentException("Invalid file header offset found. Offset is greater than the stream length.");
} }
if (skipAmount > 0) if (skipAmount > 0)
{ {
this.stream.Skip(skipAmount); stream.Skip(skipAmount);
} }
return bytesPerColorMapEntry; return bytesPerColorMapEntry;

5
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
@ -72,7 +71,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// <summary> /// <summary>
/// The global configuration. /// The global configuration.
/// </summary> /// </summary>
private Configuration configuration; private Configuration? configuration;
/// <summary> /// <summary>
/// The color depth, in number of bits per pixel. /// The color depth, in number of bits per pixel.
@ -142,7 +141,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
_ => 0 _ => 0
}; };
byte[] iccProfileData = null; byte[] iccProfileData = Array.Empty<byte>();
int iccProfileSize = 0; int iccProfileSize = 0;
if (metadata.IccProfile != null) if (metadata.IccProfile != null)
{ {

2
src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs

@ -35,7 +35,7 @@ internal sealed class PaletteDitherProcessor<TPixel> : ImageProcessor<TPixel>
ReadOnlySpan<Color> sourcePalette = definition.Palette.Span; ReadOnlySpan<Color> sourcePalette = definition.Palette.Span;
this.paletteOwner = this.Configuration.MemoryAllocator.Allocate<TPixel>(sourcePalette.Length); this.paletteOwner = this.Configuration.MemoryAllocator.Allocate<TPixel>(sourcePalette.Length);
Color.ToPixel(this.Configuration, sourcePalette, this.paletteOwner.Memory.Span); Color.ToPixel(sourcePalette, this.paletteOwner.Memory.Span);
this.ditherProcessor = new DitherProcessor( this.ditherProcessor = new DitherProcessor(
this.Configuration, this.Configuration,

4
src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs

@ -21,7 +21,7 @@ public interface IQuantizer
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param> /// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>The <see cref="IQuantizer{TPixel}"/>.</returns> /// <returns>The <see cref="IQuantizer{TPixel}"/>.</returns>
IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration) IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration)
where TPixel : unmanaged, IPixel<TPixel>; where TPixel : unmanaged, IPixel<TPixel>;
/// <summary> /// <summary>
@ -31,6 +31,6 @@ public interface IQuantizer
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param> /// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="options">The options to create the quantizer with.</param> /// <param name="options">The options to create the quantizer with.</param>
/// <returns>The <see cref="IQuantizer{TPixel}"/>.</returns> /// <returns>The <see cref="IQuantizer{TPixel}"/>.</returns>
IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration, QuantizerOptions options) IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration, QuantizerOptions options)
where TPixel : unmanaged, IPixel<TPixel>; where TPixel : unmanaged, IPixel<TPixel>;
} }

4
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs

@ -34,12 +34,12 @@ public class OctreeQuantizer : IQuantizer
public QuantizerOptions Options { get; } public QuantizerOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration) public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> this.CreatePixelSpecificQuantizer<TPixel>(configuration, this.Options); => this.CreatePixelSpecificQuantizer<TPixel>(configuration, this.Options);
/// <inheritdoc /> /// <inheritdoc />
public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration, QuantizerOptions options) public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration, QuantizerOptions options)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> new OctreeQuantizer<TPixel>(configuration, options); => new OctreeQuantizer<TPixel>(configuration, options);
} }

2
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

@ -38,7 +38,7 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="options">The quantizer options defining quantization rules.</param> /// <param name="options">The quantizer options defining quantization rules.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public OctreeQuantizer(Configuration configuration, QuantizerOptions options) public OctreeQuantizer(Configuration? configuration, QuantizerOptions options)
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));

6
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs

@ -39,19 +39,19 @@ public class PaletteQuantizer : IQuantizer
public QuantizerOptions Options { get; } public QuantizerOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration) public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> this.CreatePixelSpecificQuantizer<TPixel>(configuration, this.Options); => this.CreatePixelSpecificQuantizer<TPixel>(configuration, this.Options);
/// <inheritdoc /> /// <inheritdoc />
public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration, QuantizerOptions options) public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration, QuantizerOptions options)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
// Always use the palette length over options since the palette cannot be reduced. // Always use the palette length over options since the palette cannot be reduced.
TPixel[] palette = new TPixel[this.colorPalette.Length]; TPixel[] palette = new TPixel[this.colorPalette.Length];
Color.ToPixel(configuration, this.colorPalette.Span, palette.AsSpan()); Color.ToPixel(this.colorPalette.Span, palette.AsSpan());
return new PaletteQuantizer<TPixel>(configuration, options, palette); return new PaletteQuantizer<TPixel>(configuration, options, palette);
} }
} }

2
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs

@ -29,7 +29,7 @@ internal struct PaletteQuantizer<TPixel> : IQuantizer<TPixel>
/// <param name="options">The quantizer options defining quantization rules.</param> /// <param name="options">The quantizer options defining quantization rules.</param>
/// <param name="palette">The palette to use.</param> /// <param name="palette">The palette to use.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public PaletteQuantizer(Configuration configuration, QuantizerOptions options, ReadOnlyMemory<TPixel> palette) public PaletteQuantizer(Configuration? configuration, QuantizerOptions options, ReadOnlyMemory<TPixel> palette)
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));

4
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs

@ -33,12 +33,12 @@ public class WuQuantizer : IQuantizer
public QuantizerOptions Options { get; } public QuantizerOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration) public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> this.CreatePixelSpecificQuantizer<TPixel>(configuration, this.Options); => this.CreatePixelSpecificQuantizer<TPixel>(configuration, this.Options);
/// <inheritdoc /> /// <inheritdoc />
public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration configuration, QuantizerOptions options) public IQuantizer<TPixel> CreatePixelSpecificQuantizer<TPixel>(Configuration? configuration, QuantizerOptions options)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> new WuQuantizer<TPixel>(configuration, options); => new WuQuantizer<TPixel>(configuration, options);
} }

2
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

@ -85,7 +85,7 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="options">The quantizer options defining quantization rules.</param> /// <param name="options">The quantizer options defining quantization rules.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public WuQuantizer(Configuration configuration, QuantizerOptions options) public WuQuantizer(Configuration? configuration, QuantizerOptions options)
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));

Loading…
Cancel
Save