/** @type {function(string, Object, Function)} */ var createContextBackground = (function() { var canvas = document.createElement("canvas"), ctx = canvas.getContext('2d'), /** @const */ WIDTH = canvas.width = 250, /** @const */ HEIGHT = canvas.height = 290, /** @const */ MARGIN = 20, /** @const */ NB_ITEM = 3, /** @const */ ITEM_SIZE = (WIDTH -2*MARGIN) / NB_ITEM, /** @const */ ITEM_SPACING = 0.1 * ITEM_SIZE, /** @const */ ITEM_INNER_SIZE = Math.floor(ITEM_SIZE -(ITEM_SPACING*2)), /** @const */ AVATAR_SIZE = ITEM_INNER_SIZE * 0.5, /** @type {Object} */ RESULT = {}, drawItem = function(background, avatar, x0, y0) { var y0Index = Math.floor(y0), y1Index = Math.floor(y0 + ITEM_SIZE), topColor = [ background.data[y0Index *WIDTH *4 +0], background.data[y0Index *WIDTH *4 +1], background.data[y0Index *WIDTH *4 +2] ], botColor = [ background.data[y1Index *WIDTH *4 +0], background.data[y1Index *WIDTH *4 +1], background.data[y1Index *WIDTH *4 +2] ], targetPcent = 1.1, targetColor = (((topColor[0] *targetPcent) << 16) | ((topColor[1] *targetPcent) << 8) | (topColor[2] *targetPcent)).toString(16); ctx.fillStyle = '#' +targetColor; ctx.beginPath(); ctx.moveTo(x0 +ITEM_SIZE /2, y0 +ITEM_SPACING); ctx.lineTo(x0 -ITEM_SPACING +ITEM_SIZE, y0 +ITEM_SIZE /2); ctx.lineTo(x0 +ITEM_SIZE /2, y0 -ITEM_SPACING +ITEM_SIZE); ctx.lineTo(x0 +ITEM_SPACING, y0 +ITEM_SIZE /2); ctx.closePath(); ctx.fill(); ctx.putImageData( blend( ctx.getImageData( x0 +ITEM_SPACING, y0 +ITEM_SPACING, ITEM_INNER_SIZE, ITEM_INNER_SIZE), avatar), x0 +ITEM_SPACING, y0 +ITEM_SPACING); }, blend = function(img, img2) { var margin = (img.height -img2.height) /2; for (var i =0; i < img2.height; i++) for (var j =0; j < img2.width; j++) { var img2Grey = (img2.data[(i *img2.width +j) *4]) / 255, iImg = ((i +margin) *img.width +j +margin) *4; img.data[iImg] *= img2Grey; img.data[iImg +1] *= img2Grey; img.data[iImg +2] *= img2Grey; } return img; }, drawBackground = function() { var grd = ctx.createLinearGradient(0, 0, 0, HEIGHT); grd.addColorStop(0, "#4D394B"); grd.addColorStop(1, "#201820"); ctx.fillStyle = grd; ctx.fillRect(0, 0, WIDTH, HEIGHT); return ctx.getImageData(0, 0, WIDTH, HEIGHT); }, filterImage = function(img) { var pixelSum = 0, i; for (i =0; i < img.width *img.height *4; i +=4) { img.data[i] = img.data[i +1] = img.data[i +2] = (img.data[i] +img.data[i +1] +img.data[i +2]) / 3; img.data[i +3] = 50; pixelSum += img.data[i]; } // Invert colors if image is dark if (pixelSum / (img.height * img.width) < 50) for (i =0; i < img.width *img.height *4; i +=4) img.data[i] = img.data[i +1] = img.data[i +2] = 255 - img.data[i]; return img; }, loadImgFromUrl = function(src, cb) { new HttpRequest(src) .callbackSuccess(function(code, head, response) { if (response) { var img = new Image(); img.onload = function() { var imgCanvas = document.createElement("canvas"); imgCanvas.height = imgCanvas.width = AVATAR_SIZE; var imgCtx = imgCanvas.getContext('2d'); imgCtx.drawImage(img, 0, 0, AVATAR_SIZE, AVATAR_SIZE); cb(filterImage(imgCtx.getImageData(0, 0, AVATAR_SIZE, AVATAR_SIZE))); }; img.onerror = function() { cb(null); }; img.src = window.URL.createObjectURL(/** @type {Blob!} */ (response)); } else { cb(null); } }) .callbackError(function() { cb(null); }) .setResponseType(HttpRequestResponseType.BLOB) .send(); }, loadImages = function(userImgs, doneImgLoading) { for (var i =0, nbImgs = userImgs.length; i < nbImgs; i++) { if (userImgs[i].img === undefined) { // Do load image loadImgFromUrl(userImgs[i].src, function(img) { // jshint ignore: line userImgs[i].img = img; loadImages(userImgs, doneImgLoading); }); return; } } var imgs = []; userImgs.forEach(function(i) { if (i.img) imgs.push(i.img); }); doneImgLoading(imgs); }, compareImgs = function(a, b) { if (!a.img) return 1; if (!b.img) return -1; return Math.random() - 0.5; }, renderAvatars = function(background, imgs) { imgs.sort(function(a, b) { return Math.random() - 0.5; }); for (var imgIndex =0, i =MARGIN; i < WIDTH -MARGIN*2; i += ITEM_SIZE) for (var j =0; j +ITEM_SIZE <= HEIGHT; j += ITEM_SIZE) { drawItem(background, imgs[imgIndex], i, j); imgIndex++; if (imgIndex === imgs.length) { imgs.sort(compareImgs); imgIndex = 0; } } }, callbacks = {}, isLoading = {}; return function(ctxId, users, cb) { if (RESULT[ctxId]) { cb(RESULT[ctxId]); } else if (isLoading[ctxId]) { if (!callbacks[ctxId]) callbacks[ctxId] = [ cb ]; else callbacks[ctxId].push(cb); } else { var background = drawBackground(), userImgs = []; isLoading[ctxId] = true; if (!callbacks[ctxId]) callbacks[ctxId] = [ cb ]; else callbacks[ctxId].push(cb); for (var userId in users) { if (!users[userId].deleted && !users[userId].isBot) userImgs.push({ src: users[userId].getSmallIcon() }); } loadImages(userImgs, function(imgs) { renderAvatars(background, imgs); RESULT[ctxId] = canvas.toDataURL(); callbacks[ctxId].forEach(function(i) { i(RESULT[ctxId]); }); }); } }; })();