mirror of https://github.com/SixLabors/ImageSharp
Browse Source
# Conflicts: # src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs Former-commit-id: 26e39be6bf1ecab2e27355bd6410856aec4602e5 Former-commit-id: 5b72abf1b293d733850a5c68a0938cd123044a32 Former-commit-id: 20931e70d827c18877d1f5eec290258be6c14fd2pull/1/head
82 changed files with 1087 additions and 915 deletions
@ -1 +1 @@ |
|||
508fcf1910c42f4e080fcfd9c9f22ba724c1990c |
|||
c27ddeac481f3a9631dcb53248a98676c8016f42 |
|||
@ -0,0 +1,219 @@ |
|||
// <copyright file="Rational.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.Text; |
|||
|
|||
internal struct BigRational : IEquatable<BigRational> |
|||
{ |
|||
private bool IsIndeterminate |
|||
{ |
|||
get |
|||
{ |
|||
if (Denominator != 0) |
|||
return false; |
|||
|
|||
return Numerator == 0; |
|||
} |
|||
} |
|||
|
|||
private bool IsInteger => Denominator == 1; |
|||
|
|||
private bool IsNegativeInfinity |
|||
{ |
|||
get |
|||
{ |
|||
if (Denominator != 0) |
|||
return false; |
|||
|
|||
return Numerator == -1; |
|||
} |
|||
} |
|||
|
|||
private bool IsPositiveInfinity |
|||
{ |
|||
get |
|||
{ |
|||
if (Denominator != 0) |
|||
return false; |
|||
|
|||
return Numerator == 1; |
|||
} |
|||
} |
|||
|
|||
private bool IsZero |
|||
{ |
|||
get |
|||
{ |
|||
if (Denominator != 1) |
|||
return false; |
|||
|
|||
return Numerator == 0; |
|||
} |
|||
} |
|||
|
|||
private static long GreatestCommonDivisor(long a, long b) |
|||
{ |
|||
return b == 0 ? a : GreatestCommonDivisor(b, a % b); |
|||
} |
|||
|
|||
private void Simplify() |
|||
{ |
|||
if (IsIndeterminate) |
|||
return; |
|||
|
|||
if (IsNegativeInfinity) |
|||
return; |
|||
|
|||
if (IsPositiveInfinity) |
|||
return; |
|||
|
|||
if (IsInteger) |
|||
return; |
|||
|
|||
if (IsZero) |
|||
return; |
|||
|
|||
if (Numerator == 0) |
|||
{ |
|||
Denominator = 0; |
|||
return; |
|||
} |
|||
|
|||
if (Numerator == Denominator) |
|||
{ |
|||
Numerator = 1; |
|||
Denominator = 1; |
|||
} |
|||
|
|||
long gcd = GreatestCommonDivisor(Math.Abs(Numerator), Math.Abs(Denominator)); |
|||
if (gcd > 1) |
|||
{ |
|||
Numerator = Numerator / gcd; |
|||
Denominator = Denominator / gcd; |
|||
} |
|||
} |
|||
|
|||
public BigRational(long numerator, long denominator) |
|||
: this(numerator, denominator, false) |
|||
{ |
|||
} |
|||
|
|||
public BigRational(long numerator, long denominator, bool simplify) |
|||
{ |
|||
Numerator = numerator; |
|||
Denominator = denominator; |
|||
|
|||
if (simplify) |
|||
Simplify(); |
|||
} |
|||
|
|||
public BigRational(double value, bool bestPrecision) |
|||
{ |
|||
if (double.IsNaN(value)) |
|||
{ |
|||
Numerator = Denominator = 0; |
|||
return; |
|||
} |
|||
|
|||
if (double.IsPositiveInfinity(value)) |
|||
{ |
|||
Numerator = 1; |
|||
Denominator = 0; |
|||
return; |
|||
} |
|||
|
|||
if (double.IsNegativeInfinity(value)) |
|||
{ |
|||
Numerator = -1; |
|||
Denominator = 0; |
|||
return; |
|||
} |
|||
|
|||
Numerator = 1; |
|||
Denominator = 1; |
|||
|
|||
double val = Math.Abs(value); |
|||
double df = Numerator / Denominator; |
|||
double epsilon = bestPrecision ? double.Epsilon : .000001; |
|||
|
|||
while (Math.Abs(df - val) > epsilon) |
|||
{ |
|||
if (df < val) |
|||
Numerator++; |
|||
else |
|||
{ |
|||
Denominator++; |
|||
Numerator = (int)(val * Denominator); |
|||
} |
|||
|
|||
df = Numerator / (double)Denominator; |
|||
} |
|||
|
|||
if (value < 0.0) |
|||
Numerator *= -1; |
|||
|
|||
Simplify(); |
|||
} |
|||
|
|||
public long Denominator |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public long Numerator |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public bool Equals(BigRational other) |
|||
{ |
|||
if (Denominator == other.Denominator) |
|||
return Numerator == other.Numerator; |
|||
|
|||
if (Numerator == 0 && Denominator == 0) |
|||
return other.Numerator == 0 && other.Denominator == 0; |
|||
|
|||
if (other.Numerator == 0 && other.Denominator == 0) |
|||
return Numerator == 0 && Denominator == 0; |
|||
|
|||
return (Numerator * other.Denominator) == (Denominator * other.Numerator); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
return ((Numerator * 397) ^ Denominator).GetHashCode(); |
|||
} |
|||
|
|||
public string ToString(IFormatProvider provider) |
|||
{ |
|||
if (IsIndeterminate) |
|||
return "[ Indeterminate ]"; |
|||
|
|||
if (IsPositiveInfinity) |
|||
return "[ PositiveInfinity ]"; |
|||
|
|||
if (IsNegativeInfinity) |
|||
return "[ NegativeInfinity ]"; |
|||
|
|||
if (IsZero) |
|||
return "0"; |
|||
|
|||
if (IsInteger) |
|||
return Numerator.ToString(provider); |
|||
|
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.Append(Numerator.ToString(provider)); |
|||
sb.Append("/"); |
|||
sb.Append(Denominator.ToString(provider)); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,199 @@ |
|||
// <copyright file="Rational.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.Globalization; |
|||
|
|||
/// <summary>
|
|||
/// Represents a number that can be expressed as a fraction.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This is a very simplified implimentation of a rational number designed for use with metadata only.
|
|||
/// </remarks>
|
|||
public struct SignedRational : IEquatable<SignedRational> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
|
|||
/// </summary>
|
|||
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
|
|||
public SignedRational(double value) |
|||
: this(value, false) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
|
|||
/// </summary>
|
|||
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
|
|||
///<param name="bestPrecision">Specifies if the instance should be created with the best precision possible.</param>
|
|||
public SignedRational(double value, bool bestPrecision) |
|||
{ |
|||
BigRational rational = new BigRational(value, bestPrecision); |
|||
|
|||
Numerator = (int)rational.Numerator; |
|||
Denominator = (int)rational.Denominator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="value">The integer to create the rational from.</param>
|
|||
public SignedRational(int value) |
|||
: this(value, 1) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
|
|||
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
|
|||
public SignedRational(int numerator, int denominator) |
|||
: this(numerator, denominator, true) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
|
|||
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
|
|||
/// <param name="simplify">Specified if the rational should be simplified.</param>
|
|||
public SignedRational(int numerator, int denominator, bool simplify) |
|||
{ |
|||
BigRational rational = new BigRational(numerator, denominator, simplify); |
|||
|
|||
Numerator = (int)rational.Numerator; |
|||
Denominator = (int)rational.Denominator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the specified <see cref="SignedRational"/> instances are considered equal.
|
|||
/// </summary>
|
|||
/// <param name="left">The first <see cref="SignedRational"/> to compare.</param>
|
|||
/// <param name="right"> The second <see cref="SignedRational"/> to compare.</param>
|
|||
/// <returns></returns>
|
|||
public static bool operator ==(SignedRational left, SignedRational right) |
|||
{ |
|||
return Equals(left, right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the specified <see cref="SignedRational"/> instances are not considered equal.
|
|||
/// </summary>
|
|||
/// <param name="left">The first <see cref="SignedRational"/> to compare.</param>
|
|||
/// <param name="right"> The second <see cref="SignedRational"/> to compare.</param>
|
|||
/// <returns></returns>
|
|||
public static bool operator !=(SignedRational left, SignedRational right) |
|||
{ |
|||
return !Equals(left, right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the numerator of a number.
|
|||
/// </summary>
|
|||
public int Numerator |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the denominator of a number.
|
|||
/// </summary>
|
|||
public int Denominator |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
///<summary>
|
|||
/// Determines whether the specified <see cref="object"/> is equal to this <see cref="SignedRational"/>.
|
|||
///</summary>
|
|||
///<param name="obj">The <see cref="object"/> to compare this <see cref="SignedRational"/> with.</param>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is SignedRational) |
|||
return Equals((SignedRational)obj); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
///<summary>
|
|||
/// Determines whether the specified <see cref="SignedRational"/> is equal to this <see cref="SignedRational"/>.
|
|||
///</summary>
|
|||
///<param name="other">The <see cref="SignedRational"/> to compare this <see cref="SignedRational"/> with.</param>
|
|||
public bool Equals(SignedRational other) |
|||
{ |
|||
BigRational left = new BigRational(Numerator, Denominator); |
|||
BigRational right = new BigRational(other.Numerator, other.Denominator); |
|||
|
|||
return left.Equals(right); |
|||
} |
|||
|
|||
///<summary>
|
|||
/// Converts the specified <see cref="double"/> to an instance of this type.
|
|||
///</summary>
|
|||
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
|
|||
public static SignedRational FromDouble(double value) |
|||
{ |
|||
return new SignedRational(value, false); |
|||
} |
|||
|
|||
///<summary>
|
|||
/// Converts the specified <see cref="double"/> to an instance of this type.
|
|||
///</summary>
|
|||
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
|
|||
///<param name="bestPrecision">Specifies if the instance should be created with the best precision possible.</param>
|
|||
public static SignedRational FromDouble(double value, bool bestPrecision) |
|||
{ |
|||
return new SignedRational(value, bestPrecision); |
|||
} |
|||
|
|||
///<summary>
|
|||
/// Serves as a hash of this type.
|
|||
///</summary>
|
|||
public override int GetHashCode() |
|||
{ |
|||
BigRational self = new BigRational(Numerator, Denominator); |
|||
return self.GetHashCode(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts a rational number to the nearest <see cref="double"/>.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The <see cref="double"/>.
|
|||
/// </returns>
|
|||
public double ToDouble() |
|||
{ |
|||
return Numerator / (double)Denominator; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the numeric value of this instance to its equivalent string representation.
|
|||
/// </summary>
|
|||
public override string ToString() |
|||
{ |
|||
return ToString(CultureInfo.InvariantCulture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the numeric value of this instance to its equivalent string representation using
|
|||
/// the specified culture-specific format information.
|
|||
/// </summary>
|
|||
/// <param name="provider">
|
|||
/// An object that supplies culture-specific formatting information.
|
|||
/// </param>
|
|||
/// <returns></returns>
|
|||
public string ToString(IFormatProvider provider) |
|||
{ |
|||
BigRational rational = new BigRational(Numerator, Denominator); |
|||
return rational.ToString(provider); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
namespace ImageProcessorCore.Benchmarks.Image |
|||
{ |
|||
using System.Threading.Tasks; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using CoreColor = ImageProcessorCore.Color; |
|||
using CoreImage = ImageProcessorCore.Image; |
|||
|
|||
public class CopyPixels |
|||
{ |
|||
[Benchmark(Description = "Copy by Pixel")] |
|||
public CoreColor CopyByPixel() |
|||
{ |
|||
CoreImage source = new CoreImage(1024, 768); |
|||
CoreImage target = new CoreImage(1024, 768); |
|||
using (PixelAccessor<CoreColor, uint> sourcePixels = source.Lock()) |
|||
using (PixelAccessor<CoreColor, uint> targetPixels = target.Lock()) |
|||
{ |
|||
Parallel.For( |
|||
0, |
|||
source.Height, |
|||
Bootstrapper.Instance.ParallelOptions, |
|||
y => |
|||
{ |
|||
for (int x = 0; x < source.Width; x++) |
|||
{ |
|||
targetPixels[x, y] = sourcePixels[x, y]; |
|||
} |
|||
}); |
|||
|
|||
return targetPixels[0, 0]; |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Copy by Row")] |
|||
public CoreColor CopyByRow() |
|||
{ |
|||
CoreImage source = new CoreImage(1024, 768); |
|||
CoreImage target = new CoreImage(1024, 768); |
|||
using (PixelAccessor<CoreColor, uint> sourcePixels = source.Lock()) |
|||
using (PixelAccessor<CoreColor, uint> targetPixels = target.Lock()) |
|||
{ |
|||
Parallel.For( |
|||
0, |
|||
source.Height, |
|||
Bootstrapper.Instance.ParallelOptions, |
|||
y => |
|||
{ |
|||
sourcePixels.CopyRow(0, y, targetPixels, 0, y, source.Width); |
|||
}); |
|||
|
|||
return targetPixels[0, 0]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,122 @@ |
|||
// <copyright file="RationalTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Tests |
|||
{ |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Tests the <see cref="SignedRational"/> struct.
|
|||
/// </summary>
|
|||
public class SignedRationalTests |
|||
{ |
|||
/// <summary>
|
|||
/// Tests the equality operators for equality.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void AreEqual() |
|||
{ |
|||
SignedRational r1 = new SignedRational(3, 2); |
|||
SignedRational r2 = new SignedRational(3, 2); |
|||
|
|||
Assert.Equal(r1, r2); |
|||
Assert.True(r1 == r2); |
|||
|
|||
SignedRational r3 = new SignedRational(7.55); |
|||
SignedRational r4 = new SignedRational(755, 100); |
|||
SignedRational r5 = new SignedRational(151, 20); |
|||
|
|||
Assert.Equal(r3, r4); |
|||
Assert.Equal(r4, r5); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the equality operators for inequality.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void AreNotEqual() |
|||
{ |
|||
SignedRational first = new SignedRational(0, 100); |
|||
SignedRational second = new SignedRational(100, 100); |
|||
|
|||
Assert.NotEqual(first, second); |
|||
Assert.True(first != second); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests whether the Rational constructor correctly assign properties.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void ConstructorAssignsProperties() |
|||
{ |
|||
SignedRational rational = new SignedRational(7, -55); |
|||
Assert.Equal(7, rational.Numerator); |
|||
Assert.Equal(-55, rational.Denominator); |
|||
|
|||
rational = new SignedRational(-755, 100); |
|||
Assert.Equal(-151, rational.Numerator); |
|||
Assert.Equal(20, rational.Denominator); |
|||
|
|||
rational = new SignedRational(-755, -100, false); |
|||
Assert.Equal(-755, rational.Numerator); |
|||
Assert.Equal(-100, rational.Denominator); |
|||
|
|||
rational = new SignedRational(-151, -20); |
|||
Assert.Equal(-151, rational.Numerator); |
|||
Assert.Equal(-20, rational.Denominator); |
|||
|
|||
rational = new SignedRational(-7.55); |
|||
Assert.Equal(-151, rational.Numerator); |
|||
Assert.Equal(20, rational.Denominator); |
|||
|
|||
rational = new SignedRational(7); |
|||
Assert.Equal(7, rational.Numerator); |
|||
Assert.Equal(1, rational.Denominator); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Fraction() |
|||
{ |
|||
SignedRational first = new SignedRational(1.0 / 1600); |
|||
SignedRational second = new SignedRational(1.0 / 1600, true); |
|||
Assert.False(first.Equals(second)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ToDouble() |
|||
{ |
|||
SignedRational rational = new SignedRational(0, 0); |
|||
Assert.Equal(double.NaN, rational.ToDouble()); |
|||
|
|||
rational = new SignedRational(2, 0); |
|||
Assert.Equal(double.PositiveInfinity, rational.ToDouble()); |
|||
|
|||
rational = new SignedRational(-2, 0); |
|||
Assert.Equal(double.NegativeInfinity, rational.ToDouble()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ToStringRepresention() |
|||
{ |
|||
SignedRational rational = new SignedRational(0, 0); |
|||
Assert.Equal("[ Indeterminate ]", rational.ToString()); |
|||
|
|||
rational = new SignedRational(double.PositiveInfinity); |
|||
Assert.Equal("[ PositiveInfinity ]", rational.ToString()); |
|||
|
|||
rational = new SignedRational(double.NegativeInfinity); |
|||
Assert.Equal("[ NegativeInfinity ]", rational.ToString()); |
|||
|
|||
rational = new SignedRational(0, 1); |
|||
Assert.Equal("0", rational.ToString()); |
|||
|
|||
rational = new SignedRational(2, 1); |
|||
Assert.Equal("2", rational.ToString()); |
|||
|
|||
rational = new SignedRational(1, 2); |
|||
Assert.Equal("1/2", rational.ToString()); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue