Browse Source

Switch over to new Color struct as default.

Former-commit-id: 36ee757ac11c4a801317a501ff723424c2cb37ce
Former-commit-id: d2c80a5c23eb7dbc38f4384ea5c9497d48582fdf
Former-commit-id: 02e23e7442f61e89f3e2d903be05f11ecce509c7
af/merge-core
James Jackson-South 11 years ago
parent
commit
6c23b82c08
  1. 16
      src/ImageProcessor/Colors/Color.cs
  2. 83
      src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
  3. 10
      src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
  4. 21
      src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
  5. 12
      src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs
  6. 2
      src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
  7. 7
      src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
  8. 10
      src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
  9. 8
      src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
  10. 41
      src/ImageProcessor/Formats/Png/GrayscaleReader.cs
  11. 16
      src/ImageProcessor/Formats/Png/IColorReader.cs
  12. 41
      src/ImageProcessor/Formats/Png/PaletteIndexReader.cs
  13. 9
      src/ImageProcessor/Formats/Png/PngDecoderCore.cs
  14. 25
      src/ImageProcessor/Formats/Png/PngEncoder.cs
  15. 39
      src/ImageProcessor/Formats/Png/TrueColorReader.cs
  16. 8
      src/ImageProcessor/IImageBase.cs
  17. 52
      src/ImageProcessor/ImageBase.cs
  18. 2
      src/ImageProcessor/ParallelImageProcessor.cs
  19. 21
      tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs
  20. 1
      tests/ImageProcessor.Tests/TestImages/Formats/Png/splash.png.REMOVED.git-id

16
src/ImageProcessor/Colors/Color.cs

