Browse Source

Add 1,2, and 4 bit grayscale

af/merge-core
James Jackson-South 8 years ago
parent
commit
c5e5f4734c
  1. 71
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

71
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -3,7 +3,9 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@ -23,6 +25,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal sealed class PngEncoderCore : IDisposable
{
/// <summary>
/// The dictionary of available color types.
/// </summary>
private static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>()
{
[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, 16 },
[PngColorType.RgbWithAlpha] = new byte[] { 8, 16 }
};
/// <summary>
/// Used the manage memory allocations.
/// </summary>
@ -198,19 +212,27 @@ namespace SixLabors.ImageSharp.Formats.Png
this.pngBitDepth = this.pngBitDepth ?? pngMetaData.BitDepth;
this.use16Bit = this.pngBitDepth.Equals(PngBitDepth.Bit16);
// Ensure we are not allowing impossible combinations.
if (!ColorTypes.ContainsKey(this.pngColorType.Value))
{
throw new NotSupportedException("Color type is not supported or not valid.");
}
stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
QuantizedFrame<TPixel> quantized = null;
if (this.pngColorType == PngColorType.Palette)
{
byte bits = (byte)Math.Min(8u, (short)this.pngBitDepth);
byte bits = (byte)this.pngBitDepth;
if (!ColorTypes[this.pngColorType.Value].Contains(bits))
{
throw new NotSupportedException("Bit depth is not supported or not valid.");
}
// Use the metadata to determine what quantization depth to use if no quantizer has been set.
if (this.quantizer == null)
{
bits = (byte)Math.Min(8u, (short)this.pngBitDepth);
int colorSize = ImageMaths.GetColorCountForBitDepth(bits);
this.quantizer = new WuQuantizer(colorSize);
this.quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits));
}
// Create quantized frame returning the palette and set the bit depth.
@ -234,8 +256,11 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
// TODO: Get the correct bit depth for grayscale images.
this.bitDepth = (byte)(this.use16Bit ? 16 : 8);
this.bitDepth = (byte)this.pngBitDepth;
if (!ColorTypes[this.pngColorType.Value].Contains(this.bitDepth))
{
throw new NotSupportedException("Bit depth is not supported or not valid.");
}
}
this.bytesPerPixel = this.CalculateBytesPerPixel();
@ -295,9 +320,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.pngColorType.Equals(PngColorType.Grayscale))
{
// TODO: Realistically we should support 1, 2, 4, 8, and 16 bit grayscale images.
// we currently do the other types via palette. Maybe RC as I don't understand how the data is packed yet
// for 1, 2, and 4 bit grayscale images.
// TODO: Research and add support for grayscale plus tRNS
if (this.use16Bit)
{
// 16 bit grayscale
@ -311,12 +334,32 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
// 8 bit grayscale
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
if (this.bitDepth == 8)
{
rowSpan[x].ToRgb24(ref rgb);
rawScanlineSpan[x] = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
// 8 bit grayscale
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
{
rowSpan[x].ToRgb24(ref rgb);
rawScanlineSpan[x] = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
}
}
else
{
// 1, 2, and 4 bit grayscale
using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(rowSpan.Length, AllocationOptions.Clean))
{
int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(this.bitDepth) - 1);
Span<byte> tempSpan = temp.GetSpan();
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
{
rowSpan[x].ToRgb24(ref rgb);
float luminance = ((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)) / scaleFactor;
tempSpan[x] = (byte)luminance;
this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth);
}
}
}
}
}

Loading…
Cancel
Save