Skip to content

Commit

Permalink
Massive generic math refactor
Browse files Browse the repository at this point in the history
Breaks compatibility
  • Loading branch information
mcraiha committed Feb 5, 2025
1 parent 3338bd5 commit 96400d7
Show file tree
Hide file tree
Showing 15 changed files with 690 additions and 86 deletions.
7 changes: 4 additions & 3 deletions src/AtkinsonDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ This file implements error pushing of dithering via Atkinson kernel.
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Atkinson dithering for RGB bytes
/// Atkinson dithering for RGB
/// </summary>
public sealed class AtkinsonDitheringRGBByte : DitheringBase<byte>
public sealed class AtkinsonDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Atkinson dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public AtkinsonDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "Atkinson", "_ATK")
public AtkinsonDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "Atkinson", "_ATK")
{

}
Expand Down
7 changes: 4 additions & 3 deletions src/BurkesDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ This file implements error pushing of dithering via (Daniel) Burkes kernel.
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Burkes dithering for RGB bytes
/// Burkes dithering for RGB
/// </summary>
public sealed class BurkesDitheringRGBByte : DitheringBase<byte>
public sealed class BurkesDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Burkes dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public BurkesDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "Burkes", "_BUR")
public BurkesDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "Burkes", "_BUR")
{

}
Expand Down
6 changes: 3 additions & 3 deletions src/FakeDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Fake dithering doesn't do any dithering. It only does color reduction
/// </summary>
public sealed class FakeDitheringRGBByte : DitheringBase<byte>
public sealed class FakeDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for fake dithering (no dither, just color reduction)
/// </summary>
/// <param name="colorfunc"></param>
/// <returns></returns>
public FakeDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "No dithering", "_NONE")
public FakeDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "No dithering", "_NONE")
{

}
Expand All @@ -31,4 +32,3 @@ override protected void PushError(int x, int y, double[] quantError)
// Don't do anything
}
}

29 changes: 15 additions & 14 deletions src/FloydSteinbergDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ This file implements error pushing of dithering via (Robert) Floyd and (Louis) S
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Floyd-Steinberg dithering for RGB bytes
/// Floyd-Steinberg dithering for RGB
/// </summary>
public sealed class FloydSteinbergDitheringRGBByte : DitheringBase<byte>
public sealed class FloydSteinbergDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Floyd-Steinberg dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public FloydSteinbergDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "Floyd-Steinberg", "_FS")
{
public FloydSteinbergDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "Floyd-Steinberg", "_FS")
{

}
}

/// <summary>
/// Push error method for Floyd-Steinberg dithering
Expand All @@ -35,26 +36,26 @@ override protected void PushError(int x, int y, double[] quantError)
int xPlusOne = x + 1;
int yPlusOne = y + 1;

// Current row
if (this.IsValidCoordinate(xPlusOne, y))
// Current row
if (this.IsValidCoordinate(xPlusOne, y))
{
this.ModifyImageWithErrorAndMultiplier(xPlusOne, y, quantError, 7.0 / 16.0);
this.ModifyImageWithErrorAndMultiplier(xPlusOne, y, quantError, 7.0 / 16.0);
}

// Next row
if (this.IsValidCoordinate(xMinusOne, yPlusOne))
// Next row
if (this.IsValidCoordinate(xMinusOne, yPlusOne))
{
this.ModifyImageWithErrorAndMultiplier(xMinusOne, yPlusOne, quantError, 3.0 / 16.0);
this.ModifyImageWithErrorAndMultiplier(xMinusOne, yPlusOne, quantError, 3.0 / 16.0);
}

if (this.IsValidCoordinate(x, yPlusOne))
{
this.ModifyImageWithErrorAndMultiplier(x, yPlusOne, quantError, 5.0 / 16.0);
this.ModifyImageWithErrorAndMultiplier(x, yPlusOne, quantError, 5.0 / 16.0);
}

if (this.IsValidCoordinate(xPlusOne, yPlusOne))
{
this.ModifyImageWithErrorAndMultiplier(xPlusOne, yPlusOne, quantError, 1.0 / 16.0);
this.ModifyImageWithErrorAndMultiplier(xPlusOne, yPlusOne, quantError, 1.0 / 16.0);
}
}
}
}
7 changes: 4 additions & 3 deletions src/JarvisJudiceNinkeDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Jarvis-Judice-Ninke dithering for RGB bytes
/// Jarvis-Judice-Ninke dithering for RGB
/// </summary>
public sealed class JarvisJudiceNinkeDitheringRGBByte : DitheringBase<byte>
public sealed class JarvisJudiceNinkeDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Jarvis-Judice-Ninke dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public JarvisJudiceNinkeDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "Jarvis-Judice-Ninke", "_JJN")
public JarvisJudiceNinkeDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "Jarvis-Judice-Ninke", "_JJN")
{

}
Expand Down
2 changes: 1 addition & 1 deletion src/LibDithering.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<PackageId>LibDithering</PackageId>
<VersionPrefix>1.0.1</VersionPrefix>
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
Expand Down
7 changes: 4 additions & 3 deletions src/SierraDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ This is free and unencumbered software released into the public domain.
*/

using System;
using System.Numerics;

