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;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// 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, /// 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. /// as it avoids the need to create new values for modification operations.
/// </remarks> /// </remarks>
[StructLayout(LayoutKind.Explicit)]
public partial struct Color : IPackedVector<uint>, IEquatable<Color> public partial struct Color : IPackedVector<uint>, IEquatable<Color>
{ {
/// <summary> /// <summary>
/// Gets or sets the blue component. /// The maximum byte value
/// </summary> /// </summary>
[FieldOffset(0)] private const float MaxBytes = 255F;
public byte R;
/// <summary> /// <summary>
/// Gets or sets the green component. /// The minimum vector value
/// </summary>
private const float Zero = 0F;
/// <summary>
/// The maximum vector value
/// </summary> /// </summary>
[FieldOffset(1)] private const float One = 1F;
public byte G;
/// <summary>
/// The packed value
/// </summary>
private uint packedValue;
/// <summary> /// <summary>
/// Gets or sets the red component. /// Gets or sets the red component.
/// </summary> /// </summary>
[FieldOffset(2)] public byte R
public byte B; {
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> /// <summary>
/// Gets or sets the alpha component. /// Gets or sets the alpha component.
/// </summary> /// </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> /// <summary>
/// The packed value. /// The packed value.
/// </summary> /// </summary>
[FieldOffset(0)] public uint PackedValue { get { return this.packedValue; } set { this.packedValue = value; } }
private uint packedValue;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct. /// 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) public Color(byte r, byte g, byte b, byte a = 255)
: this() : this()
{ {
this.R = r; this.packedValue = (uint)(r | g << 8 | b << 16 | a << 24);
this.G = g;
this.B = b;
this.A = a;
} }
/// <summary> /// <summary>
@ -87,17 +142,19 @@ namespace ImageProcessorCore
if (hex.Length == 8) if (hex.Length == 8)
{ {
this.R = Convert.ToByte(hex.Substring(2, 2), 16); this.packedValue =
this.G = Convert.ToByte(hex.Substring(4, 2), 16); (uint)(Convert.ToByte(hex.Substring(2, 2), 16)
this.B = Convert.ToByte(hex.Substring(6, 2), 16); | Convert.ToByte(hex.Substring(4, 2), 16) << 8
this.A = Convert.ToByte(hex.Substring(0, 2), 16); | Convert.ToByte(hex.Substring(6, 2), 16) << 16
| Convert.ToByte(hex.Substring(0, 2), 16) << 24);
} }
else if (hex.Length == 6) else if (hex.Length == 6)
{ {
this.R = Convert.ToByte(hex.Substring(0, 2), 16); this.packedValue =
this.G = Convert.ToByte(hex.Substring(2, 2), 16); (uint)(Convert.ToByte(hex.Substring(0, 2), 16)
this.B = Convert.ToByte(hex.Substring(4, 2), 16); | Convert.ToByte(hex.Substring(2, 2), 16) << 8
this.A = 255; | Convert.ToByte(hex.Substring(4, 2), 16) << 16
| 255 << 24);
} }
else else
{ {
@ -105,10 +162,11 @@ namespace ImageProcessorCore
string gh = char.ToString(hex[1]); string gh = char.ToString(hex[1]);
string bh = char.ToString(hex[2]); string bh = char.ToString(hex[2]);
this.R = Convert.ToByte(rh + rh, 16); this.packedValue =
this.G = Convert.ToByte(gh + gh, 16); (uint)(Convert.ToByte(rh + rh, 16)
this.B = Convert.ToByte(bh + bh, 16); | Convert.ToByte(gh + gh, 16) << 8
this.A = 255; | 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) public Color(float r, float g, float b, float a = 1)
: this() : this()
{ {
Vector4 clamped = Vector4.Clamp(new Vector4(r, g, b, a), Vector4.Zero, Vector4.One) * 255F; this.packedValue = Pack(r, g, b, a);
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);
} }
/// <summary> /// <summary>
@ -138,11 +192,7 @@ namespace ImageProcessorCore
public Color(Vector3 vector) public Color(Vector3 vector)
: this() : this()
{ {
Vector3 clamped = Vector3.Clamp(vector, Vector3.Zero, Vector3.One) * 255F; this.packedValue = Pack(ref vector);
this.R = (byte)Math.Round(clamped.X);
this.G = (byte)Math.Round(clamped.Y);
this.B = (byte)Math.Round(clamped.Z);
this.A = 255;
} }
/// <summary> /// <summary>
@ -154,11 +204,7 @@ namespace ImageProcessorCore
public Color(Vector4 vector) public Color(Vector4 vector)
: this() : this()
{ {
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F; this.packedValue = Pack(ref vector);
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);
} }
/// <summary> /// <summary>
@ -191,47 +237,16 @@ namespace ImageProcessorCore
return left.packedValue != right.packedValue; return left.packedValue != right.packedValue;
} }
/// <inheritdoc/>
public uint GetPackedValue()
{
return this.packedValue;
}
/// <inheritdoc/>
public void SetPackedValue(uint value)
{
this.packedValue = value;
}
/// <inheritdoc/> /// <inheritdoc/>
public void PackFromVector4(Vector4 vector) public void PackFromVector4(Vector4 vector)
{ {
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F; this.packedValue = Pack(ref vector);
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;
} }
/// <inheritdoc/> /// <inheritdoc/>
public Vector4 ToVector4() public Vector4 ToVector4()
{ {
return new Vector4(this.R, this.G, this.B, this.A) / 255F; return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
/// <inheritdoc/>
public byte[] ToBytes()
{
return new[] { this.R, this.G, this.B, this.A };
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -261,6 +276,43 @@ namespace ImageProcessorCore
return this.GetHashCode(this); 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> /// <summary>
/// Returns the hash code for this instance. /// Returns the hash code for this instance.
/// </summary> /// </summary>

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

@ -15,21 +15,7 @@ namespace ImageProcessorCore
public interface IPackedVector<TPacked> : IPackedVector public interface IPackedVector<TPacked> : IPackedVector
where TPacked : struct where TPacked : struct
{ {
/// <summary> TPacked PackedValue { get; set; }
/// 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);
} }
/// <summary> /// <summary>
@ -43,28 +29,11 @@ namespace ImageProcessorCore
/// <param name="vector">The vector to create the packed representation from.</param> /// <param name="vector">The vector to create the packed representation from.</param>
void PackFromVector4(Vector4 vector); 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> /// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>. /// Expands the packed representation into a <see cref="Vector4"/>.
/// The vector components are typically expanded in least to greatest significance order. /// The vector components are typically expanded in least to greatest significance order.
/// </summary> /// </summary>
/// <returns>The <see cref="Vector4"/>.</returns> /// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4(); 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; 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> /// <summary>
/// Restricts a <see cref="int"/> to be within a specified range. /// Restricts a <see cref="int"/> to be within a specified range.
/// </summary> /// </summary>

8
src/ImageProcessorCore/Common/Helpers/ImageMaths.cs

@ -180,19 +180,19 @@ namespace ImageProcessorCore
switch (channel) switch (channel)
{ {
case RgbaComponent.R: 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; break;
case RgbaComponent.G: 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; break;
case RgbaComponent.B: 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; break;
default: 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; 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) protected override void AfterApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{ {
TColor packed = default(TColor); 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); 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) protected override void AfterApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{ {
TColor packedV = default(TColor); 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); new VignetteProcessor<TColor, TPacked> { VignetteColor = packedV }.Apply(source, sourceRectangle);
TColor packedG = default(TColor); 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); 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. // Stored in b-> g-> r order.
TColor packed = default(TColor); 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; imageData[arrayOffset] = packed;
} }
} }
@ -294,7 +294,7 @@ namespace ImageProcessorCore.Formats
// Stored in b-> g-> r order. // Stored in b-> g-> r order.
TColor packed = default(TColor); TColor packed = default(TColor);
packed.PackFromBytes(r, g, b, 255); packed.PackFromVector4(new Color(r, g, b).ToVector4());
imageData[arrayOffset] = packed; imageData[arrayOffset] = packed;
} }
}); });
@ -332,10 +332,9 @@ namespace ImageProcessorCore.Formats
int offset = rowOffset + (x * 3); int offset = rowOffset + (x * 3);
int arrayOffset = ((row * width) + x); 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. // Stored in b-> g-> r-> a order.
TColor packed = default(TColor); 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; imageData[arrayOffset] = packed;
} }
}); });
@ -375,7 +374,7 @@ namespace ImageProcessorCore.Formats
// Stored in b-> g-> r-> a order. // Stored in b-> g-> r-> a order.
TColor packed = default(TColor); 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; 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++) for (int x = 0; x < pixels.Width; x++)
{ {
// Convert back to b-> g-> r-> a order. // Convert back to b-> g-> r-> a order.
byte[] bytes = pixels[x, y].ToBytes(); Color color = new Color(pixels[x, y].ToVector4());
writer.Write(new[] { bytes[2], bytes[1], bytes[0], bytes[3] }); writer.Write(new[] { color.B, color.G, color.R, color.A });
} }
// Pad // Pad
@ -188,8 +188,8 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < pixels.Width; x++) for (int x = 0; x < pixels.Width; x++)
{ {
// Convert back to b-> g-> r order. // Convert back to b-> g-> r order.
byte[] bytes = pixels[x, y].ToBytes(); Color color = new Color(pixels[x, y].ToVector4());
writer.Write(new[] { bytes[2], bytes[1], bytes[0] }); writer.Write(new[] { color.B, color.G, color.R });
} }
// Pad // Pad

