Browse Source

Merge branch 'master' into bp/removeRunSerial

pull/1885/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
edf7014ecb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 3
      src/ImageSharp/Configuration.cs
  3. 104
      src/ImageSharp/Formats/ImageExtensions.Save.cs
  4. 1
      src/ImageSharp/Formats/ImageExtensions.Save.tt
  5. 194
      src/ImageSharp/Formats/Pbm/BinaryDecoder.cs
  6. 208
      src/ImageSharp/Formats/Pbm/BinaryEncoder.cs
  7. 65
      src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs
  8. 26
      src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs
  9. 21
      src/ImageSharp/Formats/Pbm/MetadataExtensions.cs
  10. 26
      src/ImageSharp/Formats/Pbm/PbmColorType.cs
  11. 26
      src/ImageSharp/Formats/Pbm/PbmComponentType.cs
  12. 19
      src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs
  13. 28
      src/ImageSharp/Formats/Pbm/PbmConstants.cs
  14. 79
      src/ImageSharp/Formats/Pbm/PbmDecoder.cs
  15. 195
      src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
  16. 69
      src/ImageSharp/Formats/Pbm/PbmEncoder.cs
  17. 187
      src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
  18. 21
      src/ImageSharp/Formats/Pbm/PbmEncoding.cs
  19. 37
      src/ImageSharp/Formats/Pbm/PbmFormat.cs
  20. 36
      src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs
  21. 46
      src/ImageSharp/Formats/Pbm/PbmMetadata.cs
  22. 198
      src/ImageSharp/Formats/Pbm/PlainDecoder.cs
  23. 251
      src/ImageSharp/Formats/Pbm/PlainEncoder.cs
  24. 2
      src/ImageSharp/ImageSharp.csproj
  25. 2
      tests/ImageSharp.Tests/ConfigurationTests.cs
  26. 9
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  27. 3
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  28. 155
      tests/ImageSharp.Tests/Formats/Pbm/ImageExtensionsTest.cs
  29. 100
      tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs
  30. 145
      tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs
  31. 86
      tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs
  32. 69
      tests/ImageSharp.Tests/Formats/Pbm/PbmRoundTripTests.cs
  33. 96
      tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
  34. 4
      tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs
  35. 8
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
  36. 67
      tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs
  37. 2
      tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
  38. 15
      tests/ImageSharp.Tests/TestImages.cs
  39. 4
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
  40. 2
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
  41. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L16_Gene-UP WebSocket RunImageMask.png
  42. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_blackandwhite_binary.png
  43. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_blackandwhite_plain.png
  44. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_grayscale_plain.png
  45. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_grayscale_plain_normalized.png
  46. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_rings.png
  47. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb24_00000_00000.png
  48. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb24_rgb_plain.png
  49. 3
      tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb24_rgb_plain_normalized.png
  50. 3
      tests/Images/Input/Pbm/00000_00000.ppm
  51. 3
      tests/Images/Input/Pbm/Gene-UP WebSocket RunImageMask.pgm
  52. 3
      tests/Images/Input/Pbm/blackandwhite_binary.pbm
  53. 3
      tests/Images/Input/Pbm/blackandwhite_plain.pbm
  54. 3
      tests/Images/Input/Pbm/grayscale_plain.pgm
  55. 3
      tests/Images/Input/Pbm/grayscale_plain_magick.pgm
  56. 3
      tests/Images/Input/Pbm/grayscale_plain_normalized.pgm
  57. 3
      tests/Images/Input/Pbm/rgb_plain.ppm
  58. 3
      tests/Images/Input/Pbm/rgb_plain_magick.ppm
  59. 3
      tests/Images/Input/Pbm/rgb_plain_normalized.ppm
  60. 3
      tests/Images/Input/Pbm/rings.pgm

5
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
@ -200,6 +201,7 @@ namespace SixLabors.ImageSharp.Advanced
default(BmpEncoderCore).Encode<TPixel>(default, default, default);
default(GifEncoderCore).Encode<TPixel>(default, default, default);
default(JpegEncoderCore).Encode<TPixel>(default, default, default);
default(PbmEncoderCore).Encode<TPixel>(default, default, default);
default(PngEncoderCore).Encode<TPixel>(default, default, default);
default(TgaEncoderCore).Encode<TPixel>(default, default, default);
default(TiffEncoderCore).Encode<TPixel>(default, default, default);
@ -217,6 +219,7 @@ namespace SixLabors.ImageSharp.Advanced
default(BmpDecoderCore).Decode<TPixel>(default, default, default);
default(GifDecoderCore).Decode<TPixel>(default, default, default);
default(JpegDecoderCore).Decode<TPixel>(default, default, default);
default(PbmDecoderCore).Decode<TPixel>(default, default, default);
default(PngDecoderCore).Decode<TPixel>(default, default, default);
default(TgaDecoderCore).Decode<TPixel>(default, default, default);
default(TiffDecoderCore).Decode<TPixel>(default, default, default);
@ -234,6 +237,7 @@ namespace SixLabors.ImageSharp.Advanced
AotCompileImageEncoder<TPixel, BmpEncoder>();
AotCompileImageEncoder<TPixel, GifEncoder>();
AotCompileImageEncoder<TPixel, JpegEncoder>();
AotCompileImageEncoder<TPixel, PbmEncoder>();
AotCompileImageEncoder<TPixel, PngEncoder>();
AotCompileImageEncoder<TPixel, TgaEncoder>();
AotCompileImageEncoder<TPixel, TiffEncoder>();
@ -251,6 +255,7 @@ namespace SixLabors.ImageSharp.Advanced
AotCompileImageDecoder<TPixel, BmpDecoder>();
AotCompileImageDecoder<TPixel, GifDecoder>();
AotCompileImageDecoder<TPixel, JpegDecoder>();
AotCompileImageDecoder<TPixel, PbmDecoder>();
AotCompileImageDecoder<TPixel, PngDecoder>();
AotCompileImageDecoder<TPixel, TgaDecoder>();
AotCompileImageDecoder<TPixel, TiffDecoder>();

3
src/ImageSharp/Configuration.cs

@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
@ -209,6 +210,7 @@ namespace SixLabors.ImageSharp
/// <see cref="JpegConfigurationModule"/>
/// <see cref="GifConfigurationModule"/>
/// <see cref="BmpConfigurationModule"/>.
/// <see cref="PbmConfigurationModule"/>.
/// <see cref="TgaConfigurationModule"/>.
/// <see cref="TiffConfigurationModule"/>.
/// <see cref="WebpConfigurationModule"/>.
@ -219,6 +221,7 @@ namespace SixLabors.ImageSharp
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule(),
new PbmConfigurationModule(),
new TgaConfigurationModule(),
new TiffConfigurationModule(),
new WebpConfigurationModule());

104
src/ImageSharp/Formats/ImageExtensions.Save.cs

