mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 222 additions and 0 deletions
@ -0,0 +1,15 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl.Processing; |
|||
|
|||
internal sealed class JxlAlphaBlendingInputLayer |
|||
{ |
|||
public ReadOnlyMemory<float> R { get; set; } |
|||
|
|||
public ReadOnlyMemory<float> G { get; set; } |
|||
|
|||
public ReadOnlyMemory<float> B { get; set; } |
|||
|
|||
public ReadOnlyMemory<float> A { get; set; } |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl.Processing; |
|||
|
|||
internal sealed class JxlAlphaBlendingOutput |
|||
{ |
|||
public Memory<float> R { get; set; } |
|||
|
|||
public Memory<float> G { get; set; } |
|||
|
|||
public Memory<float> B { get; set; } |
|||
|
|||
public Memory<float> A { get; set; } |
|||
} |
|||
@ -0,0 +1,192 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl.Processing; |
|||
|
|||
internal sealed class JxlAlphaHelper |
|||
{ |
|||
// TODO: SIMD support
|
|||
private const float SmallAlpha = 1f / (1 << 26); |
|||
|
|||
// Force x to stay within the range of 0 through 1
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static float Clamp(float x) => Math.Clamp(x, 0f, 1f); |
|||
|
|||
public static void PerformAlphaBlending( |
|||
JxlAlphaBlendingInputLayer background, |
|||
JxlAlphaBlendingInputLayer foreground, |
|||
JxlAlphaBlendingOutput output, |
|||
int pixelCount, |
|||
bool alphaIsPremultiplied, |
|||
bool clamp) |
|||
{ |
|||
// Store all channels from all parameters into spans, because
|
|||
// creating a Span<T> from Memory<T> is expensive, especially
|
|||
// in a loop.
|
|||
ReadOnlySpan<float> fgR = foreground.R.Span; |
|||
ReadOnlySpan<float> fgG = foreground.G.Span; |
|||
ReadOnlySpan<float> fgB = foreground.B.Span; |
|||
ReadOnlySpan<float> fgA = foreground.A.Span; |
|||
|
|||
ReadOnlySpan<float> bgR = background.R.Span; |
|||
ReadOnlySpan<float> bgG = background.G.Span; |
|||
ReadOnlySpan<float> bgB = background.B.Span; |
|||
ReadOnlySpan<float> bgA = background.A.Span; |
|||
|
|||
Span<float> outR = output.R.Span; |
|||
Span<float> outG = output.G.Span; |
|||
Span<float> outB = output.B.Span; |
|||
Span<float> outA = output.A.Span; |
|||
|
|||
if (alphaIsPremultiplied) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float fga = clamp ? Clamp(fgA[x]) : fgA[x]; |
|||
outR[x] = fgR[x] + (bgR[x] * (1f - fga)); |
|||
outG[x] = fgG[x] + (bgG[x] * (1f - fga)); |
|||
outB[x] = fgB[x] + (bgB[x] * (1f - fga)); |
|||
outA[x] = 1f - ((1f - fga) * (1f - bgA[x])); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float fga = clamp ? Clamp(fgA[x]) : fgA[x]; |
|||
float newA = 1f - ((1f - fga) * (1f - bgA[x])); |
|||
float rnewA = newA > 0 ? 1f / newA : 0f; |
|||
outR[x] = ((fgR[x] * fga) + (bgR[x] * bgA[x] * (1f - fga))) * rnewA; |
|||
outG[x] = ((fgG[x] * fga) + (bgG[x] * bgA[x] * (1f - fga))) * rnewA; |
|||
outB[x] = ((fgB[x] * fga) + (bgB[x] * bgA[x] * (1f - fga))) * rnewA; |
|||
outA[x] = newA; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void PerformAlphaBlending( |
|||
ReadOnlySpan<float> bg, |
|||
ReadOnlySpan<float> bga, |
|||
ReadOnlySpan<float> fg, |
|||
ReadOnlySpan<float> fga, |
|||
Span<float> output, |
|||
int pixelCount, |
|||
bool alphaIsPremultiplied, |
|||
bool clamp) |
|||
{ |
|||
if (bg == bga && fg == fga) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float fa = clamp ? Clamp(fga[x]) : fga[x]; |
|||
output[x] = 1f - ((1f - fa) * (1f - bga[x])); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (alphaIsPremultiplied) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float fa = clamp ? Clamp(fga[x]) : fga[x]; |
|||
output[x] = fg[x] + (bg[x] * (1f - fa)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float fa = clamp ? Clamp(fga[x]) : fga[x]; |
|||
float new_a = 1f - ((1f - fa) * (1f - bga[x])); |
|||
float rnew_a = new_a > 0 ? 1f / new_a : 0f; |
|||
output[x] = ((fg[x] * fa) + (bg[x] * bga[x] * (1f - fa))) * rnew_a; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void PerformAlphaWeightedAdd( |
|||
ReadOnlySpan<float> bg, |
|||
ReadOnlySpan<float> fg, |
|||
ReadOnlySpan<float> fga, |
|||
Span<float> output, |
|||
int pixelCount, |
|||
bool clamp) |
|||
{ |
|||
if (fg == fga) |
|||
{ |
|||
bg[pixelCount..].CopyTo(output); |
|||
} |
|||
else if (clamp) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
output[x] = bg[x] + (fg[x] * Clamp(fga[x])); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelCount; ++x) |
|||
{ |
|||
output[x] = bg[x] + (fg[x] * fga[x]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void PerformMultiplyBlending( |
|||
ReadOnlySpan<float> bg, |
|||
ReadOnlySpan<float> fg, |
|||
Span<float> output, |
|||
int pixelCount, |
|||
bool clamp) |
|||
{ |
|||
if (clamp) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
output[x] = bg[x] * Clamp(fg[x]); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
output[x] = bg[x] * fg[x]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void PremultiplyAlpha( |
|||
Span<float> r, |
|||
Span<float> g, |
|||
Span<float> b, |
|||
ReadOnlySpan<float> a, |
|||
int pixelCount) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float multiplier = Math.Max(SmallAlpha, a[x]); |
|||
r[x] *= multiplier; |
|||
g[x] *= multiplier; |
|||
b[x] *= multiplier; |
|||
} |
|||
} |
|||
|
|||
public static void UnpremultiplyAlpha( |
|||
Span<float> r, |
|||
Span<float> g, |
|||
Span<float> b, |
|||
ReadOnlySpan<float> a, |
|||
int pixelCount) |
|||
{ |
|||
for (int x = 0; x < pixelCount; x++) |
|||
{ |
|||
float multiplier = 1f / Math.Max(SmallAlpha, a[x]); |
|||
r[x] *= multiplier; |
|||
g[x] *= multiplier; |
|||
b[x] *= multiplier; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue