Browse Source

Merge branch 'master' into colorspace-transforms

pull/664/head
James Jackson-South 8 years ago
committed by GitHub
parent
commit
c48a6d25ef
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
  2. 4
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 18
      src/ImageSharp/Image.FromBytes.cs
  4. 5
      src/ImageSharp/PixelFormats/Alpha8.cs
  5. 7
      src/ImageSharp/PixelFormats/Argb32.cs
  6. 5
      src/ImageSharp/PixelFormats/Bgr565.cs
  7. 10
      src/ImageSharp/PixelFormats/Bgra32.cs
  8. 5
      src/ImageSharp/PixelFormats/Bgra4444.cs
  9. 5
      src/ImageSharp/PixelFormats/Bgra5551.cs
  10. 5
      src/ImageSharp/PixelFormats/Byte4.cs
  11. 5
      src/ImageSharp/PixelFormats/HalfSingle.cs
  12. 5
      src/ImageSharp/PixelFormats/HalfVector2.cs
  13. 5
      src/ImageSharp/PixelFormats/HalfVector4.cs
  14. 5
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  15. 5
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  16. 3561
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  17. 48
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  18. 2600
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  19. 298
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  20. 449
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  21. 42
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  22. 7
      src/ImageSharp/PixelFormats/Rgba32.cs
  23. 5
      src/ImageSharp/PixelFormats/Rgba64.cs
  24. 5
      src/ImageSharp/PixelFormats/Short2.cs
  25. 5
      src/ImageSharp/PixelFormats/Short4.cs
  26. 4
      tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs
  27. 309
      tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs
  28. 5
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  29. 21
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs
  30. 113
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs
  31. 84
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs
  32. 20
      tests/ImageSharp.Tests/TestImages.cs
  33. 2
      tests/Images/External
  34. BIN
      tests/Images/Input/Bmp/pal1.bmp
  35. BIN
      tests/Images/Input/Bmp/pal1p1.bmp
  36. BIN
      tests/Images/Input/Bmp/pal4.bmp

8
src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs

@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
var (from, to) = this.GetGradientSegment(positionOnCompleteGradient); (ColorStop<TPixel> from, ColorStop<TPixel> to) = this.GetGradientSegment(positionOnCompleteGradient);
if (from.Color.Equals(to.Color)) if (from.Color.Equals(to.Color))
{ {
@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing
float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio; float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio;
// TODO: this should be changeble for different gradienting functions // TODO: this should be changeble for different gradienting functions
Vector4 result = PorterDuffFunctions.Normal( Vector4 result = PorterDuffFunctions.NormalSrcOver(
fromAsVector, fromAsVector,
toAsVector, toAsVector,
onLocalGradient); onLocalGradient);
@ -153,11 +153,11 @@ namespace SixLabors.ImageSharp.Processing
private (ColorStop<TPixel> from, ColorStop<TPixel> to) GetGradientSegment( private (ColorStop<TPixel> from, ColorStop<TPixel> to) GetGradientSegment(
float positionOnCompleteGradient) float positionOnCompleteGradient)
{ {
var localGradientFrom = this.colorStops[0]; ColorStop<TPixel> localGradientFrom = this.colorStops[0];
ColorStop<TPixel> localGradientTo = default; ColorStop<TPixel> localGradientTo = default;
// TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient) // TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient)
foreach (var colorStop in this.colorStops) foreach (ColorStop<TPixel> colorStop in this.colorStops)
{ {
localGradientTo = colorStop; localGradientTo = colorStop;

4
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -373,11 +373,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int x = 0; x < arrayWidth; x++) for (int x = 0; x < arrayWidth; x++)
{ {
int colOffset = x * ppb; int colOffset = x * ppb;
for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++)
for (int shift = 0; shift < ppb && (x + shift) < width; shift++)
{ {
int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4; int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int newX = colOffset + shift;
// Stored in b-> g-> r order. // Stored in b-> g-> r order.
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[colorIndex]); rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[colorIndex]);

18
src/ImageSharp/Image.FromBytes.cs

@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -193,13 +194,24 @@ namespace SixLabors.ImageSharp
/// <returns>The mime type or null if none found.</returns> /// <returns>The mime type or null if none found.</returns>
public static unsafe IImageFormat DetectFormat(Configuration config, ReadOnlySpan<byte> data) public static unsafe IImageFormat DetectFormat(Configuration config, ReadOnlySpan<byte> data)
{ {
fixed (byte* ptr = &data.GetPinnableReference()) int maxHeaderSize = config.MaxHeaderSize;
if (maxHeaderSize <= 0)
{ {
using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) return null;
}
IImageFormat format = default;
foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors)
{
IImageFormat f = detector.DetectFormat(data);
if (f != null)
{ {
return DetectFormat(config, stream); format = f;
} }
} }
return format;
} }
/// <summary> /// <summary>

5
src/ImageSharp/PixelFormats/Alpha8.cs

@ -208,10 +208,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <summary> /// <summary>
/// Packs a <see cref="float"/> into a byte. /// Packs a <see cref="float"/> into a byte.

7
src/ImageSharp/PixelFormats/Argb32.cs

@ -362,12 +362,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.Argb.GetHashCode();
{
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.A.GetHashCode());
}
/// <summary> /// <summary>
/// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1] /// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1]

5
src/ImageSharp/PixelFormats/Bgr565.cs

@ -224,10 +224,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <summary> /// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>. /// Packs the <see cref="float"/> components into a <see cref="ushort"/>.

10
src/ImageSharp/PixelFormats/Bgra32.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -105,19 +104,14 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(Bgra32 other) public bool Equals(Bgra32 other)
{ {
return this.R == other.R && this.G == other.G && this.B == other.B && this.A == other.A; return this.Bgra == other.Bgra;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other); public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other);
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode() => this.Bgra.GetHashCode();
{
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.A.GetHashCode());
}
/// <summary> /// <summary>
/// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1] /// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1]

5
src/ImageSharp/PixelFormats/Bgra4444.cs

@ -215,10 +215,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <summary> /// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>. /// Packs the <see cref="float"/> components into a <see cref="ushort"/>.

5
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -221,10 +221,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary> /// </summary>
/// <returns>The hash code for the packed vector.</returns> /// <returns>The hash code for the packed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <summary> /// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>. /// Packs the <see cref="float"/> components into a <see cref="ushort"/>.

5
src/ImageSharp/PixelFormats/Byte4.cs

@ -210,10 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <summary> /// <summary>
/// Returns a string representation of the current instance. /// Returns a string representation of the current instance.

5
src/ImageSharp/PixelFormats/HalfSingle.cs

@ -229,10 +229,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToByteScaledVector4() private Vector4 ToByteScaledVector4()

5
src/ImageSharp/PixelFormats/HalfVector2.cs

@ -231,10 +231,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)

5
src/ImageSharp/PixelFormats/HalfVector4.cs

@ -224,10 +224,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)

5
src/ImageSharp/PixelFormats/NormalizedByte2.cs

@ -257,10 +257,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()

5
src/ImageSharp/PixelFormats/NormalizedByte4.cs

@ -250,10 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()

3561
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs

File diff suppressed because it is too large

48
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt

@ -35,10 +35,22 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
<# <#
string[] composers = new []{
"Src",
"SrcAtop",
"SrcOver",
"SrcIn",
"SrcOut",
"Dest",
"DestAtop",
"DestOver",
"DestIn",
"DestOut",
"Clear",
"Xor",
};
string[] blenders = new []{ string[] blenders = new []{
"Normal", "Normal",
"Multiply", "Multiply",
"Add", "Add",
@ -47,36 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
"Darken", "Darken",
"Lighten", "Lighten",
"Overlay", "Overlay",
"HardLight", "HardLight"
"Src" ,
"Atop" ,
"Over" ,
"In" ,
"Out" ,
"Dest" ,
"DestAtop" ,
"DestOver" ,
"DestIn" ,
"DestOut" ,
"Clear" ,
"Xor" ,
}; };
foreach(var composer in composers) {
foreach(var blender in blenders) { foreach(var blender in blenders) {
string blender_composer= $"{blender}{composer}";
#> #>
internal class <#=blender#> : PixelBlender<TPixel> internal class <#= blender_composer#> : PixelBlender<TPixel>
{ {
/// <summary> /// <summary>
/// Gets the static instance of this blender. /// Gets the static instance of this blender.
/// </summary> /// </summary>
public static <#=blender#> Instance { get; } = new <#=blender#>(); public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions.<#=blender#>(background, source, amount); return PorterDuffFunctions.<#=blender_composer#>(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -97,7 +99,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destinationSpan[i] = PorterDuffFunctions.<#=blender#>(backgroundSpan[i], sourceSpan[i], amount[i]); destinationSpan[i] = PorterDuffFunctions.<#=blender_composer#>(backgroundSpan[i], sourceSpan[i], amount[i]);
} }
PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length); PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
@ -106,7 +108,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
<# <#
}
} }
#> #>

2600
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs

File diff suppressed because it is too large

298
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt

