mirror of https://github.com/adamdruppe/arsd.git
Refactor blending functions
This commit is contained in:
parent
1aae3674dc
commit
0b2481bab6
532
pixmappaint.d
532
pixmappaint.d
|
@ -846,7 +846,7 @@ in (opacity <= 1.0) {
|
||||||
pixmap.opacity = opacity255;
|
pixmap.opacity = opacity255;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== Alpha-blending functions ====
|
// ==== Blending functions ====
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Alpha-blending accuracy level
|
Alpha-blending accuracy level
|
||||||
|
@ -882,66 +882,6 @@ enum BlendAccuracy {
|
||||||
rgba = true,
|
rgba = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
public void alphaBlend(
|
|
||||||
BlendAccuracy accuracy,
|
|
||||||
ubyte function(const ubyte, const ubyte) blend = null,
|
|
||||||
)(
|
|
||||||
scope Pixel[] target,
|
|
||||||
scope const Pixel[] source,
|
|
||||||
) @trusted
|
|
||||||
in (source.length == target.length) {
|
|
||||||
foreach (immutable idx, ref pxTarget; target) {
|
|
||||||
alphaBlend(pxTarget, source.ptr[idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
public void alphaBlend(scope Pixel[] target, scope const Pixel[] source) @safe {
|
|
||||||
return alphaBlend!(BlendAccuracy.rgba, null)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
public void alphaBlend(
|
|
||||||
BlendAccuracy accuracy,
|
|
||||||
ubyte function(const ubyte, const ubyte) blend = null,
|
|
||||||
)(
|
|
||||||
ref Pixel pxTarget,
|
|
||||||
const Pixel pxSource,
|
|
||||||
) @trusted {
|
|
||||||
pragma(inline, true);
|
|
||||||
|
|
||||||
static if (accuracy) {
|
|
||||||
immutable alphaResult = clamp255(pxSource.a + n255thsOf(pxTarget.a, (0xFF - pxSource.a)));
|
|
||||||
//immutable alphaResult = clamp255(pxTarget.a + n255thsOf(pxSource.a, (0xFF - pxTarget.a)));
|
|
||||||
}
|
|
||||||
|
|
||||||
immutable alphaSource = (pxSource.a | (pxSource.a << 8));
|
|
||||||
immutable alphaTarget = (0xFFFF - alphaSource);
|
|
||||||
|
|
||||||
foreach (immutable ib, ref px; pxTarget.components) {
|
|
||||||
static if (blend !is null) {
|
|
||||||
immutable bx = blend(px, pxSource.components.ptr[ib]);
|
|
||||||
} else {
|
|
||||||
immutable bx = pxSource.components.ptr[ib];
|
|
||||||
}
|
|
||||||
immutable d = cast(ubyte)(((px * alphaTarget) + 0x8080) >> 16);
|
|
||||||
immutable s = cast(ubyte)(((bx * alphaSource) + 0x8080) >> 16);
|
|
||||||
px = cast(ubyte)(d + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (accuracy) {
|
|
||||||
pxTarget.a = alphaResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
public void alphaBlend(ref Pixel pxTarget, const Pixel pxSource) @safe {
|
|
||||||
return alphaBlend!(BlendAccuracy.rgba, null)(pxTarget, pxSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== Blending functions ====
|
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Blend modes
|
Blend modes
|
||||||
|
|
||||||
|
@ -1002,222 +942,292 @@ alias Blend = BlendMode;
|
||||||
// undocumented
|
// undocumented
|
||||||
enum blendNormal = BlendMode.normal;
|
enum blendNormal = BlendMode.normal;
|
||||||
|
|
||||||
|
///
|
||||||
|
alias BlendFn = ubyte function(const ubyte background, const ubyte foreground) pure nothrow @nogc;
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Blends pixel `source` into pixel `target`.
|
Blends `source` into `target`
|
||||||
|
with respect to the opacity of the source image (as stored in the alpha channel).
|
||||||
|
|
||||||
|
See_Also:
|
||||||
|
[alphaBlendRGBA] and [alphaBlendRGB] are shorthand functions
|
||||||
|
in cases where no special blending algorithm is needed.
|
||||||
+/
|
+/
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
template alphaBlend(BlendFn blend = null, BlendAccuracy accuracy = BlendAccuracy.rgba) {
|
||||||
ref Pixel target,
|
/// ditto
|
||||||
const Pixel source,
|
public void alphaBlend(scope Pixel[] target, scope const Pixel[] source) @trusted
|
||||||
) if (mode == BlendMode.replace) {
|
in (source.length == target.length) {
|
||||||
target = source;
|
foreach (immutable idx, ref pxTarget; target) {
|
||||||
}
|
alphaBlend(pxTarget, source.ptr[idx]);
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.alpha) {
|
|
||||||
return alphaBlend!accuracy(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.multiply) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(a, b) => n255thsOf(a, b)
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.screen) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(a, b) => castTo!ubyte(0xFF - n255thsOf((0xFF - a), (0xFF - b)))
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.darken) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(a, b) => min(a, b)
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.lighten) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(a, b) => max(a, b)
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.overlay) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
|
||||||
if (b < 0x80) {
|
|
||||||
return n255thsOf((2 * b).castTo!ubyte, f);
|
|
||||||
}
|
}
|
||||||
return castTo!ubyte(
|
}
|
||||||
0xFF - n255thsOf(castTo!ubyte(2 * (0xFF - b)), (0xFF - f))
|
|
||||||
);
|
|
||||||
})(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
public void alphaBlend(ref Pixel pxTarget, const Pixel pxSource) @trusted {
|
||||||
ref Pixel target,
|
pragma(inline, true);
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.hardLight) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
static if (accuracy == BlendAccuracy.rgba) {
|
||||||
if (f < 0x80) {
|
immutable alphaResult = clamp255(pxSource.a + n255thsOf(pxTarget.a, (0xFF - pxSource.a)));
|
||||||
return n255thsOf(castTo!ubyte(2 * f), b);
|
//immutable alphaResult = clamp255(pxTarget.a + n255thsOf(pxSource.a, (0xFF - pxTarget.a)));
|
||||||
}
|
|
||||||
return castTo!ubyte(
|
|
||||||
0xFF - n255thsOf(castTo!ubyte(2 * (0xFF - f)), (0xFF - b))
|
|
||||||
);
|
|
||||||
})(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.softLight) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
|
||||||
if (f < 0x80) {
|
|
||||||
// dfmt off
|
|
||||||
return castTo!ubyte(
|
|
||||||
b - n255thsOf(
|
|
||||||
n255thsOf((0xFF - 2 * f).castTo!ubyte, b),
|
|
||||||
(0xFF - b),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
// dfmt on
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dfmt off
|
immutable alphaSource = (pxSource.a | (pxSource.a << 8));
|
||||||
immutable ubyte d = (b < 0x40)
|
immutable alphaTarget = (0xFFFF - alphaSource);
|
||||||
? castTo!ubyte((b * (0x3FC + (((16 * b - 0xBF4) * b) / 255))) / 255)
|
|
||||||
: intNormalizedSqrt(b);
|
|
||||||
//dfmt on
|
|
||||||
|
|
||||||
return castTo!ubyte(
|
foreach (immutable ib, ref px; pxTarget.components) {
|
||||||
b + n255thsOf((2 * f - 0xFF).castTo!ubyte, (d - b).castTo!ubyte)
|
static if (blend !is null) {
|
||||||
);
|
immutable bx = blend(px, pxSource.components.ptr[ib]);
|
||||||
})(target, source);
|
} else {
|
||||||
}
|
immutable bx = pxSource.components.ptr[ib];
|
||||||
|
}
|
||||||
/// ditto
|
immutable d = cast(ubyte)(((px * alphaTarget) + 0x8080) >> 16);
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
immutable s = cast(ubyte)(((bx * alphaSource) + 0x8080) >> 16);
|
||||||
ref Pixel target,
|
px = cast(ubyte)(d + s);
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.colorDodge) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
|
||||||
if (b == 0x00) {
|
|
||||||
return ubyte(0x00);
|
|
||||||
}
|
|
||||||
if (f == 0xFF) {
|
|
||||||
return ubyte(0xFF);
|
|
||||||
}
|
|
||||||
return min(
|
|
||||||
ubyte(0xFF),
|
|
||||||
clamp255((255 * b) / (0xFF - f))
|
|
||||||
);
|
|
||||||
})(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.colorBurn) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
|
||||||
if (b == 0xFF) {
|
|
||||||
return ubyte(0xFF);
|
|
||||||
}
|
|
||||||
if (f == 0x00) {
|
|
||||||
return ubyte(0x00);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
immutable m = min(
|
static if (accuracy == BlendAccuracy.rgba) {
|
||||||
ubyte(0xFF),
|
pxTarget.a = alphaResult;
|
||||||
clamp255(((0xFF - b) * 255) / f)
|
}
|
||||||
);
|
}
|
||||||
return castTo!ubyte(0xFF - m);
|
|
||||||
})(target, source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
template alphaBlend(BlendAccuracy accuracy, BlendFn blend = null) {
|
||||||
ref Pixel target,
|
alias alphaBlend = alphaBlend!(blend, accuracy);
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.difference) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(b, f) => (b > f) ? castTo!ubyte(b - f) : castTo!ubyte(f - b)
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.exclusion) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(b, f) => castTo!ubyte(b + f - (2 * n255thsOf(f, b)))
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.subtract) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(b, f) => (b > f) ? castTo!ubyte(b - f) : ubyte(0)
|
|
||||||
)(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
void blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
|
||||||
ref Pixel target,
|
|
||||||
const Pixel source,
|
|
||||||
) if (mode == Blend.divide) {
|
|
||||||
|
|
||||||
return alphaBlend!(accuracy,
|
|
||||||
(b, f) => (f == 0) ? ubyte(0xFF) : clamp255(0xFF * b / f)
|
|
||||||
)(target, source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Blends the pixel data of `source` into `target`.
|
Blends `source` into `target`
|
||||||
|
with respect to the opacity of the source image (as stored in the alpha channel).
|
||||||
|
|
||||||
|
This variant is $(slower than) [alphaBlendRGB],
|
||||||
|
but calculates the correct alpha-channel value of the target.
|
||||||
|
See [BlendAccuracy] for further explanation.
|
||||||
|
+/
|
||||||
|
public void alphaBlendRGBA(scope Pixel[] target, scope const Pixel[] source) @safe {
|
||||||
|
return alphaBlend!(null, BlendAccuracy.rgba)(target, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
public void alphaBlendRGBA(ref Pixel pxTarget, const Pixel pxSource) @safe {
|
||||||
|
return alphaBlend!(null, BlendAccuracy.rgba)(pxTarget, pxSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Blends `source` into `target`
|
||||||
|
with respect to the opacity of the source image (as stored in the alpha channel).
|
||||||
|
|
||||||
|
This variant is $(B faster than) [alphaBlendRGBA],
|
||||||
|
but leads to a wrong alpha-channel value in the target.
|
||||||
|
Useful because of the performance advantage in cases where the resulting
|
||||||
|
alpha does not matter.
|
||||||
|
See [BlendAccuracy] for further explanation.
|
||||||
|
+/
|
||||||
|
public void alphaBlendRGB(scope Pixel[] target, scope const Pixel[] source) @safe {
|
||||||
|
return alphaBlend!(null, BlendAccuracy.rgb)(target, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
public void alphaBlendRGB(ref Pixel pxTarget, const Pixel pxSource) @safe {
|
||||||
|
return alphaBlend!(null, BlendAccuracy.rgb)(pxTarget, pxSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Blends pixel `source` into pixel `target`
|
||||||
|
using the requested $(B blending mode).
|
||||||
|
+/
|
||||||
|
template blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba) {
|
||||||
|
|
||||||
|
static if (mode == BlendMode.replace) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel(ref Pixel target, const Pixel source) {
|
||||||
|
target = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.alpha) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!accuracy(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.multiply) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(a, b) => n255thsOf(a, b)
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.screen) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(a, b) => castTo!ubyte(0xFF - n255thsOf((0xFF - a), (0xFF - b)))
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.darken) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(a, b) => min(a, b)
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static if (mode == BlendMode.lighten) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(a, b) => max(a, b)
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.overlay) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
||||||
|
if (b < 0x80) {
|
||||||
|
return n255thsOf((2 * b).castTo!ubyte, f);
|
||||||
|
}
|
||||||
|
return castTo!ubyte(
|
||||||
|
0xFF - n255thsOf(castTo!ubyte(2 * (0xFF - b)), (0xFF - f))
|
||||||
|
);
|
||||||
|
})(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.hardLight) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
||||||
|
if (f < 0x80) {
|
||||||
|
return n255thsOf(castTo!ubyte(2 * f), b);
|
||||||
|
}
|
||||||
|
return castTo!ubyte(
|
||||||
|
0xFF - n255thsOf(castTo!ubyte(2 * (0xFF - f)), (0xFF - b))
|
||||||
|
);
|
||||||
|
})(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.softLight) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
||||||
|
if (f < 0x80) {
|
||||||
|
// dfmt off
|
||||||
|
return castTo!ubyte(
|
||||||
|
b - n255thsOf(
|
||||||
|
n255thsOf((0xFF - 2 * f).castTo!ubyte, b),
|
||||||
|
(0xFF - b),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// dfmt on
|
||||||
|
}
|
||||||
|
|
||||||
|
// dfmt off
|
||||||
|
immutable ubyte d = (b < 0x40)
|
||||||
|
? castTo!ubyte((b * (0x3FC + (((16 * b - 0xBF4) * b) / 255))) / 255)
|
||||||
|
: intNormalizedSqrt(b);
|
||||||
|
//dfmt on
|
||||||
|
|
||||||
|
return castTo!ubyte(
|
||||||
|
b + n255thsOf((2 * f - 0xFF).castTo!ubyte, (d - b).castTo!ubyte)
|
||||||
|
);
|
||||||
|
})(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.colorDodge) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
||||||
|
if (b == 0x00) {
|
||||||
|
return ubyte(0x00);
|
||||||
|
}
|
||||||
|
if (f == 0xFF) {
|
||||||
|
return ubyte(0xFF);
|
||||||
|
}
|
||||||
|
return min(
|
||||||
|
ubyte(0xFF),
|
||||||
|
clamp255((255 * b) / (0xFF - f))
|
||||||
|
);
|
||||||
|
})(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.colorBurn) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy, function(const ubyte b, const ubyte f) {
|
||||||
|
if (b == 0xFF) {
|
||||||
|
return ubyte(0xFF);
|
||||||
|
}
|
||||||
|
if (f == 0x00) {
|
||||||
|
return ubyte(0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
immutable m = min(
|
||||||
|
ubyte(0xFF),
|
||||||
|
clamp255(((0xFF - b) * 255) / f)
|
||||||
|
);
|
||||||
|
return castTo!ubyte(0xFF - m);
|
||||||
|
})(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.difference) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(b, f) => (b > f) ? castTo!ubyte(b - f) : castTo!ubyte(f - b)
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.exclusion) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(b, f) => castTo!ubyte(b + f - (2 * n255thsOf(f, b)))
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.subtract) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(b, f) => (b > f) ? castTo!ubyte(b - f) : ubyte(0)
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (mode == BlendMode.divide) {
|
||||||
|
/// ditto
|
||||||
|
void blendPixel()(ref Pixel target, const Pixel source) {
|
||||||
|
return alphaBlend!(accuracy,
|
||||||
|
(b, f) => (f == 0) ? ubyte(0xFF) : clamp255(0xFF * b / f)
|
||||||
|
)(target, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Blends the pixel data of `source` into `target`
|
||||||
|
using the requested $(B blending mode).
|
||||||
|
|
||||||
`source` and `target` MUST have the same length.
|
`source` and `target` MUST have the same length.
|
||||||
+/
|
+/
|
||||||
void blendPixels(BlendMode mode, BlendAccuracy accuracy)(scope Pixel[] target, scope const Pixel[] source) @trusted
|
void blendPixels(
|
||||||
|
BlendMode mode,
|
||||||
|
BlendAccuracy accuracy,
|
||||||
|
)(scope Pixel[] target, scope const Pixel[] source) @trusted
|
||||||
in (source.length == target.length) {
|
in (source.length == target.length) {
|
||||||
static if (mode == BlendMode.replace) {
|
static if (mode == BlendMode.replace) {
|
||||||
// explicit optimization
|
// explicit optimization
|
||||||
|
@ -1236,11 +1246,7 @@ in (source.length == target.length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
void blendPixels(BlendAccuracy accuracy = BlendAccuracy.rgba)(
|
void blendPixels(BlendAccuracy accuracy)(scope Pixel[] target, scope const Pixel[] source, BlendMode mode) {
|
||||||
scope Pixel[] target,
|
|
||||||
scope const Pixel[] source,
|
|
||||||
BlendMode mode,
|
|
||||||
) {
|
|
||||||
import std.meta : NoDuplicates;
|
import std.meta : NoDuplicates;
|
||||||
import std.traits : EnumMembers;
|
import std.traits : EnumMembers;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue