-
-
Notifications
You must be signed in to change notification settings - Fork 892
Description
Currently if someone wants to read/write opaque grayscale png files they need to convert the image to and from Rgb24 or Rgba32 which requires the user to consume more memory than necessary.
I propose we do the following:
Create the following IPixel implementations Gray8 and Gray16.
Gray8
/// <summary>
/// Packed pixel type containing one 8-bit unsigned normalized value ranging from 0 to 255.
// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Gray8 : IPixel<Gray8>
{
/// <summary>
/// Gets or sets the luminance component.
/// </summary>
public ushort L;
// etc...
}Gray16
/// <summary>
/// Packed pixel type containing one 16-bit unsigned normalized value ranging from 0 to 635535.
/// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Gray16 : IPixel<Gray16>
{
/// <summary>
/// Gets or sets the luminance component.
/// </summary>
public ushort L;
// etc...
}Add the additional methods to IPixel
/// <summary>
/// Packs the pixel from an <see cref="Gray8"/> value.
/// </summary>
/// <param name="source">The <see cref="Gray8"/> value.</param>
void PackFromGray8(Gray8 source);
/// <summary>
/// Converts the pixel to <see cref="Gray8"/> format.
/// </summary>
/// <param name="dest">The destination pixel to write to.</param>
void ToGray8(ref Gray8 dest)
/// <summary>
/// Packs the pixel from an <see cref="Gray16"/> value.
/// </summary>
/// <param name="source">The <see cref="Gray16"/> value.</param>
void PackFromGray16(Gray16 source);
/// <summary>
/// Converts the pixel to <see cref="Gray16"/> format.
/// </summary>
/// <param name="dest">The destination pixel to write to.</param>
void ToGray16(ref Gray16 dest)Add the following methods to PixelOperations (These can be added to the T4 template).
/// <summary>
/// A helper for <see cref="PackFromGray8(ReadOnlySpan{Gray8}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Gray8"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="ReadOnlySpan{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal void PackFromGray8Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count);
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Gray8"/>-s.
/// Bulk version of <see cref="IPixel.ToGray8(ref Gray8)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Gray8"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToGray8(ReadOnlySpan<TPixel> sourcePixels, Span<Gray8> dest, int count);
/// <summary>
/// A helper for <see cref="ToGray8(ReadOnlySpan{TPixel}, Span{Gray8}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Gray8"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal void ToGray8Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count);
/// <summary>
/// A helper for <see cref="PackFromGray16(ReadOnlySpan{Gray16}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Gray16"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="ReadOnlySpan{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal void PackFromGray16Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count);
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Gray16"/>-s.
/// Bulk version of <see cref="IPixel.ToGray16(ref Gray16)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Gray16"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToGray16(ReadOnlySpan<TPixel> sourcePixels, Span<Gray16> dest, int count);
/// <summary>
/// A helper for <see cref="ToGray16(ReadOnlySpan{TPixel}, Span{Gray16}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Gray16"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal void ToGray16Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count);Conversion between these formats and Rgb24 and similar would use the ITU-R Recommendation BT.709 which is the algorithm used by libpng and currently implemented by our codec. We would replace the current code with our new API methods.
// Conversion from Rgb24 to Gray16 using ITU-R recommendation 709 to match libpng.
const float RX = .2126F;
const float GX = .7152F;
const float BX = .0722F;
ushort luminance = (ushort)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));Add appropriate unit tests to existing pixel formats.
This is not a particularly difficult task, just time consuming. I would consider it appropriate for newcomers to the library.