1
0

contextBackground.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /** @type {function(string, Object<string, Chatter>, Function)} */
  2. var createContextBackground = (function() {
  3. var canvas = document.createElement("canvas"),
  4. ctx = canvas.getContext('2d'),
  5. /** @const */
  6. WIDTH = canvas.width = 250,
  7. /** @const */
  8. HEIGHT = canvas.height = 290,
  9. /** @const */
  10. MARGIN = 20,
  11. /** @const */
  12. NB_ITEM = 3,
  13. /** @const */
  14. ITEM_SIZE = (WIDTH -2*MARGIN) / NB_ITEM,
  15. /** @const */
  16. ITEM_SPACING = 0.1 * ITEM_SIZE,
  17. /** @const */
  18. ITEM_INNER_SIZE = Math.floor(ITEM_SIZE -(ITEM_SPACING*2)),
  19. /** @const */
  20. AVATAR_SIZE = ITEM_INNER_SIZE * 0.5,
  21. /** @type {Object<string, string>} */
  22. RESULT = {},
  23. drawItem = function(background, avatar, x0, y0) {
  24. var y0Index = Math.floor(y0),
  25. y1Index = Math.floor(y0 + ITEM_SIZE),
  26. topColor = [
  27. background.data[y0Index *WIDTH *4 +0],
  28. background.data[y0Index *WIDTH *4 +1],
  29. background.data[y0Index *WIDTH *4 +2]
  30. ],
  31. botColor = [
  32. background.data[y1Index *WIDTH *4 +0],
  33. background.data[y1Index *WIDTH *4 +1],
  34. background.data[y1Index *WIDTH *4 +2]
  35. ],
  36. targetPcent = 1.1,
  37. targetColor = (((topColor[0] *targetPcent) << 16) |
  38. ((topColor[1] *targetPcent) << 8) |
  39. (topColor[2] *targetPcent)).toString(16);
  40. ctx.fillStyle = '#' +targetColor;
  41. ctx.beginPath();
  42. ctx.moveTo(x0 +ITEM_SIZE /2, y0 +ITEM_SPACING);
  43. ctx.lineTo(x0 -ITEM_SPACING +ITEM_SIZE, y0 +ITEM_SIZE /2);
  44. ctx.lineTo(x0 +ITEM_SIZE /2, y0 -ITEM_SPACING +ITEM_SIZE);
  45. ctx.lineTo(x0 +ITEM_SPACING, y0 +ITEM_SIZE /2);
  46. ctx.closePath();
  47. ctx.fill();
  48. ctx.putImageData(
  49. blend(
  50. ctx.getImageData(
  51. x0 +ITEM_SPACING,
  52. y0 +ITEM_SPACING,
  53. ITEM_INNER_SIZE,
  54. ITEM_INNER_SIZE),
  55. avatar),
  56. x0 +ITEM_SPACING,
  57. y0 +ITEM_SPACING);
  58. },
  59. blend = function(img, img2) {
  60. var margin = (img.height -img2.height) /2;
  61. for (var i =0; i < img2.height; i++)
  62. for (var j =0; j < img2.width; j++) {
  63. var img2Grey = (img2.data[(i *img2.width +j) *4]) / 255,
  64. iImg = ((i +margin) *img.width +j +margin) *4;
  65. img.data[iImg] *= img2Grey;
  66. img.data[iImg +1] *= img2Grey;
  67. img.data[iImg +2] *= img2Grey;
  68. }
  69. return img;
  70. },
  71. drawBackground = function() {
  72. var grd = ctx.createLinearGradient(0, 0, 0, HEIGHT);
  73. grd.addColorStop(0, "#4D394B");
  74. grd.addColorStop(1, "#201820");
  75. ctx.fillStyle = grd;
  76. ctx.fillRect(0, 0, WIDTH, HEIGHT);
  77. return ctx.getImageData(0, 0, WIDTH, HEIGHT);
  78. },
  79. filterImage = function(img) {
  80. var pixelSum = 0,
  81. i;
  82. for (i =0; i < img.width *img.height *4; i +=4) {
  83. img.data[i] = img.data[i +1] = img.data[i +2] = (img.data[i] +img.data[i +1] +img.data[i +2]) / 3;
  84. img.data[i +3] = 50;
  85. pixelSum += img.data[i];
  86. }
  87. // Invert colors if image is dark
  88. if (pixelSum / (img.height * img.width) < 50)
  89. for (i =0; i < img.width *img.height *4; i +=4)
  90. img.data[i] = img.data[i +1] = img.data[i +2] = 255 - img.data[i];
  91. return img;
  92. },
  93. loadImgFromUrl = function(src, cb) {
  94. HttpRequestWrapper(src)
  95. .callbackSuccess(function(code, head, response) {
  96. if (response) {
  97. var img = new Image();
  98. img.onload = function() {
  99. var imgCanvas = document.createElement("canvas");
  100. imgCanvas.height = imgCanvas.width = AVATAR_SIZE;
  101. var imgCtx = imgCanvas.getContext('2d');
  102. imgCtx.drawImage(img, 0, 0, AVATAR_SIZE, AVATAR_SIZE);
  103. cb(filterImage(imgCtx.getImageData(0, 0, AVATAR_SIZE, AVATAR_SIZE)));
  104. };
  105. img.onerror = function() {
  106. cb(null);
  107. };
  108. img.src = window.URL.createObjectURL(/** @type {Blob!} */ (response));
  109. } else {
  110. cb(null);
  111. }
  112. })
  113. .callbackError(function() {
  114. cb(null);
  115. })
  116. .setResponseType(HttpRequestResponseType.BLOB)
  117. .send();
  118. },
  119. loadImages = function(userImgs, doneImgLoading) {
  120. for (var i =0, nbImgs = userImgs.length; i < nbImgs; i++) {
  121. if (userImgs[i].img === undefined) {
  122. // Do load image
  123. loadImgFromUrl(userImgs[i].src, function(img) { // jshint ignore: line
  124. userImgs[i].img = img;
  125. loadImages(userImgs, doneImgLoading);
  126. });
  127. return;
  128. }
  129. }
  130. var imgs = [];
  131. userImgs.forEach(function(i) {
  132. if (i.img)
  133. imgs.push(i.img);
  134. });
  135. doneImgLoading(imgs);
  136. },
  137. compareImgs = function(a, b) {
  138. if (!a.img)
  139. return 1;
  140. if (!b.img)
  141. return -1;
  142. return Math.random() - 0.5;
  143. },
  144. renderAvatars = function(background, imgs) {
  145. imgs.sort(function(a, b) {
  146. return Math.random() - 0.5;
  147. });
  148. for (var imgIndex =0, i =MARGIN; i < WIDTH -MARGIN*2; i += ITEM_SIZE)
  149. for (var j =0; j +ITEM_SIZE <= HEIGHT; j += ITEM_SIZE) {
  150. drawItem(background, imgs[imgIndex], i, j);
  151. imgIndex++;
  152. if (imgIndex === imgs.length) {
  153. imgs.sort(compareImgs);
  154. imgIndex = 0;
  155. }
  156. }
  157. },
  158. callbacks = {},
  159. isLoading = {};
  160. return function(ctxId, users, cb) {
  161. if (RESULT[ctxId]) {
  162. cb(RESULT[ctxId]);
  163. } else if (isLoading[ctxId]) {
  164. if (!callbacks[ctxId])
  165. callbacks[ctxId] = [ cb ];
  166. else
  167. callbacks[ctxId].push(cb);
  168. } else {
  169. var background = drawBackground(),
  170. userImgs = [];
  171. isLoading[ctxId] = true;
  172. if (!callbacks[ctxId])
  173. callbacks[ctxId] = [ cb ];
  174. else
  175. callbacks[ctxId].push(cb);
  176. for (var userId in users) {
  177. if (!users[userId].deleted && !users[userId].isBot)
  178. userImgs.push({
  179. src: users[userId].getSmallIcon()
  180. });
  181. }
  182. loadImages(userImgs, function(imgs) {
  183. renderAvatars(background, imgs);
  184. RESULT[ctxId] = canvas.toDataURL();
  185. callbacks[ctxId].forEach(function(i) {
  186. i(RESULT[ctxId]);
  187. });
  188. });
  189. }
  190. };
  191. })();