Browse Source

Merge remote-tracking branch 'refs/remotes/origin/Core-BetterPackedVector' into Core

Former-commit-id: 7097af04b554ef997f75dadb590ed36e83ba09dc
Former-commit-id: a8683179122e3fed7bfd0587a6dbb38b8c399a9c
Former-commit-id: 214b1d81d66583b6d3bfb31f736ffa38cc51d2c1
pull/1/head
James Jackson-South 10 years ago
parent
commit
e7946e36fa
  1. 208
      src/ImageProcessorCore/Colors/Color.cs
  2. 33
      src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs
  3. 24
      src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs
  4. 8
      src/ImageProcessorCore/Common/Helpers/ImageMaths.cs
  5. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
  6. 4
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
  7. 9
      src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
  8. 8
      src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs
  9. 3
      src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs
  10. 8
      src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs
  11. 2
      src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  12. 8
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs
  13. 14
      src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs
  14. 20
      src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
  15. 2
      src/ImageProcessorCore/Image/PixelAccessor.cs
  16. 30
      src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs
  17. 20
      src/ImageProcessorCore/Quantizers/Palette/PaletteQuantizer.cs
  18. 26
      src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs
  19. 4
      tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs
  20. 2
      tests/ImageProcessorCore.Tests/Colors/ColorTests.cs

208
src/ImageProcessorCore/Colors/Color.cs

@ -8,7 +8,6 @@ namespace ImageProcessorCore
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// 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.
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
public partial struct Color : IPackedVector<uint>, IEquatable<Color>
{
/// <summary>
/// Gets or sets the blue component.
/// The maximum byte value
/// </summary>
[FieldOffset(0)]
public byte R;
private const float MaxBytes = 255F;
/// <summary>
/// Gets or sets the green component.
/// The minimum vector value
/// </summary>
private const float Zero = 0F;
/// <summary>
/// The maximum vector value
/// </summary>
[FieldOffset(1)]
public byte G;
private const float One = 1F;
/// <summary>
/// The packed value
/// </summary>
private uint packedValue;
/// <summary>
/// Gets or sets the red component.
/// </summary>
[FieldOffset(2)]
public byte B;
public byte R
{
get
{
return (byte)this.packedValue;
}
set
{
// AABBGGRR
this.packedValue = (uint)(this.packedValue & -0x100 | value);
}
}
/// <summary>
/// Gets or sets the green component.
/// </summary>
public byte G
{
get
{
return (byte)(this.packedValue >> 8);
}
set
{
// AABBGGRR
this.packedValue = (uint)(this.packedValue & -0xFF01 | (uint)value << 8);
}
}
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public byte B
{
get
{
return (byte)(this.packedValue >> 16);
}
set
{
// AABBGGRR
this.packedValue = (uint)(this.packedValue & -0xFF0001 | (uint)(value << 16));
}
}
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
[FieldOffset(3)]
public byte A;
public byte A
{
get
{
return (byte)(this.packedValue >> 24);
}
set
{
// AABBGGRR
this.packedValue = this.packedValue & 0xFFFFFF | (uint)value << 24;
}
}
/// <summary>
/// The packed value.
/// </summary>
[FieldOffset(0)]
private uint packedValue;
public uint PackedValue { get { return this.packedValue; } set { this.packedValue = value; } }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> 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);
}
/// <summary>
@ -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);
}
/// <summary>
@ -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);
}
/// <summary>
@ -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);
}
/// <summary>
@ -191,47 +237,16 @@ namespace ImageProcessorCore
return left.packedValue != right.packedValue;
}
/// <inheritdoc/>
public uint GetPackedValue()
{
return this.packedValue;
}
/// <inheritdoc/>
public void SetPackedValue(uint value)
{
this.packedValue = value;
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public Vector4 ToVector4()
{
return new Vector4(this.R, this.G, this.B, this.A) / 255F;
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
@ -261,6 +276,43 @@ namespace ImageProcessorCore
return this.GetHashCode(this);
}
/// <summary>
/// Packs a <see cref="Vector4"/> into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The ulong containing the packed values.</returns>
private static uint Pack(ref Vector4 vector)
{
return Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Packs a <see cref="Vector3"/> into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The ulong containing the packed values.</returns>
private static uint Pack(ref Vector3 vector)
{
return Pack(vector.X, vector.Y, vector.Z, 1);
}
/// <summary>
/// Packs the four floats into a uint.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/></returns>
[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);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>

33
src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs

@ -15,21 +15,7 @@ namespace ImageProcessorCore
public interface IPackedVector<TPacked> : IPackedVector
where TPacked : struct
{
/// <summary>
/// Directly gets the packed representation of the packed vector.
/// Typically packed in least to greatest significance order.
/// </summary>
/// <returns>
/// The <see cref="TPacked"/>.
/// </returns>
TPacked GetPackedValue();
/// <summary>
/// Directly sets the packed representation of the packed vector.
/// Typically packed in least to greatest significance order.
/// </summary>
/// <param name="value">The packed value.</param>
void SetPackedValue(TPacked value);
TPacked PackedValue { get; set; }
}
/// <summary>
@ -43,28 +29,11 @@ namespace ImageProcessorCore
/// <param name="vector">The vector to create the packed representation from.</param>
void PackFromVector4(Vector4 vector);
/// <summary>
/// Sets the packed representation from a <see cref="T:byte[]"/>.
/// </summary>
/// <param name="x">The x-component to create the packed representation from.</param>
/// <param name="y">The y-component to create the packed representation from.</param>
/// <param name="z">The z-component to create the packed representation from.</param>
/// <param name="w">The w-component to create the packed representation from.</param>
void PackFromBytes(byte x, byte y, byte z, byte w);
/// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
/// <summary>
/// Expands the packed representation into a <see cref="T:byte[]"/>.
/// The bytes are typically expanded in least to greatest significance order.
/// Red -> Green -> Blue -> Alpha
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
byte[] ToBytes();
}
}

