diff --git a/GenericImage/ImageBase.cs b/GenericImage/ImageBase.cs
new file mode 100644
index 000000000..9eed0dccc
--- /dev/null
+++ b/GenericImage/ImageBase.cs
@@ -0,0 +1,188 @@
+namespace GenericImage
+{
+ using System;
+
+ using GenericImage.PackedVectors;
+
+ ///
+ /// Encapsulates the basic properties and methods required to manipulate images
+ /// in different pixel formats.
+ ///
+ ///
+ /// The packed vector pixels format.
+ ///
+ public abstract class ImageBase
+ where TPacked : IPackedVector
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected ImageBase()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The width of the image in pixels.
+ /// The height of the image in pixels.
+ ///
+ /// Thrown if either or are less than or equal to 0.
+ ///
+ protected ImageBase(int width, int height)
+ {
+ // Guard.MustBeGreaterThan(width, 0, nameof(width));
+ // Guard.MustBeGreaterThan(height, 0, nameof(height));
+
+ this.Width = width;
+ this.Height = height;
+ this.Pixels = new TPacked[width * height];
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The other to create this instance from.
+ ///
+ ///
+ /// Thrown if the given is null.
+ ///
+ protected ImageBase(ImageBase other)
+ {
+ // Guard.NotNull(other, nameof(other), "Other image cannot be null.");
+
+ this.Width = other.Width;
+ this.Height = other.Height;
+ this.Quality = other.Quality;
+ this.FrameDelay = other.FrameDelay;
+
+ // Copy the pixels.
+ this.Pixels = new TPacked[this.Width * this.Height];
+ Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length);
+ }
+
+ ///
+ /// Gets the pixels as an array of the given packed pixel format.
+ ///
+ public TPacked[] Pixels { get; private set; }
+
+ ///
+ /// Gets the width in pixels.
+ ///
+ public int Width { get; private set; }
+
+ ///
+ /// Gets the height in pixels.
+ ///
+ public int Height { get; private set; }
+
+ ///
+ /// Gets the pixel ratio made up of the width and height.
+ ///
+ public double PixelRatio => (double)this.Width / this.Height;
+
+ ///
+ /// Gets the representing the bounds of the image.
+ ///
+ // public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
+
+ ///
+ /// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
+ ///
+ public int Quality { get; set; }
+
+ ///
+ /// Gets or sets the frame delay for animated images.
+ /// If not 0, this field specifies the number of hundredths (1/100) of a second to
+ /// wait before continuing with the processing of the Data Stream.
+ /// The clock starts ticking immediately after the graphic is rendered.
+ ///
+ public int FrameDelay { get; set; }
+
+ ///
+ /// Sets the pixel array of the image to the given value.
+ ///
+ /// The new width of the image. Must be greater than zero.
+ /// The new height of the image. Must be greater than zero.
+ ///
+ /// The array with colors. Must be a multiple of the width and height.
+ ///
+ ///
+ /// Thrown if either or are less than or equal to 0.
+ ///
+ ///
+ /// Thrown if the length is not equal to Width * Height.
+ ///
+ public void SetPixels(int width, int height, TPacked[] pixels)
+ {
+ if (width <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
+ }
+
+ if (height <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
+ }
+
+ if (pixels.Length != width * height)
+ {
+ throw new ArgumentException("Pixel array must have the length of Width * Height.");
+ }
+
+ this.Width = width;
+ this.Height = height;
+ this.Pixels = pixels;
+ }
+
+ ///
+ /// Sets the pixel array of the image to the given value, creating a copy of
+ /// the original pixels.
+ ///
+ /// The new width of the image. Must be greater than zero.
+ /// The new height of the image. Must be greater than zero.
+ ///
+ /// The array with colors. Must be a multiple of four times the width and height.
+ ///
+ ///
+ /// Thrown if either or are less than or equal to 0.
+ ///
+ ///
+ /// Thrown if the length is not equal to Width * Height.
+ ///
+ public void ClonePixels(int width, int height, TPacked[] pixels)
+ {
+ if (width <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
+ }
+
+ if (height <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
+ }
+
+ if (pixels.Length != width * height)
+ {
+ throw new ArgumentException("Pixel array must have the length of Width * Height.");
+ }
+
+ this.Width = width;
+ this.Height = height;
+
+ // Copy the pixels.
+ this.Pixels = new TPacked[pixels.Length];
+ Array.Copy(pixels, this.Pixels, pixels.Length);
+ }
+
+ ///
+ /// Locks the image providing access to the pixels.
+ ///
+ /// It is imperative that the accessor is correctly disposed off after use.
+ ///
+ ///
+ /// The
+ public abstract IPixelAccessor Lock();
+ }
+}
diff --git a/GenericImage/ImageRgba32.cs b/GenericImage/ImageRgba32.cs
index 8ec301c2e..c5a48d0f9 100644
--- a/GenericImage/ImageRgba32.cs
+++ b/GenericImage/ImageRgba32.cs
@@ -2,22 +2,10 @@
{
using GenericImage.PackedVectors;
- public class ImageRgba32 : IImageBase
+ public class ImageRgba32 : ImageBase
{
- public ImageRgba32(int width, int height)
- {
- this.Width = width;
- this.Height = height;
- this.Pixels = new Rgba32[width * height];
- }
-
- public Rgba32[] Pixels { get; }
-
- public int Width { get; }
-
- public int Height { get; }
-
- public IPixelAccessor Lock()
+ ///
+ public override IPixelAccessor Lock()
{
return new PixelAccessorRgba32(this);
}
diff --git a/GenericImage/PackedVectors/Rgba64.cs b/GenericImage/PackedVectors/Rgba64.cs
index 53b01d1c3..f62eeb4de 100644
--- a/GenericImage/PackedVectors/Rgba64.cs
+++ b/GenericImage/PackedVectors/Rgba64.cs
@@ -140,9 +140,9 @@
///
private static ulong Pack(ref Vector4 vector)
{
- return ((ulong)Math.Round(vector.Z) << 32) |
+ return ((ulong)Math.Round(vector.X) << 32) |
((ulong)Math.Round(vector.Y) << 16) |
- (ulong)Math.Round(vector.X) |
+ (ulong)Math.Round(vector.Z) |
((ulong)Math.Round(vector.W) << 48);
}
diff --git a/GenericImage/PixelAccessorRgba32.cs b/GenericImage/PixelAccessorRgba32.cs
index 3af32aae2..25b49ea21 100644
--- a/GenericImage/PixelAccessorRgba32.cs
+++ b/GenericImage/PixelAccessorRgba32.cs
@@ -39,7 +39,7 @@
///
/// The image to provide pixel access for.
///
- public PixelAccessorRgba32(IImageBase image)
+ public PixelAccessorRgba32(ImageBase image)
{
//Guard.NotNull(image, nameof(image));
//Guard.MustBeGreaterThan(image.Width, 0, "image width");
diff --git a/src/ImageProcessorCore/Colors/Colorspaces/Bgra32.cs b/src/ImageProcessorCore/Colors/Colorspaces/Bgra32.cs
index e31d693bb..cc4fad541 100644
--- a/src/ImageProcessorCore/Colors/Colorspaces/Bgra32.cs
+++ b/src/ImageProcessorCore/Colors/Colorspaces/Bgra32.cs
@@ -1,168 +1,168 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageProcessorCore
-{
- using System;
- using System.ComponentModel;
- using System.Numerics;
-
- ///
- /// Represents an BGRA (blue, green, red, alpha) color.
- ///
- public struct Bgra32 : IEquatable
- {
- ///
- /// Represents a 32 bit that has B, G, R, and A values set to zero.
- ///
- public static readonly Bgra32 Empty = default(Bgra32);
-
- ///
- /// The backing vector for SIMD support.
- ///
- private Vector4 backingVector;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The blue component of this .
- /// The green component of this .
- /// The red component of this .
- /// The alpha component of this .
- public Bgra32(byte b, byte g, byte r, byte a = 255)
- : this()
- {
- this.backingVector = Vector4.Clamp(new Vector4(b, g, r, a), Vector4.Zero, new Vector4(255));
- }
-
- ///
- /// Gets the blue component of the color
- ///
- public byte B => (byte)this.backingVector.X;
-
- ///
- /// Gets the green component of the color
- ///
- public byte G => (byte)this.backingVector.Y;
-
- ///
- /// Gets the red component of the color
- ///
- public byte R => (byte)this.backingVector.Z;
-
- ///
- /// Gets the alpha component of the color
- ///
- public byte A => (byte)this.backingVector.W;
-
- ///
- /// Gets the integer representation of the color.
- ///
- public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24);
-
- ///
- /// Gets a value indicating whether this is empty.
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => this.Equals(Empty);
-
- ///
- /// Allows the implicit conversion of an instance of to a
- /// .
- ///
- ///
- /// The instance of to convert.
- ///
- ///
- /// An instance of .
- ///
- public static implicit operator Bgra32(Color color)
- {
- color = color.Limited * 255f;
- return new Bgra32((byte)color.B, (byte)color.G, (byte)color.R, (byte)color.A);
- }
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the current left is equal to the parameter; otherwise, false.
- ///
- public static bool operator ==(Bgra32 left, Bgra32 right)
- {
- return left.Equals(right);
- }
-
- ///
- /// Compares two objects for inequality.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the current left is unequal to the parameter; otherwise, false.
- ///
- public static bool operator !=(Bgra32 left, Bgra32 right)
- {
- return !left.Equals(right);
- }
-
- ///
- public override bool Equals(object obj)
- {
- if (obj is Bgra32)
- {
- Bgra32 color = (Bgra32)obj;
-
- return this.backingVector == color.backingVector;
- }
-
- return false;
- }
-
- ///
- public override int GetHashCode()
- {
- return GetHashCode(this);
- }
-
- ///
- public override string ToString()
- {
- if (this.IsEmpty)
- {
- return "Bgra32 [ Empty ]";
- }
-
- return $"Bgra32 [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]";
- }
-
- ///
- public bool Equals(Bgra32 other)
- {
- return this.backingVector.Equals(other.backingVector);
- }
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode();
- }
-}
+////
+//// Copyright (c) James Jackson-South and contributors.
+//// Licensed under the Apache License, Version 2.0.
+////
+
+//namespace ImageProcessorCore
+//{
+// using System;
+// using System.ComponentModel;
+// using System.Numerics;
+
+// ///
+// /// Represents an BGRA (blue, green, red, alpha) color.
+// ///
+// public struct Bgra32 : IEquatable
+// {
+// ///
+// /// Represents a 32 bit that has B, G, R, and A values set to zero.
+// ///
+// public static readonly Bgra32 Empty = default(Bgra32);
+
+// ///
+// /// The backing vector for SIMD support.
+// ///
+// private Vector4 backingVector;
+
+// ///
+// /// Initializes a new instance of the struct.
+// ///
+// /// The blue component of this .
+// /// The green component of this .
+// /// The red component of this .
+// /// The alpha component of this .
+// public Bgra32(byte b, byte g, byte r, byte a = 255)
+// : this()
+// {
+// this.backingVector = Vector4.Clamp(new Vector4(b, g, r, a), Vector4.Zero, new Vector4(255));
+// }
+
+// ///
+// /// Gets the blue component of the color
+// ///
+// public byte B => (byte)this.backingVector.X;
+
+// ///
+// /// Gets the green component of the color
+// ///
+// public byte G => (byte)this.backingVector.Y;
+
+// ///
+// /// Gets the red component of the color
+// ///
+// public byte R => (byte)this.backingVector.Z;
+
+// ///
+// /// Gets the alpha component of the color
+// ///
+// public byte A => (byte)this.backingVector.W;
+
+// ///
+// /// Gets the integer representation of the color.
+// ///
+// public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24);
+
+// ///
+// /// Gets a value indicating whether this is empty.
+// ///
+// [EditorBrowsable(EditorBrowsableState.Never)]
+// public bool IsEmpty => this.Equals(Empty);
+
+// ///
+// /// Allows the implicit conversion of an instance of to a
+// /// .
+// ///
+// ///
+// /// The instance of to convert.
+// ///
+// ///
+// /// An instance of .
+// ///
+// public static implicit operator Bgra32(Color color)
+// {
+// color = color.Limited * 255f;
+// return new Bgra32((byte)color.B, (byte)color.G, (byte)color.R, (byte)color.A);
+// }
+
+// ///
+// /// Compares two objects for equality.
+// ///
+// ///
+// /// The on the left side of the operand.
+// ///
+// ///
+// /// The on the right side of the operand.
+// ///
+// ///
+// /// True if the current left is equal to the parameter; otherwise, false.
+// ///
+// public static bool operator ==(Bgra32 left, Bgra32 right)
+// {
+// return left.Equals(right);
+// }
+
+// ///
+// /// Compares two objects for inequality.
+// ///
+// ///
+// /// The on the left side of the operand.
+// ///
+// ///
+// /// The on the right side of the operand.
+// ///
+// ///
+// /// True if the current left is unequal to the parameter; otherwise, false.
+// ///
+// public static bool operator !=(Bgra32 left, Bgra32 right)
+// {
+// return !left.Equals(right);
+// }
+
+// ///
+// public override bool Equals(object obj)
+// {
+// if (obj is Bgra32)
+// {
+// Bgra32 color = (Bgra32)obj;
+
+// return this.backingVector == color.backingVector;
+// }
+
+// return false;
+// }
+
+// ///
+// public override int GetHashCode()
+// {
+// return GetHashCode(this);
+// }
+
+// ///
+// public override string ToString()
+// {
+// if (this.IsEmpty)
+// {
+// return "Bgra32 [ Empty ]";
+// }
+
+// return $"Bgra32 [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]";
+// }
+
+// ///
+// public bool Equals(Bgra32 other)
+// {
+// return this.backingVector.Equals(other.backingVector);
+// }
+
+// ///
+// /// Returns the hash code for this instance.
+// ///
+// ///
+// /// The instance of to return the hash code for.
+// ///
+// ///
+// /// A 32-bit signed integer that is the hash code for this instance.
+// ///
+// private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode();
+// }
+//}
diff --git a/src/ImageProcessorCore/GenericImageFrame.cs b/src/ImageProcessorCore/GenericImageFrame.cs
new file mode 100644
index 000000000..b0dbe341c
--- /dev/null
+++ b/src/ImageProcessorCore/GenericImageFrame.cs
@@ -0,0 +1,40 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using System;
+
+ ///
+ /// Represents a single frame in a animation.
+ ///
+ ///
+ /// The packed vector pixels format.
+ ///
+ public abstract class GenericImageFrame : ImageBase, IImageFrame
+ where TPacked : IPackedVector
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected GenericImageFrame()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The other to create this instance from.
+ ///
+ ///
+ /// Thrown if the given is null.
+ ///
+ protected GenericImageFrame(GenericImageFrame other)
+ : base(other)
+ {
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/IImageBase.cs b/src/ImageProcessorCore/IImageBase.cs
new file mode 100644
index 000000000..85ffa0086
--- /dev/null
+++ b/src/ImageProcessorCore/IImageBase.cs
@@ -0,0 +1,18 @@
+namespace ImageProcessorCore
+{
+ public interface IImageBase
+ where TPacked : IPackedVector
+ {
+ Rectangle Bounds { get; }
+ int FrameDelay { get; set; }
+ int Height { get; }
+ double PixelRatio { get; }
+ TPacked[] Pixels { get; }
+ int Quality { get; set; }
+ int Width { get; }
+
+ void ClonePixels(int width, int height, TPacked[] pixels);
+ IPixelAccessor Lock();
+ void SetPixels(int width, int height, TPacked[] pixels);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessorCore/IImageFrame.cs b/src/ImageProcessorCore/IImageFrame.cs
new file mode 100644
index 000000000..1565879a7
--- /dev/null
+++ b/src/ImageProcessorCore/IImageFrame.cs
@@ -0,0 +1,7 @@
+namespace ImageProcessorCore
+{
+ public interface IImageFrame : IImageBase
+ where TPacked : IPackedVector
+ {
+ }
+}
diff --git a/src/ImageProcessorCore/IPixelAccessor.cs b/src/ImageProcessorCore/IPixelAccessor.cs
new file mode 100644
index 000000000..10838726c
--- /dev/null
+++ b/src/ImageProcessorCore/IPixelAccessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using System;
+
+ ///
+ /// Encapsulates properties to provides per-pixel access to an images pixels.
+ ///
+ public interface IPixelAccessor : IDisposable
+ {
+ ///
+ /// Gets or sets the pixel at the specified position.
+ ///
+ ///
+ /// The x-coordinate of the pixel. Must be greater
+ /// than zero and smaller than the width of the pixel.
+ ///
+ ///
+ /// The y-coordinate of the pixel. Must be greater
+ /// than zero and smaller than the width of the pixel.
+ ///
+ /// The at the specified position.
+ IPackedVector this[int x, int y]
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Image.cs b/src/ImageProcessorCore/Image.cs
index 3cde69543..88d4d1b6c 100644
--- a/src/ImageProcessorCore/Image.cs
+++ b/src/ImageProcessorCore/Image.cs
@@ -1,61 +1,19 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageProcessorCore
+namespace ImageProcessorCore
{
using System;
- using System.Collections.Generic;
using System.Diagnostics;
- using System.IO;
using System.Linq;
- using System.Text;
-
- using Formats;
///
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
///
///
- /// The image data is always stored in RGBA format, where the red, green, blue, and
- /// alpha values are floating point numbers.
+ /// The image data is always stored in format, where the blue, green, red, and
+ /// alpha values are 8 bit unsigned bytes.
///
[DebuggerDisplay("Image: {Width}x{Height}")]
- public class Image : ImageBase
+ public class Image : GenericImage
{
- ///
- /// The default horizontal resolution value (dots per inch) in x direction.
- /// The default value is 96 dots per inch.
- ///
- public const double DefaultHorizontalResolution = 96;
-
- ///
- /// The default vertical resolution value (dots per inch) in y direction.
- /// The default value is 96 dots per inch.
- ///
- public const double DefaultVerticalResolution = 96;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public Image()
- {
- this.CurrentImageFormat = Bootstrapper.Instance.ImageFormats.First(f => f.GetType() == typeof(PngFormat));
- }
-
- ///
- /// Initializes a new instance of the class
- /// with the height and the width of the image.
- ///
- /// The width of the image in pixels.
- /// The height of the image in pixels.
- public Image(int width, int height)
- : base(width, height)
- {
- this.CurrentImageFormat = Bootstrapper.Instance.ImageFormats.First(f => f.GetType() == typeof(PngFormat));
- }
-
///
/// Initializes a new instance of the class
/// by making a copy from another image.
@@ -63,9 +21,9 @@ namespace ImageProcessorCore
/// The other image, where the clone should be made from.
/// is null.
public Image(Image other)
- : base(other)
{
- foreach (ImageFrame frame in other.Frames)
+ // TODO: Check this. Not sure why I was getting a cast warning.
+ foreach (ImageFrame frame in other.Frames.Cast())
{
if (frame != null)
{
@@ -79,209 +37,10 @@ namespace ImageProcessorCore
this.CurrentImageFormat = other.CurrentImageFormat;
}
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The stream containing image information.
- ///
- /// Thrown if the is null.
- public Image(Stream stream)
- {
- Guard.NotNull(stream, nameof(stream));
- this.Load(stream);
- }
-
- ///
- /// Gets a list of supported image formats.
- ///
- public IReadOnlyCollection Formats { get; } = Bootstrapper.Instance.ImageFormats;
-
- ///
- /// Gets or sets the resolution of the image in x- direction. It is defined as
- /// number of dots per inch and should be an positive value.
- ///
- /// The density of the image in x- direction.
- public double HorizontalResolution { get; set; } = DefaultHorizontalResolution;
-
- ///
- /// Gets or sets the resolution of the image in y- direction. It is defined as
- /// number of dots per inch and should be an positive value.
- ///
- /// The density of the image in y- direction.
- public double VerticalResolution { get; set; } = DefaultVerticalResolution;
-
- ///
- /// Gets the width of the image in inches. It is calculated as the width of the image
- /// in pixels multiplied with the density. When the density is equals or less than zero
- /// the default value is used.
- ///
- /// The width of the image in inches.
- public double InchWidth
- {
- get
- {
- double resolution = this.HorizontalResolution;
-
- if (resolution <= 0)
- {
- resolution = DefaultHorizontalResolution;
- }
-
- return this.Width / resolution;
- }
- }
-
- ///
- /// Gets the height of the image in inches. It is calculated as the height of the image
- /// in pixels multiplied with the density. When the density is equals or less than zero
- /// the default value is used.
- ///
- /// The height of the image in inches.
- public double InchHeight
- {
- get
- {
- double resolution = this.VerticalResolution;
-
- if (resolution <= 0)
- {
- resolution = DefaultVerticalResolution;
- }
-
- return this.Height / resolution;
- }
- }
-
- ///
- /// Gets a value indicating whether this image is animated.
- ///
- ///
- /// True if this image is animated; otherwise, false.
- ///
- public bool IsAnimated => this.Frames.Count > 0;
-
- ///
- /// Gets or sets the number of times any animation is repeated.
- /// 0 means to repeat indefinitely.
- ///
- public ushort RepeatCount { get; set; }
-
- ///
- /// Gets the other frames for the animation.
- ///
- /// The list of frame images.
- public IList Frames { get; } = new List();
-
- ///
- /// Gets the list of properties for storing meta information about this image.
- ///
- /// A list of image properties.
- public IList Properties { get; } = new List();
-
- ///
- /// Gets the currently loaded image format.
- ///
- public IImageFormat CurrentImageFormat { get; internal set; }
-
- ///
- /// Saves the image to the given stream using the currently loaded image format.
- ///
- /// The stream to save the image to.
- /// Thrown if the stream is null.
- public void Save(Stream stream)
- {
- Guard.NotNull(stream, nameof(stream));
- this.CurrentImageFormat.Encoder.Encode(this, stream);
- }
-
- ///
- /// Saves the image to the given stream using the given image format.
- ///
- /// The stream to save the image to.
- /// The format to save the image as.
- /// Thrown if the stream is null.
- public void Save(Stream stream, IImageFormat format)
- {
- Guard.NotNull(stream, nameof(stream));
- format.Encoder.Encode(this, stream);
- }
-
- ///
- /// Saves the image to the given stream using the given image encoder.
- ///
- /// The stream to save the image to.
- /// The encoder to save the image with.
- /// Thrown if the stream is null.
- public void Save(Stream stream, IImageEncoder encoder)
- {
- Guard.NotNull(stream, nameof(stream));
- encoder.Encode(this, stream);
- }
-
- ///
- /// Returns a Base64 encoded string from the given image.
- ///
- /// data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA==
- /// The
- public override string ToString()
- {
- using (MemoryStream stream = new MemoryStream())
- {
- this.Save(stream);
- stream.Flush();
- return $"data:{this.CurrentImageFormat.Encoder.MimeType};base64,{Convert.ToBase64String(stream.ToArray())}";
- }
- }
-
- ///
- /// Loads the image from the given stream.
- ///
- /// The stream containing image information.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
- private void Load(Stream stream)
+ ///
+ public override IPixelAccessor Lock()
{
- if (!this.Formats.Any()) { return; }
-
- if (!stream.CanRead)
- {
- throw new NotSupportedException("Cannot read from the stream.");
- }
-
- if (!stream.CanSeek)
- {
- throw new NotSupportedException("The stream does not support seeking.");
- }
-
- int maxHeaderSize = this.Formats.Max(x => x.Decoder.HeaderSize);
- if (maxHeaderSize > 0)
- {
- byte[] header = new byte[maxHeaderSize];
-
- stream.Position = 0;
- stream.Read(header, 0, maxHeaderSize);
- stream.Position = 0;
-
- IImageFormat format = this.Formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header));
- if (format != null)
- {
- format.Decoder.Decode(this, stream);
- this.CurrentImageFormat = format;
- return;
- }
- }
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
-
- foreach (IImageFormat format in this.Formats)
- {
- stringBuilder.AppendLine("-" + format);
- }
-
- throw new NotSupportedException(stringBuilder.ToString());
+ return new PixelAccessor(this);
}
}
}
diff --git a/src/ImageProcessorCore/ImageBase.cs b/src/ImageProcessorCore/ImageBase.cs
index 5b7ab65f8..4dcb73174 100644
--- a/src/ImageProcessorCore/ImageBase.cs
+++ b/src/ImageProcessorCore/ImageBase.cs
@@ -8,25 +8,24 @@ namespace ImageProcessorCore
using System;
///
- /// The base class of all images. Encapsulates the basic properties and methods
- /// required to manipulate images.
+ /// The base class of all images. Encapsulates the basic properties and methods required to manipulate images
+ /// in different pixel formats.
///
- public abstract class ImageBase
+ ///
+ /// The packed vector pixels format.
+ ///
+ public abstract class ImageBase : IImageBase
+ where TPacked : IPackedVector
{
///
- /// The array of pixels.
- ///
- private float[] pixelsArray;
-
- ///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
protected ImageBase()
{
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The width of the image in pixels.
/// The height of the image in pixels.
@@ -40,21 +39,19 @@ namespace ImageProcessorCore
this.Width = width;
this.Height = height;
-
- // Assign the pointer and pixels.
- this.pixelsArray = new float[width * height * 4];
+ this.Pixels = new TPacked[width * height];
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
///
- /// The other to create this instance from.
+ /// The other to create this instance from.
///
///
- /// Thrown if the given is null.
+ /// Thrown if the given is null.
///
- protected ImageBase(ImageBase other)
+ protected ImageBase(ImageBase other)
{
Guard.NotNull(other, nameof(other), "Other image cannot be null.");
@@ -64,29 +61,14 @@ namespace ImageProcessorCore
this.FrameDelay = other.FrameDelay;
// Copy the pixels.
- this.pixelsArray = new float[this.Width * this.Height * 4];
- Array.Copy(other.pixelsArray, this.pixelsArray, other.pixelsArray.Length);
+ this.Pixels = new TPacked[this.Width * this.Height];
+ Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length);
}
///
- /// Gets or sets the maximum allowable width in pixels.
- ///
- public static int MaxWidth { get; set; } = int.MaxValue;
-
- ///
- /// Gets or sets the maximum allowable height in pixels.
- ///
- public static int MaxHeight { get; set; } = int.MaxValue;
-
- ///
- /// Gets the image pixels as byte array.
+ /// Gets the pixels as an array of the given packed pixel format.
///
- ///
- /// The returned array has a length of Width * Height * 4 bytes
- /// and stores the red, the green, the blue, and the alpha value for
- /// each pixel in this order.
- ///
- public float[] Pixels => this.pixelsArray;
+ public TPacked[] Pixels { get; private set; }
///
/// Gets the width in pixels.
@@ -127,15 +109,15 @@ namespace ImageProcessorCore
/// The new width of the image. Must be greater than zero.
/// The new height of the image. Must be greater than zero.
///
- /// The array with colors. Must be a multiple of four times the width and height.
+ /// The array with colors. Must be a multiple of the width and height.
///
///
/// Thrown if either or are less than or equal to 0.
///
///
- /// Thrown if the length is not equal to Width * Height * 4.
+ /// Thrown if the length is not equal to Width * Height.
///
- public void SetPixels(int width, int height, float[] pixels)
+ public void SetPixels(int width, int height, TPacked[] pixels)
{
if (width <= 0)
{
@@ -147,14 +129,14 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
}
- if (pixels.Length != width * height * 4)
+ if (pixels.Length != width * height)
{
- throw new ArgumentException("Pixel array must have the length of Width * Height * 4.");
+ throw new ArgumentException("Pixel array must have the length of Width * Height.");
}
this.Width = width;
this.Height = height;
- this.pixelsArray = pixels;
+ this.Pixels = pixels;
}
///
@@ -170,11 +152,11 @@ namespace ImageProcessorCore
/// Thrown if either or are less than or equal to 0.
///
///
- /// Thrown if the length is not equal to Width * Height * 4.
+ /// Thrown if the length is not equal to Width * Height.
///
- public void ClonePixels(int width, int height, float[] pixels)
+ public void ClonePixels(int width, int height, TPacked[] pixels)
{
- if (width <= 0)
+ if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
}
@@ -184,17 +166,17 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
}
- if (pixels.Length != width * height * 4)
+ if (pixels.Length != width * height)
{
- throw new ArgumentException("Pixel array must have the length of Width * Height * 4.");
+ throw new ArgumentException("Pixel array must have the length of Width * Height.");
}
this.Width = width;
this.Height = height;
// Copy the pixels.
- this.pixelsArray = new float[pixels.Length];
- Array.Copy(pixels, this.pixelsArray, pixels.Length);
+ this.Pixels = new TPacked[pixels.Length];
+ Array.Copy(pixels, this.Pixels, pixels.Length);
}
///
@@ -203,10 +185,7 @@ namespace ImageProcessorCore
/// It is imperative that the accessor is correctly disposed off after use.
///
///
- /// The
- public PixelAccessor Lock()
- {
- return new PixelAccessor(this);
- }
+ /// The
+ public abstract IPixelAccessor Lock();
}
}
diff --git a/src/ImageProcessorCore/ImageFrame.cs b/src/ImageProcessorCore/ImageFrame.cs
index e9ff7214b..42cf08da4 100644
--- a/src/ImageProcessorCore/ImageFrame.cs
+++ b/src/ImageProcessorCore/ImageFrame.cs
@@ -1,36 +1,16 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageProcessorCore
+namespace ImageProcessorCore
{
- using System;
-
- ///
- /// Represents a single frame in a animation.
- ///
- public class ImageFrame : ImageBase
+ public class ImageFrame : GenericImageFrame
{
- ///
- /// Initializes a new instance of the class.
- ///
- public ImageFrame()
+ public ImageFrame(ImageFrame frame)
+ : base(frame)
{
}
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The other to create this instance from.
- ///
- ///
- /// Thrown if the given is null.
- ///
- public ImageFrame(ImageFrame other)
- : base(other)
+ ///
+ public override IPixelAccessor Lock()
{
+ return new PixelAccessor(this);
}
}
}
diff --git a/src/ImageProcessorCore/PackedVector/Bgra32.cs b/src/ImageProcessorCore/PackedVector/Bgra32.cs
new file mode 100644
index 000000000..fc6a788b0
--- /dev/null
+++ b/src/ImageProcessorCore/PackedVector/Bgra32.cs
@@ -0,0 +1,168 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using System;
+ using System.Numerics;
+
+ ///
+ /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 1.
+ ///
+ public struct Bgra32 : IPackedVector, IEquatable
+ {
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The blue component.
+ /// The green component.
+ /// The red component.
+ /// The alpha component.
+ public Bgra32(float b, float g, float r, float a)
+ {
+ Vector4 clamped = Vector4.Clamp(new Vector4(b, g, r, a), Vector4.Zero, Vector4.One) * 255f;
+ this.PackedValue = Pack(ref clamped);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector containing the components for the packed vector.
+ ///
+ public Bgra32(Vector4 vector)
+ {
+ Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255f;
+ this.PackedValue = Pack(ref clamped);
+ }
+
+ ///
+ public uint PackedValue { get; set; }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Bgra32 left, Bgra32 right)
+ {
+ return left.PackedValue == right.PackedValue;
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Bgra32 left, Bgra32 right)
+ {
+ return left.PackedValue != right.PackedValue;
+ }
+
+ ///
+ public void PackVector(Vector4 vector)
+ {
+ Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255f;
+ this.PackedValue = Pack(ref clamped);
+ }
+
+ ///
+ public void PackBytes(byte x, byte y, byte z, byte w)
+ {
+ Vector4 vector = new Vector4(x, y, z, w);
+ this.PackedValue = Pack(ref vector);
+ }
+
+ ///
+ public Vector4 ToVector4()
+ {
+ return new Vector4(
+ this.PackedValue & 0xFF,
+ (this.PackedValue >> 8) & 0xFF,
+ (this.PackedValue >> 16) & 0xFF,
+ (this.PackedValue >> 24) & 0xFF) / 255f;
+ }
+
+ ///
+ public byte[] ToBytes()
+ {
+ return new[]
+ {
+ (byte)(this.PackedValue & 0xFF),
+ (byte)((this.PackedValue >> 8) & 0xFF),
+ (byte)((this.PackedValue >> 16) & 0xFF),
+ (byte)((this.PackedValue >> 24) & 0xFF)
+ };
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is Bgra32) && this.Equals((Bgra32)obj);
+ }
+
+ ///
+ public bool Equals(Bgra32 other)
+ {
+ return this.PackedValue == other.PackedValue;
+ }
+
+ ///
+ /// Gets a string representation of the packed vector.
+ ///
+ /// A string representation of the packed vector.
+ public override string ToString()
+ {
+ return this.ToVector4().ToString();
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.GetHashCode(this);
+ }
+
+ ///
+ /// Sets the packed representation from the given component values.
+ ///
+ ///
+ /// The vector containing the components for the packed vector.
+ ///
+ ///
+ /// The .
+ ///
+ private static uint Pack(ref Vector4 vector)
+ {
+ return (uint)Math.Round(vector.X) |
+ ((uint)Math.Round(vector.Y) << 8) |
+ ((uint)Math.Round(vector.Z) << 16) |
+ ((uint)Math.Round(vector.W) << 24);
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private int GetHashCode(Bgra32 packed)
+ {
+ return packed.PackedValue.GetHashCode();
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/PackedVector/IPackedVector.cs b/src/ImageProcessorCore/PackedVector/IPackedVector.cs
new file mode 100644
index 000000000..02e10cc3a
--- /dev/null
+++ b/src/ImageProcessorCore/PackedVector/IPackedVector.cs
@@ -0,0 +1,61 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using System.Numerics;
+
+ ///
+ /// An interface that converts packed vector types to and from values,
+ /// allowing multiple encodings to be manipulated in a generic way.
+ ///
+ ///
+ /// The type of object representing the packed value.
+ ///
+ public interface IPackedVector : IPackedVector
+ where TPacked : struct
+ {
+ ///
+ /// Gets or sets the packed representation of the value.
+ /// Typically packed in least to greatest significance order.
+ ///
+ TPacked PackedValue { get; set; }
+ }
+
+ ///
+ /// An interface that converts packed vector types to and from values.
+ ///
+ public interface IPackedVector
+ {
+ ///
+ /// Sets the packed representation from a .
+ ///
+ /// The vector to pack.
+ void PackVector(Vector4 vector);
+
+ ///
+ /// Sets the packed representation from a .
+ ///
+ /// The x-component.
+ /// The y-component.
+ /// The z-component.
+ /// The w-component.
+ void PackBytes(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.
+ ///
+ /// The .
+ byte[] ToBytes();
+ }
+}
diff --git a/src/ImageProcessorCore/PixelAccessor.cs b/src/ImageProcessorCore/PixelAccessor.cs
index 2553b2536..bf01cb856 100644
--- a/src/ImageProcessorCore/PixelAccessor.cs
+++ b/src/ImageProcessorCore/PixelAccessor.cs
@@ -9,14 +9,14 @@ namespace ImageProcessorCore
using System.Runtime.InteropServices;
///
- /// Provides per-pixel access to an images pixels.
+ /// Provides per-pixel access to an images pixels as 8 bit unsigned components.
///
- public sealed unsafe class PixelAccessor : IDisposable
+ public sealed unsafe class PixelAccessor : IPixelAccessor
{
///
/// The position of the first pixel in the bitmap.
///
- private Color* pixelsBase;
+ private Bgra32* pixelsBase;
///
/// Provides a way to access the pixels from unmanaged memory.
@@ -42,7 +42,7 @@ namespace ImageProcessorCore
///
/// The image to provide pixel access for.
///
- public PixelAccessor(ImageBase image)
+ public PixelAccessor(ImageBase image)
{
Guard.NotNull(image, nameof(image));
Guard.MustBeGreaterThan(image.Width, 0, "image width");
@@ -51,9 +51,8 @@ namespace ImageProcessorCore
this.Width = image.Width;
this.Height = image.Height;
- // Assign the pointer.
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
- this.pixelsBase = (Color*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
+ this.pixelsBase = (Bgra32*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
}
///
@@ -85,8 +84,8 @@ namespace ImageProcessorCore
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
///
- /// The at the specified position.
- public Color this[int x, int y]
+ /// The at the specified position.
+ public IPackedVector this[int x, int y]
{
get
{
@@ -117,7 +116,7 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
- *(this.pixelsBase + ((y * this.Width) + x)) = value;
+ *(this.pixelsBase + ((y * this.Width) + x)) = (Bgra32)value;
}
}
@@ -149,4 +148,4 @@ namespace ImageProcessorCore
GC.SuppressFinalize(this);
}
}
-}
\ No newline at end of file
+}