@ -1,112 +1,188 @@
<# <#
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
#> #>
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #> <#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #> <#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #> <#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #> <#@ output extension=".cs" #>
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
<#
<# void GeneratePixelBlenders(string blender) { #>
void GeneratePixelBlender (string blender)
{ [MethodImpl(MethodImplOptions.AggressiveInlining)]
#> public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)] {
public static TPixel <#=blender#><TPixel>(TPixel backdrop, TPixel source, float amount) opacity = opacity.Clamp(0, 1);
where TPixel : struct, IPixel<TPixel> source.W *= opacity;
{
TPixel dest = default; return source;
dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount)); }
return dest;
} [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
<# {
} opacity = opacity.Clamp(0, 1);
source.W *= opacity;
void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar)
{ return Atop(backdrop, source, <#=blender#>(backdrop, source));
int a_s = sourceVar == "Vector4.Zero" ? 0 : 1; }
int a_b = destVar == "Vector4.Zero" ? 0 : 1;
int a_x = blendVar == "Vector4.Zero" ? 0 : 1; [MethodImpl(MethodImplOptions.AggressiveInlining)]
#> public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)] {
public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float opacity) opacity = opacity.Clamp(0, 1);
{ source.W *= opacity;
opacity = opacity.Clamp(0, 1);
<# if(sourceVar != "Vector4.Zero" ) { #> return Over(backdrop, source, <#=blender#>(backdrop, source));
source.W *= opacity; }
<# } #>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// calculate weights public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity)
float xw = backdrop.W * source.W; {
float bw = backdrop.W - xw; opacity = opacity.Clamp(0, 1);
float sw = source.W - xw; source.W *= opacity;
// calculate final alpha return In(backdrop, source, <#=blender#>(backdrop, source));
float fw = (sw * <#=a_s#>) + (bw * <#=a_b#>) + (xw * <#=a_x#>); }
// calculate final value [MethodImpl(MethodImplOptions.AggressiveInlining)]
Vector4 xform = ((<#=blendVar#> * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(fw, Constants.Epsilon); public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity)
xform.W = fw; {
opacity = opacity.Clamp(0, 1);
return Vector4.Lerp(backdrop, xform, opacity); source.W *= opacity;
}
return Out(backdrop, source);
<# }
}
GenerateVectorCompositor("Src", "source", "Vector4.Zero", "source"); [MethodImpl(MethodImplOptions.AggressiveInlining)]
GenerateVectorCompositor("Atop", "Vector4.Zero", "backdrop", "source"); public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity)
GenerateVectorCompositor("Over", "source", "backdrop", "source"); {
GenerateVectorCompositor("In", "Vector4.Zero", "Vector4.Zero", "source"); return backdrop;
GenerateVectorCompositor("Out", "source", "Vector4.Zero", "Vector4.Zero"); }
GenerateVectorCompositor("Dest", "Vector4.Zero", "backdrop", "backdrop");
GenerateVectorCompositor("DestAtop", "source", "Vector4.Zero", "backdrop"); [MethodImpl(MethodImplOptions.AggressiveInlining)]
GenerateVectorCompositor("DestOver", "source", "backdrop", "backdrop"); public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity)
GenerateVectorCompositor("DestIn", "Vector4.Zero", "Vector4.Zero", "backdrop"); {
GenerateVectorCompositor("DestOut", "Vector4.Zero", "backdrop", "Vector4.Zero"); opacity = opacity.Clamp(0, 1);
GenerateVectorCompositor("Clear", "Vector4.Zero", "Vector4.Zero", "Vector4.Zero"); source.W *= opacity;
GenerateVectorCompositor("Xor", "source", "backdrop", "Vector4.Zero");
return Atop(source, backdrop, <#=blender#>(source, backdrop));
}
GeneratePixelBlender("Normal");
GeneratePixelBlender("Multiply"); [MethodImpl(MethodImplOptions.AggressiveInlining)]
GeneratePixelBlender("Add"); public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity)
GeneratePixelBlender("Subtract"); {
GeneratePixelBlender("Screen"); opacity = opacity.Clamp(0, 1);
GeneratePixelBlender("Darken"); source.W *= opacity;
GeneratePixelBlender("Lighten");
GeneratePixelBlender("Overlay"); return Over(source, backdrop, <#=blender#>(source, backdrop));
GeneratePixelBlender("HardLight"); }
GeneratePixelBlender("Src"); [MethodImpl(MethodImplOptions.AggressiveInlining)]
GeneratePixelBlender("Atop"); public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity)
GeneratePixelBlender("Over"); {
GeneratePixelBlender("In"); opacity = opacity.Clamp(0, 1);
GeneratePixelBlender("Out"); source.W *= opacity;
GeneratePixelBlender("Dest");
GeneratePixelBlender("DestAtop"); return In(source, backdrop, <#=blender#>(source, backdrop));
GeneratePixelBlender("DestOver"); }
GeneratePixelBlender("DestIn");
GeneratePixelBlender("DestOut"); [MethodImpl(MethodImplOptions.AggressiveInlining)]
GeneratePixelBlender("Clear"); public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity)
GeneratePixelBlender("Xor"); {
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
#>
} return Out(source, backdrop);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Xor(backdrop, source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Clear(backdrop, source);
}
<# } #>
<# void GenerateGenericPixelBlender(string blender, string composer) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel <#=blender#><#=composer#><TPixel>(TPixel backdrop, TPixel source, float opacity)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default;
dest.PackFromVector4(<#=blender#><#=composer#>(backdrop.ToVector4(),source.ToVector4(),opacity));
return dest;
}
<# } #>
<#
string[] composers = new []{
"Src",
"SrcAtop",
"SrcOver",
"SrcIn",
"SrcOut",
"Dest",
"DestAtop",
"DestOver",
"DestIn",
"DestOut",
"Clear",
"Xor",
};
string[] blenders = new []{
"Normal",
"Multiply",
"Add",
"Subtract",
"Screen",
"Darken",
"Lighten",
"Overlay",
"HardLight"
};
foreach(var blender in blenders)
{
GeneratePixelBlenders(blender);
foreach(var composer in composers)
{
GenerateGenericPixelBlender(blender,composer);
}
}
#>
}
} }

449
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs

@ -1,194 +1,257 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
/// <summary> /// <summary>
/// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model. /// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// These functions are designed to be a general solution for all color cases, /// These functions are designed to be a general solution for all color cases,
/// that is, they take in account the alpha value of both the backdrop /// that is, they take in account the alpha value of both the backdrop
/// and source, and there's no need to alpha-premultiply neither the backdrop /// and source, and there's no need to alpha-premultiply neither the backdrop
/// nor the source. /// nor the source.
/// Note there are faster functions for when the backdrop color is known /// Note there are faster functions for when the backdrop color is known
/// to be opaque /// to be opaque
/// </remarks> /// </remarks>
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
/// <summary> /// <summary>
/// Source over backdrop /// Source over backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <returns>Output color</returns>
/// <returns>Output color</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source)
public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) {
{ return source;
source.W *= opacity; }
return Compose(backdrop, source, source);
} /// <summary>
/// Source multiplied by backdrop
/// <summary> /// </summary>
/// Source multiplied by backdrop /// <param name="backdrop">Backdrop color</param>
/// </summary> /// <param name="source">Source color</param>
/// <param name="backdrop">Backdrop color</param> /// <returns>Output color</returns>
/// <param name="source">Source color</param> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="opacity">Opacity applied to Source Alpha</param> public static Vector4 Multiply(Vector4 backdrop, Vector4 source)
/// <returns>Output color</returns> {
[MethodImpl(MethodImplOptions.AggressiveInlining)] return backdrop * source;
public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) }
{
source.W *= opacity; /// <summary>
return Compose(backdrop, source, backdrop * source); /// Source added to backdrop
} /// </summary>
/// <param name="backdrop">Backdrop color</param>
/// <summary> /// <param name="source">Source color</param>
/// Source added to backdrop /// <returns>Output color</returns>
/// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="backdrop">Backdrop color</param> public static Vector4 Add(Vector4 backdrop, Vector4 source)
/// <param name="source">Source color</param> {
/// <param name="opacity">Opacity applied to Source Alpha</param> return Vector4.Min(Vector4.One, backdrop + source);
/// <returns>Output color</returns> }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) /// <summary>
{ /// Source subtracted from backdrop
source.W *= opacity; /// </summary>
return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); /// <param name="backdrop">Backdrop color</param>
} /// <param name="source">Source color</param>
/// <returns>Output color</returns>
/// <summary> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// Source subtracted from backdrop public static Vector4 Subtract(Vector4 backdrop, Vector4 source)
/// </summary> {
/// <param name="backdrop">Backdrop color</param> return Vector4.Max(Vector4.Zero, backdrop - source);
/// <param name="source">Source color</param> }
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Complement of source multiplied by the complement of backdrop
public static Vector4 Subtract(Vector4 backdrop, Vector4 source, float opacity) /// </summary>
{ /// <param name="backdrop">Backdrop color</param>
source.W *= opacity; /// <param name="source">Source color</param>
return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); /// <returns>Output color</returns>
} [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Screen(Vector4 backdrop, Vector4 source)
/// <summary> {
/// Complement of source multiplied by the complement of backdrop return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source));
/// </summary> }
/// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <summary>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// Per element, chooses the smallest value of source and backdrop
/// <returns>Output color</returns> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <param name="backdrop">Backdrop color</param>
public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) /// <param name="source">Source color</param>
{ /// <returns>Output color</returns>
source.W *= opacity; [MethodImpl(MethodImplOptions.AggressiveInlining)]
return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); public static Vector4 Darken(Vector4 backdrop, Vector4 source)
} {
return Vector4.Min(backdrop, source);
/// <summary> }
/// Per element, chooses the smallest value of source and backdrop
/// </summary> /// <summary>
/// <param name="backdrop">Backdrop color</param> /// Per element, chooses the largest value of source and backdrop
/// <param name="source">Source color</param> /// </summary>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="backdrop">Backdrop color</param>
/// <returns>Output color</returns> /// <param name="source">Source color</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <returns>Output color</returns>
public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static Vector4 Lighten(Vector4 backdrop, Vector4 source)
source.W *= opacity; {
return Compose(backdrop, source, Vector4.Min(backdrop, source)); return Vector4.Max(backdrop, source);
} }
/// <summary> /// <summary>
/// Per element, chooses the largest value of source and backdrop /// Overlays source over backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <returns>Output color</returns>
/// <returns>Output color</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Overlay(Vector4 backdrop, Vector4 source)
public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) {
{ float cr = OverlayValueFunction(backdrop.X, source.X);
source.W *= opacity; float cg = OverlayValueFunction(backdrop.Y, source.Y);
return Compose(backdrop, source, Vector4.Max(backdrop, source)); float cb = OverlayValueFunction(backdrop.Z, source.Z);
}
return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0));
/// <summary> }
/// Overlays source over backdrop
/// </summary> /// <summary>
/// <param name="backdrop">Backdrop color</param> /// Hard light effect
/// <param name="source">Source color</param> /// </summary>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="backdrop">Backdrop color</param>
/// <returns>Output color</returns> /// <param name="source">Source color</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <returns>Output color</returns>
public static Vector4 Overlay(Vector4 backdrop, Vector4 source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static Vector4 HardLight(Vector4 backdrop, Vector4 source)
source.W *= opacity; {
float cr = OverlayValueFunction(backdrop.X, source.X); float cr = OverlayValueFunction(source.X, backdrop.X);
float cg = OverlayValueFunction(backdrop.Y, source.Y); float cg = OverlayValueFunction(source.Y, backdrop.Y);
float cb = OverlayValueFunction(backdrop.Z, source.Z); float cb = OverlayValueFunction(source.Z, backdrop.Z);
return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0));
} }
/// <summary> /// <summary>
/// Hard light effect /// Helper function for Overlay andHardLight modes
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color element</param>
/// <param name="source">Source color</param> /// <param name="source">Source color element</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <returns>Overlay value</returns>
/// <returns>Output color</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] private static float OverlayValueFunction(float backdrop, float source)
public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) {
{ return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop));
source.W *= opacity; }
float cr = OverlayValueFunction(source.X, backdrop.X);
float cg = OverlayValueFunction(source.Y, backdrop.Y); /// <summary>
float cb = OverlayValueFunction(source.Z, backdrop.Z); /// General composition function for all modes, with a general solution for alpha channel
/// </summary>
return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); /// <param name="backdrop">Original Backdrop color</param>
} /// <param name="source">Original source color</param>
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
/// <summary> /// <returns>The final color</returns>
/// Helper function for Overlay andHardLight modes [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// </summary> private static Vector4 SrcOverReference(Vector4 backdrop, Vector4 source, Vector4 xform)
/// <param name="backdrop">Backdrop color element</param> {
/// <param name="source">Source color element</param> // calculate weights
/// <returns>Overlay value</returns> float xw = backdrop.W * source.W;
[MethodImpl(MethodImplOptions.AggressiveInlining)] float bw = backdrop.W - xw;
private static float OverlayValueFunction(float backdrop, float source) float sw = source.W - xw;
{
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); // calculate final alpha
} float a = xw + bw + sw;
/// <summary> // calculate final value
/// General composition function for all modes, with a general solution for alpha channel xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon);
/// </summary> xform.W = a;
/// <param name="backdrop">Original Backdrop color</param>
/// <param name="source">Original source color</param> return xform;
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param> }
/// <returns>The final color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend)
private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform) {
{ // calculate weights
// calculate weights float blendW = dst.W * src.W;
float xw = backdrop.W * source.W; float dstW = dst.W - blendW;
float bw = backdrop.W - xw; float srcW = src.W - blendW;
float sw = source.W - xw;
// calculate final alpha
// calculate final alpha float alpha = dstW + srcW + blendW;
float a = xw + bw + sw;
// calculate final color
// calculate final value Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW);
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon);
xform.W = a; // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon);
return xform; color.W = alpha;
}
} return color;
}
public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend)
{
// calculate weights
float blendW = dst.W * src.W;
float dstW = dst.W - blendW;
// calculate final alpha
float alpha = dstW + blendW;
// calculate final color
Vector4 color = (dst * dstW) + (blend * blendW);
// unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon);
color.W = alpha;
return color;
}
public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend)
{
float alpha = dst.W * src.W;
Vector4 color = src * alpha; // premultiply
color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply
color.W = alpha;
return color;
}
public static Vector4 Out(Vector4 dst, Vector4 src)
{
float alpha = (1 - dst.W) * src.W;
Vector4 color = src * alpha; // premultiply
color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply
color.W = alpha;
return color;
}
public static Vector4 Xor(Vector4 dst, Vector4 src)
{
float srcW = 1 - dst.W;
float dstW = 1 - src.W;
float alpha = (src.W * srcW) + (dst.W * dstW);
Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW);
// unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon);
color.W = alpha;
return color;
}
private static Vector4 Clear(Vector4 backdrop, Vector4 source)
{
return Vector4.Zero;
}
}
} }

42
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs

@ -20,30 +20,30 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
switch (mode) switch (mode)
{ {
case PixelBlenderMode.Multiply: return DefaultPixelBlenders<TPixel>.Multiply.Instance; case PixelBlenderMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance;
case PixelBlenderMode.Add: return DefaultPixelBlenders<TPixel>.Add.Instance; case PixelBlenderMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance;
case PixelBlenderMode.Subtract: return DefaultPixelBlenders<TPixel>.Subtract.Instance; case PixelBlenderMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance;
case PixelBlenderMode.Screen: return DefaultPixelBlenders<TPixel>.Screen.Instance; case PixelBlenderMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance;
case PixelBlenderMode.Darken: return DefaultPixelBlenders<TPixel>.Darken.Instance; case PixelBlenderMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance;
case PixelBlenderMode.Lighten: return DefaultPixelBlenders<TPixel>.Lighten.Instance; case PixelBlenderMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance;
case PixelBlenderMode.Overlay: return DefaultPixelBlenders<TPixel>.Overlay.Instance; case PixelBlenderMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance;
case PixelBlenderMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLight.Instance; case PixelBlenderMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance;
case PixelBlenderMode.Src: return DefaultPixelBlenders<TPixel>.Src.Instance; case PixelBlenderMode.Src: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance;
case PixelBlenderMode.Atop: return DefaultPixelBlenders<TPixel>.Atop.Instance; case PixelBlenderMode.Atop: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance;
case PixelBlenderMode.Over: return DefaultPixelBlenders<TPixel>.Over.Instance; case PixelBlenderMode.Over: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
case PixelBlenderMode.In: return DefaultPixelBlenders<TPixel>.In.Instance; case PixelBlenderMode.In: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance;
case PixelBlenderMode.Out: return DefaultPixelBlenders<TPixel>.Out.Instance; case PixelBlenderMode.Out: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance;
case PixelBlenderMode.Dest: return DefaultPixelBlenders<TPixel>.Dest.Instance; case PixelBlenderMode.Dest: return DefaultPixelBlenders<TPixel>.NormalDest.Instance;
case PixelBlenderMode.DestAtop: return DefaultPixelBlenders<TPixel>.DestAtop.Instance; case PixelBlenderMode.DestAtop: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance;
case PixelBlenderMode.DestOver: return DefaultPixelBlenders<TPixel>.DestOver.Instance; case PixelBlenderMode.DestOver: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance;
case PixelBlenderMode.DestIn: return DefaultPixelBlenders<TPixel>.DestIn.Instance; case PixelBlenderMode.DestIn: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance;
case PixelBlenderMode.DestOut: return DefaultPixelBlenders<TPixel>.DestOut.Instance; case PixelBlenderMode.DestOut: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance;
case PixelBlenderMode.Clear: return DefaultPixelBlenders<TPixel>.Clear.Instance; case PixelBlenderMode.Clear: return DefaultPixelBlenders<TPixel>.NormalClear.Instance;
case PixelBlenderMode.Xor: return DefaultPixelBlenders<TPixel>.Xor.Instance; case PixelBlenderMode.Xor: return DefaultPixelBlenders<TPixel>.NormalXor.Instance;
case PixelBlenderMode.Normal: case PixelBlenderMode.Normal:
default: default:
return DefaultPixelBlenders<TPixel>.Normal.Instance; return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
} }
} }
} }

7
src/ImageSharp/PixelFormats/Rgba32.cs

@ -448,12 +448,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.Rgba.GetHashCode();
{
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.A.GetHashCode());
}
/// <summary> /// <summary>
/// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1] /// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1]

5
src/ImageSharp/PixelFormats/Rgba64.cs

@ -295,9 +295,6 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
} }
} }

5
src/ImageSharp/PixelFormats/Short2.cs

@ -249,10 +249,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()

5
src/ImageSharp/PixelFormats/Short4.cs

@ -247,10 +247,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary> /// </summary>
/// <returns>Hash code for the instance.</returns> /// <returns>Hash code for the instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode() => this.PackedValue.GetHashCode();
{
return this.PackedValue.GetHashCode();
}
/// <summary> /// <summary>
/// Returns a string representation of the current instance. /// Returns a string representation of the current instance.

