From 467a34d97f29701463030c2b5c6c775a0a8342ed Mon Sep 17 00:00:00 2001 From: Rogan Creswick Date: Thu, 13 Jun 2013 18:03:15 -0700 Subject: added color combination utilities to the prelude --- src/js/fiveui/injected/prelude.js | 84 ++++++++++++++++++++++++++++++++++----- src/js/tests/specs/prelude.js | 52 ++++++++++++------------ 2 files changed, 100 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/js/fiveui/injected/prelude.js b/src/js/fiveui/injected/prelude.js index 161682f..fbe5307 100644 --- a/src/js/fiveui/injected/prelude.js +++ b/src/js/fiveui/injected/prelude.js @@ -320,7 +320,7 @@ fiveui.color.colorToHex = function(color) { * Covert color to RGB color object. * * @param {!String} color The color string to convert. This should be either of the form rgb(...) or #... - * @returns {!Object} An RGB color object with attributes: r, g, b + * @returns {!Object} An RGB color object with attributes: r, g, b, a * @throws {ParseError} if the rgb color string cannot be parsed */ fiveui.color.colorToRGB = function(color) { @@ -329,20 +329,84 @@ fiveui.color.colorToRGB = function(color) { return fiveui.color.hexToRGB(fiveui.color.colorToHex(color)); } - var digits = /rgba?\((\d+), (\d+), (\d+)(, (\d+))?/.exec(color); + var digits = /rgba?\((\d+), (\d+), (\d+)(, (\d.\d+))?/.exec(color); if (!digits) { throw new ParseError('could not parse color string: ' + color); } - // HACK: return white if transparency is set to 0 - if (digits[5] == 0) { - return { r: 255, g: 255, b: 255 }; - } - else { - return { r: parseInt(digits[1]), - g: parseInt(digits[2]), - b: parseInt(digits[3]) }; + var alpha = 1; + if (digits[5]) { + alpha = parseFloat(digits[5]); + } + + return { r: parseInt(digits[1]), + g: parseInt(digits[2]), + b: parseInt(digits[3]), + a: alpha }; +}; + +/** + * Computationally determine the actual displayed background color for + * an object. This accounts for parent colors that may appear when + * a bg color is unspecified, or fully transparent. + * + * It does not account for elements that are shifted out of their + * parent containers. + * + * @param {!Object} A jquery object. + * @returns {color} an RGB color object. (no alpha - this does not + * return transparent colors) + */ +fiveui.color.findBGColor = function(obj) { + var fc = fiveui.color; + var real = fc.colorToRGB(obj.css('background-color')); + var none = fc.colorToRGB('rgba(0, 0, 0, 0)'); + + if (real.a != 1) { + // find parents with a non-default bg color: + var parents = obj.parents().filter( + function() { + var color = fc.colorToRGB($(this).css('background-color')); + return color != none; + }).map( + function(i) { + return fc.colorToRGB($(this).css('background-color')); + }); + + // push a white element onto the end of parents + parents.push({ r: 255, g: 255, b: 255, a: 1}); + + var colors = []; + for (var i=0; i < parents.length; i++) { + colors.push(parents[i]); + if (parents[i].a == 1) { + break; + } } + + // compose the colors and return: + return _.reduce(colors, fc.alphaCombine, none); + } else { + return real; + } +}; + +/** + * Combines two colors, accounting for alpha values less than 1. + * + * @param {color} top The color "on top" + * @param {color} bot The color "on bottom" + * @return {color} the composite RGBA color. + */ +fiveui.color.alphaCombine = function(top, bot) { + var result = { }; + result.r = Math.floor(top.r * top.a + bot.r * bot.a * (1 - top.a)); + result.g = Math.floor(top.g * top.a + bot.g * bot.a * (1 - top.a)); + result.b = Math.floor(top.b * top.a + bot.b * bot.a * (1 - top.a)); + + result.a = top.a + bot.a * (1 - top.a); + + return result; }; /** diff --git a/src/js/tests/specs/prelude.js b/src/js/tests/specs/prelude.js index 7b37494..93a12cb 100644 --- a/src/js/tests/specs/prelude.js +++ b/src/js/tests/specs/prelude.js @@ -4,16 +4,8 @@ describe('prelude', function() { var addTestSet = function(fn, tests) { _.each(tests, function(test) { it(test[0], function() { - expect(fn(test[1])).toEqual(test[2]); - }); - }); - }; - - // 3 argument version of addTestSet - var addTestSet3 = function(fn, tests) { - _.each(tests, function(test) { - it(test[0], function() { - expect(fn(test[1], test[2], test[3])).toEqual(test[4]); + var args = test.slice(1, test.length - 1); + expect(fn.apply(undefined, args)).toEqual(test[test.length - 1]); }); }); }; @@ -96,28 +88,28 @@ describe('prelude', function() { addTestSet(fiveui.color.hexToRGB, [ // name , input , oracle - ['hexToRGB: full white' , '#000000', { r: 0, g: 0, b: 0 }], - ['hexToRGB: full black' , '#FFFFFF', { r: 255, g: 255, b: 255 }], + ['hexToRGB: full black' , '#000000', { r: 0, g: 0, b: 0 }], + ['hexToRGB: full white' , '#FFFFFF', { r: 255, g: 255, b: 255 }], ['hexToRGB: C7 grey' , '#C7C7C7', { r: 199, g: 199, b: 199 }], ['hexToRGB: full red' , '#FF0000', { r: 255, g: 0, b: 0 }], ['hexToRGB: full blue' , '#0000FF', { r: 0, g: 0, b: 255 }], ]); - addTestSet3(fiveui.color.rgbToHex, [ + addTestSet(fiveui.color.rgbToHex, [ // name , 3 inputs, oracle - ['rgbToHex: full white' , 0, 0, 0, '#000000'], - ['rgbToHex: full black' , 255, 255, 255, '#FFFFFF'], + ['rgbToHex: full black' , 0, 0, 0, '#000000'], + ['rgbToHex: full white' , 255, 255, 255, '#FFFFFF'], ['rgbToHex: C7 grey' , 199, 199, 199, '#C7C7C7'], ['rgbToHex: full red' , 255, 0, 0, '#FF0000'], - ['rgbToHex: full blue' , 0, 0, 255, '#0000FF'], + ['rgbToHex: full blue' , 0, 0, 255, '#0000FF'] ]); addTestSet(fiveui.color.colorToHex, [ - ['colorToHex: full white' , '#000000', '#000000'], - ['colorToHex: abreviated white 1' , '#0', '#000000'], - ['colorToHex: abreviated white 2' , '#00', '#000000'], - ['colorToHex: black' , '#FFFFFF', '#FFFFFF'], - ['colorToHex: abreviated black' , '#FF', '#FFFFFF'], + ['colorToHex: full black' , '#000000', '#000000'], + ['colorToHex: abreviated black 1' , '#0', '#000000'], + ['colorToHex: abreviated black 2' , '#00', '#000000'], + ['colorToHex: white' , '#FFFFFF', '#FFFFFF'], + ['colorToHex: abreviated white' , '#FF', '#FFFFFF'], ['colorToHex: abreviated C7 grey' , '#C7', '#C7C7C7'], ['colorToHex: rgb(0, 0, 0)' , 'rgb(0, 0, 0)', '#000000'], ['colorToHex: rgb(255, 255, 255)' , 'rgb(255, 255, 255)', '#FFFFFF'], @@ -126,11 +118,19 @@ describe('prelude', function() { ]); addTestSet(fiveui.color.colorToRGB, [ - ['colorToRGB: full white' , '#000000', {r: 0, g: 0, b:0} ], - ['colorToRGB: abreviated white 1' , '#0', {r: 0, g: 0, b:0} ], - ['colorToRGB: black' , '#FFFFFF', {r: 255, g: 255, b: 255} ], - ['colorToRGB: rgb(222, 173, 190)' , 'rgb(222, 173, 190)', {r: 222, g: 173, b: 190} ], - ['colorToRGB: rgba(255, 255, 255, 100)', 'rgba(255, 255, 255, 100)', {r: 255, g: 255, b: 255} ], + ['colorToRGB: full black' , '#000000', {r: 0, g: 0, b:0} ], + ['colorToRGB: abreviated black 1' , '#0', {r: 0, g: 0, b:0} ], + ['colorToRGB: white' , '#FFFFFF', {r: 255, g: 255, b: 255} ], + ['colorToRGB: rgb(222, 173, 190)' , 'rgb(222, 173, 190)', {r: 222, g: 173, b: 190, a: 1} ], + ['colorToRGB: rgba(255, 255, 255, 0.7)', 'rgba(255, 255, 255, 0.7)', {r: 255, g: 255, b: 255, a: 0.7 } ] + ]); + + addTestSet(fiveui.color.alphaCombine, [ + ['red and white make pinkish', {r: 255, g: 0, b: 0, a: 0.5}, {r: 255, g: 255, b: 255, a: 1}, + {r: 255, g: 127, b: 127, a: 1} ], + ['red and yellow make orange', {r: 255, g: 0, b: 0, a: 0.5}, {r: 255, g: 255, b: 0, a: 1}, + {r: 255, g: 127, b: 0, a: 1} ] + ]); var getFontTests = [ -- cgit v1.2.3