Browse Source

Can now decode all bit depths.

af/merge-core
James Jackson-South 8 years ago
parent
commit
b1ab83c5f2
  1. 262
      src/ImageSharp/Formats/Png/PngDecoderCore.cs

262
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -28,10 +28,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>()
{
[PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8 },
[PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8, 16 },
[PngColorType.Rgb] = new byte[] { 8, 16 },
[PngColorType.Palette] = new byte[] { 1, 2, 4, 8 },
[PngColorType.GrayscaleWithAlpha] = new byte[] { 8 },
[PngColorType.GrayscaleWithAlpha] = new byte[] { 8, 16 },
[PngColorType.RgbWithAlpha] = new byte[] { 8, 16 }
};
@ -171,9 +171,14 @@ namespace SixLabors.ImageSharp.Formats.Png
private Rgb48 rgb48Trans;
/// <summary>
/// Represents any color in a grayscale encoded png that should be transparent
/// Represents any color in an 8 bit grayscale encoded png that should be transparent
/// </summary>
private byte intensityTrans;
private byte luminanceTrans;
/// <summary>
/// Represents any color in a 16 bit grayscale encoded png that should be transparent
/// </summary>
private ushort luminance16Trans;
/// <summary>
/// Whether the image has transparency chunk and markers were decoded
@ -353,6 +358,7 @@ namespace SixLabors.ImageSharp.Formats.Png
return source;
}
// TODO: We should be pooling this.
byte[] result = new byte[bytesPerScanline * 8 / bits];
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
@ -450,30 +456,20 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Grayscale:
return 1;
return this.header.BitDepth == 16 ? 2 : 1;
case PngColorType.GrayscaleWithAlpha:
return 2;
return this.header.BitDepth == 16 ? 4 : 2;
case PngColorType.Palette:
return 1;
case PngColorType.Rgb:
if (this.header.BitDepth == 16)
{
return 6;
}
return 3;
return this.header.BitDepth == 16 ? 6 : 3;
case PngColorType.RgbWithAlpha:
default:
if (this.header.BitDepth == 16)
{
return 8;
}
return 4;
return this.header.BitDepth == 16 ? 8 : 4;
}
}
@ -682,37 +678,111 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
ReadOnlySpan<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<byte> scanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
for (int x = 0; x < this.header.Width; x++)
if (!this.hasTrans)
{
byte intensity = (byte)(newScanline1[x] * factor);
if (this.hasTrans && intensity == this.intensityTrans)
if (this.header.BitDepth == 16)
{
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 0));
Rgb48 rgb48 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
color.PackFromRgb48(rgb48);
rowSpan[x] = color;
}
}
else
{
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
// TODO: We should really be using Rgb24 here but IPixel does not have a PackFromRgb24 method.
var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < this.header.Width; x++)
{
byte luminance = (byte)(scanline[x] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
}
}
}
else
{
if (this.header.BitDepth == 16)
{
Rgba64 rgba64 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
rowSpan[x] = color;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
}
}
else
{
Rgba32 rgba32 = default;
for (int x = 0; x < this.header.Width; x++)
{
byte luminance = (byte)(scanline[x] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
}
}
}
break;
case PngColorType.GrayscaleWithAlpha:
for (int x = 0; x < this.header.Width; x++)
if (this.header.BitDepth == 16)
{
Rgba64 rgba64 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 4)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = alpha;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
}
}
else
{
int offset = x * this.bytesPerPixel;
Rgba32 rgba32 = default;
for (int x = 0; x < this.header.Width; x++)
{
int offset = x * this.bytesPerPixel;
byte luminance = scanlineBuffer[offset];
byte alpha = scanlineBuffer[offset + this.bytesPerSample];
byte intensity = scanlineBuffer[offset];
byte alpha = scanlineBuffer[offset + this.bytesPerSample];
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = alpha;
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
}
}
break;
@ -824,34 +894,112 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
ReadOnlySpan<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<byte> scanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
if (!this.hasTrans)
{
byte intensity = (byte)(newScanline1[o] * factor);
if (this.hasTrans && intensity == this.intensityTrans)
if (this.header.BitDepth == 16)
{
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 0));
Rgb48 rgb48 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
color.PackFromRgb48(rgb48);
rowSpan[x] = color;
}
}
else
{
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
// TODO: We should really be using Rgb24 here but IPixel does not have a PackFromRgb24 method.
var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
byte luminance = (byte)(scanline[o] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
}
}
}
else
{
if (this.header.BitDepth == 16)
{
Rgba64 rgba64 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
}
}
else
{
Rgba32 rgba32 = default;
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
byte luminance = (byte)(scanline[x] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue;
rowSpan[x] = color;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
}
}
}
break;
case PngColorType.GrayscaleWithAlpha:
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
if (this.header.BitDepth == 16)
{
byte intensity = scanlineBuffer[o];
byte alpha = scanlineBuffer[o + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color;
Rgba64 rgba64 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = alpha;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
}
}
else
{
Rgba32 rgba32 = default;
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
int offset = x * this.bytesPerPixel;
byte luminance = scanlineBuffer[offset];
byte alpha = scanlineBuffer[offset + this.bytesPerSample];
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = alpha;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
}
}
break;
@ -878,7 +1026,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
var rgba = new Rgba32(0, 0, 0, 255);
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
int index = newScanline[o];
@ -943,7 +1091,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
var rgba = new Rgba32(0, 0, 0, 255);
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = scanlineBuffer[o];
@ -1021,10 +1169,17 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else if (this.pngColorType == PngColorType.Grayscale)
{
// TODO: 16 bit
if (alpha.Length >= 2)
{
this.intensityTrans = ReadByteLittleEndian(alpha, 0);
if (this.header.BitDepth == 16)
{
this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2));
}
else
{
this.luminanceTrans = ReadByteLittleEndian(alpha, 0);
}
this.hasTrans = true;
}
}
@ -1043,17 +1198,16 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
var color = default(TPixel);
var rgba = default(Rgba32);
if (this.paletteAlpha?.Length > 0)
{
Rgba32 rgba = default;
// 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 x = 0; x < this.header.Width; x++)
{
int index = newScanline[x];
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue;
rgba.Rgb = pal[index];
color.PackFromRgba32(rgba);
@ -1062,7 +1216,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
rgba.A = 255;
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < this.header.Width; x++)
{
@ -1212,6 +1366,10 @@ namespace SixLabors.ImageSharp.Formats.Png
return true;
}
/// <summary>
/// Validates the png chunk.
/// </summary>
/// The <see cref="PngChunk"/>.
private void ValidateChunk(in PngChunk chunk)
{
this.crc.Reset();

Loading…
Cancel
Save