/// <summary>
/// Sierra dithering for RGB bytes
/// Sierra dithering for RGB
/// </summary>
public sealed class SierraDitheringRGBByte : DitheringBase<byte>
public sealed class SierraDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Sierra dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public SierraDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "Sierra", "_SIE")
public SierraDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "Sierra", "_SIE")
{

}
Expand Down
7 changes: 4 additions & 3 deletions src/SierraLiteDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ This file implements error pushing of dithering via (Frankie) Sierra Lite kernel
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Sierra lite dithering for RGB bytes
/// Sierra lite dithering for RGB
/// </summary>
public sealed class SierraLiteDitheringRGBByte : DitheringBase<byte>
public sealed class SierraLiteDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Sierra lite dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public SierraLiteDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "SierraLite", "_SIEL")
public SierraLiteDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "SierraLite", "_SIEL")
{

}
Expand Down
7 changes: 4 additions & 3 deletions src/SierraTwoRowDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ This file implements error pushing of dithering via (Frankie) Sierra Two Row ker
This is free and unencumbered software released into the public domain.
*/
using System;
using System.Numerics;

/// <summary>
/// Sierra two row dithering for RGB bytes
/// Sierra two row dithering for RGB
/// </summary>
public sealed class SierraTwoRowDitheringRGBByte : DitheringBase<byte>
public sealed class SierraTwoRowDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Sierra two row dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public SierraTwoRowDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "SierraTwoRow", "_SIE2R")
public SierraTwoRowDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "SierraTwoRow", "_SIE2R")
{

}
Expand Down
7 changes: 4 additions & 3 deletions src/StuckiDithering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ This is free and unencumbered software released into the public domain.
*/

using System;
using System.Numerics;

/// <summary>
/// Stucki dithering for RGB bytes
/// Stucki dithering for RGB
/// </summary>
public sealed class StuckiDitheringRGBByte : DitheringBase<byte>
public sealed class StuckiDitheringRGB<T> : DitheringBase<T> where T : INumber<T>
{
/// <summary>
/// Constructor for Stucki dithering
/// </summary>
/// <param name="colorfunc">Color function</param>
public StuckiDitheringRGBByte(ColorFunction colorfunc) : base(colorfunc, "Stucki", "_STU")
public StuckiDitheringRGB(ColorFunction colorfunc) : base(colorfunc, "Stucki", "_STU")
{

}
Expand Down
11 changes: 4 additions & 7 deletions src/TempByteImageFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,12 @@ public void ModifyPixelChannelsWithQuantError(ref byte[] modifyValues, double[]
}
}

private const double clampMin = (double)byte.MinValue;
private const double clampMax = (double)byte.MaxValue;

private static byte GetLimitedValue(byte original, double error)
{
double newValue = original + error;
return Clamp(newValue, byte.MinValue, byte.MaxValue);
}

// C# doesn't have a Clamp method so we need this
private static byte Clamp(double value, double min, double max)
{
return (value < min) ? (byte)min : (value > max) ? (byte)max : (byte)value;
return (byte)Math.Clamp(newValue, clampMin, clampMax);
}
}
56 changes: 47 additions & 9 deletions src/TempDoubleImageFormat.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.Intrinsics;

/// <summary>
/// Temp double based image format. 0.0 is zero color, 1.0 is max color
Expand Down Expand Up @@ -262,23 +263,60 @@ public void GetQuantErrorsPerChannel(in double[] originalPixel, in double[] newP
/// <param name="modifyValues">Values to modify</param>
/// <param name="quantErrors">Quantization errors</param>
/// <param name="multiplier">Multiplier</param>
#if NET9_0
public void ModifyPixelChannelsWithQuantError(ref double[] modifyValues, double[] quantErrors, double multiplier)
{
for (int i = 0; i < this.channelsPerPixel; i++)
if (this.channelsPerPixel == 1)
{
modifyValues[i] = GetLimitedValue(modifyValues[i], quantErrors[i] * multiplier);
modifyValues[0] = GetLimitedValue(modifyValues[0], quantErrors[0] * multiplier);
}
}
else if (this.channelsPerPixel == 3)
{
Span<double> temp = stackalloc double[4];

private static double GetLimitedValue(double original, double error)
Vector256<double> originals = Vector256.Create(modifyValues[0], modifyValues[1], modifyValues[2], 0);
Vector256<double> errors = Vector256.Create(quantErrors[0], quantErrors[1], quantErrors[2], 0);
errors *= multiplier;
originals += errors;

Vector256.Clamp(originals, Vector256<double>.Zero, Vector256<double>.One);
Vector256.CopyTo(originals, temp);
temp.Slice(0, 3).CopyTo(modifyValues);
}
else
{
for (int i = 0; i < this.channelsPerPixel; i++)
{
modifyValues[i] = GetLimitedValue(modifyValues[i], quantErrors[i] * multiplier);
}
}
}
#else
public void ModifyPixelChannelsWithQuantError(ref double[] modifyValues, double[] quantErrors, double multiplier)
{
double newValue = original + error;
return Clamp(newValue, 0.0, 1.0);
if (this.channelsPerPixel == 1)
{
modifyValues[0] = GetLimitedValue(modifyValues[0], quantErrors[0] * multiplier);
}
else if (this.channelsPerPixel == 3)
{
modifyValues[0] = GetLimitedValue(modifyValues[0], quantErrors[0] * multiplier);
modifyValues[1] = GetLimitedValue(modifyValues[1], quantErrors[1] * multiplier);
modifyValues[2] = GetLimitedValue(modifyValues[2], quantErrors[2] * multiplier);
}
else
{
for (int i = 0; i < this.channelsPerPixel; i++)
{
modifyValues[i] = GetLimitedValue(modifyValues[i], quantErrors[i] * multiplier);
}
}
}
#endif // NET9_0

// C# doesn't have a Clamp method so we need this
private static double Clamp(double value, double min, double max)
private static double GetLimitedValue(double original, double error)
{
return (value < min) ? 0.0 : (value > max) ? 1.0 : value;
double newValue = original + error;
return Math.Clamp(newValue, 0.0, 1.0);
}
}
Loading

0 comments on commit 96400d7

Please sign in to comment.