Browse Source

Switch over to new Color struct as default.

Former-commit-id: 1cb78f9723ddcdd4bcf45c27d7571423629ad0c6
Former-commit-id: 727c1b5eeaec657fa541ef64e118006c2b74911f
Former-commit-id: 7c9b65f6d71f10e9c3ce8193ae797f30b8c291f2
pull/17/head
James Jackson-South 10 years ago
parent
commit
ce5f235e5f
  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>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="r">
/// The red component of this <see cref="Color"/>.
/// </param>
/// <param name="g">
/// 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>
/// <param name="r">The red component of this <see cref="Color"/>.</param>
/// <param name="g">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)
: this()
{

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

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpDecoderCore.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="BmpDecoderCore.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Performs the bmp decoding operation.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -109,7 +104,7 @@ namespace ImageProcessor.Formats
+ $"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)
{
@ -180,12 +175,12 @@ namespace ImageProcessor.Formats
/// <summary>
/// Reads the color palette from the stream.
/// </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="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</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)
int ppb = 8 / bits;
@ -224,13 +219,15 @@ namespace ImageProcessor.Formats
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;
imageData[arrayOffset + 0] = colors[colorIndex * 4];
imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1];
imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2];
imageData[arrayOffset + 3] = 255;
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
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>
/// Reads the 16 bit color palette from the stream
/// </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="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;
const int ScaleG = 256 / 64;
// We divide here as we will store the colors in our floating point format.
const int ScaleR = (256 / 32) / 32;
const int ScaleG = (256 / 64) / 64;
int alignment;
byte[] data = this.GetImageArray(width, height, 2, out alignment);
@ -266,16 +264,17 @@ namespace ImageProcessor.Formats
short temp = BitConverter.ToInt16(data, offset);
byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
byte b = (byte)((temp & Rgb16BMask) * ScaleR);
float r = ((temp & Rgb16RMask) >> 11) * ScaleR;
float g = ((temp & Rgb16GMask) >> 5) * ScaleG;
float b = (temp & Rgb16BMask) * ScaleR;
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 + 2] = r;
imageData[arrayOffset + 3] = 255;
imageData[arrayOffset + 2] = b;
imageData[arrayOffset + 3] = 1;
}
});
}
@ -283,10 +282,10 @@ namespace ImageProcessor.Formats
/// <summary>
/// Reads the 24 bit color palette from the stream
/// </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="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;
byte[] data = this.GetImageArray(width, height, 3, out alignment);
@ -306,10 +305,12 @@ namespace ImageProcessor.Formats
int offset = rowOffset + (x * 3);
int arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = data[offset + 0];
imageData[arrayOffset + 1] = data[offset + 1];
imageData[arrayOffset + 2] = data[offset + 2];
imageData[arrayOffset + 3] = 255;
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
imageData[arrayOffset] = data[offset + 2] / 255f;
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>
/// Reads the 32 bit color palette from the stream
/// </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="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;
byte[] data = this.GetImageArray(width, height, 4, out alignment);
@ -338,12 +339,14 @@ namespace ImageProcessor.Formats
for (int x = 0; x < width; x++)
{
int offset = rowOffset + (x * 4);
int arrayOffset = ((row * width) + x) * 4;
var arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = data[offset + 0];
imageData[arrayOffset + 1] = data[offset + 1];
imageData[arrayOffset + 2] = data[offset + 2];
imageData[arrayOffset + 3] = 255; // Can we get alpha here?
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
imageData[arrayOffset] = data[offset + 2] / 255f;
imageData[arrayOffset + 1] = data[offset + 1] / 255f;
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;
}
byte[] data = image.Pixels;
float[] data = image.Pixels;
for (int y = image.Height - 1; y >= 0; y--)
{
@ -108,9 +108,11 @@ namespace ImageProcessor.Formats
{
int offset = ((y * image.Width) + x) * 4;
writer.Write(data[offset + 0]);
writer.Write(data[offset + 1]);
writer.Write(data[offset + 2]);
// Limit the output range and multiply out from our floating point.
// Convert back to b-> g-> r-> a order.
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++)

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

@ -31,7 +31,7 @@ namespace ImageProcessor.Formats
/// <summary>
/// The current frame.
/// </summary>
private byte[] currentFrame;
private float[] currentFrame;
/// <summary>
/// The logical screen descriptor.
@ -288,15 +288,15 @@ namespace ImageProcessor.Formats
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 &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
lastFrame = new byte[imageWidth * imageHeight * 4];
lastFrame = new float[imageWidth * imageHeight * 4];
Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
}
@ -352,18 +352,20 @@ namespace ImageProcessor.Formats
this.graphicsControlExtension.TransparencyFlag == false ||
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;
this.currentFrame[offset + 0] = colorTable[indexOffset + 2];
this.currentFrame[offset + 1] = colorTable[indexOffset + 1];
this.currentFrame[offset + 2] = colorTable[indexOffset + 0];
this.currentFrame[offset + 3] = 255;
this.currentFrame[offset + 0] = colorTable[indexOffset] / 255f; // r
this.currentFrame[offset + 1] = colorTable[indexOffset + 1] / 255f; // g
this.currentFrame[offset + 2] = colorTable[indexOffset + 2] / 255f; // b
this.currentFrame[offset + 3] = 1; // a
}
i++;
}
}
byte[] pixels = new byte[imageWidth * imageHeight * 4];
float[] pixels = new float[imageWidth * imageHeight * 4];
Array.Copy(this.currentFrame, pixels, pixels.Length);
@ -406,6 +408,7 @@ namespace ImageProcessor.Formats
{
offset = ((y * imageWidth) + x) * 4;
// Stored in r-> g-> b-> a order.
this.currentFrame[offset + 0] = 0;
this.currentFrame[offset + 1] = 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
// happens to be black, with an alpha component of zero.
if (null == this.previousNode)
if (this.previousNode == null)
{
this.previousColor = pixel.BGRA;
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
int index = this.maxColorBits - 1;
while ((index > 0) && (null == this.reducibleNodes[index]))
while ((index > 0) && (this.reducibleNodes[index] == null))
{
index--;
}
@ -407,7 +407,7 @@ namespace ImageProcessor.Formats
OctreeNode child = this.children[index];
if (null == child)
if (child == null)
{
// Create a new child node and store it in the array
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
for (int index = 0; index < 8; index++)
{
if (null != this.children[index])
if (this.children[index] != null)
{
this.red += this.children[index].red;
this.green += this.children[index].green;
@ -477,7 +477,7 @@ namespace ImageProcessor.Formats
// Loop through children looking for leaves
for (int i = 0; i < 8; i++)
{
if (null != this.children[i])
if (this.children[i] != null)
{
this.children[i].ConstructPalette(palette, ref index);
}
@ -508,7 +508,7 @@ namespace ImageProcessor.Formats
((pixel.G & Mask[level]) >> (shift - 1)) |
((pixel.B & Mask[level]) >> shift);
if (null != this.children[pixelIndex])
if (this.children[pixelIndex] != null)
{
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();
int pixelCount = this.Pixels.Length;
byte[] bgraPixels = new byte[pixelCount * 4];
float[] bgraPixels = new float[pixelCount * 4];
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;
/// <summary>
/// Initializes a new instance of the <see cref="Quantizer"/> class.
/// Initializes a new instance of the <see cref="Quantizer"/> class.
/// </summary>
/// <param name="singlePass">
/// 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];
// Get the pallete
List<Bgra32> palette = this.GetPalette();
this.SecondPass(imageBase, quantizedPixels, width, height);
@ -94,7 +95,8 @@ namespace ImageProcessor.Formats
{
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];
byte pixelValue = this.QuantizePixel(previousPixel);
@ -104,6 +106,7 @@ namespace ImageProcessor.Formats
{
for (int x = 0; x < width; x++)
{
// Implicit cast here from Color.
Bgra32 sourcePixel = source[x, y];
// 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 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))
{
@ -119,10 +119,10 @@ namespace ImageProcessor.Formats
int offset = ((y * pixelWidth) + x) * 4;
pixels[offset + 0] = (byte)sample[2];
pixels[offset + 1] = (byte)sample[1];
pixels[offset + 2] = (byte)sample[0];
pixels[offset + 3] = 255;
pixels[offset + 0] = sample[0] / 255f;
pixels[offset + 1] = sample[1] / 255f;
pixels[offset + 2] = sample[2] / 255f;
pixels[offset + 3] = 1;
}
});

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

@ -89,7 +89,7 @@ namespace ImageProcessor.Formats
int pixelWidth = image.Width;
int pixelHeight = image.Height;
byte[] sourcePixels = image.Pixels;
float[] sourcePixels = image.Pixels;
SampleRow[] rows = new SampleRow[pixelHeight];
@ -105,9 +105,9 @@ namespace ImageProcessor.Formats
int start = x * 3;
int source = ((y * pixelWidth) + x) * 4;
samples[start] = sourcePixels[source + 2];
samples[start + 1] = sourcePixels[source + 1];
samples[start + 2] = sourcePixels[source];
samples[start] = (byte)(sourcePixels[source].Clamp(0, 1) * 255);
samples[start + 1] = (byte)(sourcePixels[source + 1].Clamp(0, 1) * 255);
samples[start + 2] = (byte)(sourcePixels[source + 2].Clamp(0, 1) * 255);
}
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 (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="GrayscaleReader.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Color reader for reading grayscale colors from a png file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -37,31 +32,25 @@ namespace ImageProcessor.Formats
this.useAlpha = useAlpha;
}
/// <summary>
/// Reads the specified scanline.
/// </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)
/// <inheritdoc/>
public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
{
int offset;
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)
{
for (int x = 0; x < header.Width / 2; x++)
{
offset = ((this.row * header.Width) + x) * 4;
pixels[offset + 0] = newScanline[x * 2];
pixels[offset + 1] = newScanline[x * 2];
pixels[offset + 2] = newScanline[x * 2];
pixels[offset + 3] = newScanline[(x * 2) + 1];
pixels[offset] = newScanline[x * 2] / 255f;
pixels[offset + 1] = newScanline[x * 2] / 255f;
pixels[offset + 2] = newScanline[x * 2] / 255f;
pixels[offset + 3] = newScanline[(x * 2) + 1] / 255f;
}
}
else
@ -70,10 +59,10 @@ namespace ImageProcessor.Formats
{
offset = ((this.row * header.Width) + x) * 4;
pixels[offset + 0] = newScanline[x];
pixels[offset + 1] = newScanline[x];
pixels[offset + 2] = newScanline[x];
pixels[offset + 3] = 255;
pixels[offset] = newScanline[x] / 255f;
pixels[offset + 1] = newScanline[x] / 255f;
pixels[offset + 2] = newScanline[x] / 255f;
pixels[offset + 3] = 1;
}
}

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

@ -1,18 +1,12 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="IColorReader.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="IColorReader.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods for color readers, which are responsible for reading
// different color formats from a png file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
/// <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.
/// </summary>
public interface IColorReader
@ -26,6 +20,6 @@ namespace ImageProcessor.Formats
/// The header, which contains information about the png file, like
/// the width of the image and the height.
/// </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 (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="PaletteIndexReader.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// A color reader for reading palette indices from the png file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -43,14 +38,8 @@ namespace ImageProcessor.Formats
this.paletteAlpha = paletteAlpha;
}
/// <summary>
/// Reads the specified scanline.
/// </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)
/// <inheritdoc/>
public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
{
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
int offset, index;
@ -65,13 +54,14 @@ namespace ImageProcessor.Formats
index = newScanline[i];
offset = ((this.row * header.Width) + i) * 4;
int pixelOffset = index * 3;
pixels[offset + 0] = this.palette[(index * 3) + 2];
pixels[offset + 1] = this.palette[(index * 3) + 1];
pixels[offset + 2] = this.palette[(index * 3) + 0];
pixels[offset] = this.palette[pixelOffset] / 255f;
pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f;
pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f;
pixels[offset + 3] = this.paletteAlpha.Length > index
? this.paletteAlpha[index]
: (byte)255;
? this.paletteAlpha[index] / 255f
: 1;
}
}
else
@ -81,11 +71,12 @@ namespace ImageProcessor.Formats
index = newScanline[i];
offset = ((this.row * header.Width) + i) * 4;
int pixelOffset = index * 3;
pixels[offset + 0] = this.palette[(index * 3) + 2];
pixels[offset + 1] = this.palette[(index * 3) + 1];
pixels[offset + 2] = this.palette[(index * 3) + 0];
pixels[offset + 3] = 255;
pixels[offset] = this.palette[pixelOffset] / 255f;
pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f;
pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f;
pixels[offset + 3] = 1;
}
}

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

@ -16,9 +16,6 @@ namespace ImageProcessor.Formats
using System.Linq;
using System.Text;
//using ICSharpCode.SharpZipLib.Checksums;
//using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
/// <summary>
/// Performs the png decoding operation.
/// </summary>
@ -145,7 +142,7 @@ namespace ImageProcessor.Formats
+ $"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];
@ -248,10 +245,10 @@ namespace ImageProcessor.Formats
/// </summary>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <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="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;

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

@ -8,9 +8,6 @@ namespace ImageProcessor.Formats
using System;
using System.IO;
//using ICSharpCode.SharpZipLib.Checksums;
//using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
/// <summary>
/// Image encoder for writing image data to a stream in png format.
/// </summary>
@ -36,7 +33,7 @@ namespace ImageProcessor.Formats
public int Quality { get; set; }
/// <inheritdoc/>
public string MimeType => "image/jpepngg";
public string MimeType => "image/png";
/// <inheritdoc/>
public string Extension => "png";
@ -225,7 +222,7 @@ namespace ImageProcessor.Formats
/// <param name="imageBase">The image base.</param>
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
// the filter byte.
@ -297,7 +294,7 @@ namespace ImageProcessor.Formats
/// <param name="imageBase">The image base.</param>
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];
@ -321,19 +318,19 @@ namespace ImageProcessor.Formats
// Calculate the offset for the original pixel array.
int pixelOffset = ((y * imageBase.Width) + x) * 4;
data[dataOffset + 0] = pixels[pixelOffset + 2];
data[dataOffset + 1] = pixels[pixelOffset + 1];
data[dataOffset + 2] = pixels[pixelOffset + 0];
data[dataOffset + 3] = pixels[pixelOffset + 3];
data[dataOffset] = (byte)(pixels[pixelOffset].Clamp(0, 1) * 255);
data[dataOffset + 1] = (byte)(pixels[pixelOffset + 1].Clamp(0, 1) * 255);
data[dataOffset + 2] = (byte)(pixels[pixelOffset + 2].Clamp(0, 1) * 255);
data[dataOffset + 3] = (byte)(pixels[pixelOffset + 3].Clamp(0, 1) * 255);
if (y > 0)
{
int lastOffset = (((y - 1) * imageBase.Width) + x) * 4;
data[dataOffset + 0] -= pixels[lastOffset + 2];
data[dataOffset + 1] -= pixels[lastOffset + 1];
data[dataOffset + 2] -= pixels[lastOffset + 0];
data[dataOffset + 3] -= pixels[lastOffset + 3];
data[dataOffset] -= (byte)(pixels[lastOffset].Clamp(0, 1) * 255);
data[dataOffset + 1] -= (byte)(pixels[lastOffset + 1].Clamp(0, 1) * 255);
data[dataOffset + 2] -= (byte)(pixels[lastOffset + 2].Clamp(0, 1) * 255);
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 (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="TrueColorReader.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </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
{
@ -37,14 +31,8 @@ namespace ImageProcessor.Formats
this.useAlpha = useAlpha;
}
/// <summary>
/// Reads the specified scanline.
/// </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)
/// <inheritdoc/>
public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
{
int offset;
@ -56,10 +44,10 @@ namespace ImageProcessor.Formats
{
offset = ((this.row * header.Width) + (x >> 2)) * 4;
pixels[offset + 0] = newScanline[x + 2];
pixels[offset + 1] = newScanline[x + 1];
pixels[offset + 2] = newScanline[x + 0];
pixels[offset + 3] = newScanline[x + 3];
pixels[offset + 0] = newScanline[x] / 255f;
pixels[offset + 1] = newScanline[x + 1] / 255f;
pixels[offset + 2] = newScanline[x + 2] / 255f;
pixels[offset + 3] = newScanline[x + 3] / 255f;
}
}
else
@ -67,11 +55,12 @@ namespace ImageProcessor.Formats
for (int x = 0; x < newScanline.Length / 3; x++)
{
offset = ((this.row * header.Width) + x) * 4;
int pixelOffset = x * 3;
pixels[offset + 0] = newScanline[(x * 3) + 2];
pixels[offset + 1] = newScanline[(x * 3) + 1];
pixels[offset + 2] = newScanline[(x * 3) + 0];
pixels[offset + 3] = 255;
pixels[offset + 0] = newScanline[pixelOffset] / 255f;
pixels[offset + 1] = newScanline[pixelOffset + 1] / 255f;
pixels[offset + 2] = newScanline[pixelOffset + 2] / 255f;
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
/// each pixel in this order.
/// </remarks>
byte[] Pixels { get; }
float[] Pixels { get; }
/// <summary>
/// Gets the width in pixels.
@ -66,8 +66,8 @@ namespace ImageProcessor
/// 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>
Bgra32 this[int x, int y] { get; set; }
/// <returns>The <see cref="Color"/> at the specified position.</returns>
Color this[int x, int y] { get; set; }
/// <summary>
/// Sets the pixel array of the image.
@ -85,6 +85,6 @@ namespace ImageProcessor
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </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.Height = height;
this.Pixels = new byte[width * height * 4];
this.Pixels = new float[width * height * 4];
}
/// <summary>
@ -56,13 +56,13 @@ namespace ImageProcessor
{
Guard.NotNull(other, nameof(other), "Other image cannot be null.");
byte[] pixels = other.Pixels;
float[] pixels = other.Pixels;
this.Width = other.Width;
this.Height = other.Height;
this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay;
this.Pixels = new byte[pixels.Length];
this.Pixels = new float[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
/// each pixel in this order.
/// </remarks>
public byte[] Pixels { get; private set; }
public float[] Pixels { get; private set; }
/// <summary>
/// Gets the width in pixels.
@ -106,9 +106,7 @@ namespace ImageProcessor
/// </summary>
public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
/// <summary>
/// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
/// </summary>
/// <inheritdoc/>
public int Quality { get; set; }
/// <summary>
@ -119,19 +117,8 @@ namespace ImageProcessor
/// </summary>
public int FrameDelay { get; set; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </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]
/// <inheritdoc/>
public Color this[int x, int y]
{
get
{
@ -148,7 +135,7 @@ namespace ImageProcessor
#endif
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
@ -166,30 +153,15 @@ namespace ImageProcessor
#endif
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 + 2] = value.R;
this.Pixels[start + 2] = value.B;
this.Pixels[start + 3] = value.A;
}
}
/// <summary>
/// Sets the pixel array of the image.
/// </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)
/// <inheritdoc/>
public void SetPixels(int width, int height, float[] pixels)
{
if (width <= 0)
{

2
src/ImageProcessor/ParallelImageProcessor.cs

@ -53,7 +53,7 @@ namespace ImageProcessor
/// <inheritdoc/>
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);
if (targetRectangle == Rectangle.Empty)

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

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