@ -31,18 +31,10 @@ namespace ImageProcessor
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct. /// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary> /// </summary>
/// <param name="r"> /// <param name="r">The red component of this <see cref="Color"/>.</param>
/// The red component of this <see cref="Color"/>. /// <param name="g">The green component of this <see cref="Color"/>.</param>
/// </param> /// <param name="b">The blue component of this <see cref="Color"/>.</param>
/// <param name="g"> /// <param name="a">The alpha component of this <see cref="Color"/>.</param>
/// The green component of this <see cref="Color"/>.
/// </param>
/// <param name="b">
/// The blue component of this <see cref="Color"/>.
/// </param>
/// <param name="a">
/// The alpha component of this <see cref="Color"/>.
/// </param>
public Color(float r, float g, float b, float a) public Color(float r, float g, float b, float a)
: this() : this()
{ {

83
src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs

@ -1,12 +1,7 @@
// -------------------------------------------------------------------------------------------------------------------- // <copyright file="BmpDecoderCore.cs" company="James South">
// <copyright file="BmpDecoderCore.cs" company="James South"> // Copyright (c) James South and contributors.
// Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0.
// Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary>
// Performs the bmp decoding operation.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats namespace ImageProcessor.Formats
{ {
@ -109,7 +104,7 @@ namespace ImageProcessor.Formats
+ $"bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); + $"bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'");
} }
byte[] imageData = new byte[this.infoHeader.Width * this.infoHeader.Height * 4]; float[] imageData = new float[this.infoHeader.Width * this.infoHeader.Height * 4];
switch (this.infoHeader.Compression) switch (this.infoHeader.Compression)
{ {
@ -180,12 +175,12 @@ namespace ImageProcessor.Formats
/// <summary> /// <summary>
/// Reads the color palette from the stream. /// Reads the color palette from the stream.
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param> /// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="bits">The number of bits per pixel.</param> /// <param name="bits">The number of bits per pixel.</param>
private void ReadRgbPalette(byte[] imageData, byte[] colors, int width, int height, int bits) private void ReadRgbPalette(float[] imageData, byte[] colors, int width, int height, int bits)
{ {
// Pixels per byte (bits per pixel) // Pixels per byte (bits per pixel)
int ppb = 8 / bits; int ppb = 8 / bits;
@ -224,13 +219,15 @@ namespace ImageProcessor.Formats
for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++) for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
{ {
int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask; int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int arrayOffset = ((row * width) + (colOffset + shift)) * 4; int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
imageData[arrayOffset + 0] = colors[colorIndex * 4];
imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1]; // We divide by 255 as we will store the colors in our floating point format.
imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2]; // Stored in r-> g-> b-> a order.
imageData[arrayOffset + 3] = 255; imageData[arrayOffset] = colors[colorIndex + 2] / 255f; // r
imageData[arrayOffset + 1] = colors[colorIndex + 1] / 255f; // g
imageData[arrayOffset + 2] = colors[colorIndex] / 255f; // b
imageData[arrayOffset + 3] = 1; // a
} }
} }
}); });
@ -239,13 +236,14 @@ namespace ImageProcessor.Formats
/// <summary> /// <summary>
/// Reads the 16 bit color palette from the stream /// Reads the 16 bit color palette from the stream
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
private void ReadRgb16(byte[] imageData, int width, int height) private void ReadRgb16(float[] imageData, int width, int height)
{ {
const int ScaleR = 256 / 32; // We divide here as we will store the colors in our floating point format.
const int ScaleG = 256 / 64; const int ScaleR = (256 / 32) / 32;
const int ScaleG = (256 / 64) / 64;
int alignment; int alignment;
byte[] data = this.GetImageArray(width, height, 2, out alignment); byte[] data = this.GetImageArray(width, height, 2, out alignment);
@ -266,16 +264,17 @@ namespace ImageProcessor.Formats
short temp = BitConverter.ToInt16(data, offset); short temp = BitConverter.ToInt16(data, offset);
byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); float r = ((temp & Rgb16RMask) >> 11) * ScaleR;
byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); float g = ((temp & Rgb16GMask) >> 5) * ScaleG;
byte b = (byte)((temp & Rgb16BMask) * ScaleR); float b = (temp & Rgb16BMask) * ScaleR;
int arrayOffset = ((row * width) + x) * 4; int arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = b; // Stored in r-> g-> b-> a order.
imageData[arrayOffset] = r;
imageData[arrayOffset + 1] = g; imageData[arrayOffset + 1] = g;
imageData[arrayOffset + 2] = r; imageData[arrayOffset + 2] = b;
imageData[arrayOffset + 3] = 255; imageData[arrayOffset + 3] = 1;
} }
}); });
} }
@ -283,10 +282,10 @@ namespace ImageProcessor.Formats
/// <summary> /// <summary>
/// Reads the 24 bit color palette from the stream /// Reads the 24 bit color palette from the stream
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
private void ReadRgb24(byte[] imageData, int width, int height) private void ReadRgb24(float[] imageData, int width, int height)
{ {
int alignment; int alignment;
byte[] data = this.GetImageArray(width, height, 3, out alignment); byte[] data = this.GetImageArray(width, height, 3, out alignment);
@ -306,10 +305,12 @@ namespace ImageProcessor.Formats
int offset = rowOffset + (x * 3); int offset = rowOffset + (x * 3);
int arrayOffset = ((row * width) + x) * 4; int arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = data[offset + 0]; // We divide by 255 as we will store the colors in our floating point format.
imageData[arrayOffset + 1] = data[offset + 1]; // Stored in r-> g-> b-> a order.
imageData[arrayOffset + 2] = data[offset + 2]; imageData[arrayOffset] = data[offset + 2] / 255f;
imageData[arrayOffset + 3] = 255; imageData[arrayOffset + 1] = data[offset + 1] / 255f;
imageData[arrayOffset + 2] = data[offset] / 255f;
imageData[arrayOffset + 3] = 1;
} }
}); });
} }
@ -317,10 +318,10 @@ namespace ImageProcessor.Formats
/// <summary> /// <summary>
/// Reads the 32 bit color palette from the stream /// Reads the 32 bit color palette from the stream
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
private void ReadRgb32(byte[] imageData, int width, int height) private void ReadRgb32(float[] imageData, int width, int height)
{ {
int alignment; int alignment;
byte[] data = this.GetImageArray(width, height, 4, out alignment); byte[] data = this.GetImageArray(width, height, 4, out alignment);
@ -338,12 +339,14 @@ namespace ImageProcessor.Formats
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
int offset = rowOffset + (x * 4); int offset = rowOffset + (x * 4);
int arrayOffset = ((row * width) + x) * 4;
var arrayOffset = ((row * width) + x) * 4; // We divide by 255 as we will store the colors in our floating point format.
imageData[arrayOffset + 0] = data[offset + 0]; // Stored in r-> g-> b-> a order.
imageData[arrayOffset + 1] = data[offset + 1]; imageData[arrayOffset] = data[offset + 2] / 255f;
imageData[arrayOffset + 2] = data[offset + 2]; imageData[arrayOffset + 1] = data[offset + 1] / 255f;
imageData[arrayOffset + 3] = 255; // Can we get alpha here? imageData[arrayOffset + 2] = data[offset] / 255f;
imageData[arrayOffset + 3] = 1; // TODO: Can we use our real alpha here?
} }
}); });
} }

10
src/ImageProcessor/Formats/Bmp/BmpEncoder.cs