3
src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs

@ -358,9 +358,8 @@ namespace ImageProcessorCore.Formats
{ {
// Stored in r-> g-> b-> a order. // Stored in r-> g-> b-> a order.
int indexOffset = index * 3; int indexOffset = index * 3;
TColor pixel = default(TColor); 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; this.currentFrame[offset] = pixel;
} }

8
src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs

@ -281,11 +281,11 @@ namespace ImageProcessorCore.Formats
i => i =>
{ {
int offset = i * 3; int offset = i * 3;
byte[] color = palette[i].ToBytes(); Color color = new Color(palette[i].ToVector4());
colorTable[offset] = color[0]; colorTable[offset] = color.R;
colorTable[offset + 1] = color[1]; colorTable[offset + 1] = color.G;
colorTable[offset + 2] = color[2]; colorTable[offset + 2] = color.B;
}); });
writer.Write(colorTable, 0, colorTableLength); 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Numerics;
namespace ImageProcessorCore.Formats namespace ImageProcessorCore.Formats
{ {
using System; using System;
@ -349,10 +352,7 @@ namespace ImageProcessorCore.Formats
{ {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
// Bytes are expected in r->g->b->a oder. YCbCr color = new Color(pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToVector4());
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]);
int index = (8 * j) + i; int index = (8 * j) + i;
yBlock[index] = (int)color.Y; yBlock[index] = (int)color.Y;
cbBlock[index] = (int)color.Cb; cbBlock[index] = (int)color.Cb;

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

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Numerics;
namespace ImageProcessorCore.Formats namespace ImageProcessorCore.Formats
{ {
using System; using System;
@ -336,7 +338,7 @@ namespace ImageProcessorCore.Formats
byte intensity = defilteredScanline[offset]; byte intensity = defilteredScanline[offset];
TColor color = default(TColor); 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; pixels[(row * this.header.Width) + x] = color;
} }
@ -352,7 +354,7 @@ namespace ImageProcessorCore.Formats
byte alpha = defilteredScanline[offset + bytesPerSample]; byte alpha = defilteredScanline[offset + bytesPerSample];
TColor color = default(TColor); 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; pixels[(row * this.header.Width) + x] = color;
} }
@ -379,7 +381,7 @@ namespace ImageProcessorCore.Formats
byte r = this.palette[pixelOffset]; byte r = this.palette[pixelOffset];
byte g = this.palette[pixelOffset + 1]; byte g = this.palette[pixelOffset + 1];
byte b = this.palette[pixelOffset + 2]; byte b = this.palette[pixelOffset + 2];
color.PackFromBytes(r, g, b, a); color.PackFromVector4(new Vector4(r, g, b, a) / 255F);
} }
pixels[offset] = color; pixels[offset] = color;
@ -398,7 +400,7 @@ namespace ImageProcessorCore.Formats
byte b = this.palette[pixelOffset + 2]; byte b = this.palette[pixelOffset + 2];
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromBytes(r, g, b, 255); color.PackFromVector4(new Vector4(r, g, b, 255) / 255F);
pixels[offset] = color; pixels[offset] = color;
} }
} }
@ -416,7 +418,7 @@ namespace ImageProcessorCore.Formats
byte b = defilteredScanline[offset + 2 * bytesPerSample]; byte b = defilteredScanline[offset + 2 * bytesPerSample];
TColor color = default(TColor); 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; pixels[(row * this.header.Width) + x] = color;
} }
@ -434,7 +436,7 @@ namespace ImageProcessorCore.Formats
byte a = defilteredScanline[offset + 3 * bytesPerSample]; byte a = defilteredScanline[offset + 3 * bytesPerSample];
TColor color = default(TColor); 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; pixels[(row * this.header.Width) + x] = color;
} }

