(function () { var ns = $.namespace('pskl.utils'); var colorCache = {}; ns.FrameUtils = { merge : function (frames) { var merged = null; if (frames.length) { merged = frames[0].clone(); var w = merged.getWidth(), h = merged.getHeight(); for (var i = 1 ; i < frames.length ; i++) { pskl.utils.FrameUtils.mergeFrames_(merged, frames[i]); } } return merged; }, mergeFrames_ : function (frameA, frameB) { frameB.forEachPixel(function (p, col, row) { if (p != Constants.TRANSPARENT_COLOR) { frameA.setPixel(col, row, p); } }); }, /** * Alpha compositing using porter duff algorithm : * http://en.wikipedia.org/wiki/Alpha_compositing * http://keithp.com/~keithp/porterduff/p253-porter.pdf * @param {String} strColor1 color over * @param {String} strColor2 color under * @return {String} the composite color */ mergePixels : function (strColor1, strColor2, globalOpacity1) { var col1 = pskl.utils.FrameUtils.toRgba(strColor1); var col2 = pskl.utils.FrameUtils.toRgba(strColor2); if (typeof globalOpacity1 == 'number') { col1 = JSON.parse(JSON.stringify(col1)); col1.a = globalOpacity1 * col1.a; } var a = col1.a + col2.a * (1 - col1.a); var r = ((col1.r * col1.a + col2.r * col2.a * (1 - col1.a)) / a)|0; var g = ((col1.g * col1.a + col2.g * col2.a * (1 - col1.a)) / a)|0; var b = ((col1.b * col1.a + col2.b * col2.a * (1 - col1.a)) / a)|0; return 'rgba('+r+','+g+','+b+','+a+')'; }, /** * Convert a color defined as a string (hex, rgba, rgb, 'TRANSPARENT') to an Object with r,g,b,a properties. * r, g and b are integers between 0 and 255, a is a float between 0 and 1 * @param {String} c color as a string * @return {Object} {r:Number,g:Number,b:Number,a:Number} */ toRgba : function (c) { if (colorCache[c]) { return colorCache[c]; } var color, matches; if (c === 'TRANSPARENT') { color = { r : 0, g : 0, b : 0, a : 0 }; } else if (c.indexOf('rgba(') != -1) { matches = /rgba\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(1|0\.\d+)\s*\)/.exec(c); color = { r : parseInt(matches[1],10), g : parseInt(matches[2],10), b : parseInt(matches[3],10), a : parseFloat(matches[4]) }; } else if (c.indexOf('rgb(') != -1) { matches = /rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.exec(c); color = { r : parseInt(matches[1],10), g : parseInt(matches[2],10), b : parseInt(matches[3],10), a : 1 }; } else { matches = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(c); color = { r : parseInt(matches[1], 16), g : parseInt(matches[2], 16), b : parseInt(matches[3], 16), a : 1 }; } colorCache[c] = color; return color; }, /* * Create a pskl.model.Frame from an Image object. * Transparent pixels will either be converted to completely opaque or completely transparent pixels. * @param {Image} image source image * @return {pskl.model.Frame} corresponding frame */ createFromImage : function (image) { var w = image.width, h = image.height; var canvas = pskl.CanvasUtils.createCanvas(w, h); var context = canvas.getContext('2d'); context.drawImage(image, 0,0,w,h,0,0,w,h); var imgData = context.getImageData(0,0,w,h).data; // Draw the zoomed-up pixels to a different canvas context var frame = []; for (var x=0;x