Browse Source

Better decoder

Better organisation, handles all color types + maintains image quality.


Former-commit-id: 27d65b60f5e468e9678d0f9dd0314700e658036e
Former-commit-id: 1ed0afaccc0218f60cdc749a83725c9f9693ab4a
Former-commit-id: d20d7fd77362ad6cbb570b46105f2817df99c85c
af/merge-core
James Jackson-South 10 years ago
parent
commit
aab73f6524
  1. 76
      src/ImageProcessorCore/Formats/Png/GrayscaleReader.cs
  2. 29
      src/ImageProcessorCore/Formats/Png/IColorReader.cs
  3. 95
      src/ImageProcessorCore/Formats/Png/PaletteIndexReader.cs
  4. 61
      src/ImageProcessorCore/Formats/Png/PngColorTypeInformation.cs
  5. 412
      src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs
  6. 2
      src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
  7. 81
      src/ImageProcessorCore/Formats/Png/TrueColorReader.cs

76
src/ImageProcessorCore/Formats/Png/GrayscaleReader.cs

@ -1,76 +0,0 @@
// <copyright file="GrayscaleReader.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <summary>
/// Color reader for reading grayscale colors from a png file.
/// </summary>
internal sealed class GrayscaleReader : IColorReader
{
/// <summary>
/// Whether t also read the alpha channel.
/// </summary>
private readonly bool useAlpha;
/// <summary>
/// The current row.
/// </summary>
private int row;
/// <summary>
/// Initializes a new instance of the <see cref="GrayscaleReader"/> class.
/// </summary>
/// <param name="useAlpha">
/// If set to <c>true</c> the color reader will also read the
/// alpha channel from the scanline.
/// </param>
public GrayscaleReader(bool useAlpha)
{
this.useAlpha = useAlpha;
}
/// <inheritdoc/>
public void ReadScanline<T, TP>(byte[] scanline, T[] pixels, PngHeader header)
where T : IPackedVector<TP>
where TP : struct
{
int offset;
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
// 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;
byte rgb = newScanline[x * 2];
byte a = newScanline[(x * 2) + 1];
T color = default(T);
color.PackFromBytes(rgb, rgb, rgb, a);
pixels[offset] = color;
}
}
else
{
for (int x = 0; x < header.Width; x++)
{
offset = (this.row * header.Width) + x;
byte rgb = newScanline[x];
T color = default(T);
color.PackFromBytes(rgb, rgb, rgb, 255);
pixels[offset] = color;
}
}
this.row++;
}
}
}

29
src/ImageProcessorCore/Formats/Png/IColorReader.cs

@ -1,29 +0,0 @@
// <copyright file="IColorReader.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <summary>
/// Encapsulates methods for color readers, which are responsible for reading
/// different color formats from a png file.
/// </summary>
public interface IColorReader
{
/// <summary>
/// Reads the specified scanline.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="scanline">The scanline.</param>
/// <param name="pixels">The pixels to read the image row to.</param>
/// <param name="header">
/// The header, which contains information about the png file, like
/// the width of the image and the height.
/// </param>
void ReadScanline<T, TP>(byte[] scanline, T[] pixels, PngHeader header)
where T : IPackedVector<TP>
where TP : struct;
}
}

95
src/ImageProcessorCore/Formats/Png/PaletteIndexReader.cs

@ -1,95 +0,0 @@
// <copyright file="PaletteIndexReader.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <summary>
/// A color reader for reading palette indices from the png file.
/// </summary>
internal sealed class PaletteIndexReader : IColorReader
{
/// <summary>
/// The palette.
/// </summary>
private readonly byte[] palette;
/// <summary>
/// The alpha palette.
/// </summary>
private readonly byte[] paletteAlpha;
/// <summary>
/// The current row.
/// </summary>
private int row;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteIndexReader"/> class.
/// </summary>
/// <param name="palette">The palette as simple byte array. It will contains 3 values for each
/// color, which represents the red-, the green- and the blue channel.</param>
/// <param name="paletteAlpha">The alpha palette. Can be null, if the image does not have an
/// alpha channel and can contain less entries than the number of colors in the palette.</param>
public PaletteIndexReader(byte[] palette, byte[] paletteAlpha)
{
this.palette = palette;
this.paletteAlpha = paletteAlpha;
}
/// <inheritdoc/>
public void ReadScanline<T, TP>(byte[] scanline, T[] pixels, PngHeader header)
where T : IPackedVector<TP>
where TP : struct
{
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
int offset, index;
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
// If the alpha palette is not null and does one or
// more entries, this means, that the image contains and alpha
// channel and we should try to read it.
for (int i = 0; i < header.Width; i++)
{
index = newScanline[i];
offset = (this.row * header.Width) + i;
int pixelOffset = index * 3;
byte r = this.palette[pixelOffset];
byte g = this.palette[pixelOffset + 1];
byte b = this.palette[pixelOffset + 2];
byte a = this.paletteAlpha.Length > index
? this.paletteAlpha[index]
: (byte)255;
T color = default(T);
color.PackFromBytes(r, g, b, a);
pixels[offset] = color;
}
}
else
{
for (int i = 0; i < header.Width; i++)
{
index = newScanline[i];
offset = (this.row * header.Width) + i;
int pixelOffset = index * 3;
byte r = this.palette[pixelOffset];
byte g = this.palette[pixelOffset + 1];
byte b = this.palette[pixelOffset + 2];
T color = default(T);
color.PackFromBytes(r, g, b, 255);
pixels[offset] = color;
}
}
this.row++;
}
}
}

61
src/ImageProcessorCore/Formats/Png/PngColorTypeInformation.cs

@ -1,61 +0,0 @@
// <copyright file="PngColorTypeInformation.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
using System;
/// <summary>
/// Contains information that are required when loading a png with a specific color type.
/// </summary>
internal sealed class PngColorTypeInformation
{
/// <summary>
/// Initializes a new instance of the <see cref="PngColorTypeInformation"/> class with
/// the scanline factory, the function to create the color reader and the supported bit depths.
/// </summary>
/// <param name="scanlineFactor">The scanline factor.</param>
/// <param name="supportedBitDepths">The supported bit depths.</param>
/// <param name="scanlineReaderFactory">The factory to create the color reader.</param>
public PngColorTypeInformation(int scanlineFactor, int[] supportedBitDepths, Func<byte[], byte[], IColorReader> scanlineReaderFactory)
{
this.ChannelsPerColor = scanlineFactor;
this.ScanlineReaderFactory = scanlineReaderFactory;
this.SupportedBitDepths = supportedBitDepths;
}
/// <summary>
/// Gets an array with the bit depths that are supported for the color type
/// where this object is created for.
/// </summary>
/// <value>The supported bit depths that can be used in combination with this color type.</value>
public int[] SupportedBitDepths { get; private set; }
/// <summary>
/// Gets a function that is used the create the color reader for the color type where
/// this object is created for.
/// </summary>
/// <value>The factory function to create the color type.</value>
public Func<byte[], byte[], IColorReader> ScanlineReaderFactory { get; private set; }
/// <summary>
/// Gets a factor that is used when iterating through the scan lines.
/// </summary>
/// <value>The scanline factor.</value>
public int ChannelsPerColor { get; private set; }
/// <summary>
/// Creates the color reader for the color type where this object is create for.
/// </summary>
/// <param name="palette">The palette of the image. Can be null when no palette is used.</param>
/// <param name="paletteAlpha">The alpha palette of the image. Can be null when
/// no palette is used for the image or when the image has no alpha.</param>
/// <returns>The color reader for the image.</returns>
public IColorReader CreateColorReader(byte[] palette, byte[] paletteAlpha)
{
return this.ScanlineReaderFactory(palette, paletteAlpha);
}
}
}

412
src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs

@ -16,11 +16,10 @@ namespace ImageProcessorCore.Formats
/// </summary>
internal class PngDecoderCore
{
/// <summary>
/// The dictionary of available color types.
/// </summary>
private static readonly Dictionary<int, PngColorTypeInformation> ColorTypes
= new Dictionary<int, PngColorTypeInformation>();
///// <summary>
///// The dictionary of available color types.
///// </summary>
private static readonly Dictionary<int, byte[]> ColorTypes = new Dictionary<int, byte[]>();
/// <summary>
/// The stream to decode from.
@ -32,29 +31,50 @@ namespace ImageProcessorCore.Formats
/// </summary>
private PngHeader header;
/// <summary>
/// The number of bytes per pixel.
/// </summary>
private int bytesPerPixel;
/// <summary>
/// The number of bytes per sample
/// </summary>
private int bytesPerSample;
/// <summary>
/// The number of bytes per scanline
/// </summary>
private int bytesPerScanline;
/// <summary>
/// The palette containing color information for indexed pngs
/// </summary>
private byte[] palette;
/// <summary>
/// The palette containing alpha channel color information for indexed pngs
/// </summary>
private byte[] paletteAlpha;
/// <summary>
/// Gets or sets the png color type
/// </summary>
public PngColorType PngColorType { get; set; }
/// <summary>
/// Initializes static members of the <see cref="PngDecoderCore"/> class.
/// </summary>
static PngDecoderCore()
{
ColorTypes.Add(
0,
new PngColorTypeInformation(1, new[] { 1, 2, 4, 8 }, (p, a) => new GrayscaleReader(false)));
ColorTypes.Add((int)PngColorType.Grayscale, new byte[] { 1, 2, 4, 8 });
ColorTypes.Add(
2,
new PngColorTypeInformation(3, new[] { 8 }, (p, a) => new TrueColorReader(false)));
ColorTypes.Add((int)PngColorType.Rgb, new byte[] { 8 });
ColorTypes.Add(
3,
new PngColorTypeInformation(1, new[] { 1, 2, 4, 8 }, (p, a) => new PaletteIndexReader(p, a)));
ColorTypes.Add((int)PngColorType.Palette, new byte[] { 1, 2, 4, 8 });
ColorTypes.Add(
4,
new PngColorTypeInformation(2, new[] { 8 }, (p, a) => new GrayscaleReader(true)));
ColorTypes.Add((int)PngColorType.GrayscaleWithAlpha, new byte[] { 8 });
ColorTypes.Add(6,
new PngColorTypeInformation(4, new[] { 8 }, (p, a) => new TrueColorReader(true)));
ColorTypes.Add((int)PngColorType.RgbWithAlpha, new byte[] { 8 });
}
/// <summary>
@ -80,9 +100,6 @@ namespace ImageProcessorCore.Formats
bool isEndChunkReached = false;
byte[] palette = null;
byte[] paletteAlpha = null;
using (MemoryStream dataStream = new MemoryStream())
{
PngChunk currentChunk;
@ -108,11 +125,12 @@ namespace ImageProcessorCore.Formats
}
else if (currentChunk.Type == PngChunkTypes.Palette)
{
palette = currentChunk.Data;
this.palette = currentChunk.Data;
image.Quality = this.palette.Length / 3;
}
else if (currentChunk.Type == PngChunkTypes.PaletteAlpha)
{
paletteAlpha = currentChunk.Data;
this.paletteAlpha = currentChunk.Data;
}
else if (currentChunk.Type == PngChunkTypes.Text)
{
@ -133,54 +151,14 @@ namespace ImageProcessorCore.Formats
T[] pixels = new T[this.header.Width * this.header.Height];
PngColorTypeInformation colorTypeInformation = ColorTypes[this.header.ColorType];
if (colorTypeInformation != null)
{
IColorReader colorReader = colorTypeInformation.CreateColorReader(palette, paletteAlpha);
this.ReadScanlines<T, TP>(dataStream, pixels);
this.ReadScanlines<T, TP>(dataStream, pixels, colorReader, colorTypeInformation);
}
image.SetPixels(this.header.Width, this.header.Height, pixels);
}
}
/// <summary>
/// Computes a simple linear function of the three neighboring pixels (left, above, upper left), then chooses
/// as predictor the neighboring pixel closest to the computed value.
/// </summary>
/// <param name="left">The left neighbour pixel.</param>
/// <param name="above">The above neighbour pixel.</param>
/// <param name="upperLeft">The upper left neighbour pixel.</param>
/// <returns>
/// The <see cref="byte"/>.
/// </returns>
private static byte PaethPredicator(byte left, byte above, byte upperLeft)
{
byte predicator;
int p = left + above - upperLeft;
int pa = Math.Abs(p - left);
int pb = Math.Abs(p - above);
int pc = Math.Abs(p - upperLeft);
if (pa <= pb && pa <= pc)
{
predicator = left;
}
else if (pb <= pc)
{
predicator = above;
}
else
{
predicator = upperLeft;
}
return predicator;
}
/// <summary>
/// Reads the data chunk containing physical dimension data.
/// </summary>
@ -200,14 +178,41 @@ namespace ImageProcessorCore.Formats
image.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
}
/// <summary>
/// Calculates the correct number of bytes per pixel for the given color type.
/// </summary>
/// <returns>The <see cref="int"/></returns>
private int CalculateBytesPerPixel()
{
switch (this.PngColorType)
{
case PngColorType.Grayscale:
return 1;
case PngColorType.GrayscaleWithAlpha:
return 2;
case PngColorType.Palette:
return 1;
case PngColorType.Rgb:
return 3;
// PngColorType.RgbWithAlpha
// TODO: Maybe figure out a way to detect if there are any transparent
// pixels and encode RGB if none.
default:
return 4;
}
}
/// <summary>
/// Calculates the scanline length.
/// </summary>
/// <param name="colorTypeInformation">The color type information.</param>
/// <returns>The <see cref="int"/> representing the length.</returns>
private int CalculateScanlineLength(PngColorTypeInformation colorTypeInformation)
private int CalculateScanlineLength()
{
int scanlineLength = this.header.Width * this.header.BitDepth * colorTypeInformation.ChannelsPerColor;
int scanlineLength = this.header.Width * this.header.BitDepth * this.bytesPerPixel;
int amount = scanlineLength % 8;
if (amount != 0)
@ -219,103 +224,227 @@ namespace ImageProcessorCore.Formats
}
/// <summary>
/// Calculates a scanline step.
/// Reads the scanlines within the image.
/// </summary>
/// <param name="colorTypeInformation">The color type information.</param>
/// <returns>The <see cref="int"/> representing the length of each step.</returns>
private int CalculateScanlineStep(PngColorTypeInformation colorTypeInformation)
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels">
/// The <see cref="T:float[]"/> containing pixel data.</param>
private void ReadScanlines<T, TP>(MemoryStream dataStream, T[] pixels)
where T : IPackedVector<TP>
where TP : struct
{
int scanlineStep = 1;
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength() + 1;
this.bytesPerSample = 1;
if (this.header.BitDepth >= 8)
{
scanlineStep = (colorTypeInformation.ChannelsPerColor * this.header.BitDepth) / 8;
this.bytesPerSample = (this.header.BitDepth) / 8;
}
return scanlineStep;
dataStream.Position = 0;
using (ZlibInflateStream compressedStream = new ZlibInflateStream(dataStream))
{
using (MemoryStream decompressedStream = new MemoryStream())
{
compressedStream.CopyTo(decompressedStream);
decompressedStream.Flush();
byte[] decompressedBytes = decompressedStream.ToArray();
DecodePixelData<T, TP>(decompressedBytes, pixels);
}
}
}
/// <summary>
/// Reads the scanlines within the image.
/// Decodes the raw pixel data row by row
/// </summary>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels">
/// 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<T, TP>(MemoryStream dataStream, T[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation)
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="pixelData">The pixel data.</param>
/// <param name="pixels">The image pixels.</param>
private void DecodePixelData<T, TP>(byte[] pixelData, T[] pixels)
where T : IPackedVector<TP>
where TP : struct
{
dataStream.Position = 0;
byte[] previousScanline = new byte[this.bytesPerScanline];
int scanlineLength = this.CalculateScanlineLength(colorTypeInformation);
int scanlineStep = this.CalculateScanlineStep(colorTypeInformation);
for (int y = 0; y < this.header.Height; y++)
{
byte[] scanline = new byte[this.bytesPerScanline];
Array.Copy(pixelData, y * this.bytesPerScanline, scanline, 0, this.bytesPerScanline);
FilterType filterType = (FilterType)scanline[0];
byte[] defilteredScanline;
byte[] lastScanline = new byte[scanlineLength];
byte[] currentScanline = new byte[scanlineLength];
int filter = 0, column = -1;
switch (filterType)
{
case FilterType.None:
using (ZlibInflateStream compressedStream = new ZlibInflateStream(dataStream))
defilteredScanline = NoneFilter.Decode(scanline);
break;
case FilterType.Sub:
defilteredScanline = SubFilter.Decode(scanline, bytesPerPixel);
break;
case FilterType.Up:
defilteredScanline = UpFilter.Decode(scanline, previousScanline);
break;
case FilterType.Average:
defilteredScanline = AverageFilter.Decode(scanline, previousScanline, bytesPerPixel);
break;
case FilterType.Paeth:
defilteredScanline = PaethFilter.Decode(scanline, previousScanline, bytesPerPixel);
break;
default:
throw new ImageFormatException("Unknown filter type.");
}
previousScanline = defilteredScanline;
ProcessDefilteredScanline<T, TP>(defilteredScanline, y, pixels);
}
}
/// <summary>
/// Processes the defiltered scanline filling the image pixel data
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="defilteredScanline"></param>
/// <param name="row">The current image row.</param>
/// <param name="pixels">The image pixels</param>
private void ProcessDefilteredScanline<T, TP>(byte[] defilteredScanline, int row, T[] pixels)
where T : IPackedVector<TP>
where TP : struct
{
switch (this.PngColorType)
{
int readByte;
while ((readByte = compressedStream.ReadByte()) >= 0)
{
if (column == -1)
case PngColorType.Grayscale:
for (int x = 0; x < this.header.Width; x++)
{
filter = readByte;
int offset = 1 + (x * bytesPerPixel);
byte intensity = defilteredScanline[offset];
column++;
T color = default(T);
color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[(row * this.header.Width) + x] = color;
}
else
break;
case PngColorType.GrayscaleWithAlpha:
for (int x = 0; x < this.header.Width; x++)
{
currentScanline[column] = (byte)readByte;
int offset = 1 + (x * bytesPerPixel);
byte a;
byte b;
byte c;
byte intensity = defilteredScanline[offset];
byte alpha = defilteredScanline[offset + bytesPerSample];
if (column >= scanlineStep)
{
a = currentScanline[column - scanlineStep];
c = lastScanline[column - scanlineStep];
}
else
{
a = 0;
c = 0;
}
T color = default(T);
color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[(row * this.header.Width) + x] = color;
}
b = lastScanline[column];
break;
if (filter == 1)
{
currentScanline[column] = (byte)(currentScanline[column] + a);
}
else if (filter == 2)
{
currentScanline[column] = (byte)(currentScanline[column] + b);
}
else if (filter == 3)
case PngColorType.Palette:
byte[] newScanline = defilteredScanline.ToArrayByBitsLength(header.BitDepth);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
// channel and we should try to read it.
for (int i = 0; i < header.Width; i++)
{
currentScanline[column] = (byte)(currentScanline[column] + (byte)((a + b) / 2));
int index = newScanline[i];
int offset = (row * header.Width) + i;
int pixelOffset = index * 3;
byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
T color = default(T);
if (a > 0)
{
byte r = this.palette[pixelOffset];
byte g = this.palette[pixelOffset + 1];
byte b = this.palette[pixelOffset + 2];
color.PackFromBytes(r, g, b, a);
}
pixels[offset] = color;
}
else if (filter == 4)
}
else
{
for (int i = 0; i < header.Width; i++)
{
currentScanline[column] = (byte)(currentScanline[column] + PaethPredicator(a, b, c));
int index = newScanline[i];
int offset = (row * header.Width) + i;
int pixelOffset = index * 3;
byte r = this.palette[pixelOffset];
byte g = this.palette[pixelOffset + 1];
byte b = this.palette[pixelOffset + 2];
T color = default(T);
color.PackFromBytes(r, g, b, 255);
pixels[offset] = color;
}
}
column++;
break;
if (column == scanlineLength)
{
colorReader.ReadScanline<T, TP>(currentScanline, pixels, this.header);
column = -1;
case PngColorType.Rgb:
this.Swap(ref currentScanline, ref lastScanline);
}
for (int x = 0; x < this.header.Width; x++)
{
int offset = 1 + (x * bytesPerPixel);
byte r = defilteredScanline[offset];
byte g = defilteredScanline[offset + bytesPerSample];
byte b = defilteredScanline[offset + 2 * bytesPerSample];
T color = default(T);
color.PackFromBytes(r, g, b, 255);
pixels[(row * this.header.Width) + x] = color;
}
}
break;
case PngColorType.RgbWithAlpha:
for (int x = 0; x < this.header.Width; x++)
{
int offset = 1 + (x * bytesPerPixel);
byte r = defilteredScanline[offset];
byte g = defilteredScanline[offset + bytesPerSample];
byte b = defilteredScanline[offset + 2 * bytesPerSample];
byte a = defilteredScanline[offset + 3 * bytesPerSample];
T color = default(T);
color.PackFromBytes(r, g, b, a);
pixels[(row * this.header.Width) + x] = color;
}
break;
default:
break;
}
}
@ -381,7 +510,7 @@ namespace ImageProcessorCore.Formats
throw new NotSupportedException("Color type is not supported or not valid.");
}
if (!ColorTypes[this.header.ColorType].SupportedBitDepths.Contains(this.header.BitDepth))
if (!ColorTypes[this.header.ColorType].Contains(this.header.BitDepth))
{
throw new NotSupportedException("Bit depth is not supported or not valid.");
}
@ -396,6 +525,8 @@ namespace ImageProcessorCore.Formats
// TODO: Support interlacing
throw new NotSupportedException("Interlacing is not supported.");
}
this.PngColorType = (PngColorType)this.header.ColorType;
}
/// <summary>
@ -525,20 +656,5 @@ namespace ImageProcessorCore.Formats
return numBytes;
}
/// <summary>
/// Swaps two references.
/// </summary>
/// <typeparam name="TRef">The type of the references to swap.</typeparam>
/// <param name="lhs">The first reference.</param>
/// <param name="rhs">The second reference.</param>
private void Swap<TRef>(ref TRef lhs, ref TRef rhs)
where TRef : class
{
TRef tmp = lhs;
lhs = rhs;
rhs = tmp;
}
}
}

