contextBackground.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. var 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. var 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. var 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. var filterImage = function(img) {
  80. var pixelSum = 0;
  81. for (var i =0; i < img.width *img.height *4; i +=4) {
  82. img.data[i] = img.data[i +1] = img.data[i +2] = (img.data[i] +img.data[i +1] +img.data[i +2]) / 3;
  83. img.data[i +3] = 50;
  84. pixelSum += img.data[i];
  85. }
  86. // Invert colors if image is dark
  87. if (pixelSum / (img.height * img.width) < 50)
  88. for (var i =0; i < img.width *img.height *4; i +=4)
  89. img.data[i] = img.data[i +1] = img.data[i +2] = 255 - img.data[i];
  90. return img;
  91. }
  92. var loadImgFromUrl = function(src, cb) {
  93. var xhr = new XMLHttpRequest();
  94. xhr.responseType = 'blob';
  95. xhr.onreadystatechange = function() {
  96. if (xhr.readyState === 4) {
  97. if (xhr.response) {
  98. var img = new Image();
  99. img.onload = function() {
  100. var imgCanvas = document.createElement("canvas");
  101. imgCanvas.height = imgCanvas.width = AVATAR_SIZE;
  102. var imgCtx = imgCanvas.getContext('2d');
  103. imgCtx.drawImage(img, 0, 0, AVATAR_SIZE, AVATAR_SIZE);
  104. cb(filterImage(imgCtx.getImageData(0, 0, AVATAR_SIZE, AVATAR_SIZE)));
  105. };
  106. img.onerror = function() {
  107. cb(null);
  108. };
  109. img.src = window.URL.createObjectURL(/** @type {Blob!} */ (xhr.response));
  110. } else {
  111. cb(null);
  112. }
  113. }
  114. };
  115. xhr.open('GET', src, true);
  116. xhr.send(null);
  117. };
  118. var loadImages = function(userImgs, doneImgLoading) {
  119. for (var i =0, nbImgs = userImgs.length; i < nbImgs; i++) {
  120. if (userImgs[i].img === undefined) {
  121. // Do load image
  122. loadImgFromUrl(userImgs[i].src, function(img) {
  123. userImgs[i].img = img;
  124. loadImages(userImgs, doneImgLoading);
  125. });
  126. return;
  127. }
  128. }
  129. var imgs = [];
  130. userImgs.forEach(function(i) {
  131. if (i.img)
  132. imgs.push(i.img);
  133. });
  134. doneImgLoading(imgs);
  135. };
  136. var renderAvatars = function(background, imgs) {
  137. imgs.sort(function(a, b) {
  138. return Math.random() - 0.5;
  139. });
  140. for (var imgIndex =0, i =MARGIN; i < WIDTH -MARGIN*2; i += ITEM_SIZE)
  141. for (var j =0; j +ITEM_SIZE <= HEIGHT; j += ITEM_SIZE) {
  142. drawItem(background, imgs[imgIndex], i, j);
  143. imgIndex++;
  144. if (imgIndex === imgs.length) {
  145. imgs.sort(function(a, b) {
  146. if (!a.img)
  147. return 1;
  148. if (!b.img)
  149. return -1;
  150. return Math.random() - 0.5;
  151. });
  152. imgIndex = 0;
  153. }
  154. }
  155. }
  156. var callbacks = {},
  157. isLoading = {};
  158. return function(ctxId, users, cb) {
  159. if (RESULT[ctxId]) {
  160. cb(RESULT[ctxId]);
  161. } else if (isLoading[ctxId]) {
  162. if (!callbacks[ctxId])
  163. callbacks[ctxId] = [ cb ];
  164. else
  165. callbacks[ctxId].push(cb);
  166. } else {
  167. var background = drawBackground()
  168. ,userImgs = [];
  169. isLoading[ctxId] = true;
  170. if (!callbacks[ctxId])
  171. callbacks[ctxId] = [ cb ];
  172. else
  173. callbacks[ctxId].push(cb);
  174. for (var userId in users) {
  175. if (!users[userId].deleted && !users[userId].isBot)
  176. userImgs.push({
  177. src: users[userId].getSmallIcon()
  178. });
  179. }
  180. loadImages(userImgs, function(imgs) {
  181. renderAvatars(background, imgs);
  182. RESULT[ctxId] = canvas.toDataURL();
  183. callbacks[ctxId].forEach(function(i) {
  184. i(RESULT[ctxId]);
  185. });
  186. });
  187. }
  188. }
  189. })();