-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathUtil.js
124 lines (109 loc) · 4.44 KB
/
Util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class Util {
/**
* Renders the given emoji text at that given font size on a canvas. Returns that canvas.
*
* @param {string} emojiText The emoji text to render.
* @param {number} width The desired width of the canvas.
* @param {number} height The desired height of the canvas.
* @param {boolean} flip True to horizontally flip the image; otherwise false.
* @param {boolean} fill True to set the fill style to red; otherwise false.
*
* @returns The created canvas with the rendered emoji text at the given font size.
*/
static renderEmoji(emojiText, width, height, flip=false, fill=true) {
let size = Math.max(width, height);
let canvas = document.createElement('canvas');
canvas.height = size + (size / 8) + 20;
canvas.width = size * 1.4 + 10;
let ctx = canvas.getContext('2d');
ctx.font = `${size}px 'Segoe UI Emoji'`;
ctx.textBaseline = 'bottom';
if (fill) ctx.fillStyle = 'red';
ctx.fillText(emojiText, 5, canvas.height - 5);
// On Windows, this reduces the thick black edges.
let [minX, minY, maxX, maxY] = Util.reduceEdges(ctx, 0, 0);
// Redraw the canvas, so that we can remove white space and add a shadow.
let emojiCanvas = document.createElement('canvas');
let newWidth = ((maxX - minX) + 3);
let newHeight = ((maxY - minY) + 3);
emojiCanvas.width = width;
emojiCanvas.height = height;
let emojiCtx = emojiCanvas.getContext('2d');
if (newWidth > 0 && newHeight > 0) {
emojiCtx.shadowColor = "black";
emojiCtx.shadowBlur = 3;
if (flip) {
emojiCtx.translate(width, 0);
emojiCtx.scale(-1, 1);
}
emojiCtx.drawImage(
canvas,
minX-1, minY-1, newWidth, newHeight,
0, 0, width, height,
);
}
return [ emojiCanvas, emojiCtx.getImageData(0, 0, width, height) ];
}
/**
* For the image data in the given canvas context, applies a fill algorithm
* to trim off the thick black edges that Windows emojis have around them.
* On other operating systems, this will hopefully do nothing, but lets
* see.
*
* @param {CanvasRenderingContext2D} ctx
* @param {number} startX
* @param {number} startY
*/
static reduceEdges(ctx, startX, startY) {
let { width, height } = ctx.canvas;
let dataWidth = (width << 2);
let imgData = ctx.getImageData(0, 0, width, height);
let visitMap = new Uint32Array(imgData.data.length);
let queue = [[startX, startY]];
let [minX, minY] = [width, height];
let maxX = 0;
let maxY = 0;
while (queue.length) {
// Get pixel position to test from queue.
let [x, y] = queue.shift();
let pos = ((y * dataWidth) + (x << 2));
// Check if we've been here before.
if (visitMap[pos]) continue;
visitMap[pos] = 1;
let [red, green, blue, alpha] = imgData.data.slice(pos, pos + 4);
let brightness = Math.round(Math.sqrt((red * red * 0.241) + (green * green * 0.691) + (blue * blue * 0.068)));
let spread = false;
if (alpha == 0) {
spread = true;
}
else if ((red == 0) && (green == 0) && (blue == 0)) {
if (Util.WIN) {
imgData.data[pos + 3] = 0;
spread = true;
}
}
else if (brightness < 70) {
if (Util.WIN) {
imgData.data[pos + 3] = Math.round(brightness * (255 / 70));
spread = true;
}
}
if (spread) {
if (x > 0) queue.push([x - 1, y]);
if (x < width - 1) queue.push([x + 1, y]);
if (y > 0) queue.push([x, y - 1]);
if (y < height - 1) queue.push([x, y + 1]);
}
if (imgData.data[pos + 3]) {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
ctx.putImageData(imgData, 0, 0);
return [minX, minY, maxX, maxY];
}
}
Util.WIN = navigator.platform.indexOf('Win') > -1;
Util.MAC = navigator.platform.indexOf('Mac') > -1;