Browse Source

progress on pixel conposer/blender combinations

pull/641/head
Vicente Penades 8 years ago
parent
commit
4bb5f9f45e
  1. 4451
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  2. 426
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  3. 159
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  4. 23
      tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs

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

File diff suppressed because it is too large

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

@ -1,237 +1,191 @@
<#
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
#>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// <auto-generated />
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{
internal static partial class PorterDuffFunctions
{
<# void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar)
{
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 <#=name#>(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float fw = (sw * <#=a_s#>) + (bw * <#=a_b#>) + (xw * <#=a_x#>);
// calculate final value
xform = ((<#=blendVar#> * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(fw, Constants.Epsilon);
xform.W = fw;
return xform;
}
<# } #>
<# void GeneratePixelBlenders(string blender) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_Src(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Src(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return SrcAtop(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcOver(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return SrcOver(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcIn(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return SrcIn(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcOut(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return SrcOut(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_Dest(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Dest(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestAtop(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return DestAtop(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestOver(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return DestOver(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestIn(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return DestIn(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestOut(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return DestOut(backdrop, source, <#=blender#>(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, source);
}
[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, 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;
}
<# } #>
#region Blenders
<#
// GenerateVectorCompositor("Src", "source", "Vector4.Zero", "xform");
GenerateVectorCompositor("SrcAtop", "Vector4.Zero", "backdrop", "xform");
GenerateVectorCompositor("SrcOver", "source", "backdrop", "xform");
// GenerateVectorCompositor("SrcIn", "Vector4.Zero", "Vector4.Zero", "xform");
// GenerateVectorCompositor("SrcOut", "source", "Vector4.Zero", "Vector4.Zero");
GenerateVectorCompositor("Dest", "Vector4.Zero", "backdrop", "backdrop");
// GenerateVectorCompositor("DestAtop", "source", "Vector4.Zero", "backdrop");
// GenerateVectorCompositor("DestOver", "source", "backdrop", "backdrop");
// GenerateVectorCompositor("DestIn", "Vector4.Zero", "Vector4.Zero", "backdrop");
GenerateVectorCompositor("DestOut", "Vector4.Zero", "backdrop", "Vector4.Zero");
// GenerateVectorCompositor("Clear", "Vector4.Zero", "Vector4.Zero", "Vector4.Zero");
GenerateVectorCompositor("Xor", "source", "backdrop", "Vector4.Zero");
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);
}
}
#>
#endregion
}
<#
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
#>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// <auto-generated />
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{
internal static partial class PorterDuffFunctions
{
<# void GeneratePixelBlenders(string blender) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_Src(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Atop(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcOver(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Over(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcIn(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return In(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_SrcOut(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Out(backdrop, source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_Dest(Vector4 backdrop, Vector4 source, float opacity)
{
return backdrop;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestAtop(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Atop(source, backdrop, <#=blender#>(source, backdrop));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestOver(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return Over(source, backdrop, <#=blender#>(source, backdrop));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestIn(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return In(source, backdrop, <#=blender#>(source, backdrop));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_DestOut(Vector4 backdrop, Vector4 source, float opacity)
{
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;
}
<# } #>
#region Blenders
<#
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);
}
}
#>
#endregion
}
}

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

@ -179,125 +179,78 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return xform;
}
public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend)
{
// calculate weights
float blendW = dst.W * src.W;
float dstW = dst.W - blendW;
float srcW = src.W - blendW;
// calculate final alpha
float alpha = dstW + srcW + blendW;
// calculate final color
Vector4 color = dst * dstW + src * srcW + blend * blendW;
// unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon);
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;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Src(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float fw = (sw * 1) + (bw * 0) + (xw * 1);
// calculate final value
xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon);
xform.W = fw;
return Vector4.Lerp(backdrop, xform, source.W);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 SrcIn(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
// calculate final value
xform.W = xw;
return Vector4.Lerp(backdrop, xform, source.W);
}
// 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;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 SrcOut(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float fw = sw;
// calculate final value
xform = source;
xform.W = fw;
return Vector4.Lerp(backdrop, xform, source.W);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float fw = (sw * 1) + (bw * 0) + (xw * 1);
// calculate final value
xform = ((backdrop * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon);
xform.W = fw;
return Vector4.Lerp(backdrop, xform, source.W);
public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend)
{
blend.W = dst.W * src.W;
return blend;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 DestOver(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float fw = (sw * 1) + (bw * 1) + (xw * 1);
// calculate final value
xform = ((backdrop * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon);
xform.W = fw;
return Vector4.Lerp(backdrop, xform, source.W);
public static Vector4 Out(Vector4 dst, Vector4 src)
{
// calculate final alpha
src.W = (1 - dst.W) * src.W;
return src;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 DestIn(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float fw = (sw * 0) + (bw * 0) + (xw * 1);
// calculate final value
xform = ((backdrop * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon);
xform.W = fw;
return Vector4.Lerp(backdrop, xform, source.W);
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;
}
/// <summary>
/// General composition function for all modes, with a general solution for alpha channel
/// </summary>
/// <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>
/// <returns>The final color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 Clear(Vector4 backdrop, Vector4 source, Vector4 xform)
private static Vector4 Clear(Vector4 backdrop, Vector4 source)
{
return Vector4.Lerp(backdrop, Vector4.Zero, xform.W);
return Vector4.Lerp(backdrop, Vector4.Zero, source.W);
}
#endregion

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

@ -116,21 +116,26 @@ namespace SixLabors.ImageSharp.Tests.Drawing
public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
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 scaleY = (img.Height / 100);
img.Mutate(
int scaleX = (dstImg.Width / 100);
int scaleY = (dstImg.Height / 100);
dstImg.Mutate(
x => x.Fill(
NamedColors<TPixel>.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
img.Mutate(
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
srcImg.Mutate(
x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode },
NamedColors<TPixel>.Black,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
dstImg.Mutate(
x => x.DrawImage(new GraphicsOptions(true) { BlenderMode = mode }, srcImg)
);
VerifyImage(provider, mode, img);
VerifyImage(provider, mode, dstImg);
}
}

Loading…
Cancel
Save