@ -100,7 +100,7 @@ namespace ImageProcessor.Formats
amount = 4 - amount; amount = 4 - amount;
} }
byte[] data = image.Pixels; float[] data = image.Pixels;
for (int y = image.Height - 1; y >= 0; y--) for (int y = image.Height - 1; y >= 0; y--)
{ {
@ -108,9 +108,11 @@ namespace ImageProcessor.Formats
{ {
int offset = ((y * image.Width) + x) * 4; int offset = ((y * image.Width) + x) * 4;
writer.Write(data[offset + 0]); // Limit the output range and multiply out from our floating point.
writer.Write(data[offset + 1]); // Convert back to b-> g-> r-> a order.
writer.Write(data[offset + 2]); writer.Write((byte)(data[offset + 2].Clamp(0, 1) * 255));
writer.Write((byte)(data[offset + 1].Clamp(0, 1) * 255));
writer.Write((byte)(data[offset].Clamp(0, 1) * 255));
} }
for (int i = 0; i < amount; i++) for (int i = 0; i < amount; i++)

21
src/ImageProcessor/Formats/Gif/GifDecoderCore.cs

@ -31,7 +31,7 @@ namespace ImageProcessor.Formats
/// <summary> /// <summary>
/// The current frame. /// The current frame.
/// </summary> /// </summary>
private byte[] currentFrame; private float[] currentFrame;
/// <summary> /// <summary>
/// The logical screen descriptor. /// The logical screen descriptor.
@ -288,15 +288,15 @@ namespace ImageProcessor.Formats
if (this.currentFrame == null) if (this.currentFrame == null)
{ {
this.currentFrame = new byte[imageWidth * imageHeight * 4]; this.currentFrame = new float[imageWidth * imageHeight * 4];
} }
byte[] lastFrame = null; float[] lastFrame = null;
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{ {
lastFrame = new byte[imageWidth * imageHeight * 4]; lastFrame = new float[imageWidth * imageHeight * 4];
Array.Copy(this.currentFrame, lastFrame, lastFrame.Length); Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
} }
@ -352,18 +352,20 @@ namespace ImageProcessor.Formats
this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index) this.graphicsControlExtension.TransparencyIndex != index)
{ {
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
int indexOffset = index * 3; int indexOffset = index * 3;
this.currentFrame[offset + 0] = colorTable[indexOffset + 2]; this.currentFrame[offset + 0] = colorTable[indexOffset] / 255f; // r
this.currentFrame[offset + 1] = colorTable[indexOffset + 1]; this.currentFrame[offset + 1] = colorTable[indexOffset + 1] / 255f; // g
this.currentFrame[offset + 2] = colorTable[indexOffset + 0]; this.currentFrame[offset + 2] = colorTable[indexOffset + 2] / 255f; // b
this.currentFrame[offset + 3] = 255; this.currentFrame[offset + 3] = 1; // a
} }
i++; i++;
} }
} }
byte[] pixels = new byte[imageWidth * imageHeight * 4]; float[] pixels = new float[imageWidth * imageHeight * 4];
Array.Copy(this.currentFrame, pixels, pixels.Length); Array.Copy(this.currentFrame, pixels, pixels.Length);
@ -406,6 +408,7 @@ namespace ImageProcessor.Formats
{ {
offset = ((y * imageWidth) + x) * 4; offset = ((y * imageWidth) + x) * 4;
// Stored in r-> g-> b-> a order.
this.currentFrame[offset + 0] = 0; this.currentFrame[offset + 0] = 0;
this.currentFrame[offset + 1] = 0; this.currentFrame[offset + 1] = 0;
this.currentFrame[offset + 2] = 0; this.currentFrame[offset + 2] = 0;

12
src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs

@ -199,7 +199,7 @@ namespace ImageProcessor.Formats
{ {
// If so, check if I have a previous node setup. This will only occur if the first color in the image // If so, check if I have a previous node setup. This will only occur if the first color in the image
// happens to be black, with an alpha component of zero. // happens to be black, with an alpha component of zero.
if (null == this.previousNode) if (this.previousNode == null)
{ {
this.previousColor = pixel.BGRA; this.previousColor = pixel.BGRA;
this.root.AddColor(pixel, this.maxColorBits, 0, this); this.root.AddColor(pixel, this.maxColorBits, 0, this);
@ -274,7 +274,7 @@ namespace ImageProcessor.Formats
{ {
// Find the deepest level containing at least one reducible node // Find the deepest level containing at least one reducible node
int index = this.maxColorBits - 1; int index = this.maxColorBits - 1;
while ((index > 0) && (null == this.reducibleNodes[index])) while ((index > 0) && (this.reducibleNodes[index] == null))
{ {
index--; index--;
} }
@ -407,7 +407,7 @@ namespace ImageProcessor.Formats
OctreeNode child = this.children[index]; OctreeNode child = this.children[index];
if (null == child) if (child == null)
{ {
// Create a new child node and store it in the array // Create a new child node and store it in the array
child = new OctreeNode(level + 1, colorBits, octree); child = new OctreeNode(level + 1, colorBits, octree);
@ -431,7 +431,7 @@ namespace ImageProcessor.Formats
// Loop through all children and add their information to this node // Loop through all children and add their information to this node
for (int index = 0; index < 8; index++) for (int index = 0; index < 8; index++)
{ {
if (null != this.children[index]) if (this.children[index] != null)
{ {
this.red += this.children[index].red; this.red += this.children[index].red;
this.green += this.children[index].green; this.green += this.children[index].green;
@ -477,7 +477,7 @@ namespace ImageProcessor.Formats
// Loop through children looking for leaves // Loop through children looking for leaves
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
if (null != this.children[i]) if (this.children[i] != null)
{ {
this.children[i].ConstructPalette(palette, ref index); this.children[i].ConstructPalette(palette, ref index);
} }
@ -508,7 +508,7 @@ namespace ImageProcessor.Formats
((pixel.G & Mask[level]) >> (shift - 1)) | ((pixel.G & Mask[level]) >> (shift - 1)) |
((pixel.B & Mask[level]) >> shift); ((pixel.B & Mask[level]) >> shift);
if (null != this.children[pixelIndex]) if (this.children[pixelIndex] != null)
{ {
index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1); index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1);
} }

2
src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs

@ -69,7 +69,7 @@ namespace ImageProcessor.Formats
Image image = new Image(); Image image = new Image();
int pixelCount = this.Pixels.Length; int pixelCount = this.Pixels.Length;
byte[] bgraPixels = new byte[pixelCount * 4]; float[] bgraPixels = new float[pixelCount * 4];
for (int i = 0; i < pixelCount; i++) for (int i = 0; i < pixelCount; i++)
{ {

7
src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs

@ -18,7 +18,7 @@ namespace ImageProcessor.Formats
private readonly bool singlePass; private readonly bool singlePass;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Quantizer"/> class. /// Initializes a new instance of the <see cref="Quantizer"/> class.
/// </summary> /// </summary>
/// <param name="singlePass"> /// <param name="singlePass">
/// If true, the quantization only needs to loop through the source pixels once /// If true, the quantization only needs to loop through the source pixels once
@ -56,6 +56,7 @@ namespace ImageProcessor.Formats
byte[] quantizedPixels = new byte[width * height]; byte[] quantizedPixels = new byte[width * height];
// Get the pallete
List<Bgra32> palette = this.GetPalette(); List<Bgra32> palette = this.GetPalette();
this.SecondPass(imageBase, quantizedPixels, width, height); this.SecondPass(imageBase, quantizedPixels, width, height);
@ -94,7 +95,8 @@ namespace ImageProcessor.Formats
{ {
int i = 0; int i = 0;
// Convert the first pixel, so that I have values going into the loop // Convert the first pixel, so that I have values going into the loop.
// Implicit cast here from Color.
Bgra32 previousPixel = source[0, 0]; Bgra32 previousPixel = source[0, 0];
byte pixelValue = this.QuantizePixel(previousPixel); byte pixelValue = this.QuantizePixel(previousPixel);
@ -104,6 +106,7 @@ namespace ImageProcessor.Formats
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
// Implicit cast here from Color.
Bgra32 sourcePixel = source[x, y]; Bgra32 sourcePixel = source[x, y];
// Check if this is the same as the last pixel. If so use that value // Check if this is the same as the last pixel. If so use that value

10
src/ImageProcessor/Formats/Jpg/JpegDecoder.cs

@ -99,7 +99,7 @@ namespace ImageProcessor.Formats
int pixelWidth = jpg.Width; int pixelWidth = jpg.Width;
int pixelHeight = jpg.Height; int pixelHeight = jpg.Height;
byte[] pixels = new byte[pixelWidth * pixelHeight * 4]; float[] pixels = new float[pixelWidth * pixelHeight * 4];
if (!(jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8)) if (!(jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8))
{ {
@ -119,10 +119,10 @@ namespace ImageProcessor.Formats
int offset = ((y * pixelWidth) + x) * 4; int offset = ((y * pixelWidth) + x) * 4;
pixels[offset + 0] = (byte)sample[2]; pixels[offset + 0] = sample[0] / 255f;
pixels[offset + 1] = (byte)sample[1]; pixels[offset + 1] = sample[1] / 255f;
pixels[offset + 2] = (byte)sample[0]; pixels[offset + 2] = sample[2] / 255f;
pixels[offset + 3] = 255; pixels[offset + 3] = 1;
} }
}); });

8
src/ImageProcessor/Formats/Jpg/JpegEncoder.cs

@ -89,7 +89,7 @@ namespace ImageProcessor.Formats
int pixelWidth = image.Width; int pixelWidth = image.Width;
int pixelHeight = image.Height; int pixelHeight = image.Height;
byte[] sourcePixels = image.Pixels; float[] sourcePixels = image.Pixels;
SampleRow[] rows = new SampleRow[pixelHeight]; SampleRow[] rows = new SampleRow[pixelHeight];
@ -105,9 +105,9 @@ namespace ImageProcessor.Formats
int start = x * 3; int start = x * 3;
int source = ((y * pixelWidth) + x) * 4; int source = ((y * pixelWidth) + x) * 4;
samples[start] = sourcePixels[source + 2]; samples[start] = (byte)(sourcePixels[source].Clamp(0, 1) * 255);
samples[start + 1] = sourcePixels[source + 1]; samples[start + 1] = (byte)(sourcePixels[source + 1].Clamp(0, 1) * 255);
samples[start + 2] = sourcePixels[source]; samples[start + 2] = (byte)(sourcePixels[source + 2].Clamp(0, 1) * 255);
} }
rows[y] = new SampleRow(samples, pixelWidth, 8, 3); rows[y] = new SampleRow(samples, pixelWidth, 8, 3);

41
src/ImageProcessor/Formats/Png/GrayscaleReader.cs

@ -1,12 +1,7 @@
// -------------------------------------------------------------------------------------------------------------------- // <copyright file="GrayscaleReader.cs" company="James South">
// <copyright file="GrayscaleReader.cs" company="James South"> // Copyright (c) James South and contributors.
// Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0.
// Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary>
// Color reader for reading grayscale colors from a png file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats namespace ImageProcessor.Formats
{ {
@ -37,31 +32,25 @@ namespace ImageProcessor.Formats
this.useAlpha = useAlpha; this.useAlpha = useAlpha;
} }
/// <summary> /// <inheritdoc/>
/// Reads the specified scanline. public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
/// </summary>
/// <param name="scanline">The scanline.</param>
/// <param name="pixels">The pixels, where the colors should be stored in BGRA format.</param>
/// <param name="header">
/// The header, which contains information about the png file, like
/// the width of the image and the height.
/// </param>
public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header)
{ {
int offset; int offset;
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth); byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
if (this.useAlpha) if (this.useAlpha)
{ {
for (int x = 0; x < header.Width / 2; x++) for (int x = 0; x < header.Width / 2; x++)
{ {
offset = ((this.row * header.Width) + x) * 4; offset = ((this.row * header.Width) + x) * 4;
pixels[offset + 0] = newScanline[x * 2]; pixels[offset] = newScanline[x * 2] / 255f;
pixels[offset + 1] = newScanline[x * 2]; pixels[offset + 1] = newScanline[x * 2] / 255f;
pixels[offset + 2] = newScanline[x * 2]; pixels[offset + 2] = newScanline[x * 2] / 255f;
pixels[offset + 3] = newScanline[(x * 2) + 1]; pixels[offset + 3] = newScanline[(x * 2) + 1] / 255f;
} }
} }
else else
@ -70,10 +59,10 @@ namespace ImageProcessor.Formats
{ {
offset = ((this.row * header.Width) + x) * 4; offset = ((this.row * header.Width) + x) * 4;
pixels[offset + 0] = newScanline[x]; pixels[offset] = newScanline[x] / 255f;
pixels[offset + 1] = newScanline[x]; pixels[offset + 1] = newScanline[x] / 255f;
pixels[offset + 2] = newScanline[x]; pixels[offset + 2] = newScanline[x] / 255f;
pixels[offset + 3] = 255; pixels[offset + 3] = 1;
} }
} }

16
src/ImageProcessor/Formats/Png/IColorReader.cs

@ -1,18 +1,12 @@
// -------------------------------------------------------------------------------------------------------------------- // <copyright file="IColorReader.cs" company="James South">
// <copyright file="IColorReader.cs" company="James South"> // Copyright (c) James South and contributors.
// Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0.
// Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary>
// Encapsulates methods for color readers, which are responsible for reading
// different color formats from a png file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats namespace ImageProcessor.Formats
{ {
/// <summary> /// <summary>
/// Encapsulates methods for color readers, which are responsible for reading /// Encapsulates methods for color readers, which are responsible for reading
/// different color formats from a png file. /// different color formats from a png file.
/// </summary> /// </summary>
public interface IColorReader public interface IColorReader
@ -26,6 +20,6 @@ namespace ImageProcessor.Formats
/// The header, which contains information about the png file, like /// The header, which contains information about the png file, like
/// the width of the image and the height. /// the width of the image and the height.
/// </param> /// </param>
void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header); void ReadScanline(byte[] scanline, float[] pixels, PngHeader header);
} }
} }

41
src/ImageProcessor/Formats/Png/PaletteIndexReader.cs

@ -1,12 +1,7 @@
// -------------------------------------------------------------------------------------------------------------------- // <copyright file="PaletteIndexReader.cs" company="James South">
// <copyright file="PaletteIndexReader.cs" company="James South"> // Copyright (c) James South and contributors.
// Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0.
// Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary>
// A color reader for reading palette indices from the png file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats namespace ImageProcessor.Formats
{ {
@ -43,14 +38,8 @@ namespace ImageProcessor.Formats
this.paletteAlpha = paletteAlpha; this.paletteAlpha = paletteAlpha;
} }
/// <summary> /// <inheritdoc/>
/// Reads the specified scanline. public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
/// </summary>
/// <param name="scanline">The scanline.</param>
/// <param name="pixels">The pixels, where the colors should be stored in BGRA format.</param>
/// <param name="header">The header, which contains information about the png file, like
/// the width of the image and the height.</param>
public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header)
{ {
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth); byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
int offset, index; int offset, index;
@ -65,13 +54,14 @@ namespace ImageProcessor.Formats
index = newScanline[i]; index = newScanline[i];
offset = ((this.row * header.Width) + i) * 4; offset = ((this.row * header.Width) + i) * 4;
int pixelOffset = index * 3;
pixels[offset + 0] = this.palette[(index * 3) + 2]; pixels[offset] = this.palette[pixelOffset] / 255f;
pixels[offset + 1] = this.palette[(index * 3) + 1]; pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f;
pixels[offset + 2] = this.palette[(index * 3) + 0]; pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f;
pixels[offset + 3] = this.paletteAlpha.Length > index pixels[offset + 3] = this.paletteAlpha.Length > index
? this.paletteAlpha[index] ? this.paletteAlpha[index] / 255f
: (byte)255; : 1;
} }
} }
else else
@ -81,11 +71,12 @@ namespace ImageProcessor.Formats
index = newScanline[i]; index = newScanline[i];
offset = ((this.row * header.Width) + i) * 4; offset = ((this.row * header.Width) + i) * 4;
int pixelOffset = index * 3;
pixels[offset + 0] = this.palette[(index * 3) + 2]; pixels[offset] = this.palette[pixelOffset] / 255f;
pixels[offset + 1] = this.palette[(index * 3) + 1]; pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f;
pixels[offset + 2] = this.palette[(index * 3) + 0]; pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f;
pixels[offset + 3] = 255; pixels[offset + 3] = 1;
} }
} }

