|
|
|
@ -2,6 +2,8 @@ |
|
|
|
// Licensed under the Six Labors Split License.
|
|
|
|
|
|
|
|
using System.Buffers; |
|
|
|
using System.Buffers.Binary; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
using SixLabors.ImageSharp.IO; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
@ -13,21 +15,25 @@ namespace SixLabors.ImageSharp.Formats.Pbm; |
|
|
|
/// </summary>
|
|
|
|
internal class BinaryDecoder |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// The luminance value written for an unset bit in the black and white format.
|
|
|
|
/// </summary>
|
|
|
|
private static L8 white = new(255); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The luminance value written for a set bit in the black and white format.
|
|
|
|
/// </summary>
|
|
|
|
private static L8 black = new(0); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decode the specified pixels.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to encode to.</typeparam>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to decode to.</typeparam>
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
|
/// <param name="pixels">The pixel array to encode into.</param>
|
|
|
|
/// <param name="pixels">The pixel buffer to decode into.</param>
|
|
|
|
/// <param name="stream">The stream to read the data from.</param>
|
|
|
|
/// <param name="colorType">The ColorType to decode.</param>
|
|
|
|
/// <param name="componentType">Data type of the pixles components.</param>
|
|
|
|
/// <exception cref="InvalidImageContentException">
|
|
|
|
/// Thrown if an invalid combination of setting is requested.
|
|
|
|
/// </exception>
|
|
|
|
/// <param name="colorType">The color type of the encoded pixels.</param>
|
|
|
|
/// <param name="componentType">The data type of the pixel components.</param>
|
|
|
|
public static void Process<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream, PbmColorType colorType, PbmComponentType componentType) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
@ -59,6 +65,15 @@ internal class BinaryDecoder |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decodes 8-bit binary grayscale (PGM) pixel data.
|
|
|
|
/// Each pixel is a single byte that holds its luminance value.
|
|
|
|
/// When the stream ends early, the rows that were not read keep their default value.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to decode to.</typeparam>
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
|
/// <param name="pixels">The pixel buffer to decode into.</param>
|
|
|
|
/// <param name="stream">The stream to read the data from.</param>
|
|
|
|
private static void ProcessGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
@ -85,6 +100,15 @@ internal class BinaryDecoder |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decodes 16-bit binary grayscale (PGM) pixel data.
|
|
|
|
/// Each pixel is one 16-bit sample, stored most significant byte first.
|
|
|
|
/// When the stream ends early, the rows that were not read keep their default value.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to decode to.</typeparam>
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
|
/// <param name="pixels">The pixel buffer to decode into.</param>
|
|
|
|
/// <param name="stream">The stream to read the data from.</param>
|
|
|
|
private static void ProcessWideGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
@ -102,6 +126,10 @@ internal class BinaryDecoder |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// The binary format stores 16-bit samples most significant byte first,
|
|
|
|
// but L16 expects native (little-endian) byte order.
|
|
|
|
SwapSampleBytes(rowSpan); |
|
|
|
|
|
|
|
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.FromL16Bytes( |
|
|
|
configuration, |
|
|
|
@ -111,6 +139,15 @@ internal class BinaryDecoder |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decodes 8-bit binary color (PPM) pixel data.
|
|
|
|
/// Each pixel is three bytes in red, green, blue order.
|
|
|
|
/// When the stream ends early, the rows that were not read keep their default value.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to decode to.</typeparam>
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
|
/// <param name="pixels">The pixel buffer to decode into.</param>
|
|
|
|
/// <param name="stream">The stream to read the data from.</param>
|
|
|
|
private static void ProcessRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
@ -137,6 +174,15 @@ internal class BinaryDecoder |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decodes 16-bit binary color (PPM) pixel data.
|
|
|
|
/// Each pixel is three 16-bit samples in red, green, blue order, stored most significant byte first.
|
|
|
|
/// When the stream ends early, the rows that were not read keep their default value.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to decode to.</typeparam>
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
|
/// <param name="pixels">The pixel buffer to decode into.</param>
|
|
|
|
/// <param name="stream">The stream to read the data from.</param>
|
|
|
|
private static void ProcessWideRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
@ -154,6 +200,10 @@ internal class BinaryDecoder |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// The binary format stores 16-bit samples most significant byte first,
|
|
|
|
// but Rgb48 expects native (little-endian) byte order.
|
|
|
|
SwapSampleBytes(rowSpan); |
|
|
|
|
|
|
|
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.FromRgb48Bytes( |
|
|
|
configuration, |
|
|
|
@ -163,6 +213,30 @@ internal class BinaryDecoder |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reverses the byte order of each 16-bit sample in the given row when the host is little-endian.
|
|
|
|
/// The binary PGM and PPM formats store multi-byte samples most significant byte first.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="rowSpan">The row of big-endian sample data to convert in place.</param>
|
|
|
|
private static void SwapSampleBytes(Span<byte> rowSpan) |
|
|
|
{ |
|
|
|
if (BitConverter.IsLittleEndian) |
|
|
|
{ |
|
|
|
Span<ushort> samples = MemoryMarshal.Cast<byte, ushort>(rowSpan); |
|
|
|
BinaryPrimitives.ReverseEndianness(samples, samples); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decodes binary black and white (PBM) pixel data.
|
|
|
|
/// Each byte holds eight pixels, most significant bit first, and a set bit means black.
|
|
|
|
/// Each row starts on a byte boundary, so the last byte of a row can hold unused bits.
|
|
|
|
/// When the stream ends early, the pixels that were not read keep their default value.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The type of pixel to decode to.</typeparam>
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
|
/// <param name="pixels">The pixel buffer to decode into.</param>
|
|
|
|
/// <param name="stream">The stream to read the data from.</param>
|
|
|
|
private static void ProcessBlackAndWhite<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
|