@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Webp;
@ -331,6 +332,109 @@ namespace SixLabors.ImageSharp
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsPbm(this Image source, string path) => SaveAsPbm(source, path, null);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, string path) => SaveAsPbmAsync(source, path, null);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsPbmAsync(source, path, null, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsPbm(this Image source, string path, PbmEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPbm(this Image source, Stream stream)
=> SaveAsPbm(source, stream, null);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsPbmAsync(source, stream, null, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static void SaveAsPbm(this Image source, Stream stream, PbmEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Pbm format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Png format.
/// </summary>

1
src/ImageSharp/Formats/ImageExtensions.Save.tt

@ -15,6 +15,7 @@ using SixLabors.ImageSharp.Advanced;
"Bmp",
"Gif",
"Jpeg",
"Pbm",
"Png",
"Tga",
"Webp",

194
src/ImageSharp/Formats/Pbm/BinaryDecoder.cs

@ -0,0 +1,194 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Pixel decoding methods for the PBM binary encoding.
/// </summary>
internal class BinaryDecoder
{
private static L8 white = new(255);
private static L8 black = new(0);
/// <summary>
/// Decode the specified pixels.
/// </summary>
/// <typeparam name="TPixel">The type of pixel to encode to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="pixels">The pixel array to encode 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>
public static void Process<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream, PbmColorType colorType, PbmComponentType componentType)
where TPixel : unmanaged, IPixel<TPixel>
{
if (colorType == PbmColorType.Grayscale)
{
if (componentType == PbmComponentType.Byte)
{
ProcessGrayscale(configuration, pixels, stream);
}
else
{
ProcessWideGrayscale(configuration, pixels, stream);
}
}
else if (colorType == PbmColorType.Rgb)
{
if (componentType == PbmComponentType.Byte)
{
ProcessRgb(configuration, pixels, stream);
}
else
{
ProcessWideRgb(configuration, pixels, stream);
}
}
else
{
ProcessBlackAndWhite(configuration, pixels, stream);
}
}
private static void ProcessGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 1;
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
stream.Read(rowSpan);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8Bytes(
configuration,
rowSpan,
pixelSpan,
width);
}
}
private static void ProcessWideGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 2;
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
stream.Read(rowSpan);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL16Bytes(
configuration,
rowSpan,
pixelSpan,
width);
}
}
private static void ProcessRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 3;
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
stream.Read(rowSpan);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromRgb24Bytes(
configuration,
rowSpan,
pixelSpan,
width);
}
}
private static void ProcessWideRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 6;
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
stream.Read(rowSpan);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromRgb48Bytes(
configuration,
rowSpan,
pixelSpan,
width);
}
}
private static void ProcessBlackAndWhite<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
int startBit = 0;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
Span<L8> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width;)
{
int raw = stream.ReadByte();
int bit = startBit;
startBit = 0;
for (; bit < 8; bit++)
{
bool bitValue = (raw & (0x80 >> bit)) != 0;
rowSpan[x] = bitValue ? black : white;
x++;
if (x == width)
{
startBit = (bit + 1) & 7; // Round off to below 8.
if (startBit != 0)
{
stream.Seek(-1, System.IO.SeekOrigin.Current);
}
break;
}
}
}
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8(
configuration,
rowSpan,
pixelSpan);
}
}
}
}

208
src/ImageSharp/Formats/Pbm/BinaryEncoder.cs

@ -0,0 +1,208 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Pixel encoding methods for the PBM binary encoding.
/// </summary>
internal class BinaryEncoder
{
/// <summary>
/// Decode pixels into the PBM binary encoding.
/// </summary>
/// <typeparam name="TPixel">The type of input pixel.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="stream">The bytestream to write to.</param>
/// <param name="image">The input image.</param>
/// <param name="colorType">The ColorType to use.</param>
/// <param name="componentType">Data type of the pixles components.</param>
/// <exception cref="InvalidImageContentException">
/// Thrown if an invalid combination of setting is requested.
/// </exception>
public static void WritePixels<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image, PbmColorType colorType, PbmComponentType componentType)
where TPixel : unmanaged, IPixel<TPixel>
{
if (colorType == PbmColorType.Grayscale)
{
if (componentType == PbmComponentType.Byte)
{
WriteGrayscale(configuration, stream, image);
}
else
{
WriteWideGrayscale(configuration, stream, image);
}
}
else if (colorType == PbmColorType.Rgb)
{
if (componentType == PbmComponentType.Byte)
{
WriteRgb(configuration, stream, image);
}
else
{
WriteWideRgb(configuration, stream, image);
}
}
else
{
WriteBlackAndWhite(configuration, stream, image);
}
}
private static void WriteGrayscale<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8Bytes(
configuration,
pixelSpan,
rowSpan,
width);
stream.Write(rowSpan);
}
}
private static void WriteWideGrayscale<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 2;
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL16Bytes(
configuration,
pixelSpan,
rowSpan,
width);
stream.Write(rowSpan);
}
}
private static void WriteRgb<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 3;
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
configuration,
pixelSpan,
rowSpan,
width);
stream.Write(rowSpan);
}
}
private static void WriteWideRgb<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
const int bytesPerPixel = 6;
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgb48Bytes(
configuration,
pixelSpan,
rowSpan,
width);
stream.Write(rowSpan);
}
}
private static void WriteBlackAndWhite<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
Span<L8> rowSpan = row.GetSpan();
int previousValue = 0;
int startBit = 0;
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(
configuration,
pixelSpan,
rowSpan);
for (int x = 0; x < width;)
{
int value = previousValue;
for (int i = startBit; i < 8; i++)
{
if (rowSpan[x].PackedValue < 128)
{
value |= 0x80 >> i;
}
x++;
if (x == width)
{
previousValue = value;
startBit = (i + 1) & 7; // Round off to below 8.
break;
}
}
if (startBit == 0)
{
stream.WriteByte((byte)value);
previousValue = 0;
}
}
}
}
}
}

65
src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Extensions methods for <see cref="BufferedReadStream"/>.
/// </summary>
internal static class BufferedReadStreamExtensions
{
/// <summary>
/// Skip over any whitespace or any comments.
/// </summary>
public static void SkipWhitespaceAndComments(this BufferedReadStream stream)
{
bool isWhitespace;
do
{
int val = stream.ReadByte();
// Comments start with '#' and end at the next new-line.
if (val == 0x23)
{
int innerValue;
do
{
innerValue = stream.ReadByte();
}
while (innerValue != 0x0a);
// Continue searching for whitespace.
val = innerValue;
}
isWhitespace = val is 0x09 or 0x0a or 0x0d or 0x20;
}
while (isWhitespace);
stream.Seek(-1, SeekOrigin.Current);
}
/// <summary>
/// Read a decimal text value.
/// </summary>
/// <returns>The integer value of the decimal.</returns>
public static int ReadDecimal(this BufferedReadStream stream)
{
int value = 0;
while (true)
{
int current = stream.ReadByte() - 0x30;
if ((uint)current > 9)
{
break;
}
value = (value * 10) + current;
}
return value;
}
}
}

26
src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Configuration options for use during PBM encoding.
/// </summary>
internal interface IPbmEncoderOptions
{
/// <summary>
/// Gets the encoding of the pixels.
/// </summary>
PbmEncoding? Encoding { get; }
/// <summary>
/// Gets the Color type of the resulting image.
/// </summary>
PbmColorType? ColorType { get; }
/// <summary>
/// Gets the Data Type of the pixel components.
/// </summary>
PbmComponentType? ComponentType { get; }
}
}

21
src/ImageSharp/Formats/Pbm/MetadataExtensions.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the pbm format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="PbmMetadata"/>.</returns>
public static PbmMetadata GetPbmMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PbmFormat.Instance);
}
}

26
src/ImageSharp/Formats/Pbm/PbmColorType.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Provides enumeration of available PBM color types.
/// </summary>
public enum PbmColorType : byte
{
/// <summary>
/// PBM
/// </summary>
BlackAndWhite = 0,
/// <summary>
/// PGM - Greyscale. Single component.
/// </summary>
Grayscale = 1,
/// <summary>
/// PPM - RGB Color. 3 components.
/// </summary>
Rgb = 2,
}
}

26
src/ImageSharp/Formats/Pbm/PbmComponentType.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// The data type of the components of the pixels.
/// </summary>
public enum PbmComponentType : byte
{
/// <summary>
/// Single bit per pixel, exclusively for <see cref="PbmColorType.BlackAndWhite"/>.
/// </summary>
Bit = 0,
/// <summary>
/// 8 bits unsigned integer per component.
/// </summary>
Byte = 1,
/// <summary>
/// 16 bits unsigned integer per component.
/// </summary>
Short = 2
}
}

19
src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the Pbm format.
/// </summary>
public sealed class PbmConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(PbmFormat.Instance, new PbmEncoder());
configuration.ImageFormatsManager.SetDecoder(PbmFormat.Instance, new PbmDecoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new PbmImageFormatDetector());
}
}
}

28
src/ImageSharp/Formats/Pbm/PbmConstants.cs

