diff --git a/src/ImageProcessorCore/Colors/Color.cs b/src/ImageProcessorCore/Colors/Color.cs index d5570d6470..80ee03e99c 100644 --- a/src/ImageProcessorCore/Colors/Color.cs +++ b/src/ImageProcessorCore/Colors/Color.cs @@ -8,7 +8,6 @@ namespace ImageProcessorCore using System; using System.Numerics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; /// /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -18,38 +17,97 @@ namespace ImageProcessorCore /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - [StructLayout(LayoutKind.Explicit)] public partial struct Color : IPackedVector, IEquatable { /// - /// Gets or sets the blue component. + /// The maximum byte value /// - [FieldOffset(0)] - public byte R; + private const float MaxBytes = 255F; /// - /// Gets or sets the green component. + /// The minimum vector value + /// + private const float Zero = 0F; + + /// + /// The maximum vector value /// - [FieldOffset(1)] - public byte G; + private const float One = 1F; + + /// + /// The packed value + /// + private uint packedValue; /// /// Gets or sets the red component. /// - [FieldOffset(2)] - public byte B; + public byte R + { + get + { + return (byte)this.packedValue; + } + set + { + // AABBGGRR + this.packedValue = (uint)(this.packedValue & -0x100 | value); + } + } + + + /// + /// Gets or sets the green component. + /// + public byte G + { + get + { + return (byte)(this.packedValue >> 8); + } + set + { + // AABBGGRR + this.packedValue = (uint)(this.packedValue & -0xFF01 | (uint)value << 8); + } + } + + /// + /// Gets or sets the blue component. + /// + public byte B + { + get + { + return (byte)(this.packedValue >> 16); + } + set + { + // AABBGGRR + this.packedValue = (uint)(this.packedValue & -0xFF0001 | (uint)(value << 16)); + } + } /// /// Gets or sets the alpha component. /// - [FieldOffset(3)] - public byte A; + public byte A + { + get + { + return (byte)(this.packedValue >> 24); + } + set + { + // AABBGGRR + this.packedValue = this.packedValue & 0xFFFFFF | (uint)value << 24; + } + } /// /// The packed value. /// - [FieldOffset(0)] - private uint packedValue; + public uint PackedValue { get { return this.packedValue; } set { this.packedValue = value; } } /// /// Initializes a new instance of the struct. @@ -61,10 +119,7 @@ namespace ImageProcessorCore public Color(byte r, byte g, byte b, byte a = 255) : this() { - this.R = r; - this.G = g; - this.B = b; - this.A = a; + this.packedValue = (uint)(r | g << 8 | b << 16 | a << 24); } /// @@ -87,17 +142,19 @@ namespace ImageProcessorCore if (hex.Length == 8) { - this.R = Convert.ToByte(hex.Substring(2, 2), 16); - this.G = Convert.ToByte(hex.Substring(4, 2), 16); - this.B = Convert.ToByte(hex.Substring(6, 2), 16); - this.A = Convert.ToByte(hex.Substring(0, 2), 16); + this.packedValue = + (uint)(Convert.ToByte(hex.Substring(2, 2), 16) + | Convert.ToByte(hex.Substring(4, 2), 16) << 8 + | Convert.ToByte(hex.Substring(6, 2), 16) << 16 + | Convert.ToByte(hex.Substring(0, 2), 16) << 24); } else if (hex.Length == 6) { - this.R = Convert.ToByte(hex.Substring(0, 2), 16); - this.G = Convert.ToByte(hex.Substring(2, 2), 16); - this.B = Convert.ToByte(hex.Substring(4, 2), 16); - this.A = 255; + this.packedValue = + (uint)(Convert.ToByte(hex.Substring(0, 2), 16) + | Convert.ToByte(hex.Substring(2, 2), 16) << 8 + | Convert.ToByte(hex.Substring(4, 2), 16) << 16 + | 255 << 24); } else { @@ -105,10 +162,11 @@ namespace ImageProcessorCore string gh = char.ToString(hex[1]); string bh = char.ToString(hex[2]); - this.R = Convert.ToByte(rh + rh, 16); - this.G = Convert.ToByte(gh + gh, 16); - this.B = Convert.ToByte(bh + bh, 16); - this.A = 255; + this.packedValue = + (uint)(Convert.ToByte(rh + rh, 16) + | Convert.ToByte(gh + gh, 16) << 8 + | Convert.ToByte(bh + bh, 16) << 16 + | 255 << 24); } } @@ -122,11 +180,7 @@ namespace ImageProcessorCore public Color(float r, float g, float b, float a = 1) : this() { - Vector4 clamped = Vector4.Clamp(new Vector4(r, g, b, a), Vector4.Zero, Vector4.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = (byte)Math.Round(clamped.W); + this.packedValue = Pack(r, g, b, a); } /// @@ -138,11 +192,7 @@ namespace ImageProcessorCore public Color(Vector3 vector) : this() { - Vector3 clamped = Vector3.Clamp(vector, Vector3.Zero, Vector3.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = 255; + this.packedValue = Pack(ref vector); } /// @@ -154,11 +204,7 @@ namespace ImageProcessorCore public Color(Vector4 vector) : this() { - Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = (byte)Math.Round(clamped.W); + this.packedValue = Pack(ref vector); } /// @@ -191,47 +237,16 @@ namespace ImageProcessorCore return left.packedValue != right.packedValue; } - /// - public uint GetPackedValue() - { - return this.packedValue; - } - - /// - public void SetPackedValue(uint value) - { - this.packedValue = value; - } - /// public void PackFromVector4(Vector4 vector) { - Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = (byte)Math.Round(clamped.W); - } - - /// - public void PackFromBytes(byte x, byte y, byte z, byte w) - { - this.R = x; - this.G = y; - this.B = z; - this.A = w; + this.packedValue = Pack(ref vector); } /// public Vector4 ToVector4() { - return new Vector4(this.R, this.G, this.B, this.A) / 255F; - } - - /// - public byte[] ToBytes() - { - return new[] { this.R, this.G, this.B, this.A }; + return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; } /// @@ -261,6 +276,43 @@ namespace ImageProcessorCore return this.GetHashCode(this); } + /// + /// Packs a into a uint. + /// + /// The vector containing the values to pack. + /// The ulong containing the packed values. + private static uint Pack(ref Vector4 vector) + { + return Pack(vector.X, vector.Y, vector.Z, vector.W); + } + + /// + /// Packs a into a uint. + /// + /// The vector containing the values to pack. + /// The ulong containing the packed values. + private static uint Pack(ref Vector3 vector) + { + return Pack(vector.X, vector.Y, vector.Z, 1); + } + + /// + /// Packs the four floats into a uint. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(float x, float y, float z, float w) + { + return (uint)((byte)Math.Round(x.Clamp(Zero, One) * MaxBytes) + | ((byte)Math.Round(y.Clamp(Zero, One) * MaxBytes) << 8) + | (byte)Math.Round(z.Clamp(Zero, One) * MaxBytes) << 16 + | (byte)Math.Round(w.Clamp(Zero, One) * MaxBytes) << 24); + } + /// /// Returns the hash code for this instance. /// diff --git a/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs b/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs index 114a173eb0..2deb378f4f 100644 --- a/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs +++ b/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs @@ -15,21 +15,7 @@ namespace ImageProcessorCore public interface IPackedVector : IPackedVector where TPacked : struct { - /// - /// Directly gets the packed representation of the packed vector. - /// Typically packed in least to greatest significance order. - /// - /// - /// The . - /// - TPacked GetPackedValue(); - - /// - /// Directly sets the packed representation of the packed vector. - /// Typically packed in least to greatest significance order. - /// - /// The packed value. - void SetPackedValue(TPacked value); + TPacked PackedValue { get; set; } } /// @@ -43,28 +29,11 @@ namespace ImageProcessorCore /// The vector to create the packed representation from. void PackFromVector4(Vector4 vector); - /// - /// Sets the packed representation from a . - /// - /// The x-component to create the packed representation from. - /// The y-component to create the packed representation from. - /// The z-component to create the packed representation from. - /// The w-component to create the packed representation from. - void PackFromBytes(byte x, byte y, byte z, byte w); - /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. /// /// The . Vector4 ToVector4(); - - /// - /// Expands the packed representation into a . - /// The bytes are typically expanded in least to greatest significance order. - /// Red -> Green -> Blue -> Alpha - /// - /// The . - byte[] ToBytes(); } } diff --git a/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs b/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs index cb0288fb7b..a2704ae65a 100644 --- a/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs +++ b/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs @@ -37,6 +37,30 @@ namespace ImageProcessorCore return value; } + /// + /// Restricts a to be within a specified range. + /// + /// The The value to clamp. + /// The minimum value. If value is less than min, min will be returned. + /// The maximum value. If value is greater than max, max will be returned. + /// + /// The representing the clamped value. + /// + public static uint Clamp(this uint value, uint min, uint max) + { + if (value > max) + { + return max; + } + + if (value < min) + { + return min; + } + + return value; + } + /// /// Restricts a to be within a specified range. /// diff --git a/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs b/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs index f356841395..19da0af187 100644 --- a/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs +++ b/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs @@ -180,19 +180,19 @@ namespace ImageProcessorCore switch (channel) { case RgbaComponent.R: - delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[0] - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().X - b) > Epsilon; break; case RgbaComponent.G: - delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[1] - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Y - b) > Epsilon; break; case RgbaComponent.B: - delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[2] - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Z - b) > Epsilon; break; default: - delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[3] - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().W - b) > Epsilon; break; } diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs index 16831e8d82..b4f7967d80 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs @@ -31,7 +31,7 @@ namespace ImageProcessorCore.Processors protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { TColor packed = default(TColor); - packed.PackFromBytes(0, 10, 0, 255); // Very dark (mostly black) lime green. + packed.PackFromVector4(new Color(0, 10, 0).ToVector4()); // Very dark (mostly black) lime green. new VignetteProcessor { VignetteColor = packed }.Apply(source, sourceRectangle); } } diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs index 638997ce5d..6a464da841 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs @@ -37,11 +37,11 @@ namespace ImageProcessorCore.Processors protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { TColor packedV = default(TColor); - packedV.PackFromBytes(102, 34, 0, 255); // Very dark orange [Brown tone] + packedV.PackFromVector4(new Color(102, 34, 0).ToVector4()); // Very dark orange [Brown tone] new VignetteProcessor { VignetteColor = packedV }.Apply(source, sourceRectangle); TColor packedG = default(TColor); - packedG.PackFromBytes(255, 153, 102, 178); // Light orange + packedG.PackFromVector4(new Color(255, 153, 102, 178).ToVector4()); // Light orange new GlowProcessor { GlowColor = packedG, Radius = source.Width / 4F }.Apply(source, sourceRectangle); } } diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs index 9aa612b806..d353e5723c 100644 --- a/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs @@ -242,7 +242,7 @@ namespace ImageProcessorCore.Formats // Stored in b-> g-> r order. TColor packed = default(TColor); - packed.PackFromBytes(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex], 255); + packed.PackFromVector4(new Color(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex]).ToVector4()); imageData[arrayOffset] = packed; } } @@ -294,7 +294,7 @@ namespace ImageProcessorCore.Formats // Stored in b-> g-> r order. TColor packed = default(TColor); - packed.PackFromBytes(r, g, b, 255); + packed.PackFromVector4(new Color(r, g, b).ToVector4()); imageData[arrayOffset] = packed; } }); @@ -332,10 +332,9 @@ namespace ImageProcessorCore.Formats int offset = rowOffset + (x * 3); int arrayOffset = ((row * width) + x); - // We divide by 255 as we will store the colors in our floating point format. // Stored in b-> g-> r-> a order. TColor packed = default(TColor); - packed.PackFromBytes(data[offset + 2], data[offset + 1], data[offset], 255); + packed.PackFromVector4(new Color(data[offset + 2], data[offset + 1], data[offset]).ToVector4()); imageData[arrayOffset] = packed; } }); @@ -375,7 +374,7 @@ namespace ImageProcessorCore.Formats // Stored in b-> g-> r-> a order. TColor packed = default(TColor); - packed.PackFromBytes(data[offset + 2], data[offset + 1], data[offset], data[offset + 3]); + packed.PackFromVector4(new Color(data[offset + 2], data[offset + 1], data[offset], data[offset + 3]).ToVector4()); imageData[arrayOffset] = packed; } }); diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs b/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs index d296d32417..edd6639977 100644 --- a/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs @@ -160,8 +160,8 @@ namespace ImageProcessorCore.Formats for (int x = 0; x < pixels.Width; x++) { // Convert back to b-> g-> r-> a order. - byte[] bytes = pixels[x, y].ToBytes(); - writer.Write(new[] { bytes[2], bytes[1], bytes[0], bytes[3] }); + Color color = new Color(pixels[x, y].ToVector4()); + writer.Write(new[] { color.B, color.G, color.R, color.A }); } // Pad @@ -188,8 +188,8 @@ namespace ImageProcessorCore.Formats for (int x = 0; x < pixels.Width; x++) { // Convert back to b-> g-> r order. - byte[] bytes = pixels[x, y].ToBytes(); - writer.Write(new[] { bytes[2], bytes[1], bytes[0] }); + Color color = new Color(pixels[x, y].ToVector4()); + writer.Write(new[] { color.B, color.G, color.R }); } // Pad diff --git a/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs b/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs index 766e5bb330..a587a7df30 100644 --- a/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs @@ -358,9 +358,8 @@ namespace ImageProcessorCore.Formats { // Stored in r-> g-> b-> a order. int indexOffset = index * 3; - TColor pixel = default(TColor); - pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255); + pixel.PackFromVector4(new Color(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2]).ToVector4()); this.currentFrame[offset] = pixel; } diff --git a/src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs b/src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs index cf4d39a89c..b73a5bab59 100644 --- a/src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs @@ -281,11 +281,11 @@ namespace ImageProcessorCore.Formats i => { int offset = i * 3; - byte[] color = palette[i].ToBytes(); + Color color = new Color(palette[i].ToVector4()); - colorTable[offset] = color[0]; - colorTable[offset + 1] = color[1]; - colorTable[offset + 2] = color[2]; + colorTable[offset] = color.R; + colorTable[offset + 1] = color.G; + colorTable[offset + 2] = color.B; }); writer.Write(colorTable, 0, colorTableLength); diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id index 2fb48cb7e2..1f1ad448b0 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id +++ b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id @@ -1 +1 @@ -457ce2b70d772a8f3b259b2d624fcb41c75357e1 \ No newline at end of file +c27ddeac481f3a9631dcb53248a98676c8016f42 \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs index ef6df20ab3..57ab791817 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs @@ -2,6 +2,9 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + +using System.Numerics; + namespace ImageProcessorCore.Formats { using System; @@ -349,10 +352,7 @@ namespace ImageProcessorCore.Formats { for (int i = 0; i < 8; i++) { - // Bytes are expected in r->g->b->a oder. - byte[] pixel = pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToBytes(); - - YCbCr color = new Color(pixel[0], pixel[1], pixel[2], pixel[3]); + YCbCr color = new Color(pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToVector4()); int index = (8 * j) + i; yBlock[index] = (int)color.Y; cbBlock[index] = (int)color.Cb; diff --git a/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs b/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs index ab27ccd179..c449b72d99 100644 --- a/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs +++ b/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs @@ -3,6 +3,8 @@ // Licensed under the Apache License, Version 2.0. // +using System.Numerics; + namespace ImageProcessorCore.Formats { using System; @@ -336,7 +338,7 @@ namespace ImageProcessorCore.Formats byte intensity = defilteredScanline[offset]; TColor color = default(TColor); - color.PackFromBytes(intensity, intensity, intensity, 255); + color.PackFromVector4(new Vector4(intensity, intensity, intensity, 255) / 255F); pixels[(row * this.header.Width) + x] = color; } @@ -352,7 +354,7 @@ namespace ImageProcessorCore.Formats byte alpha = defilteredScanline[offset + bytesPerSample]; TColor color = default(TColor); - color.PackFromBytes(intensity, intensity, intensity, alpha); + color.PackFromVector4(new Vector4(intensity, intensity, intensity, alpha) / 255F); pixels[(row * this.header.Width) + x] = color; } @@ -379,7 +381,7 @@ namespace ImageProcessorCore.Formats byte r = this.palette[pixelOffset]; byte g = this.palette[pixelOffset + 1]; byte b = this.palette[pixelOffset + 2]; - color.PackFromBytes(r, g, b, a); + color.PackFromVector4(new Vector4(r, g, b, a) / 255F); } pixels[offset] = color; @@ -398,7 +400,7 @@ namespace ImageProcessorCore.Formats byte b = this.palette[pixelOffset + 2]; TColor color = default(TColor); - color.PackFromBytes(r, g, b, 255); + color.PackFromVector4(new Vector4(r, g, b, 255) / 255F); pixels[offset] = color; } } @@ -416,7 +418,7 @@ namespace ImageProcessorCore.Formats byte b = defilteredScanline[offset + 2 * bytesPerSample]; TColor color = default(TColor); - color.PackFromBytes(r, g, b, 255); + color.PackFromVector4(new Vector4(r, g, b, 255) / 255F); pixels[(row * this.header.Width) + x] = color; } @@ -434,7 +436,7 @@ namespace ImageProcessorCore.Formats byte a = defilteredScanline[offset + 3 * bytesPerSample]; TColor color = default(TColor); - color.PackFromBytes(r, g, b, a); + color.PackFromVector4(new Vector4(r, g, b, a) / 255F); pixels[(row * this.header.Width) + x] = color; } diff --git a/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs b/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs index f666f0f0ee..0613c26ad9 100644 --- a/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs @@ -264,15 +264,17 @@ namespace ImageProcessorCore.Formats Bootstrapper.Instance.ParallelOptions, y => { - // Color data is stored in r -> g -> b -> a order for (int x = 0; x < this.width; x++) { int dataOffset = (y * stride) + (x * this.bytesPerPixel); - byte[] source = pixels[x, y].ToBytes(); + Color source = new Color(pixels[x, y].ToVector4()); - for (int i = 0; i < this.bytesPerPixel; i++) + this.pixelData[dataOffset] = source.R; + this.pixelData[dataOffset + 1] = source.G; + this.pixelData[dataOffset + 2] = source.B; + if (this.bytesPerPixel == 4) { - this.pixelData[dataOffset + i] = source[i]; + this.pixelData[dataOffset + 3] = source.A; } } }); @@ -511,12 +513,10 @@ namespace ImageProcessorCore.Formats i => { int offset = i * 3; - byte[] color = palette[i].ToBytes(); - - // Expected format r->g->b - colorTable[offset] = color[0]; - colorTable[offset + 1] = color[1]; - colorTable[offset + 2] = color[2]; + Color color = new Color(palette[i].ToVector4()); + colorTable[offset] = color.R; + colorTable[offset + 1] = color.G; + colorTable[offset + 2] = color.B; }); this.WriteChunk(stream, PngChunkTypes.Palette, colorTable); diff --git a/src/ImageProcessorCore/Image/PixelAccessor.cs b/src/ImageProcessorCore/Image/PixelAccessor.cs index d6f15047ee..8ebfb355e8 100644 --- a/src/ImageProcessorCore/Image/PixelAccessor.cs +++ b/src/ImageProcessorCore/Image/PixelAccessor.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore using System.Runtime.InteropServices; /// - /// Encapsulates properties to provides per-pixel access to an images pixels. + /// Provides per-pixel access to generic pixels. /// /// The pixel format. /// The packed format. uint, long, float. diff --git a/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs index 7e872bed12..89d18575a6 100644 --- a/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs @@ -84,7 +84,7 @@ namespace ImageProcessorCore.Quantizers byte paletteIndex = (byte)this.colors; // Get the palette index if it's transparency meets criterea. - if (pixel.ToBytes()[3] > this.Threshold) + if (new Color(pixel.ToVector4()).A > this.Threshold) { paletteIndex = (byte)this.octree.GetPaletteIndex(pixel); } @@ -195,7 +195,7 @@ namespace ImageProcessorCore.Quantizers /// public void AddColor(TColor pixel) { - TPacked packed = pixel.GetPackedValue(); + TPacked packed = pixel.PackedValue; // Check if this request is for the same color as the last if (this.previousColor.Equals(packed)) { @@ -391,10 +391,10 @@ namespace ImageProcessorCore.Quantizers { // Go to the next level down in the tree int shift = 7 - level; - byte[] components = pixel.ToBytes(); - int index = ((components[2] & Mask[level]) >> (shift - 2)) | - ((components[1] & Mask[level]) >> (shift - 1)) | - ((components[0] & Mask[level]) >> shift); + Color color = new Color(pixel.ToVector4()); + int index = ((color.B & Mask[level]) >> (shift - 2)) | + ((color.G & Mask[level]) >> (shift - 1)) | + ((color.R & Mask[level]) >> shift); OctreeNode child = this.children[index]; @@ -458,7 +458,7 @@ namespace ImageProcessorCore.Quantizers // And set the color of the palette entry TColor pixel = default(TColor); - pixel.PackFromBytes(r, g, b, 255); + pixel.PackFromVector4(new Color(r, g, b).ToVector4()); palette.Add(pixel); } else @@ -489,10 +489,10 @@ namespace ImageProcessorCore.Quantizers if (!this.leaf) { int shift = 7 - level; - byte[] components = pixel.ToBytes(); - int pixelIndex = ((components[2] & Mask[level]) >> (shift - 2)) | - ((components[1] & Mask[level]) >> (shift - 1)) | - ((components[0] & Mask[level]) >> shift); + Color color = new Color(pixel.ToVector4()); + int pixelIndex = ((color.B & Mask[level]) >> (shift - 2)) | + ((color.G & Mask[level]) >> (shift - 1)) | + ((color.R & Mask[level]) >> shift); if (this.children[pixelIndex] != null) { @@ -516,10 +516,10 @@ namespace ImageProcessorCore.Quantizers public void Increment(TColor pixel) { this.pixelCount++; - byte[] components = pixel.ToBytes(); - this.red += components[0]; - this.green += components[1]; - this.blue += components[2]; + Color color = new Color(pixel.ToVector4()); + this.red += color.R; + this.green += color.G; + this.blue += color.B; } } } diff --git a/src/ImageProcessorCore/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageProcessorCore/Quantizers/Palette/PaletteQuantizer.cs index a075705358..0a3f12ba8d 100644 --- a/src/ImageProcessorCore/Quantizers/Palette/PaletteQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Palette/PaletteQuantizer.cs @@ -81,13 +81,13 @@ namespace ImageProcessorCore.Quantizers { // Not found - loop through the palette and find the nearest match. // Firstly check the alpha value - if less than the threshold, lookup the transparent color - byte[] bytes = pixel.ToBytes(); - if (!(bytes[3] > this.Threshold)) + Color color =new Color(pixel.ToVector4()); + if (!(color.A > this.Threshold)) { // Transparent. Lookup the first color with an alpha value of 0 for (int index = 0; index < this.colors.Length; index++) { - if (this.colors[index].ToBytes()[3] == 0) + if (new Color(this.colors[index].ToVector4()).A == 0) { colorIndex = (byte)index; this.TransparentIndex = colorIndex; @@ -99,17 +99,17 @@ namespace ImageProcessorCore.Quantizers { // Not transparent... int leastDistance = int.MaxValue; - int red = bytes[0]; - int green = bytes[1]; - int blue = bytes[2]; + int red = color.R; + int green = color.G; + int blue = color.B; // Loop through the entire palette, looking for the closest color match for (int index = 0; index < this.colors.Length; index++) { - byte[] paletteColor = this.colors[index].ToBytes(); - int redDistance = paletteColor[0] - red; - int greenDistance = paletteColor[1] - green; - int blueDistance = paletteColor[2] - blue; + Color paletteColor = new Color(this.colors[index].ToVector4()); + int redDistance = paletteColor.R - red; + int greenDistance = paletteColor.G - green; + int blueDistance = paletteColor.B - blue; int distance = (redDistance * redDistance) + (greenDistance * greenDistance) + diff --git a/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs b/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs index 5a9aefcb24..ed48203063 100644 --- a/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs @@ -3,6 +3,8 @@ // Licensed under the Apache License, Version 2.0. // +using System.Numerics; + namespace ImageProcessorCore.Quantizers { using System; @@ -332,12 +334,12 @@ namespace ImageProcessorCore.Quantizers for (int x = 0; x < pixels.Width; x++) { // Colors are expected in r->g->b->a format - byte[] color = pixels[x, y].ToBytes(); + Color color = new Color(pixels[x, y].ToVector4()); - byte r = color[0]; - byte g = color[1]; - byte b = color[2]; - byte a = color[3]; + byte r = color.R; + byte g = color.G; + byte b = color.B; + byte a = color.A; int inr = r >> (8 - IndexBits); int ing = g >> (8 - IndexBits); @@ -745,7 +747,7 @@ namespace ImageProcessorCore.Quantizers byte a = (byte)(Volume(cube[k], this.vma) / weight); TColor color = default(TColor); - color.PackFromBytes(r, g, b, a); + color.PackFromVector4(new Vector4(r, g, b, a) / 255F); if (color.Equals(default(TColor))) { @@ -770,13 +772,13 @@ namespace ImageProcessorCore.Quantizers for (int x = 0; x < width; x++) { // Expected order r->g->b->a - byte[] color = imagePixels[x, y].ToBytes(); - int r = color[0] >> (8 - IndexBits); - int g = color[1] >> (8 - IndexBits); - int b = color[2] >> (8 - IndexBits); - int a = color[3] >> (8 - IndexAlphaBits); + Color color = new Color(imagePixels[x, y].ToVector4()); + int r = color.R >> (8 - IndexBits); + int g = color.G >> (8 - IndexBits); + int b = color.B >> (8 - IndexBits); + int a = color.A >> (8 - IndexAlphaBits); - if (transparentIndex > -1 && color[3] <= this.Threshold) + if (transparentIndex > -1 && color.A <= this.Threshold) { pixels[(y * width) + x] = (byte)transparentIndex; continue; diff --git a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs index eb742404ef..09d8c862f9 100644 --- a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs @@ -10,7 +10,7 @@ public class GetSetPixel { - [Benchmark(Baseline = true, Description = "System.Drawing GetSeTColor pixel")] + [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")] public SystemColor ResizeSystemDrawing() { using (Bitmap source = new Bitmap(400, 400)) @@ -20,7 +20,7 @@ } } - [Benchmark(Description = "ImageProcessorCore GetSeTColor pixel")] + [Benchmark(Description = "ImageProcessorCore GetSet pixel")] public CoreColor ResizeCore() { CoreImage image = new CoreImage(400, 400); diff --git a/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs b/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs index 69ed6ff73d..82e1bd80e5 100644 --- a/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs +++ b/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs @@ -96,7 +96,7 @@ namespace ImageProcessorCore.Tests { const string First = "FF000000"; Color color = Color.Black; - string second = color.GetPackedValue().ToString("X"); + string second = color.PackedValue.ToString("X"); Assert.Equal(First, second); } }