diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index e0e11ad9ee..689c9a85b4 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -17,12 +17,12 @@ namespace SixLabors.ImageSharp.Dithering /// The source pixel /// The color to apply to the pixels above the threshold. /// The color to apply to the pixels below the threshold. - /// The byte array to pack/unpack to. Must have a length of 4. Bytes are unpacked to Xyzw order. + /// The to pack/unpack to. /// The component index to test the threshold against. Must range from 0 to 3. /// The column index. /// The row index. /// The pixel format. - void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) + void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs index 09c30eb272..b29de4cf23 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs @@ -26,14 +26,11 @@ namespace SixLabors.ImageSharp.Dithering.Base } /// - public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) where TPixel : struct, IPixel { - // TODO: This doesn't really cut it for me. - // I'd rather be using float but we need to add some sort of normalization vector methods to all IPixel implementations - // before we can do that as the vectors all cover different ranges. - source.ToXyzwBytes(bytes, 0); - image[x, y] = this.matrix[y % 3, x % 3] >= bytes[index] ? lower : upper; + source.ToRgba32(ref rgba); + image[x, y] = this.matrix[y % 3, x % 3] >= rgba[index] ? lower : upper; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 1b145a79eb..41c8e944d8 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -351,16 +351,16 @@ namespace SixLabors.ImageSharp.Formats.Gif // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); - + var rgb = default(Rgb24); try { for (int i = 0; i < pixelCount; i++) { int offset = i * 3; - image.Palette[i].ToXyzBytes(this.buffer, 0); - colorTable[offset] = this.buffer[0]; - colorTable[offset + 1] = this.buffer[1]; - colorTable[offset + 2] = this.buffer[2]; + image.Palette[i].ToRgb24(ref rgb); + colorTable[offset] = rgb.R; + colorTable[offset + 1] = rgb.G; + colorTable[offset + 2] = rgb.B; } writer.Write(colorTable, 0, colorTableLength); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 86a63f5b47..0efd46ec74 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -318,6 +318,7 @@ namespace SixLabors.ImageSharp.Formats.Png where TPixel : struct, IPixel { byte[] rawScanlineArray = this.rawScanline.Array; + var rgba = default(Rgba32); // Copy the pixels across from the image. // Reuse the chunk type buffer. @@ -326,8 +327,8 @@ namespace SixLabors.ImageSharp.Formats.Png // Convert the color to YCbCr and store the luminance // Optionally store the original color alpha. int offset = x * this.bytesPerPixel; - rowSpan[x].ToXyzwBytes(this.chunkTypeBuffer, 0); - byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2])); + rowSpan[x].ToRgba32(ref rgba); + byte luminance = (byte)((0.299F * rgba.R) + (0.587F * rgba.G) + (0.114F * rgba.B)); for (int i = 0; i < this.bytesPerPixel; i++) { @@ -337,7 +338,7 @@ namespace SixLabors.ImageSharp.Formats.Png } else { - rawScanlineArray[offset + i] = this.chunkTypeBuffer[3]; + rawScanlineArray[offset + i] = rgba.A; } } } @@ -518,7 +519,7 @@ namespace SixLabors.ImageSharp.Formats.Png int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); byte[] alphaTable = ArrayPool.Shared.Rent(pixelCount); - byte[] bytes = ArrayPool.Shared.Rent(4); + var rgba = default(Rgba32); bool anyAlpha = false; try { @@ -527,13 +528,13 @@ namespace SixLabors.ImageSharp.Formats.Png if (quantized.Pixels.Contains(i)) { int offset = i * 3; - palette[i].ToXyzwBytes(bytes, 0); + palette[i].ToRgba32(ref rgba); - byte alpha = bytes[3]; + byte alpha = rgba.A; - colorTable[offset] = bytes[0]; - colorTable[offset + 1] = bytes[1]; - colorTable[offset + 2] = bytes[2]; + colorTable[offset] = rgba.R; + colorTable[offset + 1] = rgba.G; + colorTable[offset + 2] = rgba.B; if (alpha > this.threshold) { @@ -557,7 +558,6 @@ namespace SixLabors.ImageSharp.Formats.Png { ArrayPool.Shared.Return(colorTable); ArrayPool.Shared.Return(alphaTable); - ArrayPool.Shared.Return(bytes); } return quantized; diff --git a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs b/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs deleted file mode 100644 index 37b225d2e0..0000000000 --- a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Extension methods for copying single pixel data into byte Spans. - /// TODO: This utility class exists for legacy reasons. Need to do a lot of chore work to remove it (mostly in test classes). - /// - internal static class PixelConversionExtensions - { - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToXyzBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Rgb24 dest = ref bytes.GetRgb24(startIndex); - pixel.ToRgb24(ref dest); - } - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToXyzwBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Rgba32 dest = ref Unsafe.As(ref bytes[startIndex]); - pixel.ToRgba32(ref dest); - } - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToZyxBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Bgr24 dest = ref Unsafe.As(ref bytes[startIndex]); - pixel.ToBgr24(ref dest); - } - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToZyxwBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Bgra32 dest = ref Unsafe.As(ref bytes[startIndex]); - pixel.ToBgra32(ref dest); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs index 51647fc1f9..b213de019e 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.cs @@ -220,6 +220,32 @@ namespace SixLabors.ImageSharp set => this.Rgba = value; } + /// + /// Gets the component value at the given index + /// + /// The component index + /// The + public byte this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + DebugGuard.MustBeGreaterThanOrEqualTo(index, 0, nameof(index)); + DebugGuard.MustBeLessThanOrEqualTo(index, 3, nameof(index)); + switch (index) + { + case 0: + return this.R; + case 1: + return this.G; + case 2: + return this.B; + default: + return this.A; + } + } + } + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs index 203a64cf16..a2fd17c94b 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int startX = interest.X; int endX = interest.Right; - byte[] bytes = new byte[4]; + var rgba = default(Rgba32); for (int y = startY; y < endY; y++) { Span row = source.GetPixelRowSpan(y); @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors for (int x = startX; x < endX; x++) { TPixel sourceColor = row[x]; - this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, x, y); + this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, ref rgba, this.Index, x, y); } } } diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index 8ab390f4e7..44311e080b 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -60,11 +60,6 @@ namespace SixLabors.ImageSharp.Quantizers /// private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; - /// - /// A buffer for storing pixels - /// - private readonly byte[] rgbaBuffer = new byte[4]; - /// /// A lookup table for colors /// @@ -199,19 +194,15 @@ namespace SixLabors.ImageSharp.Quantizers { // Add the color to a 3-D color histogram. // Colors are expected in r->g->b->a format - pixel.ToXyzwBytes(this.rgbaBuffer, 0); - - byte r = this.rgbaBuffer[0]; - byte g = this.rgbaBuffer[1]; - byte b = this.rgbaBuffer[2]; - byte a = this.rgbaBuffer[3]; + var rgba = default(Rgba32); + pixel.ToRgba32(ref rgba); - int inr = r >> (8 - IndexBits); - int ing = g >> (8 - IndexBits); - int inb = b >> (8 - IndexBits); - int ina = a >> (8 - IndexAlphaBits); + int r = rgba.R >> 2; // 8 - IndexBits + int g = rgba.G >> 2; + int b = rgba.B >> 2; + int a = rgba.A >> 5; // 8 - IndexAlphaBits - int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); + int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); this.vwt[ind]++; this.vmr[ind] += r; @@ -840,12 +831,13 @@ namespace SixLabors.ImageSharp.Quantizers } // Expected order r->g->b->a - pixel.ToXyzwBytes(this.rgbaBuffer, 0); + var rgba = default(Rgba32); + pixel.ToRgba32(ref rgba); - int r = this.rgbaBuffer[0] >> (8 - IndexBits); - int g = this.rgbaBuffer[1] >> (8 - IndexBits); - int b = this.rgbaBuffer[2] >> (8 - IndexBits); - int a = this.rgbaBuffer[3] >> (8 - IndexAlphaBits); + int r = rgba.R >> (8 - IndexBits); + int g = rgba.G >> (8 - IndexBits); + int b = rgba.B >> (8 - IndexBits); + int a = rgba.A >> (8 - IndexAlphaBits); return this.tag[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; }