2
src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs

@ -126,7 +126,7 @@ namespace ImageProcessorCore.Formats
this.Quality = quality > 0 ? quality.Clamp(1, int.MaxValue) : int.MaxValue;
// Set correct color type if the color count is 256 or less.
if (Quality <= 256)
if (this.Quality <= 256)
{
this.PngColorType = PngColorType.Palette;
}

81
src/ImageProcessorCore/Formats/Png/TrueColorReader.cs

@ -1,81 +0,0 @@
// <copyright file="TrueColorReader.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <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>
internal sealed class TrueColorReader : IColorReader
{
/// <summary>
/// Whether t also read the alpha channel.
/// </summary>
private readonly bool useAlpha;
/// <summary>
/// The current row.
/// </summary>
private int row;
/// <summary>
/// Initializes a new instance of the <see cref="TrueColorReader"/> class.
/// </summary>
/// <param name="useAlpha">if set to <c>true</c> the color reader will also read the
/// alpha channel from the scanline.</param>
public TrueColorReader(bool useAlpha)
{
this.useAlpha = useAlpha;
}
/// <inheritdoc/>
public void ReadScanline<T, TP>(byte[] scanline, T[] pixels, PngHeader header)
where T : IPackedVector<TP>
where TP : struct
{
int offset;
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
if (this.useAlpha)
{
for (int x = 0; x < newScanline.Length; x += 4)
{
offset = (this.row * header.Width) + (x >> 2);
// We want to convert to premultiplied alpha here.
byte r = newScanline[x];
byte g = newScanline[x + 1];
byte b = newScanline[x + 2];
byte a = newScanline[x + 3];
T color = default(T);
color.PackFromBytes(r, g, b, a);
pixels[offset] = color;
}
}
else
{
for (int x = 0; x < newScanline.Length / 3; x++)
{
offset = (this.row * header.Width) + x;
int pixelOffset = x * 3;
byte r = newScanline[pixelOffset];
byte g = newScanline[pixelOffset + 1];
byte b = newScanline[pixelOffset + 2];
T color = default(T);
color.PackFromBytes(r, g, b, 255);
pixels[offset] = color;
}
}
this.row++;
}
}
}
Loading…
Cancel
Save