140 lines
4.5 KiB
C#
140 lines
4.5 KiB
C#
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
|
|
namespace OpenMacroBoard.SDK {
|
|
/// <summary>
|
|
/// Represents a bitmap that can be used as key images
|
|
/// </summary>
|
|
public sealed class KeyBitmap : IEquatable<KeyBitmap>, IKeyBitmapDataAccess {
|
|
/// <summary>
|
|
/// Byte order is B-G-R, and pixels are stored left-to-right and top-to-bottom
|
|
/// </summary>
|
|
private readonly Byte[] rawBitmapData;
|
|
|
|
private Int32? cachedHashCode = null;
|
|
|
|
internal KeyBitmap(Int32 width, Int32 height, Byte[] bitmapData) {
|
|
this.Width = width;
|
|
this.Height = height;
|
|
this.rawBitmapData = bitmapData ?? throw new ArgumentNullException(nameof(bitmapData));
|
|
}
|
|
|
|
/// <summary>
|
|
/// This property can be used to create new KeyBitmaps
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>This property just serves as an anchor point for extension methods
|
|
/// to create new <see cref="KeyBitmap"/> objects</para>
|
|
/// </remarks>
|
|
public static IKeyBitmapFactory Create {
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Solid black bitmap
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>If you need a black bitmap (for example to clear keys) use this property for better performance (in theory ^^)</para>
|
|
/// </remarks>
|
|
public static KeyBitmap Black { get; } = new(1, 1, []);
|
|
|
|
/// <summary>
|
|
/// Gets the width of the bitmap.
|
|
/// </summary>
|
|
public Int32 Width {
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the height of the bitmap.
|
|
/// </summary>
|
|
public Int32 Height {
|
|
get;
|
|
}
|
|
|
|
Boolean IKeyBitmapDataAccess.IsEmpty => this.rawBitmapData.Length == 0;
|
|
|
|
/// <summary>
|
|
/// The == operator
|
|
/// </summary>
|
|
public static Boolean operator ==(KeyBitmap a, KeyBitmap b) => Equals(a, b);
|
|
|
|
/// <summary>
|
|
/// The != operator
|
|
/// </summary>
|
|
public static Boolean operator !=(KeyBitmap a, KeyBitmap b) => !Equals(a, b);
|
|
|
|
/// <summary>
|
|
/// Compares the content of two given <see cref="KeyBitmap"/>s
|
|
/// </summary>
|
|
/// <param name="a">KeyBitmap a</param>
|
|
/// <param name="b">KeyBitmap b</param>
|
|
/// <returns>Returns true of the <see cref="KeyBitmap"/>s are equal and false otherwise.</returns>
|
|
public static Boolean Equals(KeyBitmap a, KeyBitmap b) =>
|
|
ReferenceEquals(a, b) || (
|
|
a is not null &&
|
|
b is not null &&
|
|
a.Width == b.Width &&
|
|
a.Height == b.Height && (
|
|
ReferenceEquals(a.rawBitmapData, b.rawBitmapData) || (
|
|
a.rawBitmapData is not null &&
|
|
b.rawBitmapData is not null &&
|
|
a.rawBitmapData.SequenceEqual(b.rawBitmapData)
|
|
)
|
|
)
|
|
);
|
|
|
|
/// <summary>
|
|
/// Compares the content of this <see cref="KeyBitmap"/> to another KeyBitmap
|
|
/// </summary>
|
|
/// <param name="other">The other <see cref="KeyBitmap"/></param>
|
|
/// <returns>True if both bitmaps are equals and false otherwise.</returns>
|
|
public Boolean Equals(KeyBitmap other) => Equals(this, other);
|
|
|
|
/// <summary>
|
|
/// Compares the content of this <see cref="KeyBitmap"/> to another object
|
|
/// </summary>
|
|
/// <param name="obj">The other object</param>
|
|
/// <returns>Return true if the other object is a <see cref="KeyBitmap"/> and equal to this one. Returns false otherwise.</returns>
|
|
public override Boolean Equals(Object obj) => Equals(this, obj as KeyBitmap);
|
|
|
|
/// <summary>
|
|
/// Get the hash code for this object.
|
|
/// </summary>
|
|
/// <returns>The hash code</returns>
|
|
public override Int32 GetHashCode() => this.cachedHashCode ??= this.CalculateObjectHash();
|
|
|
|
/// <inheritdoc />
|
|
ReadOnlySpan<Byte> IKeyBitmapDataAccess.GetData() => new(this.rawBitmapData);
|
|
|
|
private Int32 CalculateObjectHash() {
|
|
const Int32 initalValue = 17;
|
|
const Int32 primeFactor = 23;
|
|
const Int32 imageSampleSize = 1000;
|
|
|
|
unchecked {
|
|
Int32 hash = initalValue;
|
|
hash = (hash * primeFactor) + this.Width;
|
|
hash = (hash * primeFactor) + this.Height;
|
|
|
|
if(this.rawBitmapData.Length == 0) {
|
|
return hash;
|
|
}
|
|
|
|
Int32 stepSize = 1;
|
|
if(this.rawBitmapData.Length > imageSampleSize) {
|
|
stepSize = this.rawBitmapData.Length / imageSampleSize;
|
|
}
|
|
|
|
for(Int32 i = 0; i < this.rawBitmapData.Length; i += stepSize) {
|
|
hash *= 23;
|
|
hash += this.rawBitmapData[i];
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
}
|
|
}
|
|
}
|