24
src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs

@ -37,6 +37,30 @@ namespace ImageProcessorCore
return value;
}
/// <summary>
/// Restricts a <see cref="uint"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="int"/> representing the clamped value.
/// </returns>
public static uint Clamp(this uint value, uint min, uint max)
{
if (value > max)
{
return max;
}
if (value < min)
{
return min;
}
return value;
}
/// <summary>
/// Restricts a <see cref="int"/> to be within a specified range.
/// </summary>

8
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;
}

2
src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs

@ -31,7 +31,7 @@ namespace ImageProcessorCore.Processors
protected override void AfterApply(ImageBase<TColor, TPacked> 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<TColor, TPacked> { VignetteColor = packed }.Apply(source, sourceRectangle);
}
}

4
src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs

@ -37,11 +37,11 @@ namespace ImageProcessorCore.Processors
protected override void AfterApply(ImageBase<TColor, TPacked> 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<TColor, TPacked> { 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<TColor, TPacked> { GlowColor = packedG, Radius = source.Width / 4F }.Apply(source, sourceRectangle);
}
}

9
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;
}
});

8
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

3
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;
}

8
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);

2
src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id

@ -1 +1 @@
457ce2b70d772a8f3b259b2d624fcb41c75357e1
c27ddeac481f3a9631dcb53248a98676c8016f42

8
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.
// </copyright>
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;

14
src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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;
}

20
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);

2
src/ImageProcessorCore/Image/PixelAccessor.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore
using System.Runtime.InteropServices;
/// <summary>
/// Encapsulates properties to provides per-pixel access to an images pixels.
/// Provides per-pixel access to generic <see cref="Image{TColor,TPacked}"/> pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>

30
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
/// </param>
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;
}
}
}

20
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) +

26
src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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;

4
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);

2
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);
}
}

Loading…
Cancel
Save