From 61ff14b63c51ea2ba1687747a8ced35cd4bde0d4 Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Sat, 30 Nov 2013 00:15:13 +0400 Subject: [PATCH 1/4] color-blending-with-transparency: initial implementation --- lib/less/functions.js | 139 +++++++++++++++++++++++------------------ test/css/functions.css | 2 +- 2 files changed, 80 insertions(+), 61 deletions(-) diff --git a/lib/less/functions.js b/lib/less/functions.js index 3e38d5ebe..85189bfa6 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -367,66 +367,6 @@ tree.functions = { _isa: function (n, Type) { return (n instanceof Type) ? tree.True : tree.False; }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, tint: function(color, amount) { return this.mix(this.rgb(255,255,255), color, amount); }, @@ -637,6 +577,85 @@ for(var i = 0; i < mathFunctions.length; i++) { tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); } +// Color Blending +// ref: http://www.w3.org/TR/compositing-1 + +function colorBlend(color1, color2, mode) { + var ab = color1.alpha, cb, // backdrop + as = color2.alpha, cs, // source + ar, cr, r = []; // result + + ar = as + ab * (1 - as); + for (var i = 0; i < 3; i++) { + cb = color1.rgb[i] / 255; + cs = color2.rgb[i] / 255; + cr = mode(cb, cs); + if (ar > 0) { + cr = (as * cs + ab * (cb + - as * (cb + cs - cr))) / ar; + } + r[i] = cr * 255; + } + + return new(tree.Color)(r, ar); +} + +var colorBlendMode = { + multiply: function(cb, cs) { + return cb * cs; + }, + screen: function(cb, cs) { + return cb + cs - cb * cs; + }, + overlay: function(cb, cs) { + cb *= 2; + return (cb <= 1) + ? colorBlendMode.multiply(cb, cs) + : colorBlendMode.screen(cb - 1, cs); + }, + softlight: function(cb, cs) { + var d = 1, e = cb; + if (cs > 0.5) { + e = 1; + d = (cb > 0.25) ? Math.sqrt(cb) + : ((16 * cb - 12) * cb + 4) * cb; + } + return cb - (1 - 2 * cs) * e * (d - cb); + }, + hardlight: function(cb, cs) { + return colorBlendMode.overlay(cs, cb); + }, + difference: function(cb, cs) { + return Math.abs(cb - cs); + }, + exclusion: function(cb, cs) { + return cb + cs - 2 * cb * cs; + }, + + // non-w3c functions: + average: function(cb, cs) { + return (cb + cs) / 2; + }, + negation: function(cb, cs) { + return 1 - Math.abs(cb + cs - 1); + } +}; + +function colorBlendInit() { + function f(m) { + return function(c1, c2) { + return colorBlend(c1, c2, m); + }; + } + + for (var m in colorBlendMode) { + tree.functions[m] = f(colorBlendMode[m]); + } + +} colorBlendInit(); + +// ~ End of Color Blending + function hsla(color) { return tree.functions.hsla(color.h, color.s, color.l, color.a); } diff --git a/test/css/functions.css b/test/css/functions.css index 9694a7a3b..5c899072a 100644 --- a/test/css/functions.css +++ b/test/css/functions.css @@ -126,7 +126,7 @@ multiply: #ed0000; screen: #f600f6; overlay: #ed0000; - softlight: #ff0000; + softlight: #fa0000; hardlight: #0000ed; difference: #f600f6; exclusion: #f600f6; From 28e5295dae637b00233f9ff57224c059f666ebe8 Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Mon, 2 Dec 2013 01:54:59 +0400 Subject: [PATCH 2/4] color-blending-with-transparency: fixed negative result alpha handling (though allowing out-of-range alpha inputs is subject for further discussions/decision) --- lib/less/functions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/less/functions.js b/lib/less/functions.js index 85189bfa6..39127fc6a 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -586,11 +586,12 @@ function colorBlend(color1, color2, mode) { ar, cr, r = []; // result ar = as + ab * (1 - as); + console.log(ar); for (var i = 0; i < 3; i++) { cb = color1.rgb[i] / 255; cs = color2.rgb[i] / 255; cr = mode(cb, cs); - if (ar > 0) { + if (ar) { cr = (as * cs + ab * (cb - as * (cb + cs - cr))) / ar; } From f835b1701cf6d2ba7c2f3289263a40d9cde76cac Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Tue, 3 Dec 2013 04:58:07 +0400 Subject: [PATCH 3/4] color-blending-with-transparency: removed debug log statement mistakenly left out --- lib/less/functions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/less/functions.js b/lib/less/functions.js index 39127fc6a..1704d5fdb 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -586,7 +586,6 @@ function colorBlend(color1, color2, mode) { ar, cr, r = []; // result ar = as + ab * (1 - as); - console.log(ar); for (var i = 0; i < 3; i++) { cb = color1.rgb[i] / 255; cs = color2.rgb[i] / 255; From 1f57576d903781d20bd31a31a0f00410b2d8282f Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Sat, 7 Dec 2013 18:51:07 +0400 Subject: [PATCH 4/4] color-blending-with-transparency: changed func dispatch method from closure to `bind()` --- lib/less/functions.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/less/functions.js b/lib/less/functions.js index 1704d5fdb..20aab9885 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -580,7 +580,7 @@ for(var i = 0; i < mathFunctions.length; i++) { // Color Blending // ref: http://www.w3.org/TR/compositing-1 -function colorBlend(color1, color2, mode) { +function colorBlend(mode, color1, color2) { var ab = color1.alpha, cb, // backdrop as = color2.alpha, cs, // source ar, cr, r = []; // result @@ -642,16 +642,9 @@ var colorBlendMode = { }; function colorBlendInit() { - function f(m) { - return function(c1, c2) { - return colorBlend(c1, c2, m); - }; - } - - for (var m in colorBlendMode) { - tree.functions[m] = f(colorBlendMode[m]); + for (var f in colorBlendMode) { + tree.functions[f] = colorBlend.bind(null, colorBlendMode[f]); } - } colorBlendInit(); // ~ End of Color Blending