@ -0,0 +1,28 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Contains PBM constant values defined in the specification.
/// </summary>
internal static class PbmConstants
{
/// <summary>
/// The maximum allowable pixel value of a ppm image.
/// </summary>
public const ushort MaxLength = 65535;
/// <summary>
/// The list of mimetypes that equate to a ppm.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/x-portable-pixmap", "image/x-portable-anymap" };
/// <summary>
/// The list of file extensions that equate to a ppm.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "ppm", "pbm", "pgm" };
}
}

79
src/ImageSharp/Formats/Pbm/PbmDecoder.cs

@ -0,0 +1,79 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Image decoder for reading PGM, PBM or PPM bitmaps from a stream. These images are from
/// the family of PNM images.
/// <list type="bullet">
/// <item>
/// <term>PBM</term>
/// <description>Black and white images.</description>
/// </item>
/// <item>
/// <term>PGM</term>
/// <description>Grayscale images.</description>
/// </item>
/// <item>
/// <term>PPM</term>
/// <description>Color images, with RGB pixels.</description>
/// </item>
/// </list>
/// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>.
/// </summary>
public sealed class PbmDecoder : IImageDecoder, IImageInfoDetector
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(stream, nameof(stream));
var decoder = new PbmDecoderCore(configuration);
return decoder.Decode<TPixel>(configuration, stream);
}
/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream)
=> this.Decode<Rgb24>(configuration, stream);
/// <inheritdoc/>
public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(stream, nameof(stream));
var decoder = new PbmDecoderCore(configuration);
return decoder.DecodeAsync<TPixel>(configuration, stream, cancellationToken);
}
/// <inheritdoc />
public async Task<Image> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
=> await this.DecodeAsync<Rgba32>(configuration, stream, cancellationToken)
.ConfigureAwait(false);
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
var decoder = new PbmDecoderCore(configuration);
return decoder.Identify(configuration, stream);
}
/// <inheritdoc/>
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
var decoder = new PbmDecoderCore(configuration);
return decoder.IdentifyAsync(configuration, stream, cancellationToken);
}
}
}

195
src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs

@ -0,0 +1,195 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Threading;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Performs the PBM decoding operation.
/// </summary>
internal sealed class PbmDecoderCore : IImageDecoderInternals
{
private int maxPixelValue;
/// <summary>
/// Initializes a new instance of the <see cref="PbmDecoderCore" /> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public PbmDecoderCore(Configuration configuration) => this.Configuration = configuration ?? Configuration.Default;
/// <inheritdoc />
public Configuration Configuration { get; }
/// <summary>
/// Gets the colortype to use
/// </summary>
public PbmColorType ColorType { get; private set; }
/// <summary>
/// Gets the size of the pixel array
/// </summary>
public Size PixelSize { get; private set; }
/// <summary>
/// Gets the component data type
/// </summary>
public PbmComponentType ComponentType { get; private set; }
/// <summary>
/// Gets the Encoding of pixels
/// </summary>
public PbmEncoding Encoding { get; private set; }
/// <summary>
/// Gets the <see cref="ImageMetadata"/> decoded by this decoder instance.
/// </summary>
public ImageMetadata Metadata { get; private set; }
/// <inheritdoc/>
Size IImageDecoderInternals.Dimensions => this.PixelSize;
private bool NeedsUpscaling => this.ColorType != PbmColorType.BlackAndWhite && this.maxPixelValue is not 255 and not 65535;
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
this.ProcessHeader(stream);
var image = new Image<TPixel>(this.Configuration, this.PixelSize.Width, this.PixelSize.Height, this.Metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
this.ProcessPixels(stream, pixels);
if (this.NeedsUpscaling)
{
this.ProcessUpscaling(image);
}
return image;
}
/// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ProcessHeader(stream);
// BlackAndWhite pixels are encoded into a byte.
int bitsPerPixel = this.ComponentType == PbmComponentType.Short ? 16 : 8;
return new ImageInfo(new PixelTypeInfo(bitsPerPixel), this.PixelSize.Width, this.PixelSize.Height, this.Metadata);
}
/// <summary>
/// Processes the ppm header.
/// </summary>
/// <param name="stream">The input stream.</param>
private void ProcessHeader(BufferedReadStream stream)
{
Span<byte> buffer = stackalloc byte[2];
int bytesRead = stream.Read(buffer);
if (bytesRead != 2 || buffer[0] != 'P')
{
throw new InvalidImageContentException("Empty or not an PPM image.");
}
switch ((char)buffer[1])
{
case '1':
// Plain PBM format: 1 component per pixel, boolean value ('0' or '1').
this.ColorType = PbmColorType.BlackAndWhite;
this.Encoding = PbmEncoding.Plain;
break;
case '2':
// Plain PGM format: 1 component per pixel, in decimal text.
this.ColorType = PbmColorType.Grayscale;
this.Encoding = PbmEncoding.Plain;
break;
case '3':
// Plain PPM format: 3 components per pixel, in decimal text.
this.ColorType = PbmColorType.Rgb;
this.Encoding = PbmEncoding.Plain;
break;
case '4':
// Binary PBM format: 1 component per pixel, 8 pixels per byte.
this.ColorType = PbmColorType.BlackAndWhite;
this.Encoding = PbmEncoding.Binary;
break;
case '5':
// Binary PGM format: 1 components per pixel, in binary integers.
this.ColorType = PbmColorType.Grayscale;
this.Encoding = PbmEncoding.Binary;
break;
case '6':
// Binary PPM format: 3 components per pixel, in binary integers.
this.ColorType = PbmColorType.Rgb;
this.Encoding = PbmEncoding.Binary;
break;
case '7':
// PAM image: sequence of images.
// Not implemented yet
default:
throw new InvalidImageContentException("Unknown of not implemented image type encountered.");
}
stream.SkipWhitespaceAndComments();
int width = stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
int height = stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
if (this.ColorType != PbmColorType.BlackAndWhite)
{
this.maxPixelValue = stream.ReadDecimal();
if (this.maxPixelValue > 255)
{
this.ComponentType = PbmComponentType.Short;
}
else
{
this.ComponentType = PbmComponentType.Byte;
}
stream.SkipWhitespaceAndComments();
}
else
{
this.ComponentType = PbmComponentType.Bit;
}
this.PixelSize = new Size(width, height);
this.Metadata = new ImageMetadata();
PbmMetadata meta = this.Metadata.GetPbmMetadata();
meta.Encoding = this.Encoding;
meta.ColorType = this.ColorType;
meta.ComponentType = this.ComponentType;
}
private void ProcessPixels<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
if (this.Encoding == PbmEncoding.Binary)
{
BinaryDecoder.Process(this.Configuration, pixels, stream, this.ColorType, this.ComponentType);
}
else
{
PlainDecoder.Process(this.Configuration, pixels, stream, this.ColorType, this.ComponentType);
}
}
private void ProcessUpscaling<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int maxAllocationValue = this.ComponentType == PbmComponentType.Short ? 65535 : 255;
float factor = maxAllocationValue / this.maxPixelValue;
image.Mutate(x => x.Brightness(factor));
}
}
}

69
src/ImageSharp/Formats/Pbm/PbmEncoder.cs

@ -0,0 +1,69 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Image encoder for writing an image to a stream as PGM, PBM or PPM bitmap. These images are from
/// the family of PNM images.
/// <para>
/// The PNM formats are a fairly simple image format. They share a plain text header, consisting of:
/// signature, width, height and max_pixel_value only. The pixels follow thereafter and can be in
/// plain text decimals separated by spaces, or binary encoded.
/// <list type="bullet">
/// <item>
/// <term>PBM</term>
/// <description>Black and white images, with 1 representing black and 0 representing white.</description>
/// </item>
/// <item>
/// <term>PGM</term>
/// <description>Grayscale images, scaling from 0 to max_pixel_value, 0 representing black and max_pixel_value representing white.</description>
/// </item>
/// <item>
/// <term>PPM</term>
/// <description>Color images, with RGB pixels (in that order), with 0 representing black and 2 representing full color.</description>
/// </item>
/// </list>
/// </para>
/// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>.
/// </summary>
public sealed class PbmEncoder : IImageEncoder, IPbmEncoderOptions
{
/// <summary>
/// Gets or sets the Encoding of the pixels.
/// </summary>
public PbmEncoding? Encoding { get; set; }
/// <summary>
/// Gets or sets the Color type of the resulting image.
/// </summary>
public PbmColorType? ColorType { get; set; }
/// <summary>
/// Gets or sets the data type of the pixels components.
/// </summary>
public PbmComponentType? ComponentType { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new PbmEncoderCore(image.GetConfiguration(), this);
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new PbmEncoderCore(image.GetConfiguration(), this);
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}
}

187
src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs

@ -0,0 +1,187 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Text;
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Image encoder for writing an image to a stream as a PGM, PBM, PPM or PAM bitmap.
/// </summary>
internal sealed class PbmEncoderCore : IImageEncoderInternals
{
private const byte NewLine = (byte)'\n';
private const byte Space = (byte)' ';
private const byte P = (byte)'P';
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// The encoder options.
/// </summary>
private readonly IPbmEncoderOptions options;
/// <summary>
/// The encoding for the pixels.
/// </summary>
private PbmEncoding encoding;
/// <summary>
/// Gets the Color type of the resulting image.
/// </summary>
private PbmColorType colorType;
/// <summary>
/// Gets the maximum pixel value, per component.
/// </summary>
private PbmComponentType componentType;
/// <summary>
/// Initializes a new instance of the <see cref="PbmEncoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The encoder options.</param>
public PbmEncoderCore(Configuration configuration, IPbmEncoderOptions options)
{
this.configuration = configuration;
this.options = options;
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="cancellationToken">The token to request cancellation.</param>
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));
this.DeduceOptions(image);
byte signature = this.DeduceSignature();
this.WriteHeader(stream, signature, image.Size());
this.WritePixels(stream, image.Frames.RootFrame);
stream.Flush();
}
private void DeduceOptions<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
this.configuration = image.GetConfiguration();
PbmMetadata metadata = image.Metadata.GetPbmMetadata();
this.encoding = this.options.Encoding ?? metadata.Encoding;
this.colorType = this.options.ColorType ?? metadata.ColorType;
if (this.colorType != PbmColorType.BlackAndWhite)
{
this.componentType = this.options.ComponentType ?? metadata.ComponentType;
}
else
{
this.componentType = PbmComponentType.Bit;
}
}
private byte DeduceSignature()
{
byte signature;
if (this.colorType == PbmColorType.BlackAndWhite)
{
if (this.encoding == PbmEncoding.Plain)
{
signature = (byte)'1';
}
else
{
signature = (byte)'4';
}
}
else if (this.colorType == PbmColorType.Grayscale)
{
if (this.encoding == PbmEncoding.Plain)
{
signature = (byte)'2';
}
else
{
signature = (byte)'5';
}
}
else
{
// RGB ColorType
if (this.encoding == PbmEncoding.Plain)
{
signature = (byte)'3';
}
else
{
signature = (byte)'6';
}
}
return signature;
}
private void WriteHeader(Stream stream, byte signature, Size pixelSize)
{
Span<byte> buffer = stackalloc byte[128];
int written = 3;
buffer[0] = P;
buffer[1] = signature;
buffer[2] = NewLine;
Utf8Formatter.TryFormat(pixelSize.Width, buffer.Slice(written), out int bytesWritten);
written += bytesWritten;
buffer[written++] = Space;
Utf8Formatter.TryFormat(pixelSize.Height, buffer.Slice(written), out bytesWritten);
written += bytesWritten;
buffer[written++] = NewLine;
if (this.colorType != PbmColorType.BlackAndWhite)
{
int maxPixelValue = this.componentType == PbmComponentType.Short ? 65535 : 255;
Utf8Formatter.TryFormat(maxPixelValue, buffer.Slice(written), out bytesWritten);
written += bytesWritten;
buffer[written++] = NewLine;
}
stream.Write(buffer, 0, written);
}
/// <summary>
/// Writes the pixel data to the binary stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</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 WritePixels<TPixel>(Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
if (this.encoding == PbmEncoding.Plain)
{
PlainEncoder.WritePixels(this.configuration, stream, image, this.colorType, this.componentType);
}
else
{
BinaryEncoder.WritePixels(this.configuration, stream, image, this.colorType, this.componentType);
}
}
}
}

21
src/ImageSharp/Formats/Pbm/PbmEncoding.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Provides enumeration of available PBM encodings.
/// </summary>
public enum PbmEncoding : byte
{
/// <summary>
/// Plain text decimal encoding.
/// </summary>
Plain = 0,
/// <summary>
/// Binary integer encoding.
/// </summary>
Binary = 1,
}
}

37
src/ImageSharp/Formats/Pbm/PbmFormat.cs

@ -0,0 +1,37 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the PBM format.
/// </summary>
public sealed class PbmFormat : IImageFormat<PbmMetadata>
{
private PbmFormat()
{
}
/// <summary>
/// Gets the current instance.
/// </summary>
public static PbmFormat Instance { get; } = new();
/// <inheritdoc/>
public string Name => "PBM";
/// <inheritdoc/>
public string DefaultMimeType => "image/x-portable-pixmap";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => PbmConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => PbmConstants.FileExtensions;
/// <inheritdoc/>
public PbmMetadata CreateDefaultFormatMetadata() => new();
}
}

36
src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Detects Pbm file headers.
/// </summary>
public sealed class PbmImageFormatDetector : IImageFormatDetector
{
private const byte P = (byte)'P';
private const byte Zero = (byte)'0';
private const byte Seven = (byte)'7';
/// <inheritdoc/>
public int HeaderSize => 2;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? PbmFormat.Instance : null;
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
#pragma warning disable SA1131 // Use readable conditions
if (1 < (uint)header.Length)
#pragma warning restore SA1131 // Use readable conditions
{
// Signature should be between P1 and P6.
return header[0] == P && (uint)(header[1] - Zero - 1) < (Seven - Zero - 1);
}
return false;
}
}
}

46
src/ImageSharp/Formats/Pbm/PbmMetadata.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Provides PBM specific metadata information for the image.
/// </summary>
public class PbmMetadata : IDeepCloneable
{
/// <summary>
/// Initializes a new instance of the <see cref="PbmMetadata"/> class.
/// </summary>
public PbmMetadata() =>
this.ComponentType = this.ColorType == PbmColorType.BlackAndWhite ? PbmComponentType.Bit : PbmComponentType.Byte;
/// <summary>
/// Initializes a new instance of the <see cref="PbmMetadata"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private PbmMetadata(PbmMetadata other)
{
this.Encoding = other.Encoding;
this.ColorType = other.ColorType;
this.ComponentType = other.ComponentType;
}
/// <summary>
/// Gets or sets the encoding of the pixels.
/// </summary>
public PbmEncoding Encoding { get; set; } = PbmEncoding.Plain;
/// <summary>
/// Gets or sets the color type.
/// </summary>
public PbmColorType ColorType { get; set; } = PbmColorType.Grayscale;
/// <summary>
/// Gets or sets the data type of the pixel components.
/// </summary>
public PbmComponentType ComponentType { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new PbmMetadata(this);
}
}

198
src/ImageSharp/Formats/Pbm/PlainDecoder.cs

@ -0,0 +1,198 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Pixel decoding methods for the PBM plain encoding.
/// </summary>
internal class PlainDecoder
{
private static readonly L8 White = new(255);
private static readonly L8 Black = new(0);
/// <summary>
/// Decode the specified pixels.
/// </summary>
/// <typeparam name="TPixel">The type of pixel to encode to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="pixels">The pixel array to encode 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>
public static void Process<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream, PbmColorType colorType, PbmComponentType componentType)
where TPixel : unmanaged, IPixel<TPixel>
{
if (colorType == PbmColorType.Grayscale)
{
if (componentType == PbmComponentType.Byte)
{
ProcessGrayscale(configuration, pixels, stream);
}
else
{
ProcessWideGrayscale(configuration, pixels, stream);
}
}
else if (colorType == PbmColorType.Rgb)
{
if (componentType == PbmComponentType.Byte)
{
ProcessRgb(configuration, pixels, stream);
}
else
{
ProcessWideRgb(configuration, pixels, stream);
}
}
else
{
ProcessBlackAndWhite(configuration, pixels, stream);
}
}
private static void ProcessGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
Span<L8> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte value = (byte)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
rowSpan[x] = new L8(value);
}
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8(
configuration,
rowSpan,
pixelSpan);
}
}
private static void ProcessWideGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L16> row = allocator.Allocate<L16>(width);
Span<L16> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
ushort value = (ushort)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
rowSpan[x] = new L16(value);
}
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL16(
configuration,
rowSpan,
pixelSpan);
}
}
private static void ProcessRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<Rgb24> row = allocator.Allocate<Rgb24>(width);
Span<Rgb24> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte red = (byte)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
byte green = (byte)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
byte blue = (byte)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
rowSpan[x] = new Rgb24(red, green, blue);
}
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromRgb24(
configuration,
rowSpan,
pixelSpan);
}
}
private static void ProcessWideRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<Rgb48> row = allocator.Allocate<Rgb48>(width);
Span<Rgb48> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
ushort red = (ushort)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
ushort green = (ushort)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
ushort blue = (ushort)stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
rowSpan[x] = new Rgb48(red, green, blue);
}
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromRgb48(
configuration,
rowSpan,
pixelSpan);
}
}
private static void ProcessBlackAndWhite<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
Span<L8> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int value = stream.ReadDecimal();
stream.SkipWhitespaceAndComments();
rowSpan[x] = value == 0 ? White : Black;
}
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8(
configuration,
rowSpan,
pixelSpan);
}
}
}
}

251
src/ImageSharp/Formats/Pbm/PlainEncoder.cs

@ -0,0 +1,251 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Buffers.Text;
using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Pixel encoding methods for the PBM plain encoding.
/// </summary>
internal class PlainEncoder
{
private const byte NewLine = 0x0a;
private const byte Space = 0x20;
private const byte Zero = 0x30;
private const byte One = 0x31;
private const int MaxCharsPerPixelBlackAndWhite = 2;
private const int MaxCharsPerPixelGrayscale = 4;
private const int MaxCharsPerPixelGrayscaleWide = 6;
private const int MaxCharsPerPixelRgb = 4 * 3;
private const int MaxCharsPerPixelRgbWide = 6 * 3;
private static readonly StandardFormat DecimalFormat = StandardFormat.Parse("D");
/// <summary>
/// Decode pixels into the PBM plain encoding.
/// </summary>
/// <typeparam name="TPixel">The type of input pixel.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="stream">The bytestream to write to.</param>
/// <param name="image">The input image.</param>
/// <param name="colorType">The ColorType to use.</param>
/// <param name="componentType">Data type of the pixles components.</param>
public static void WritePixels<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image, PbmColorType colorType, PbmComponentType componentType)
where TPixel : unmanaged, IPixel<TPixel>
{
if (colorType == PbmColorType.Grayscale)
{
if (componentType == PbmComponentType.Byte)
{
WriteGrayscale(configuration, stream, image);
}
else
{
WriteWideGrayscale(configuration, stream, image);
}
}
else if (colorType == PbmColorType.Rgb)
{
if (componentType == PbmComponentType.Byte)
{
WriteRgb(configuration, stream, image);
}
else
{
WriteWideRgb(configuration, stream, image);
}
}
else
{
WriteBlackAndWhite(configuration, stream, image);
}
// Write EOF indicator, as some encoders expect it.
stream.WriteByte(Space);
}
private static void WriteGrayscale<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
Span<L8> rowSpan = row.GetSpan();
using IMemoryOwner<byte> plainMemory = allocator.Allocate<byte>(width * MaxCharsPerPixelGrayscale);
Span<byte> plainSpan = plainMemory.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(
configuration,
pixelSpan,
rowSpan);
int written = 0;
for (int x = 0; x < width; x++)
{
Utf8Formatter.TryFormat(rowSpan[x].PackedValue, plainSpan.Slice(written), out int bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
}
plainSpan[written - 1] = NewLine;
stream.Write(plainSpan, 0, written);
}
}
private static void WriteWideGrayscale<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L16> row = allocator.Allocate<L16>(width);
Span<L16> rowSpan = row.GetSpan();
using IMemoryOwner<byte> plainMemory = allocator.Allocate<byte>(width * MaxCharsPerPixelGrayscaleWide);
Span<byte> plainSpan = plainMemory.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL16(
configuration,
pixelSpan,
rowSpan);
int written = 0;
for (int x = 0; x < width; x++)
{
Utf8Formatter.TryFormat(rowSpan[x].PackedValue, plainSpan.Slice(written), out int bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
}
plainSpan[written - 1] = NewLine;
stream.Write(plainSpan, 0, written);
}
}
private static void WriteRgb<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<Rgb24> row = allocator.Allocate<Rgb24>(width);
Span<Rgb24> rowSpan = row.GetSpan();
using IMemoryOwner<byte> plainMemory = allocator.Allocate<byte>(width * MaxCharsPerPixelRgb);
Span<byte> plainSpan = plainMemory.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgb24(
configuration,
pixelSpan,
rowSpan);
int written = 0;
for (int x = 0; x < width; x++)
{
Utf8Formatter.TryFormat(rowSpan[x].R, plainSpan.Slice(written), out int bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
Utf8Formatter.TryFormat(rowSpan[x].G, plainSpan.Slice(written), out bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
Utf8Formatter.TryFormat(rowSpan[x].B, plainSpan.Slice(written), out bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
}
plainSpan[written - 1] = NewLine;
stream.Write(plainSpan, 0, written);
}
}
private static void WriteWideRgb<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<Rgb48> row = allocator.Allocate<Rgb48>(width);
Span<Rgb48> rowSpan = row.GetSpan();
using IMemoryOwner<byte> plainMemory = allocator.Allocate<byte>(width * MaxCharsPerPixelRgbWide);
Span<byte> plainSpan = plainMemory.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgb48(
configuration,
pixelSpan,
rowSpan);
int written = 0;
for (int x = 0; x < width; x++)
{
Utf8Formatter.TryFormat(rowSpan[x].R, plainSpan.Slice(written), out int bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
Utf8Formatter.TryFormat(rowSpan[x].G, plainSpan.Slice(written), out bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
Utf8Formatter.TryFormat(rowSpan[x].B, plainSpan.Slice(written), out bytesWritten, DecimalFormat);
written += bytesWritten;
plainSpan[written++] = Space;
}
plainSpan[written - 1] = NewLine;
stream.Write(plainSpan, 0, written);
}
}
private static void WriteBlackAndWhite<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.PixelBuffer;
MemoryAllocator allocator = configuration.MemoryAllocator;
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
Span<L8> rowSpan = row.GetSpan();
using IMemoryOwner<byte> plainMemory = allocator.Allocate<byte>(width * MaxCharsPerPixelBlackAndWhite);
Span<byte> plainSpan = plainMemory.GetSpan();
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(
configuration,
pixelSpan,
rowSpan);
int written = 0;
for (int x = 0; x < width; x++)
{
byte value = (rowSpan[x].PackedValue < 128) ? One : Zero;
plainSpan[written++] = value;
plainSpan[written++] = Space;
}
plainSpan[written - 1] = NewLine;
stream.Write(plainSpan, 0, written);
}
}
}
}

2
src/ImageSharp/ImageSharp.csproj

@ -10,7 +10,7 @@
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<RepositoryUrl Condition="'$(RepositoryUrl)' == ''">https://github.com/SixLabors/ImageSharp/</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<PackageTags>Image Resize Crop Gif Jpg Jpeg Bitmap Png Tga NetCore</PackageTags>
<PackageTags>Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga NetCore</PackageTags>
<Description>A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET</Description>
<Configurations>Debug;Release;Debug-InnerLoop;Release-InnerLoop</Configurations>
</PropertyGroup>