9
src/ImageProcessor/Formats/Png/PngDecoderCore.cs

@ -16,9 +16,6 @@ namespace ImageProcessor.Formats
using System.Linq; using System.Linq;
using System.Text; using System.Text;
//using ICSharpCode.SharpZipLib.Checksums;
//using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
/// <summary> /// <summary>
/// Performs the png decoding operation. /// Performs the png decoding operation.
/// </summary> /// </summary>
@ -145,7 +142,7 @@ namespace ImageProcessor.Formats
+ $"max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); + $"max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'");
} }
byte[] pixels = new byte[this.header.Width * this.header.Height * 4]; float[] pixels = new float[this.header.Width * this.header.Height * 4];
PngColorTypeInformation colorTypeInformation = ColorTypes[this.header.ColorType]; PngColorTypeInformation colorTypeInformation = ColorTypes[this.header.ColorType];
@ -248,10 +245,10 @@ namespace ImageProcessor.Formats
/// </summary> /// </summary>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param> /// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels"> /// <param name="pixels">
/// The <see cref="T:byte[]"/> containing pixel data.</param> /// The <see cref="T:float[]"/> containing pixel data.</param>
/// <param name="colorReader">The color reader.</param> /// <param name="colorReader">The color reader.</param>
/// <param name="colorTypeInformation">The color type information.</param> /// <param name="colorTypeInformation">The color type information.</param>
private void ReadScanlines(MemoryStream dataStream, byte[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation) private void ReadScanlines(MemoryStream dataStream, float[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation)
{ {
dataStream.Position = 0; dataStream.Position = 0;

25
src/ImageProcessor/Formats/Png/PngEncoder.cs

@ -8,9 +8,6 @@ namespace ImageProcessor.Formats
using System; using System;
using System.IO; using System.IO;
//using ICSharpCode.SharpZipLib.Checksums;
//using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
/// <summary> /// <summary>
/// Image encoder for writing image data to a stream in png format. /// Image encoder for writing image data to a stream in png format.
/// </summary> /// </summary>
@ -36,7 +33,7 @@ namespace ImageProcessor.Formats
public int Quality { get; set; } public int Quality { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public string MimeType => "image/jpepngg"; public string MimeType => "image/png";
/// <inheritdoc/> /// <inheritdoc/>
public string Extension => "png"; public string Extension => "png";
@ -225,7 +222,7 @@ namespace ImageProcessor.Formats
/// <param name="imageBase">The image base.</param> /// <param name="imageBase">The image base.</param>
private void WriteDataChunksFast(Stream stream, ImageBase imageBase) private void WriteDataChunksFast(Stream stream, ImageBase imageBase)
{ {
byte[] pixels = imageBase.Pixels; float[] pixels = imageBase.Pixels;
// Convert the pixel array to a new array for adding // Convert the pixel array to a new array for adding
// the filter byte. // the filter byte.
@ -297,7 +294,7 @@ namespace ImageProcessor.Formats
/// <param name="imageBase">The image base.</param> /// <param name="imageBase">The image base.</param>
private void WriteDataChunks(Stream stream, ImageBase imageBase) private void WriteDataChunks(Stream stream, ImageBase imageBase)
{ {
byte[] pixels = imageBase.Pixels; float[] pixels = imageBase.Pixels;
byte[] data = new byte[(imageBase.Width * imageBase.Height * 4) + imageBase.Height]; byte[] data = new byte[(imageBase.Width * imageBase.Height * 4) + imageBase.Height];
@ -321,19 +318,19 @@ namespace ImageProcessor.Formats
// Calculate the offset for the original pixel array. // Calculate the offset for the original pixel array.
int pixelOffset = ((y * imageBase.Width) + x) * 4; int pixelOffset = ((y * imageBase.Width) + x) * 4;
data[dataOffset + 0] = pixels[pixelOffset + 2]; data[dataOffset] = (byte)(pixels[pixelOffset].Clamp(0, 1) * 255);
data[dataOffset + 1] = pixels[pixelOffset + 1]; data[dataOffset + 1] = (byte)(pixels[pixelOffset + 1].Clamp(0, 1) * 255);
data[dataOffset + 2] = pixels[pixelOffset + 0]; data[dataOffset + 2] = (byte)(pixels[pixelOffset + 2].Clamp(0, 1) * 255);
data[dataOffset + 3] = pixels[pixelOffset + 3]; data[dataOffset + 3] = (byte)(pixels[pixelOffset + 3].Clamp(0, 1) * 255);
if (y > 0) if (y > 0)
{ {
int lastOffset = (((y - 1) * imageBase.Width) + x) * 4; int lastOffset = (((y - 1) * imageBase.Width) + x) * 4;
data[dataOffset + 0] -= pixels[lastOffset + 2]; data[dataOffset] -= (byte)(pixels[lastOffset].Clamp(0, 1) * 255);
data[dataOffset + 1] -= pixels[lastOffset + 1]; data[dataOffset + 1] -= (byte)(pixels[lastOffset + 1].Clamp(0, 1) * 255);
data[dataOffset + 2] -= pixels[lastOffset + 0]; data[dataOffset + 2] -= (byte)(pixels[lastOffset + 2].Clamp(0, 1) * 255);
data[dataOffset + 3] -= pixels[lastOffset + 3]; data[dataOffset + 3] -= (byte)(pixels[lastOffset + 3].Clamp(0, 1) * 255);
} }
} }
} }

39
src/ImageProcessor/Formats/Png/TrueColorReader.cs

@ -1,13 +1,7 @@
// -------------------------------------------------------------------------------------------------------------------- // <copyright file="TrueColorReader.cs" company="James South">
// <copyright file="TrueColorReader.cs" company="James South"> // Copyright (c) James South and contributors.
// Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0.
// Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary>
// Color reader for reading true colors from a png file. Only colors
// with 24 or 32 bit (3 or 4 bytes) per pixel are supported at the moment.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats namespace ImageProcessor.Formats
{ {
@ -37,14 +31,8 @@ namespace ImageProcessor.Formats
this.useAlpha = useAlpha; this.useAlpha = useAlpha;
} }
/// <summary> /// <inheritdoc/>
/// Reads the specified scanline. public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
/// </summary>
/// <param name="scanline">The scanline.</param>
/// <param name="pixels">The pixels, where the colors should be stored in BGRA format.</param>
/// <param name="header">The header, which contains information about the png file, like
/// the width of the image and the height.</param>
public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header)
{ {
int offset; int offset;
@ -56,10 +44,10 @@ namespace ImageProcessor.Formats
{ {
offset = ((this.row * header.Width) + (x >> 2)) * 4; offset = ((this.row * header.Width) + (x >> 2)) * 4;
pixels[offset + 0] = newScanline[x + 2]; pixels[offset + 0] = newScanline[x] / 255f;
pixels[offset + 1] = newScanline[x + 1]; pixels[offset + 1] = newScanline[x + 1] / 255f;
pixels[offset + 2] = newScanline[x + 0]; pixels[offset + 2] = newScanline[x + 2] / 255f;
pixels[offset + 3] = newScanline[x + 3]; pixels[offset + 3] = newScanline[x + 3] / 255f;
} }
} }
else else
@ -67,11 +55,12 @@ namespace ImageProcessor.Formats
for (int x = 0; x < newScanline.Length / 3; x++) for (int x = 0; x < newScanline.Length / 3; x++)
{ {
offset = ((this.row * header.Width) + x) * 4; offset = ((this.row * header.Width) + x) * 4;
int pixelOffset = x * 3;
pixels[offset + 0] = newScanline[(x * 3) + 2]; pixels[offset + 0] = newScanline[pixelOffset] / 255f;
pixels[offset + 1] = newScanline[(x * 3) + 1]; pixels[offset + 1] = newScanline[pixelOffset + 1] / 255f;
pixels[offset + 2] = newScanline[(x * 3) + 0]; pixels[offset + 2] = newScanline[pixelOffset + 2] / 255f;
pixels[offset + 3] = 255; pixels[offset + 3] = 1;
} }
} }

8
src/ImageProcessor/IImageBase.cs

@ -20,7 +20,7 @@ namespace ImageProcessor
/// and stores the blue, the green, the red and the alpha value for /// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order. /// each pixel in this order.
/// </remarks> /// </remarks>
byte[] Pixels { get; } float[] Pixels { get; }
/// <summary> /// <summary>
/// Gets the width in pixels. /// Gets the width in pixels.
@ -66,8 +66,8 @@ namespace ImageProcessor
/// The y-coordinate of the pixel. Must be greater /// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel. /// than zero and smaller than the width of the pixel.
/// </param> /// </param>
/// <returns>The <see cref="Bgra32"/> at the specified position.</returns> /// <returns>The <see cref="Color"/> at the specified position.</returns>
Bgra32 this[int x, int y] { get; set; } Color this[int x, int y] { get; set; }
/// <summary> /// <summary>
/// Sets the pixel array of the image. /// Sets the pixel array of the image.
@ -85,6 +85,6 @@ namespace ImageProcessor
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4. /// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception> /// </exception>
void SetPixels(int width, int height, byte[] pixels); void SetPixels(int width, int height, float[] pixels);
} }
} }

