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