2
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests
public Configuration DefaultConfiguration { get; }
private readonly int expectedDefaultConfigurationCount = 7;
private readonly int expectedDefaultConfigurationCount = 8;
public ConfigurationTests()
{

9
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -132,6 +132,11 @@ namespace SixLabors.ImageSharp.Tests.Formats
image.SaveAsJpeg(output);
}
using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.pbm")))
{
image.SaveAsPbm(output);
}
using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.png")))
{
image.SaveAsPng(output);
@ -179,6 +184,10 @@ namespace SixLabors.ImageSharp.Tests.Formats
}
[Theory]
[InlineData(10, 10, "pbm")]
[InlineData(100, 100, "pbm")]
[InlineData(100, 10, "pbm")]
[InlineData(10, 100, "pbm")]
[InlineData(10, 10, "png")]
[InlineData(100, 100, "png")]
[InlineData(100, 10, "png")]

3
tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs

@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
@ -33,6 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Formats
[Fact]
public void IfAutoLoadWellKnownFormatsIsTrueAllFormatsAreLoaded()
{
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<PbmEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<PngEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<BmpEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<JpegEncoder>().Count());
@ -41,6 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Formats
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<TiffEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<WebpEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<PbmDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<PngDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<JpegDecoder>().Count());

155
tests/ImageSharp.Tests/Formats/Pbm/ImageExtensionsTest.cs

@ -0,0 +1,155 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Pbm
{
public class ImageExtensionsTest
{
[Fact]
public void SaveAsPbm_Path()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsPbm_Path.pbm");
using (var image = new Image<L8>(10, 10))
{
image.SaveAsPbm(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public async Task SaveAsPbmAsync_Path()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsPbmAsync_Path.pbm");
using (var image = new Image<L8>(10, 10))
{
await image.SaveAsPbmAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public void SaveAsPbm_Path_Encoder()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsPbm_Path_Encoder.pbm");
using (var image = new Image<L8>(10, 10))
{
image.SaveAsPbm(file, new PbmEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public async Task SaveAsPbmAsync_Path_Encoder()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsPbmAsync_Path_Encoder.pbm");
using (var image = new Image<L8>(10, 10))
{
await image.SaveAsPbmAsync(file, new PbmEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public void SaveAsPbm_Stream()
{
using var memoryStream = new MemoryStream();
using (var image = new Image<L8>(10, 10))
{
image.SaveAsPbm(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public async Task SaveAsPbmAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using (var image = new Image<L8>(10, 10))
{
await image.SaveAsPbmAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public void SaveAsPbm_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using (var image = new Image<L8>(10, 10))
{
image.SaveAsPbm(memoryStream, new PbmEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
[Fact]
public async Task SaveAsPbmAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using (var image = new Image<L8>(10, 10))
{
await image.SaveAsPbmAsync(memoryStream, new PbmEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
}
}
}

100
tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs

@ -0,0 +1,100 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using static SixLabors.ImageSharp.Tests.TestImages.Pbm;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Pbm
{
[Trait("Format", "Pbm")]
public class PbmDecoderTests
{
[Theory]
[InlineData(BlackAndWhitePlain, PbmColorType.BlackAndWhite, PbmComponentType.Bit)]
[InlineData(BlackAndWhiteBinary, PbmColorType.BlackAndWhite, PbmComponentType.Bit)]
[InlineData(GrayscalePlain, PbmColorType.Grayscale, PbmComponentType.Byte)]
[InlineData(GrayscalePlainMagick, PbmColorType.Grayscale, PbmComponentType.Byte)]
[InlineData(GrayscaleBinary, PbmColorType.Grayscale, PbmComponentType.Byte)]
[InlineData(GrayscaleBinaryWide, PbmColorType.Grayscale, PbmComponentType.Short)]
[InlineData(RgbPlain, PbmColorType.Rgb, PbmComponentType.Byte)]
[InlineData(RgbPlainMagick, PbmColorType.Rgb, PbmComponentType.Byte)]
[InlineData(RgbBinary, PbmColorType.Rgb, PbmComponentType.Byte)]
public void ImageLoadCanDecode(string imagePath, PbmColorType expectedColorType, PbmComponentType expectedComponentType)
{
// Arrange
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
// Act
using var image = Image.Load(stream);
// Assert
Assert.NotNull(image);
PbmMetadata metadata = image.Metadata.GetPbmMetadata();
Assert.NotNull(metadata);
Assert.Equal(expectedColorType, metadata.ColorType);
Assert.Equal(expectedComponentType, metadata.ComponentType);
}
[Theory]
[InlineData(BlackAndWhitePlain)]
[InlineData(BlackAndWhiteBinary)]
[InlineData(GrayscalePlain)]
[InlineData(GrayscalePlainMagick)]
[InlineData(GrayscaleBinary)]
[InlineData(GrayscaleBinaryWide)]
public void ImageLoadL8CanDecode(string imagePath)
{
// Arrange
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
// Act
using var image = Image.Load<L8>(stream);
// Assert
Assert.NotNull(image);
}
[Theory]
[InlineData(RgbPlain)]
[InlineData(RgbPlainMagick)]
[InlineData(RgbBinary)]
public void ImageLoadRgb24CanDecode(string imagePath)
{
// Arrange
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
// Act
using var image = Image.Load<Rgb24>(stream);
// Assert
Assert.NotNull(image);
}
[Theory]
[WithFile(BlackAndWhitePlain, PixelTypes.L8, "pbm")]
[WithFile(BlackAndWhiteBinary, PixelTypes.L8, "pbm")]
[WithFile(GrayscalePlain, PixelTypes.L8, "pgm")]
[WithFile(GrayscalePlainNormalized, PixelTypes.L8, "pgm")]
[WithFile(GrayscaleBinary, PixelTypes.L8, "pgm")]
[WithFile(GrayscaleBinaryWide, PixelTypes.L16, "pgm")]
[WithFile(RgbPlain, PixelTypes.Rgb24, "ppm")]
[WithFile(RgbPlainNormalized, PixelTypes.Rgb24, "ppm")]
[WithFile(RgbBinary, PixelTypes.Rgb24, "ppm")]
public void DecodeReferenceImage<TPixel>(TestImageProvider<TPixel> provider, string extension)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
image.DebugSave(provider, extension: extension);
bool isGrayscale = extension is "pgm" or "pbm";
image.CompareToReferenceOutput(provider, grayscale: isGrayscale);
}
}
}

145
tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs

@ -0,0 +1,145 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
using static SixLabors.ImageSharp.Tests.TestImages.Pbm;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Pbm
{
[Collection("RunSerial")]
[Trait("Format", "Pbm")]
public class PbmEncoderTests
{
public static readonly TheoryData<PbmColorType> ColorType =
new()
{
PbmColorType.BlackAndWhite,
PbmColorType.Grayscale,
PbmColorType.Rgb
};
public static readonly TheoryData<string, PbmColorType> PbmColorTypeFiles =
new()
{
{ BlackAndWhiteBinary, PbmColorType.BlackAndWhite },
{ BlackAndWhitePlain, PbmColorType.BlackAndWhite },
{ GrayscaleBinary, PbmColorType.Grayscale },
{ GrayscaleBinaryWide, PbmColorType.Grayscale },
{ GrayscalePlain, PbmColorType.Grayscale },
{ RgbBinary, PbmColorType.Rgb },
{ RgbPlain, PbmColorType.Rgb },
};
[Theory]
[MemberData(nameof(PbmColorTypeFiles))]
public void PbmEncoder_PreserveColorType(string imagePath, PbmColorType pbmColorType)
{
var options = new PbmEncoder();
var testFile = TestFile.Create(imagePath);
using (Image<Rgba32> input = testFile.CreateRgba32Image())
{
using (var memStream = new MemoryStream())
{
input.Save(memStream, options);
memStream.Position = 0;
using (var output = Image.Load<Rgba32>(memStream))
{
PbmMetadata meta = output.Metadata.GetPbmMetadata();
Assert.Equal(pbmColorType, meta.ColorType);
}
}
}
}
[Theory]
[MemberData(nameof(PbmColorTypeFiles))]
public void PbmEncoder_WithPlainEncoding_PreserveBitsPerPixel(string imagePath, PbmColorType pbmColorType)
{
var options = new PbmEncoder()
{
Encoding = PbmEncoding.Plain
};
var testFile = TestFile.Create(imagePath);
using (Image<Rgba32> input = testFile.CreateRgba32Image())
{
using (var memStream = new MemoryStream())
{
input.Save(memStream, options);
// EOF indicator for plain is a Space.
memStream.Seek(-1, SeekOrigin.End);
int lastByte = memStream.ReadByte();
Assert.Equal(0x20, lastByte);
memStream.Seek(0, SeekOrigin.Begin);
using (var output = Image.Load<Rgba32>(memStream))
{
PbmMetadata meta = output.Metadata.GetPbmMetadata();
Assert.Equal(pbmColorType, meta.ColorType);
}
}
}
}
[Theory]
[WithFile(BlackAndWhitePlain, PixelTypes.Rgb24)]
public void PbmEncoder_P1_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.BlackAndWhite, PbmEncoding.Plain);
[Theory]
[WithFile(BlackAndWhiteBinary, PixelTypes.Rgb24)]
public void PbmEncoder_P4_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.BlackAndWhite, PbmEncoding.Binary);
[Theory]
[WithFile(GrayscalePlainMagick, PixelTypes.Rgb24)]
public void PbmEncoder_P2_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.Grayscale, PbmEncoding.Plain);
[Theory]
[WithFile(GrayscaleBinary, PixelTypes.Rgb24)]
public void PbmEncoder_P5_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.Grayscale, PbmEncoding.Binary);
[Theory]
[WithFile(RgbPlainMagick, PixelTypes.Rgb24)]
public void PbmEncoder_P3_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.Rgb, PbmEncoding.Plain);
[Theory]
[WithFile(RgbBinary, PixelTypes.Rgb24)]
public void PbmEncoder_P6_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.Rgb, PbmEncoding.Binary);
private static void TestPbmEncoderCore<TPixel>(
TestImageProvider<TPixel> provider,
PbmColorType colorType,
PbmEncoding encoding,
bool useExactComparer = true,
float compareTolerance = 0.01f)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
var encoder = new PbmEncoder { ColorType = colorType, Encoding = encoding };
using (var memStream = new MemoryStream())
{
image.Save(memStream, encoder);
memStream.Position = 0;
using (var encodedImage = (Image<TPixel>)Image.Load(memStream))
{
ImageComparingUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance);
}
}
}
}
}
}

86
tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs

@ -0,0 +1,86 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Pbm;
using Xunit;
using static SixLabors.ImageSharp.Tests.TestImages.Pbm;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Pbm
{
[Trait("Format", "Pbm")]
public class PbmMetadataTests
{
[Fact]
public void CloneIsDeep()
{
var meta = new PbmMetadata { ColorType = PbmColorType.Grayscale };
var clone = (PbmMetadata)meta.DeepClone();
clone.ColorType = PbmColorType.Rgb;
clone.ComponentType = PbmComponentType.Short;
Assert.False(meta.ColorType.Equals(clone.ColorType));
Assert.False(meta.ComponentType.Equals(clone.ComponentType));
}
[Theory]
[InlineData(BlackAndWhitePlain, PbmEncoding.Plain)]
[InlineData(BlackAndWhiteBinary, PbmEncoding.Binary)]
[InlineData(GrayscaleBinary, PbmEncoding.Binary)]
[InlineData(GrayscaleBinaryWide, PbmEncoding.Binary)]
[InlineData(GrayscalePlain, PbmEncoding.Plain)]
[InlineData(RgbBinary, PbmEncoding.Binary)]
[InlineData(RgbPlain, PbmEncoding.Plain)]
public void Identify_DetectsCorrectEncoding(string imagePath, PbmEncoding expectedEncoding)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);
Assert.Equal(expectedEncoding, bitmapMetadata.Encoding);
}
[Theory]
[InlineData(BlackAndWhitePlain, PbmColorType.BlackAndWhite)]
[InlineData(BlackAndWhiteBinary, PbmColorType.BlackAndWhite)]
[InlineData(GrayscaleBinary, PbmColorType.Grayscale)]
[InlineData(GrayscaleBinaryWide, PbmColorType.Grayscale)]
[InlineData(GrayscalePlain, PbmColorType.Grayscale)]
[InlineData(RgbBinary, PbmColorType.Rgb)]
[InlineData(RgbPlain, PbmColorType.Rgb)]
public void Identify_DetectsCorrectColorType(string imagePath, PbmColorType expectedColorType)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);
Assert.Equal(expectedColorType, bitmapMetadata.ColorType);
}
[Theory]
[InlineData(BlackAndWhitePlain, PbmComponentType.Bit)]
[InlineData(BlackAndWhiteBinary, PbmComponentType.Bit)]
[InlineData(GrayscaleBinary, PbmComponentType.Byte)]
[InlineData(GrayscaleBinaryWide, PbmComponentType.Short)]
[InlineData(GrayscalePlain, PbmComponentType.Byte)]
[InlineData(RgbBinary, PbmComponentType.Byte)]
[InlineData(RgbPlain, PbmComponentType.Byte)]
public void Identify_DetectsCorrectComponentType(string imagePath, PbmComponentType expectedComponentType)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);
Assert.Equal(expectedComponentType, bitmapMetadata.ComponentType);
}
}
}

69
tests/ImageSharp.Tests/Formats/Pbm/PbmRoundTripTests.cs

@ -0,0 +1,69 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
using static SixLabors.ImageSharp.Tests.TestImages.Pbm;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Pbm
{
[Trait("Format", "Pbm")]
public class PbmRoundTripTests
{
[Theory]
[InlineData(BlackAndWhitePlain)]
[InlineData(BlackAndWhiteBinary)]
[InlineData(GrayscalePlain)]
[InlineData(GrayscalePlainNormalized)]
[InlineData(GrayscalePlainMagick)]
[InlineData(GrayscaleBinary)]
public void PbmGrayscaleImageCanRoundTrip(string imagePath)
{
// Arrange
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
// Act
using var originalImage = Image.Load(stream);
using Image<Rgb24> colorImage = originalImage.CloneAs<Rgb24>();
using Image<Rgb24> encodedImage = this.RoundTrip(colorImage);
// Assert
Assert.NotNull(encodedImage);
ImageComparer.Exact.VerifySimilarity(colorImage, encodedImage);
}
[Theory]
[InlineData(RgbPlain)]
[InlineData(RgbPlainNormalized)]
[InlineData(RgbPlainMagick)]
[InlineData(RgbBinary)]
public void PbmColorImageCanRoundTrip(string imagePath)
{
// Arrange
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
// Act
using var originalImage = Image.Load<Rgb24>(stream);
using Image<Rgb24> encodedImage = this.RoundTrip(originalImage);
// Assert
Assert.NotNull(encodedImage);
ImageComparer.Exact.VerifySimilarity(originalImage, encodedImage);
}
private Image<TPixel> RoundTrip<TPixel>(Image<TPixel> originalImage)
where TPixel : unmanaged, IPixel<TPixel>
{
using var decodedStream = new MemoryStream();
originalImage.SaveAsPbm(decodedStream);
decodedStream.Seek(0, SeekOrigin.Begin);
var encodedImage = Image.Load<TPixel>(decodedStream);
return encodedImage;
}
}
}

96
tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -256,7 +256,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -268,7 +268,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -280,7 +280,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -292,7 +292,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -316,7 +316,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -328,7 +328,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -352,7 +352,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -364,7 +364,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -376,7 +376,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -388,7 +388,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -400,7 +400,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -412,7 +412,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -424,7 +424,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -436,7 +436,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -448,7 +448,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -460,7 +460,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -472,7 +472,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -484,7 +484,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -496,7 +496,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -508,7 +508,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -520,7 +520,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -532,7 +532,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -544,7 +544,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -556,7 +556,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -568,7 +568,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -580,7 +580,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -592,7 +592,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -604,7 +604,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -616,7 +616,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -628,7 +628,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -640,7 +640,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -652,7 +652,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -664,7 +664,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -676,7 +676,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -688,7 +688,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -700,7 +700,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}
@ -713,7 +713,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
{
image.DebugSave(provider);
TgaTestUtils.CompareWithReferenceDecoder(provider, image);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
}
}

4
tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs

@ -4,7 +4,7 @@
using System.IO;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
using static SixLabors.ImageSharp.Tests.TestImages.Tga;
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
memStream.Position = 0;
using (var encodedImage = (Image<TPixel>)Image.Load(memStream))
{
TgaTestUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance);
ImageComparingUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance);
}
}
}

8
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Common.Helpers;
@ -21,6 +22,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
private static TiffDecoder TiffDecoder => new();
private class NumberComparer : IEqualityComparer<Number>
{
public bool Equals(Number x, Number y) => x.Equals(y);
public int GetHashCode(Number obj) => obj.GetHashCode();
}
[Fact]
public void TiffMetadata_CloneIsDeep()
{

67
tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs

@ -1,67 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using ImageMagick;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
public static class TiffTestUtils
{
public static void CompareWithReferenceDecoder<TPixel>(
string encodedImagePath,
Image<TPixel> image,
bool useExactComparer = true,
float compareTolerance = 0.01f)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
var testFile = TestFile.Create(encodedImagePath);
Image<Rgba32> magickImage = DecodeWithMagick<Rgba32>(new FileInfo(testFile.FullPath));
if (useExactComparer)
{
ImageComparer.Exact.VerifySimilarity(magickImage, image);
}
else
{
ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image);
}
}
public static Image<TPixel> DecodeWithMagick<TPixel>(FileInfo fileInfo)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
Configuration configuration = Configuration.Default.Clone();
configuration.PreferContiguousImageBuffers = true;
using var magickImage = new MagickImage(fileInfo);
magickImage.AutoOrient();
var result = new Image<TPixel>(configuration, magickImage.Width, magickImage.Height);
Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory<TPixel> resultPixels));
using IUnsafePixelCollection<ushort> pixels = magickImage.GetPixelsUnsafe();
byte[] data = pixels.ToByteArray(PixelMapping.RGBA);
PixelOperations<TPixel>.Instance.FromRgba32Bytes(
configuration,
data,
resultPixels.Span,
resultPixels.Length);
return result;
}
}
internal class NumberComparer : IEqualityComparer<Number>
{
public bool Equals(Number x, Number y) => x.Equals(y);
public int GetHashCode(Number obj) => obj.GetHashCode();
}
}

2
tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs

@ -71,6 +71,7 @@ namespace SixLabors.ImageSharp.Tests
}
[Theory]
[InlineData("test.pbm", "image/x-portable-pixmap")]
[InlineData("test.png", "image/png")]
[InlineData("test.tga", "image/tga")]
[InlineData("test.bmp", "image/bmp")]
@ -114,6 +115,7 @@ namespace SixLabors.ImageSharp.Tests
}
[Theory]
[InlineData("test.pbm")]
[InlineData("test.png")]
[InlineData("test.tga")]
[InlineData("test.bmp")]

15
tests/ImageSharp.Tests/TestImages.cs

@ -866,5 +866,20 @@ namespace SixLabors.ImageSharp.Tests
public static readonly string[] Metadata = { SampleMetadata };
}
public static class Pbm
{
public const string BlackAndWhitePlain = "Pbm/blackandwhite_plain.pbm";
public const string BlackAndWhiteBinary = "Pbm/blackandwhite_binary.pbm";
public const string GrayscaleBinary = "Pbm/rings.pgm";
public const string GrayscaleBinaryWide = "Pbm/Gene-UP WebSocket RunImageMask.pgm";
public const string GrayscalePlain = "Pbm/grayscale_plain.pgm";
public const string GrayscalePlainNormalized = "Pbm/grayscale_plain_normalized.pgm";
public const string GrayscalePlainMagick = "Pbm/grayscale_plain_magick.pgm";
public const string RgbBinary = "Pbm/00000_00000.ppm";
public const string RgbPlain = "Pbm/rgb_plain.ppm";
public const string RgbPlainNormalized = "Pbm/rgb_plain_normalized.ppm";
public const string RgbPlainMagick = "Pbm/rgb_plain_magick.ppm";
}
}
}

4
tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs → tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs

@ -8,9 +8,9 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tga
namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
{
public static class TgaTestUtils
public static class ImageComparingUtils
{
public static void CompareWithReferenceDecoder<TPixel>(
TestImageProvider<TPixel> provider,

2
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
@ -57,6 +58,7 @@ namespace SixLabors.ImageSharp.Tests
var cfg = new Configuration(
new JpegConfigurationModule(),
new GifConfigurationModule(),
new PbmConfigurationModule(),
new TgaConfigurationModule(),
new WebpConfigurationModule(),
new TiffConfigurationModule());

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L16_Gene-UP WebSocket RunImageMask.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:78fc668be9f82c01c277cb2560253b04a1ff74a5af4daaf19327591420a71fec
size 4521

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_blackandwhite_binary.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1339a8170408a7bcde261617cc599587c8f25c4dc94f780976ee1638879888e9
size 147

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_blackandwhite_plain.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:82d0397f38971cf90d7c064db332093e686196e244ece1196cca2071d27f0a6f
size 147

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_grayscale_plain.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f8e8b8a1a05e76b1eeb577373c3a6f492e356f0dd58489afded248415cec4a07
size 145

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_grayscale_plain_normalized.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f8e8b8a1a05e76b1eeb577373c3a6f492e356f0dd58489afded248415cec4a07
size 145

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_L8_rings.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:388c86b3dd472ef17fb911ae424b81baeeeff74c4161cf5825eab50698d54348
size 27884

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb24_00000_00000.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e3fc46b9f0546941ef95be7b750fb29376a679a921f2581403882b0e76e9caf
size 2250

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb24_rgb_plain.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c44322c4bf461acea27053057f5241afb029d9a1e66e94dcf1be6f86f7f97727
size 152

3
tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb24_rgb_plain_normalized.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c44322c4bf461acea27053057f5241afb029d9a1e66e94dcf1be6f86f7f97727
size 152

3
tests/Images/Input/Pbm/00000_00000.ppm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b291c0d3a0747c7425a3445bea1de1fa7c112a183d2f78bb9fc96ec5ae9804e
size 2623

3
tests/Images/Input/Pbm/Gene-UP WebSocket RunImageMask.pgm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:38c6aadba17548dbe6496de2c81c3cb719d2330499c3cf7d4237e78dec098e53
size 614417

3
tests/Images/Input/Pbm/blackandwhite_binary.pbm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0313a99c2acdd34d6ba67815d1daa25f2452bfada71a1828dbcbb3cc48a20b20
size 48

3
tests/Images/Input/Pbm/blackandwhite_plain.pbm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:12ccfacadea1c97c15b6d192ee3ae3b6a1d79bdca30fddbe597390f71e86d59c
size 367

3
tests/Images/Input/Pbm/grayscale_plain.pgm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08068b4d30f19024e716176033f13f7203a45513e6ae73e79dc824509c92621a
size 507

3
tests/Images/Input/Pbm/grayscale_plain_magick.pgm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ec652ee7ea1a82d8ea2fd344670ab9aee2c2f52af86458d9991754204e1fc2bb
size 464

3
tests/Images/Input/Pbm/grayscale_plain_normalized.pgm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e39342751c2a57a060a029213fd7d83cb9a72881b8b01dd6d5b0e897df5077de
size 599

3
tests/Images/Input/Pbm/rgb_plain.ppm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:895cd889f6723a5357936e852308cff25b74ead01618bf8efa0f876a86dc18c1
size 205

3
tests/Images/Input/Pbm/rgb_plain_magick.ppm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f38a31162f31e77f5ad80da968a386b2cbccc6998a88a4c6b311b48919119a1
size 149

3
tests/Images/Input/Pbm/rgb_plain_normalized.ppm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:59be6295e3983708ffba811a408acd83df8e9736b487a94d30132dee0edd6cb6
size 234

3
tests/Images/Input/Pbm/rings.pgm

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b5382e745e0447f13387ac02636b37baf3b4bbd3bc545177d407fd98a7cbe17
size 40038
Loading…
Cancel
Save