1 | 'use strict'
|
2 |
|
3 | var Font = require('css-font')
|
4 | var pick = require('pick-by-alias')
|
5 | var createRegl = require('regl')
|
6 | var createGl = require('gl-util/context')
|
7 | var WeakMap = require('es6-weak-map')
|
8 | var rgba = require('color-normalize')
|
9 | var fontAtlas = require('font-atlas')
|
10 | var pool = require('typedarray-pool')
|
11 | var parseRect = require('parse-rect')
|
12 | var isObj = require('is-plain-obj')
|
13 | var parseUnit = require('parse-unit')
|
14 | var px = require('to-px')
|
15 | var kerning = require('detect-kerning')
|
16 | var extend = require('object-assign')
|
17 | var metrics = require('font-measure')
|
18 | var flatten = require('flatten-vertex-data')
|
19 | var ref = require('bit-twiddle');
|
20 | var nextPow2 = ref.nextPow2;
|
21 |
|
22 | var shaderCache = new WeakMap
|
23 |
|
24 |
|
25 |
|
26 | var isStretchSupported = false
|
27 | if (document.body) {
|
28 | var el = document.body.appendChild(document.createElement('div'))
|
29 | el.style.font = 'italic small-caps bold condensed 16px/2 cursive'
|
30 | if (getComputedStyle(el).fontStretch) {
|
31 | isStretchSupported = true
|
32 | }
|
33 | document.body.removeChild(el)
|
34 | }
|
35 |
|
36 | var GlText = function GlText (o) {
|
37 | if (isRegl(o)) {
|
38 | o = {regl: o}
|
39 | this.gl = o.regl._gl
|
40 | }
|
41 | else {
|
42 | this.gl = createGl(o)
|
43 | }
|
44 |
|
45 | this.shader = shaderCache.get(this.gl)
|
46 |
|
47 | if (!this.shader) {
|
48 | this.regl = o.regl || createRegl({ gl: this.gl })
|
49 | }
|
50 | else {
|
51 | this.regl = this.shader.regl
|
52 | }
|
53 |
|
54 | this.charBuffer = this.regl.buffer({ type: 'uint8', usage: 'stream' })
|
55 | this.sizeBuffer = this.regl.buffer({ type: 'float', usage: 'stream' })
|
56 |
|
57 | if (!this.shader) {
|
58 | this.shader = this.createShader()
|
59 | shaderCache.set(this.gl, this.shader)
|
60 | }
|
61 |
|
62 | this.batch = []
|
63 |
|
64 |
|
65 | this.fontSize = []
|
66 | this.font = []
|
67 | this.fontAtlas = []
|
68 |
|
69 | this.draw = this.shader.draw.bind(this)
|
70 | this.render = function () {
|
71 |
|
72 |
|
73 | this.regl._refresh()
|
74 | this.draw(this.batch)
|
75 | }
|
76 | this.canvas = this.gl.canvas
|
77 |
|
78 | this.update(isObj(o) ? o : {})
|
79 | };
|
80 |
|
81 | GlText.prototype.createShader = function createShader () {
|
82 | var regl = this.regl
|
83 |
|
84 | var draw = regl({
|
85 | blend: {
|
86 | enable: true,
|
87 | color: [0,0,0,1],
|
88 |
|
89 | func: {
|
90 | srcRGB: 'src alpha',
|
91 | dstRGB: 'one minus src alpha',
|
92 | srcAlpha: 'one minus dst alpha',
|
93 | dstAlpha: 'one'
|
94 | }
|
95 | },
|
96 | stencil: {enable: false},
|
97 | depth: {enable: false},
|
98 |
|
99 | count: regl.prop('count'),
|
100 | offset: regl.prop('offset'),
|
101 | attributes: {
|
102 | charOffset: {
|
103 | offset: 4,
|
104 | stride: 8,
|
105 | buffer: regl.this('sizeBuffer')
|
106 | },
|
107 | width: {
|
108 | offset: 0,
|
109 | stride: 8,
|
110 | buffer: regl.this('sizeBuffer')
|
111 | },
|
112 | char: regl.this('charBuffer'),
|
113 | position: regl.this('position')
|
114 | },
|
115 | uniforms: {
|
116 | atlasSize: function (c, p) { return [p.atlas.width, p.atlas.height]; },
|
117 | atlasDim: function (c, p) { return [p.atlas.cols, p.atlas.rows]; },
|
118 | atlas: function (c, p) { return p.atlas.texture; },
|
119 | charStep: function (c, p) { return p.atlas.step; },
|
120 | em: function (c, p) { return p.atlas.em; },
|
121 | color: regl.prop('color'),
|
122 | opacity: regl.prop('opacity'),
|
123 | viewport: regl.this('viewportArray'),
|
124 | scale: regl.this('scale'),
|
125 | align: regl.prop('align'),
|
126 | baseline: regl.prop('baseline'),
|
127 | translate: regl.this('translate'),
|
128 | positionOffset: regl.prop('positionOffset')
|
129 | },
|
130 | primitive: 'points',
|
131 | viewport: regl.this('viewport'),
|
132 |
|
133 | vert: "\n\t\t\tprecision highp float;\n\t\t\tattribute float width, charOffset, char;\n\t\t\tattribute vec2 position;\n\t\t\tuniform float fontSize, charStep, em, align, baseline;\n\t\t\tuniform vec4 viewport;\n\t\t\tuniform vec4 color;\n\t\t\tuniform vec2 atlasSize, atlasDim, scale, translate, positionOffset;\n\t\t\tvarying vec2 charCoord, charId;\n\t\t\tvarying float charWidth;\n\t\t\tvarying vec4 fontColor;\n\t\t\tvoid main () {\n\t\t\t\tvec2 offset = floor(em * (vec2(align + charOffset, baseline)\n\t\t\t\t\t+ vec2(positionOffset.x, -positionOffset.y)))\n\t\t\t\t\t/ (viewport.zw * scale.xy);\n\n\t\t\t\tvec2 position = (position + translate) * scale;\n\t\t\t\tposition += offset * scale;\n\n\t\t\t\tcharCoord = position * viewport.zw + viewport.xy;\n\n\t\t\t\tgl_Position = vec4(position * 2. - 1., 0, 1);\n\n\t\t\t\tgl_PointSize = charStep;\n\n\t\t\t\tcharId.x = mod(char, atlasDim.x);\n\t\t\t\tcharId.y = floor(char / atlasDim.x);\n\n\t\t\t\tcharWidth = width * em;\n\n\t\t\t\tfontColor = color / 255.;\n\t\t\t}",
|
134 |
|
135 | frag: "\n\t\t\tprecision highp float;\n\t\t\tuniform sampler2D atlas;\n\t\t\tuniform float fontSize, charStep, opacity;\n\t\t\tuniform vec2 atlasSize;\n\t\t\tuniform vec4 viewport;\n\t\t\tvarying vec4 fontColor;\n\t\t\tvarying vec2 charCoord, charId;\n\t\t\tvarying float charWidth;\n\n\t\t\tfloat lightness(vec4 color) {\n\t\t\t\treturn color.r * 0.299 + color.g * 0.587 + color.b * 0.114;\n\t\t\t}\n\n\t\t\tvoid main () {\n\t\t\t\tvec2 uv = gl_FragCoord.xy - charCoord + charStep * .5;\n\t\t\t\tfloat halfCharStep = floor(charStep * .5 + .5);\n\n\t\t\t\t// invert y and shift by 1px (FF expecially needs that)\n\t\t\t\tuv.y = charStep - uv.y;\n\n\t\t\t\t// ignore points outside of character bounding box\n\t\t\t\tfloat halfCharWidth = ceil(charWidth * .5);\n\t\t\t\tif (floor(uv.x) > halfCharStep + halfCharWidth ||\n\t\t\t\t\tfloor(uv.x) < halfCharStep - halfCharWidth) return;\n\n\t\t\t\tuv += charId * charStep;\n\t\t\t\tuv = uv / atlasSize;\n\n\t\t\t\tvec4 color = fontColor;\n\t\t\t\tvec4 mask = texture2D(atlas, uv);\n\n\t\t\t\tfloat maskY = lightness(mask);\n\t\t\t\t// float colorY = lightness(color);\n\t\t\t\tcolor.a *= maskY;\n\t\t\t\tcolor.a *= opacity;\n\n\t\t\t\t// color.a += .1;\n\n\t\t\t\t// antialiasing, see yiq color space y-channel formula\n\t\t\t\t// color.rgb += (1. - color.rgb) * (1. - mask.rgb);\n\n\t\t\t\tgl_FragColor = color;\n\t\t\t}"
|
136 | })
|
137 |
|
138 |
|
139 | var atlas = {}
|
140 |
|
141 | return { regl: regl, draw: draw, atlas: atlas }
|
142 | };
|
143 |
|
144 | GlText.prototype.update = function update (o) {
|
145 | var this$1 = this;
|
146 |
|
147 | if (typeof o === 'string') { o = { text: o } }
|
148 | else if (!o) { return }
|
149 |
|
150 |
|
151 | o = pick(o, {
|
152 | position: 'position positions coord coords coordinates',
|
153 | font: 'font fontFace fontface typeface cssFont css-font family fontFamily',
|
154 | fontSize: 'fontSize fontsize size font-size',
|
155 | text: 'text texts chars characters value values symbols',
|
156 | align: 'align alignment textAlign textbaseline',
|
157 | baseline: 'baseline textBaseline textbaseline',
|
158 | direction: 'dir direction textDirection',
|
159 | color: 'color colour fill fill-color fillColor textColor textcolor',
|
160 | kerning: 'kerning kern',
|
161 | range: 'range dataBox',
|
162 | viewport: 'vp viewport viewBox viewbox viewPort',
|
163 | opacity: 'opacity alpha transparency visible visibility opaque',
|
164 | offset: 'offset positionOffset padding shift indent indentation'
|
165 | }, true)
|
166 |
|
167 |
|
168 | if (o.opacity != null) {
|
169 | if (Array.isArray(o.opacity)) {
|
170 | this.opacity = o.opacity.map(function (o) { return parseFloat(o); })
|
171 | }
|
172 | else {
|
173 | this.opacity = parseFloat(o.opacity)
|
174 | }
|
175 | }
|
176 |
|
177 | if (o.viewport != null) {
|
178 | this.viewport = parseRect(o.viewport)
|
179 |
|
180 | this.viewportArray = [this.viewport.x, this.viewport.y, this.viewport.width, this.viewport.height]
|
181 |
|
182 | }
|
183 | if (this.viewport == null) {
|
184 | this.viewport = {
|
185 | x: 0, y: 0,
|
186 | width: this.gl.drawingBufferWidth,
|
187 | height: this.gl.drawingBufferHeight
|
188 | }
|
189 | this.viewportArray = [this.viewport.x, this.viewport.y, this.viewport.width, this.viewport.height]
|
190 | }
|
191 |
|
192 | if (o.kerning != null) { this.kerning = o.kerning }
|
193 |
|
194 | if (o.offset != null) {
|
195 | if (typeof o.offset === 'number') { o.offset = [o.offset, 0] }
|
196 |
|
197 | this.positionOffset = flatten(o.offset)
|
198 | }
|
199 |
|
200 | if (o.direction) { this.direction = o.direction }
|
201 |
|
202 | if (o.range) {
|
203 | this.range = o.range
|
204 | this.scale = [1 / (o.range[2] - o.range[0]), 1 / (o.range[3] - o.range[1])]
|
205 | this.translate = [-o.range[0], -o.range[1]]
|
206 | }
|
207 | if (o.scale) { this.scale = o.scale }
|
208 | if (o.translate) { this.translate = o.translate }
|
209 |
|
210 |
|
211 | if (!this.scale) { this.scale = [1 / this.viewport.width, 1 / this.viewport.height] }
|
212 |
|
213 | if (!this.translate) { this.translate = [0, 0] }
|
214 |
|
215 | if (!this.font.length && !o.font) { o.font = GlText.baseFontSize + 'px sans-serif' }
|
216 |
|
217 |
|
218 | var newFont = false, newFontSize = false
|
219 |
|
220 |
|
221 | if (o.font) {
|
222 | (Array.isArray(o.font) ? o.font : [o.font]).forEach(function (font, i) {
|
223 |
|
224 | if (typeof font === 'string') {
|
225 | try {
|
226 | font = Font.parse(font)
|
227 | } catch (e) {
|
228 | font = Font.parse(GlText.baseFontSize + 'px ' + font)
|
229 | }
|
230 | }
|
231 | else { font = Font.parse(Font.stringify(font)) }
|
232 |
|
233 | var baseString = Font.stringify({
|
234 | size: GlText.baseFontSize,
|
235 | family: font.family,
|
236 | stretch: isStretchSupported ? font.stretch : undefined,
|
237 | variant: font.variant,
|
238 | weight: font.weight,
|
239 | style: font.style
|
240 | })
|
241 |
|
242 | var unit = parseUnit(font.size)
|
243 | var fs = Math.round(unit[0] * px(unit[1]))
|
244 | if (fs !== this$1.fontSize[i]) {
|
245 | newFontSize = true
|
246 | this$1.fontSize[i] = fs
|
247 | }
|
248 |
|
249 |
|
250 | if (!this$1.font[i] || baseString != this$1.font[i].baseString) {
|
251 | newFont = true
|
252 |
|
253 |
|
254 | this$1.font[i] = GlText.fonts[baseString]
|
255 | if (!this$1.font[i]) {
|
256 | var family = font.family.join(', ')
|
257 | var style = [font.style]
|
258 | if (font.style != font.variant) { style.push(font.variant) }
|
259 | if (font.variant != font.weight) { style.push(font.weight) }
|
260 | if (isStretchSupported && font.weight != font.stretch) { style.push(font.stretch) }
|
261 |
|
262 | this$1.font[i] = {
|
263 | baseString: baseString,
|
264 |
|
265 |
|
266 | family: family,
|
267 | weight: font.weight,
|
268 | stretch: font.stretch,
|
269 | style: font.style,
|
270 | variant: font.variant,
|
271 |
|
272 |
|
273 | width: {},
|
274 |
|
275 |
|
276 | kerning: {},
|
277 |
|
278 | metrics: metrics(family, {
|
279 | origin: 'top',
|
280 | fontSize: GlText.baseFontSize,
|
281 | fontStyle: style.join(' ')
|
282 | })
|
283 | }
|
284 |
|
285 | GlText.fonts[baseString] = this$1.font[i]
|
286 | }
|
287 | }
|
288 | })
|
289 | }
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 | if (newFont || newFontSize) {
|
303 | this.font.forEach(function (font, i) {
|
304 | var fontString = Font.stringify({
|
305 | size: this$1.fontSize[i],
|
306 | family: font.family,
|
307 | stretch: isStretchSupported ? font.stretch : undefined,
|
308 | variant: font.variant,
|
309 | weight: font.weight,
|
310 | style: font.style
|
311 | })
|
312 |
|
313 |
|
314 | this$1.fontAtlas[i] = this$1.shader.atlas[fontString]
|
315 |
|
316 | if (!this$1.fontAtlas[i]) {
|
317 | var metrics = font.metrics
|
318 |
|
319 | this$1.shader.atlas[fontString] =
|
320 | this$1.fontAtlas[i] = {
|
321 | fontString: fontString,
|
322 |
|
323 | step: Math.ceil(this$1.fontSize[i] * metrics.bottom * .5) * 2,
|
324 | em: this$1.fontSize[i],
|
325 | cols: 0,
|
326 | rows: 0,
|
327 | height: 0,
|
328 | width: 0,
|
329 | chars: [],
|
330 | ids: {},
|
331 | texture: this$1.regl.texture()
|
332 | }
|
333 | }
|
334 |
|
335 |
|
336 | if (o.text == null) { o.text = this$1.text }
|
337 | })
|
338 | }
|
339 |
|
340 |
|
341 |
|
342 | if (typeof o.text === 'string' && o.position && o.position.length > 2) {
|
343 | var textArray = Array(o.position.length * .5)
|
344 | for (var i = 0; i < textArray.length; i++) {
|
345 | textArray[i] = o.text
|
346 | }
|
347 | o.text = textArray
|
348 | }
|
349 |
|
350 |
|
351 | var newAtlasChars
|
352 | if (o.text != null || newFont) {
|
353 |
|
354 |
|
355 | this.textOffsets = [0]
|
356 |
|
357 | if (Array.isArray(o.text)) {
|
358 | this.count = o.text[0].length
|
359 | this.counts = [this.count]
|
360 | for (var i$1 = 1; i$1 < o.text.length; i$1++) {
|
361 | this.textOffsets[i$1] = this.textOffsets[i$1 - 1] + o.text[i$1 - 1].length
|
362 | this.count += o.text[i$1].length
|
363 | this.counts.push(o.text[i$1].length)
|
364 | }
|
365 | this.text = o.text.join('')
|
366 | }
|
367 | else {
|
368 | this.text = o.text
|
369 | this.count = this.text.length
|
370 | this.counts = [this.count]
|
371 | }
|
372 |
|
373 | newAtlasChars = []
|
374 |
|
375 |
|
376 | this.font.forEach(function (font, idx) {
|
377 | GlText.atlasContext.font = font.baseString
|
378 |
|
379 | var atlas = this$1.fontAtlas[idx]
|
380 |
|
381 | for (var i = 0; i < this$1.text.length; i++) {
|
382 | var char = this$1.text.charAt(i)
|
383 |
|
384 | if (atlas.ids[char] == null) {
|
385 | atlas.ids[char] = atlas.chars.length
|
386 | atlas.chars.push(char)
|
387 | newAtlasChars.push(char)
|
388 | }
|
389 |
|
390 | if (font.width[char] == null) {
|
391 | font.width[char] = GlText.atlasContext.measureText(char).width / GlText.baseFontSize
|
392 |
|
393 |
|
394 | if (this$1.kerning) {
|
395 | var pairs = []
|
396 | for (var baseChar in font.width) {
|
397 | pairs.push(baseChar + char, char + baseChar)
|
398 | }
|
399 | extend(font.kerning, kerning(font.family, {
|
400 | pairs: pairs
|
401 | }))
|
402 | }
|
403 | }
|
404 | }
|
405 | })
|
406 | }
|
407 |
|
408 |
|
409 | if (o.position) {
|
410 | if (o.position.length > 2) {
|
411 | var flat = !o.position[0].length
|
412 | var positionData = pool.mallocFloat(this.count * 2)
|
413 | for (var i$2 = 0, ptr = 0; i$2 < this.counts.length; i$2++) {
|
414 | var count = this.counts[i$2]
|
415 | if (flat) {
|
416 | for (var j = 0; j < count; j++) {
|
417 | positionData[ptr++] = o.position[i$2 * 2]
|
418 | positionData[ptr++] = o.position[i$2 * 2 + 1]
|
419 | }
|
420 | }
|
421 | else {
|
422 | for (var j$1 = 0; j$1 < count; j$1++) {
|
423 | positionData[ptr++] = o.position[i$2][0]
|
424 | positionData[ptr++] = o.position[i$2][1]
|
425 | }
|
426 | }
|
427 | }
|
428 | if (this.position.call) {
|
429 | this.position({
|
430 | type: 'float',
|
431 | data: positionData
|
432 | })
|
433 | } else {
|
434 | this.position = this.regl.buffer({
|
435 | type: 'float',
|
436 | data: positionData
|
437 | })
|
438 | }
|
439 | pool.freeFloat(positionData)
|
440 | }
|
441 | else {
|
442 | if (this.position.destroy) { this.position.destroy() }
|
443 | this.position = {
|
444 | constant: o.position
|
445 | }
|
446 | }
|
447 | }
|
448 |
|
449 |
|
450 |
|
451 |
|
452 | if (o.text || newFont) {
|
453 | var charIds = pool.mallocUint8(this.count)
|
454 | var sizeData = pool.mallocFloat(this.count * 2)
|
455 | this.textWidth = []
|
456 |
|
457 | for (var i$3 = 0, ptr$1 = 0; i$3 < this.counts.length; i$3++) {
|
458 | var count$1 = this.counts[i$3]
|
459 | var font = this.font[i$3] || this.font[0]
|
460 | var atlas = this.fontAtlas[i$3] || this.fontAtlas[0]
|
461 |
|
462 | for (var j$2 = 0; j$2 < count$1; j$2++) {
|
463 | var char = this.text.charAt(ptr$1)
|
464 | var prevChar = this.text.charAt(ptr$1 - 1)
|
465 |
|
466 | charIds[ptr$1] = atlas.ids[char]
|
467 | sizeData[ptr$1 * 2] = font.width[char]
|
468 |
|
469 | if (j$2) {
|
470 | var prevWidth = sizeData[ptr$1 * 2 - 2]
|
471 | var currWidth = sizeData[ptr$1 * 2]
|
472 | var prevOffset = sizeData[ptr$1 * 2 - 1]
|
473 | var offset = prevOffset + prevWidth * .5 + currWidth * .5;
|
474 |
|
475 | if (this.kerning) {
|
476 | var kerning$1 = font.kerning[prevChar + char]
|
477 | if (kerning$1) {
|
478 | offset += kerning$1 * 1e-3
|
479 | }
|
480 | }
|
481 |
|
482 | sizeData[ptr$1 * 2 + 1] = offset
|
483 | }
|
484 | else {
|
485 | sizeData[ptr$1 * 2 + 1] = sizeData[ptr$1 * 2] * .5
|
486 | }
|
487 |
|
488 | ptr$1++
|
489 | }
|
490 | this.textWidth.push(
|
491 | !sizeData.length ? 0 :
|
492 |
|
493 | sizeData[ptr$1 * 2 - 2] * .5 + sizeData[ptr$1 * 2 - 1]
|
494 | )
|
495 | }
|
496 |
|
497 |
|
498 |
|
499 | if (!o.align) { o.align = this.align }
|
500 | this.charBuffer({data: charIds, type: 'uint8', usage: 'stream'})
|
501 | this.sizeBuffer({data: sizeData, type: 'float', usage: 'stream'})
|
502 | pool.freeUint8(charIds)
|
503 | pool.freeFloat(sizeData)
|
504 |
|
505 |
|
506 | if (newAtlasChars.length) {
|
507 | this.font.forEach(function (font, i) {
|
508 | var atlas = this$1.fontAtlas[i]
|
509 |
|
510 |
|
511 | var step = atlas.step
|
512 |
|
513 | var maxCols = Math.floor(GlText.maxAtlasSize / step)
|
514 | var cols = Math.min(maxCols, atlas.chars.length)
|
515 | var rows = Math.ceil(atlas.chars.length / cols)
|
516 |
|
517 | var atlasWidth = nextPow2( cols * step )
|
518 |
|
519 | var atlasHeight = nextPow2( rows * step );
|
520 |
|
521 | atlas.width = atlasWidth
|
522 | atlas.height = atlasHeight;
|
523 | atlas.rows = rows
|
524 | atlas.cols = cols
|
525 |
|
526 | if (!atlas.em) { return }
|
527 |
|
528 | atlas.texture({
|
529 | data: fontAtlas({
|
530 | canvas: GlText.atlasCanvas,
|
531 | font: atlas.fontString,
|
532 | chars: atlas.chars,
|
533 | shape: [atlasWidth, atlasHeight],
|
534 | step: [step, step]
|
535 | })
|
536 | })
|
537 |
|
538 | })
|
539 | }
|
540 | }
|
541 |
|
542 | if (o.align) {
|
543 | this.align = o.align
|
544 | this.alignOffset = this.textWidth.map(function (textWidth, i) {
|
545 | var align = !Array.isArray(this$1.align) ? this$1.align : this$1.align.length > 1 ? this$1.align[i] : this$1.align[0]
|
546 |
|
547 | if (typeof align === 'number') { return align }
|
548 | switch (align) {
|
549 | case 'right':
|
550 | case 'end':
|
551 | return -textWidth
|
552 | case 'center':
|
553 | case 'centre':
|
554 | case 'middle':
|
555 | return -textWidth * .5
|
556 | }
|
557 |
|
558 | return 0
|
559 | })
|
560 | }
|
561 |
|
562 | if (this.baseline == null && o.baseline == null) {
|
563 | o.baseline = 0
|
564 | }
|
565 | if (o.baseline != null) {
|
566 | this.baseline = o.baseline
|
567 | if (!Array.isArray(this.baseline)) { this.baseline = [this.baseline] }
|
568 | this.baselineOffset = this.baseline.map(function (baseline, i) {
|
569 | var m = (this$1.font[i] || this$1.font[0]).metrics
|
570 | var base = 0
|
571 |
|
572 | base += m.bottom * .5
|
573 |
|
574 | if (typeof baseline === 'number') {
|
575 | base += (baseline - m.baseline)
|
576 | }
|
577 | else {
|
578 | base += -m[baseline]
|
579 | }
|
580 |
|
581 | base *= -1
|
582 | return base
|
583 | })
|
584 | }
|
585 |
|
586 |
|
587 | if (o.color != null) {
|
588 | if (!o.color) { o.color = 'transparent' }
|
589 |
|
590 |
|
591 | if (typeof o.color === 'string' || !isNaN(o.color)) {
|
592 | this.color = rgba(o.color, 'uint8')
|
593 | }
|
594 |
|
595 | else {
|
596 | var colorData
|
597 |
|
598 |
|
599 | if (typeof o.color[0] === 'number' && o.color.length > this.counts.length) {
|
600 | var l = o.color.length
|
601 | colorData = pool.mallocUint8(l)
|
602 | var sub = (o.color.subarray || o.color.slice).bind(o.color)
|
603 | for (var i$4 = 0; i$4 < l; i$4 += 4) {
|
604 | colorData.set(rgba(sub(i$4, i$4 + 4), 'uint8'), i$4)
|
605 | }
|
606 | }
|
607 |
|
608 | else {
|
609 | var l$1 = o.color.length
|
610 | colorData = pool.mallocUint8(l$1 * 4)
|
611 | for (var i$5 = 0; i$5 < l$1; i$5++) {
|
612 | colorData.set(rgba(o.color[i$5] || 0, 'uint8'), i$5 * 4)
|
613 | }
|
614 | }
|
615 |
|
616 | this.color = colorData
|
617 | }
|
618 | }
|
619 |
|
620 |
|
621 | if (o.position || o.text || o.color || o.baseline || o.align || o.font || o.offset || o.opacity) {
|
622 | var isBatch = (this.color.length > 4)
|
623 | || (this.baselineOffset.length > 1)
|
624 | || (this.align && this.align.length > 1)
|
625 | || (this.fontAtlas.length > 1)
|
626 | || (this.positionOffset.length > 2)
|
627 | if (isBatch) {
|
628 | var length = Math.max(
|
629 | this.position.length * .5 || 0,
|
630 | this.color.length * .25 || 0,
|
631 | this.baselineOffset.length || 0,
|
632 | this.alignOffset.length || 0,
|
633 | this.font.length || 0,
|
634 | this.opacity.length || 0,
|
635 | this.positionOffset.length * .5 || 0
|
636 | )
|
637 | this.batch = Array(length)
|
638 | for (var i$6 = 0; i$6 < this.batch.length; i$6++) {
|
639 | this.batch[i$6] = {
|
640 | count: this.counts.length > 1 ? this.counts[i$6] : this.counts[0],
|
641 | offset: this.textOffsets.length > 1 ? this.textOffsets[i$6] : this.textOffsets[0],
|
642 | color: !this.color ? [0,0,0,255] : this.color.length <= 4 ? this.color : this.color.subarray(i$6 * 4, i$6 * 4 + 4),
|
643 | opacity: Array.isArray(this.opacity) ? this.opacity[i$6] : this.opacity,
|
644 | baseline: this.baselineOffset[i$6] != null ? this.baselineOffset[i$6] : this.baselineOffset[0],
|
645 | align: !this.align ? 0 : this.alignOffset[i$6] != null ? this.alignOffset[i$6] : this.alignOffset[0],
|
646 | atlas: this.fontAtlas[i$6] || this.fontAtlas[0],
|
647 | positionOffset: this.positionOffset.length > 2 ? this.positionOffset.subarray(i$6 * 2, i$6 * 2 + 2) : this.positionOffset
|
648 | }
|
649 | }
|
650 | }
|
651 |
|
652 | else {
|
653 | if (this.count) {
|
654 | this.batch = [{
|
655 | count: this.count,
|
656 | offset: 0,
|
657 | color: this.color || [0,0,0,255],
|
658 | opacity: Array.isArray(this.opacity) ? this.opacity[0] : this.opacity,
|
659 | baseline: this.baselineOffset[0],
|
660 | align: this.alignOffset ? this.alignOffset[0] : 0,
|
661 | atlas: this.fontAtlas[0],
|
662 | positionOffset: this.positionOffset
|
663 | }]
|
664 | }
|
665 | else {
|
666 | this.batch = []
|
667 | }
|
668 | }
|
669 | }
|
670 | };
|
671 |
|
672 | GlText.prototype.destroy = function destroy () {
|
673 |
|
674 | };
|
675 |
|
676 |
|
677 |
|
678 | GlText.prototype.kerning = true
|
679 | GlText.prototype.position = { constant: new Float32Array(2) }
|
680 | GlText.prototype.translate = null
|
681 | GlText.prototype.scale = null
|
682 | GlText.prototype.font = null
|
683 | GlText.prototype.text = ''
|
684 | GlText.prototype.positionOffset = [0, 0]
|
685 | GlText.prototype.opacity = 1
|
686 | GlText.prototype.color = new Uint8Array([0, 0, 0, 255])
|
687 | GlText.prototype.alignOffset = [0, 0]
|
688 |
|
689 |
|
690 |
|
691 | GlText.maxAtlasSize = 1024
|
692 |
|
693 |
|
694 | GlText.atlasCanvas = document.createElement('canvas')
|
695 | GlText.atlasContext = GlText.atlasCanvas.getContext('2d', {alpha: false})
|
696 |
|
697 |
|
698 | GlText.baseFontSize = 64
|
699 |
|
700 |
|
701 | GlText.fonts = {}
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 | function isRegl (o) {
|
708 | return typeof o === 'function' &&
|
709 | o._gl &&
|
710 | o.prop &&
|
711 | o.texture &&
|
712 | o.buffer
|
713 | }
|
714 |
|
715 |
|
716 | module.exports = GlText
|
717 |
|