20
src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs

@ -264,15 +264,17 @@ namespace ImageProcessorCore.Formats
Bootstrapper.Instance.ParallelOptions, Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
// Color data is stored in r -> g -> b -> a order
for (int x = 0; x < this.width; x++) for (int x = 0; x < this.width; x++)
{ {
int dataOffset = (y * stride) + (x * this.bytesPerPixel); 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 => i =>
{ {
int offset = i * 3; int offset = i * 3;
byte[] color = palette[i].ToBytes(); Color color = new Color(palette[i].ToVector4());
colorTable[offset] = color.R;
// Expected format r->g->b colorTable[offset + 1] = color.G;
colorTable[offset] = color[0]; colorTable[offset + 2] = color.B;
colorTable[offset + 1] = color[1];
colorTable[offset + 2] = color[2];
}); });
this.WriteChunk(stream, PngChunkTypes.Palette, colorTable); this.WriteChunk(stream, PngChunkTypes.Palette, colorTable);

2
src/ImageProcessorCore/Image/PixelAccessor.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
/// <summary> /// <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> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></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; byte paletteIndex = (byte)this.colors;
// Get the palette index if it's transparency meets criterea. // 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); paletteIndex = (byte)this.octree.GetPaletteIndex(pixel);
} }
@ -195,7 +195,7 @@ namespace ImageProcessorCore.Quantizers
/// </param> /// </param>
public void AddColor(TColor pixel) 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 // Check if this request is for the same color as the last
if (this.previousColor.Equals(packed)) if (this.previousColor.Equals(packed))
{ {
@ -391,10 +391,10 @@ namespace ImageProcessorCore.Quantizers
{ {
// Go to the next level down in the tree // Go to the next level down in the tree
int shift = 7 - level; int shift = 7 - level;
byte[] components = pixel.ToBytes(); Color color = new Color(pixel.ToVector4());
int index = ((components[2] & Mask[level]) >> (shift - 2)) | int index = ((color.B & Mask[level]) >> (shift - 2)) |
((components[1] & Mask[level]) >> (shift - 1)) | ((color.G & Mask[level]) >> (shift - 1)) |
((components[0] & Mask[level]) >> shift); ((color.R & Mask[level]) >> shift);
OctreeNode child = this.children[index]; OctreeNode child = this.children[index];
@ -458,7 +458,7 @@ namespace ImageProcessorCore.Quantizers
// And set the color of the palette entry // And set the color of the palette entry
TColor pixel = default(TColor); TColor pixel = default(TColor);
pixel.PackFromBytes(r, g, b, 255); pixel.PackFromVector4(new Color(r, g, b).ToVector4());
palette.Add(pixel); palette.Add(pixel);
} }
else else
@ -489,10 +489,10 @@ namespace ImageProcessorCore.Quantizers
if (!this.leaf) if (!this.leaf)
{ {
int shift = 7 - level; int shift = 7 - level;
byte[] components = pixel.ToBytes(); Color color = new Color(pixel.ToVector4());
int pixelIndex = ((components[2] & Mask[level]) >> (shift - 2)) | int pixelIndex = ((color.B & Mask[level]) >> (shift - 2)) |
((components[1] & Mask[level]) >> (shift - 1)) | ((color.G & Mask[level]) >> (shift - 1)) |
((components[0] & Mask[level]) >> shift); ((color.R & Mask[level]) >> shift);
if (this.children[pixelIndex] != null) if (this.children[pixelIndex] != null)
{ {
@ -516,10 +516,10 @@ namespace ImageProcessorCore.Quantizers
public void Increment(TColor pixel) public void Increment(TColor pixel)
{ {
this.pixelCount++; this.pixelCount++;
byte[] components = pixel.ToBytes(); Color color = new Color(pixel.ToVector4());
this.red += components[0]; this.red += color.R;
this.green += components[1]; this.green += color.G;
this.blue += components[2]; 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. // 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 // Firstly check the alpha value - if less than the threshold, lookup the transparent color
byte[] bytes = pixel.ToBytes(); Color color =new Color(pixel.ToVector4());
if (!(bytes[3] > this.Threshold)) if (!(color.A > this.Threshold))
{ {
// Transparent. Lookup the first color with an alpha value of 0 // Transparent. Lookup the first color with an alpha value of 0
for (int index = 0; index < this.colors.Length; index++) 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; colorIndex = (byte)index;
this.TransparentIndex = colorIndex; this.TransparentIndex = colorIndex;
@ -99,17 +99,17 @@ namespace ImageProcessorCore.Quantizers
{ {
// Not transparent... // Not transparent...
int leastDistance = int.MaxValue; int leastDistance = int.MaxValue;
int red = bytes[0]; int red = color.R;
int green = bytes[1]; int green = color.G;
int blue = bytes[2]; int blue = color.B;
// Loop through the entire palette, looking for the closest color match // Loop through the entire palette, looking for the closest color match
for (int index = 0; index < this.colors.Length; index++) for (int index = 0; index < this.colors.Length; index++)
{ {
byte[] paletteColor = this.colors[index].ToBytes(); Color paletteColor = new Color(this.colors[index].ToVector4());
int redDistance = paletteColor[0] - red; int redDistance = paletteColor.R - red;
int greenDistance = paletteColor[1] - green; int greenDistance = paletteColor.G - green;
int blueDistance = paletteColor[2] - blue; int blueDistance = paletteColor.B - blue;
int distance = (redDistance * redDistance) + int distance = (redDistance * redDistance) +
(greenDistance * greenDistance) + (greenDistance * greenDistance) +

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

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Numerics;
namespace ImageProcessorCore.Quantizers namespace ImageProcessorCore.Quantizers
{ {
using System; using System;
@ -332,12 +334,12 @@ namespace ImageProcessorCore.Quantizers
for (int x = 0; x < pixels.Width; x++) for (int x = 0; x < pixels.Width; x++)
{ {
// Colors are expected in r->g->b->a format // 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 r = color.R;
byte g = color[1]; byte g = color.G;
byte b = color[2]; byte b = color.B;
byte a = color[3]; byte a = color.A;
int inr = r >> (8 - IndexBits); int inr = r >> (8 - IndexBits);
int ing = g >> (8 - IndexBits); int ing = g >> (8 - IndexBits);
@ -745,7 +747,7 @@ namespace ImageProcessorCore.Quantizers
byte a = (byte)(Volume(cube[k], this.vma) / weight); byte a = (byte)(Volume(cube[k], this.vma) / weight);
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromBytes(r, g, b, a); color.PackFromVector4(new Vector4(r, g, b, a) / 255F);
if (color.Equals(default(TColor))) if (color.Equals(default(TColor)))
{ {
@ -770,13 +772,13 @@ namespace ImageProcessorCore.Quantizers
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
// Expected order r->g->b->a // Expected order r->g->b->a
byte[] color = imagePixels[x, y].ToBytes(); Color color = new Color(imagePixels[x, y].ToVector4());
int r = color[0] >> (8 - IndexBits); int r = color.R >> (8 - IndexBits);
int g = color[1] >> (8 - IndexBits); int g = color.G >> (8 - IndexBits);
int b = color[2] >> (8 - IndexBits); int b = color.B >> (8 - IndexBits);
int a = color[3] >> (8 - IndexAlphaBits); 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; pixels[(y * width) + x] = (byte)transparentIndex;
continue; continue;

4
tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs

@ -10,7 +10,7 @@
public class GetSetPixel public class GetSetPixel
{ {
[Benchmark(Baseline = true, Description = "System.Drawing GetSeTColor pixel")] [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")]
public SystemColor ResizeSystemDrawing() public SystemColor ResizeSystemDrawing()
{ {
using (Bitmap source = new Bitmap(400, 400)) using (Bitmap source = new Bitmap(400, 400))
@ -20,7 +20,7 @@
} }
} }
[Benchmark(Description = "ImageProcessorCore GetSeTColor pixel")] [Benchmark(Description = "ImageProcessorCore GetSet pixel")]
public CoreColor ResizeCore() public CoreColor ResizeCore()
{ {
CoreImage image = new CoreImage(400, 400); CoreImage image = new CoreImage(400, 400);

2
tests/ImageProcessorCore.Tests/Colors/ColorTests.cs

@ -96,7 +96,7 @@ namespace ImageProcessorCore.Tests
{ {
const string First = "FF000000"; const string First = "FF000000";
Color color = Color.Black; Color color = Color.Black;
string second = color.GetPackedValue().ToString("X"); string second = color.PackedValue.ToString("X");
Assert.Equal(First, second); Assert.Equal(First, second);
} }
} }

Loading…
Cancel
Save