4
tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Benchmarks
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destinationSpan[i] = PorterDuffFunctions.Normal(backgroundSpan[i], sourceSpan[i], amount[i]); destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]);
} }
PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length); PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Benchmarks
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destination[i] = PorterDuffFunctions.Normal(destination[i], source[i], amount[i]); destination[i] = PorterDuffFunctions.NormalSrcOver(destination[i], source[i], amount[i]);
} }
} }

309
tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs

@ -1,153 +1,158 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Drawing namespace SixLabors.ImageSharp.Tests.Drawing
{ {
[GroupOutput("Drawing")] [GroupOutput("Drawing")]
public class SolidFillBlendedShapesTests public class SolidFillBlendedShapesTests
{ {
public static IEnumerable<object[]> modes = public static IEnumerable<object[]> modes =
((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))).Select(x => new object[] { x }); ((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))).Select(x => new object[] { x });
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect<TPixel>( public void _1DarkBlueRect_2BlendHotPinkRect<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelBlenderMode mode) PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using (Image<TPixel> img = provider.GetImage())
{ {
int scaleX = img.Width / 100; int scaleX = img.Width / 100;
int scaleY = img.Height / 100; int scaleY = img.Height / 100;
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
NamedColors<TPixel>.DarkBlue, NamedColors<TPixel>.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)
) )
.Fill(new GraphicsOptions(true) { BlenderMode = mode }, .Fill(new GraphicsOptions(true) { BlenderMode = mode },
NamedColors<TPixel>.HotPink, NamedColors<TPixel>.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))
); );
VerifyImage(provider, mode, img); VerifyImage(provider, mode, img);
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse<TPixel>( public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelBlenderMode mode) PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using (Image<TPixel> img = provider.GetImage())
{ {
int scaleX = img.Width / 100; int scaleX = img.Width / 100;
int scaleY = img.Height / 100; int scaleY = img.Height / 100;
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
NamedColors<TPixel>.DarkBlue, NamedColors<TPixel>.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode }, new GraphicsOptions(true) { BlenderMode = mode },
NamedColors<TPixel>.HotPink, NamedColors<TPixel>.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode }, new GraphicsOptions(true) { BlenderMode = mode },
NamedColors<TPixel>.Transparent, NamedColors<TPixel>.Transparent,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
); );
VerifyImage(provider, mode, img); VerifyImage(provider, mode, img);
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse<TPixel>( public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelBlenderMode mode) PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using (Image<TPixel> img = provider.GetImage())
{ {
int scaleX = (img.Width / 100); int scaleX = (img.Width / 100);
int scaleY = (img.Height / 100); int scaleY = (img.Height / 100);
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
NamedColors<TPixel>.DarkBlue, NamedColors<TPixel>.DarkBlue,
new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode }, new GraphicsOptions(true) { BlenderMode = mode },
NamedColors<TPixel>.HotPink, NamedColors<TPixel>.HotPink,
new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY)));
var c = NamedColors<TPixel>.Red.ToVector4(); var c = NamedColors<TPixel>.Red.ToVector4();
c.W *= 0.5f; c.W *= 0.5f;
var pixel = default(TPixel); var pixel = default(TPixel);
pixel.PackFromVector4(c); pixel.PackFromVector4(c);
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode }, new GraphicsOptions(true) { BlenderMode = mode },
pixel, pixel,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
); );
VerifyImage(provider, mode, img); ; VerifyImage(provider, mode, img); ;
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode) public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using(Image<TPixel> dstImg = provider.GetImage(), srcImg = provider.GetImage())
{ {
int scaleX = (img.Width / 100); int scaleX = (dstImg.Width / 100);
int scaleY = (img.Height / 100); int scaleY = (dstImg.Height / 100);
img.Mutate(
x => x.Fill( dstImg.Mutate(
NamedColors<TPixel>.DarkBlue, x => x.Fill(
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); NamedColors<TPixel>.DarkBlue,
img.Mutate( new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode }, srcImg.Mutate(
NamedColors<TPixel>.Black, x => x.Fill(
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); NamedColors<TPixel>.Black,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
VerifyImage(provider, mode, img);
} dstImg.Mutate(
} x => x.DrawImage(new GraphicsOptions(true) { BlenderMode = mode }, srcImg)
);
private static void VerifyImage<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode, Image<TPixel> img)
where TPixel : struct, IPixel<TPixel> VerifyImage(provider, mode, dstImg);
{ }
img.DebugSave( }
provider,
new { mode }, private static void VerifyImage<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode, Image<TPixel> img)
appendPixelTypeToFileName: false, where TPixel : struct, IPixel<TPixel>
appendSourceFileOrDescription: false); {
img.DebugSave(
var comparer = ImageComparer.TolerantPercentage(0.01f, 3); provider,
img.CompareFirstFrameToReferenceOutput(comparer, new { mode },
provider, appendPixelTypeToFileName: false,
new { mode }, appendSourceFileOrDescription: false);
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false); var comparer = ImageComparer.TolerantPercentage(0.01f, 3);
} img.CompareFirstFrameToReferenceOutput(comparer,
} provider,
new { mode },
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
}
} }

5
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -17,10 +17,7 @@ namespace SixLabors.ImageSharp.Tests
{ {
public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector;
public static readonly string[] AllBmpFiles = public static readonly string[] AllBmpFiles = All;
{
Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted, Bit16, Bit16Inverted
};
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles = public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit> new TheoryData<string, int, int, PixelResolutionUnit>

21
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs

@ -1,10 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Text;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
@ -22,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(NormalBlendFunctionData))] [MemberData(nameof(NormalBlendFunctionData))]
public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Normal((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.NormalSrcOver((Vector4)back, source, amount);
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
@ -41,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(MultiplyFunctionData))] [MemberData(nameof(MultiplyFunctionData))]
public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.MultiplySrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -60,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(AddFunctionData))] [MemberData(nameof(AddFunctionData))]
public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.MultiplySrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -79,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(SubstractFunctionData))] [MemberData(nameof(SubstractFunctionData))]
public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Subtract((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.SubtractSrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -98,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(ScreenFunctionData))] [MemberData(nameof(ScreenFunctionData))]
public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Screen((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.ScreenSrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -117,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(DarkenFunctionData))] [MemberData(nameof(DarkenFunctionData))]
public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Darken((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.DarkenSrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -136,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(LightenFunctionData))] [MemberData(nameof(LightenFunctionData))]
public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Lighten((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.LightenSrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -155,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(OverlayFunctionData))] [MemberData(nameof(OverlayFunctionData))]
public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Overlay((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.OverlaySrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -174,7 +171,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(HardLightFunctionData))] [MemberData(nameof(HardLightFunctionData))]
public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.HardLight((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.HardLightSrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
} }

113
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs

@ -2,9 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
@ -14,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
{ {
using SixLabors.Memory; using SixLabors.Memory;
public class PorterDuffFunctionsTests_TPixel public class PorterDuffFunctionsTestsTPixel
{ {
private static Span<T> AsSpan<T>(T value) private static Span<T> AsSpan<T>(T value)
where T : struct where T : struct
@ -34,26 +31,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void NormalBlendFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void NormalBlendFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Normal((TPixel)(TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.NormalSrcOver((TPixel)(TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(NormalBlendFunctionData))] [MemberData(nameof(NormalBlendFunctionData))]
public void NormalBlendFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void NormalBlendFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Normal().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.NormalSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(NormalBlendFunctionData))] [MemberData(nameof(NormalBlendFunctionData))]
public void NormalBlendFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void NormalBlendFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Normal().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.NormalSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -73,26 +70,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void MultiplyFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void MultiplyFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Multiply((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.MultiplySrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(MultiplyFunctionData))] [MemberData(nameof(MultiplyFunctionData))]
public void MultiplyFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void MultiplyFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Multiply().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.MultiplySrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(MultiplyFunctionData))] [MemberData(nameof(MultiplyFunctionData))]
public void MultiplyFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void MultiplyFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Multiply().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.MultiplySrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -112,26 +109,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void AddFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void AddFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Add((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.AddSrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(AddFunctionData))] [MemberData(nameof(AddFunctionData))]
public void AddFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void AddFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Add().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.AddSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(AddFunctionData))] [MemberData(nameof(AddFunctionData))]
public void AddFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void AddFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Add().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.AddSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -151,26 +148,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void SubstractFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void SubstractFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Subtract((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.SubtractSrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(SubstractFunctionData))] [MemberData(nameof(SubstractFunctionData))]
public void SubstractFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void SubstractFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Subtract().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.SubtractSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(SubstractFunctionData))] [MemberData(nameof(SubstractFunctionData))]
public void SubstractFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void SubstractFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Subtract().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.SubtractSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -190,26 +187,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void ScreenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void ScreenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Screen((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.ScreenSrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(ScreenFunctionData))] [MemberData(nameof(ScreenFunctionData))]
public void ScreenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void ScreenFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Screen().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.ScreenSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(ScreenFunctionData))] [MemberData(nameof(ScreenFunctionData))]
public void ScreenFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void ScreenFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Screen().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.ScreenSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -229,26 +226,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void DarkenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void DarkenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Darken((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.DarkenSrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(DarkenFunctionData))] [MemberData(nameof(DarkenFunctionData))]
public void DarkenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void DarkenFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Darken().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.DarkenSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(DarkenFunctionData))] [MemberData(nameof(DarkenFunctionData))]
public void DarkenFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void DarkenFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Darken().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.DarkenSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -268,26 +265,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void LightenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void LightenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Lighten((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.LightenSrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(LightenFunctionData))] [MemberData(nameof(LightenFunctionData))]
public void LightenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void LightenFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Lighten().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.LightenSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(LightenFunctionData))] [MemberData(nameof(LightenFunctionData))]
public void LightenFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void LightenFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Lighten().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.LightenSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -307,26 +304,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void OverlayFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void OverlayFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Overlay((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.OverlaySrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(OverlayFunctionData))] [MemberData(nameof(OverlayFunctionData))]
public void OverlayFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void OverlayFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Overlay().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.OverlaySrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(OverlayFunctionData))] [MemberData(nameof(OverlayFunctionData))]
public void OverlayFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void OverlayFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Overlay().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.OverlaySrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -346,26 +343,26 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void HardLightFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void HardLightFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.HardLight((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.HardLightSrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(HardLightFunctionData))] [MemberData(nameof(HardLightFunctionData))]
public void HardLightFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void HardLightFunctionBlender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.HardLight().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.HardLightSrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
[Theory] [Theory]
[MemberData(nameof(HardLightFunctionData))] [MemberData(nameof(HardLightFunctionData))]
public void HardLightFunction_Blender_Bulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void HardLightFunctionBlenderBulk<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); var dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.HardLight().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.HardLightSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
} }

84
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs

@ -17,50 +17,50 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
public static TheoryData<object, Type, PixelBlenderMode> BlenderMappings = new TheoryData<object, Type, PixelBlenderMode>() public static TheoryData<object, Type, PixelBlenderMode> BlenderMappings = new TheoryData<object, Type, PixelBlenderMode>()
{ {
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal), PixelBlenderMode.Normal }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelBlenderMode.Normal },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Screen), PixelBlenderMode.Screen }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcOver), PixelBlenderMode.Screen },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.HardLight), PixelBlenderMode.HardLight }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcOver), PixelBlenderMode.HardLight },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Overlay), PixelBlenderMode.Overlay }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcOver), PixelBlenderMode.Overlay },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Darken), PixelBlenderMode.Darken }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcOver), PixelBlenderMode.Darken },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Lighten), PixelBlenderMode.Lighten }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.LightenSrcOver), PixelBlenderMode.Lighten },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Add), PixelBlenderMode.Add }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.AddSrcOver), PixelBlenderMode.Add },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Subtract), PixelBlenderMode.Subtract }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcOver), PixelBlenderMode.Subtract },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Multiply), PixelBlenderMode.Multiply }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcOver), PixelBlenderMode.Multiply },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Src), PixelBlenderMode.Src }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrc), PixelBlenderMode.Src },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Atop), PixelBlenderMode.Atop }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcAtop), PixelBlenderMode.Atop },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Over), PixelBlenderMode.Over }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelBlenderMode.Over },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.In), PixelBlenderMode.In }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcIn), PixelBlenderMode.In },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Out), PixelBlenderMode.Out }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOut), PixelBlenderMode.Out },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Dest), PixelBlenderMode.Dest }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDest), PixelBlenderMode.Dest },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestAtop), PixelBlenderMode.DestAtop }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestAtop), PixelBlenderMode.DestAtop },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestOver), PixelBlenderMode.DestOver }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestOver), PixelBlenderMode.DestOver },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestIn), PixelBlenderMode.DestIn }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestIn), PixelBlenderMode.DestIn },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestOut), PixelBlenderMode.DestOut }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestOut), PixelBlenderMode.DestOut },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Clear), PixelBlenderMode.Clear }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalClear), PixelBlenderMode.Clear },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Xor), PixelBlenderMode.Xor }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalXor), PixelBlenderMode.Xor },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal), PixelBlenderMode.Normal }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOver), PixelBlenderMode.Normal },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Screen), PixelBlenderMode.Screen }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.ScreenSrcOver), PixelBlenderMode.Screen },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.HardLight), PixelBlenderMode.HardLight }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.HardLightSrcOver), PixelBlenderMode.HardLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Overlay), PixelBlenderMode.Overlay }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.OverlaySrcOver), PixelBlenderMode.Overlay },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Darken), PixelBlenderMode.Darken }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DarkenSrcOver), PixelBlenderMode.Darken },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Lighten), PixelBlenderMode.Lighten }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.LightenSrcOver), PixelBlenderMode.Lighten },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Add), PixelBlenderMode.Add }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.AddSrcOver), PixelBlenderMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Subtract), PixelBlenderMode.Subtract }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.SubtractSrcOver), PixelBlenderMode.Subtract },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Multiply), PixelBlenderMode.Multiply }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelBlenderMode.Multiply },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Src), PixelBlenderMode.Src }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrc), PixelBlenderMode.Src },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Atop), PixelBlenderMode.Atop }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcAtop), PixelBlenderMode.Atop },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Over), PixelBlenderMode.Over }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOver), PixelBlenderMode.Over },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.In), PixelBlenderMode.In }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcIn), PixelBlenderMode.In },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Out), PixelBlenderMode.Out }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOut), PixelBlenderMode.Out },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Dest), PixelBlenderMode.Dest }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDest), PixelBlenderMode.Dest },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestAtop), PixelBlenderMode.DestAtop }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestAtop), PixelBlenderMode.DestAtop },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestOver), PixelBlenderMode.DestOver }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestOver), PixelBlenderMode.DestOver },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestIn), PixelBlenderMode.DestIn }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestIn), PixelBlenderMode.DestIn },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestOut), PixelBlenderMode.DestOut }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestOut), PixelBlenderMode.DestOut },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Clear), PixelBlenderMode.Clear }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalClear), PixelBlenderMode.Clear },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Xor), PixelBlenderMode.Xor }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalXor), PixelBlenderMode.Xor },
}; };

20
tests/ImageSharp.Tests/TestImages.cs

@ -165,12 +165,30 @@ namespace SixLabors.ImageSharp.Tests
public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp";
public const string RLE = "Bmp/RunLengthEncoded.bmp"; public const string RLE = "Bmp/RunLengthEncoded.bmp";
public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp"; public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp";
public const string Bit1 = "Bmp/pal1.bmp";
public const string Bit1Pal1 = "Bmp/pal1p1.bmp";
public const string Bit4 = "Bmp/pal4.bmp";
public const string Bit8 = "Bmp/test8.bmp"; public const string Bit8 = "Bmp/test8.bmp";
public const string Bit8Inverted = "Bmp/test8-inverted.bmp"; public const string Bit8Inverted = "Bmp/test8-inverted.bmp";
public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16 = "Bmp/test16.bmp";
public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp";
public static readonly string[] All = { Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted, Bit16, Bit16Inverted }; public static readonly string[] All
= {
Car,
F,
NegHeight,
CoreHeader,
V5Header, RLE,
RLEInverted,
Bit1,
Bit1Pal1,
Bit4,
Bit8,
Bit8Inverted,
Bit16,
Bit16Inverted
};
} }
public static class Gif public static class Gif

2
tests/Images/External

@ -1 +1 @@
Subproject commit 98fb7e2e4d5935b1c733bd2b206b6145b71ef378 Subproject commit 825220cdc4e9d1b4b3b474c63139e18e1cdb800e

BIN
tests/Images/Input/Bmp/pal1.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
tests/Images/Input/Bmp/pal1p1.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
tests/Images/Input/Bmp/pal4.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Loading…
Cancel
Save