diff --git a/packages/shader-ast-stdlib/README.md b/packages/shader-ast-stdlib/README.md index e93d22bec4..50741f2566 100644 --- a/packages/shader-ast-stdlib/README.md +++ b/packages/shader-ast-stdlib/README.md @@ -19,6 +19,7 @@ This project is part of the - [Using higher order functions](#using-higher-order-functions) - [API](#api) - [Color](#color) + - [Porter-Duff alpha blending](#porter-duff-alpha-blending) - [Fog](#fog) - [Lighting](#lighting) - [Math](#math) @@ -286,6 +287,26 @@ TODO. For now, please see doc strings in source for details... - `toSRGB` - `luminanceRGB` +### Porter-Duff alpha blending + +Use the `porterDuff` higher order function to define new blend modes. +See +[@thi.ng/porter-duff](https://github.com/thi-ng/umbrella/tree/develop/packages/porter-duff) +for reference. + +12 standard PD operators for `vec4` RGBA colors: + +- `blendSrcOver` +- `blendDestOver` +- `blendSrcIn` +- `blendDestIn` +- `blendSrcOut` +- `blendDestOut` +- `blendSrcAtop` +- `blendDestAtop` +- `blendXor` +- `blendPlus` + ### Fog [/src/fog](https://github.com/thi-ng/umbrella/tree/master/packages/shader-ast-stdlib/src/fog/) diff --git a/packages/shader-ast-stdlib/src/color/porter-duff.ts b/packages/shader-ast-stdlib/src/color/porter-duff.ts new file mode 100644 index 0000000000..a53c732ad1 --- /dev/null +++ b/packages/shader-ast-stdlib/src/color/porter-duff.ts @@ -0,0 +1,73 @@ +import { Fn2 } from "@thi.ng/api"; +import { + $w, + add, + defn, + FLOAT0, + FLOAT1, + FloatTerm, + mul, + ret, + sub +} from "@thi.ng/shader-ast"; +import { clamp01 } from "../math/clamp"; + +/** + * Higher-order Porter-Duff alpha compositing operator. See + * thi.ng/porter-duff for reference. Returns an optimized AST function + * which accepts 2 RGBA colors (`vec4`) and returns blended & clamped + * result (also `vec4`). All built-in PD operators are defined via this + * HOF. + * + * The two given JS functions are used to extract blending coefficients + * for src/dest colors and are called with the alpha components of both + * colors. + * + * @param name function name + * @param fa src coeff fn + * @param fb dest coeff fn + */ +export const porterDuff = ( + name: string, + fa: Fn2, + fb: Fn2 +) => + defn("vec4", name, ["vec4", "vec4"], (a, b) => { + const src = + fa === ZERO ? FLOAT0 : fa === ONE ? a : mul(a, fa($w(a), $w(b))); + const dest = + fb === ZERO ? FLOAT0 : fb === ONE ? b : mul(b, fb($w(a), $w(b))); + return [ + ret( + clamp01( + src === FLOAT0 + ? dest + : dest === FLOAT0 + ? src + : add(src, dest) + ) + ) + ]; + }); + +// coefficient functions + +export const ZERO = () => FLOAT0; +export const ONE = () => FLOAT1; +export const A = (a: FloatTerm) => a; +export const B = (_: FloatTerm, b: FloatTerm) => b; +export const ONE_MINUS_A = (a: FloatTerm) => sub(FLOAT1, a); +export const ONE_MINUS_B = (_: FloatTerm, b: FloatTerm) => sub(FLOAT1, b); + +// standard Porter-Duff operators + +export const blendSrcOver = porterDuff("blendSrcOver", ONE, ONE_MINUS_A); +export const blendDestOver = porterDuff("blendDestOver", ONE_MINUS_B, ONE); +export const blendSrcIn = porterDuff("blendSrcIn", B, ZERO); +export const blendDestIn = porterDuff("blendDestIn", ZERO, A); +export const blendSrcOut = porterDuff("blendSrcOut", ONE_MINUS_B, ZERO); +export const blendDestOut = porterDuff("blendDestOut", ZERO, ONE_MINUS_A); +export const blendSrcAtop = porterDuff("blendSrcAtop", B, ONE_MINUS_A); +export const blendDestAtop = porterDuff("blendDestAtop", ONE_MINUS_B, A); +export const blendXor = porterDuff("blendXor", ONE_MINUS_B, ONE_MINUS_A); +export const blendPlus = porterDuff("blendPlus", ONE, ONE); diff --git a/packages/shader-ast-stdlib/src/index.ts b/packages/shader-ast-stdlib/src/index.ts index 552c93779f..ad140b0c7b 100644 --- a/packages/shader-ast-stdlib/src/index.ts +++ b/packages/shader-ast-stdlib/src/index.ts @@ -2,6 +2,7 @@ export * from "./api"; export * from "./color/linear-srgb"; export * from "./color/luminance"; +export * from "./color/porter-duff"; export * from "./fog/exp"; export * from "./fog/exp2";