52
src/ImageProcessor/ImageBase.cs

@ -40,7 +40,7 @@ namespace ImageProcessor
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.Pixels = new byte[width * height * 4]; this.Pixels = new float[width * height * 4];
} }
/// <summary> /// <summary>
@ -56,13 +56,13 @@ namespace ImageProcessor
{ {
Guard.NotNull(other, nameof(other), "Other image cannot be null."); Guard.NotNull(other, nameof(other), "Other image cannot be null.");
byte[] pixels = other.Pixels; float[] pixels = other.Pixels;
this.Width = other.Width; this.Width = other.Width;
this.Height = other.Height; this.Height = other.Height;
this.Quality = other.Quality; this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay; this.FrameDelay = other.FrameDelay;
this.Pixels = new byte[pixels.Length]; this.Pixels = new float[pixels.Length];
Array.Copy(pixels, this.Pixels, pixels.Length); Array.Copy(pixels, this.Pixels, pixels.Length);
} }
@ -84,7 +84,7 @@ namespace ImageProcessor
/// and stores the blue, the green, the red and the alpha value for /// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order. /// each pixel in this order.
/// </remarks> /// </remarks>
public byte[] Pixels { get; private set; } public float[] Pixels { get; private set; }
/// <summary> /// <summary>
/// Gets the width in pixels. /// Gets the width in pixels.
@ -106,9 +106,7 @@ namespace ImageProcessor
/// </summary> /// </summary>
public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
/// <summary> /// <inheritdoc/>
/// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
/// </summary>
public int Quality { get; set; } public int Quality { get; set; }
/// <summary> /// <summary>
@ -119,19 +117,8 @@ namespace ImageProcessor
/// </summary> /// </summary>
public int FrameDelay { get; set; } public int FrameDelay { get; set; }
/// <summary> /// <inheritdoc/>
/// Gets or sets the color of a pixel at the specified position. public Color this[int x, int y]
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="Bgra32"/> at the specified position.</returns>
public Bgra32 this[int x, int y]
{ {
get get
{ {
@ -148,7 +135,7 @@ namespace ImageProcessor
#endif #endif
int start = ((y * this.Width) + x) * 4; int start = ((y * this.Width) + x) * 4;
return new Bgra32(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]); return new Color(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]);
} }
set set
@ -166,30 +153,15 @@ namespace ImageProcessor
#endif #endif
int start = ((y * this.Width) + x) * 4; int start = ((y * this.Width) + x) * 4;
this.Pixels[start + 0] = value.B; this.Pixels[start + 0] = value.R;
this.Pixels[start + 1] = value.G; this.Pixels[start + 1] = value.G;
this.Pixels[start + 2] = value.R; this.Pixels[start + 2] = value.B;
this.Pixels[start + 3] = value.A; this.Pixels[start + 3] = value.A;
} }
} }
/// <summary> /// <inheritdoc/>
/// Sets the pixel array of the image. public void SetPixels(int width, int height, float[] pixels)
/// </summary>
/// <param name="width">
/// The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple
/// of four, width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception>
public void SetPixels(int width, int height, byte[] pixels)
{ {
if (width <= 0) if (width <= 0)
{ {

2
src/ImageProcessor/ParallelImageProcessor.cs

@ -53,7 +53,7 @@ namespace ImageProcessor
/// <inheritdoc/> /// <inheritdoc/>
public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle)) public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
{ {
byte[] pixels = new byte[width * height * 4]; float[] pixels = new float[width * height * 4];
target.SetPixels(width, height, pixels); target.SetPixels(width, height, pixels);
if (targetRectangle == Rectangle.Empty) if (targetRectangle == Rectangle.Empty)

21
tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs

@ -19,16 +19,17 @@ namespace ImageProcessor.Tests
/// </summary> /// </summary>
public static readonly List<string> Files = new List<string> public static readonly List<string> Files = new List<string>
{ {
//"../../TestImages/Formats/Jpg/Backdrop.jpg", "../../TestImages/Formats/Jpg/Backdrop.jpg",
//"../../TestImages/Formats/Jpg/Calliphora.jpg", "../../TestImages/Formats/Jpg/Calliphora.jpg",
//"../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", "../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg",
//"../../TestImages/Formats/Jpg/greyscale.jpg", "../../TestImages/Formats/Jpg/greyscale.jpg",
//"../../TestImages/Formats/Bmp/Car.bmp", "../../TestImages/Formats/Bmp/Car.bmp",
//"../../TestImages/Formats/Png/cmyk.png", "../../TestImages/Formats/Png/cmyk.png",
//"../../TestImages/Formats/Png/gamma-1.0-or-2.2.png", "../../TestImages/Formats/Png/gamma-1.0-or-2.2.png",
//"../../TestImages/Formats/Gif/leaf.gif", "../../TestImages/Formats/Png/splash.png",
//"../../TestImages/Formats/Gif/rings.gif", "../../TestImages/Formats/Gif/leaf.gif",
//"../../TestImages/Formats/Gif/ani2.gif" , "../../TestImages/Formats/Gif/rings.gif",
"../../TestImages/Formats/Gif/ani2.gif" ,
"../../TestImages/Formats/Gif/giphy.gif" "../../TestImages/Formats/Gif/giphy.gif"
}; };
} }

1
tests/ImageProcessor.Tests/TestImages/Formats/Png/splash.png.REMOVED.git-id

@ -0,0 +1 @@
e37d569208ff9490b77b4131330feb323e367fd3
Loading…
Cancel
Save