Skip to content

Commit aef20c0

Browse files
committed
sdf-text initial
1 parent 3470f37 commit aef20c0

24 files changed

+14040
-0
lines changed

sdf-text/README.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# sdf-text
2+
3+
Text rendering using Signed Distance Fields
4+
5+
# How to
6+
7+
## Image generation
8+
9+
https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
10+
11+
1. Install BMFont app for Windows and generate .png texture file and .fnt metrics file.
12+
13+
## Conversion
14+
15+
https://github.com/mattdesl/bmfont2json
16+
https://www.npmjs.com/package/image-sdf
17+
18+
npm install bmfont2json -g
19+
npm install image-sdf -g
20+
21+
https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
22+
https://libgdx.googlecode.com/files/hiero.jar

sdf-text/SpriteTextBox.js

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
var wordwrap = require('word-wrapper');
2+
3+
function SpriteTextBox(ctx, text, opts) {
4+
this.ctx = ctx;
5+
this.text = text;
6+
this.opts = opts;
7+
this.opts.lineHeight = this.opts.lineHeight || 1;
8+
if (this.opts.fontSize) {
9+
this.opts.scale = this.opts.fontSize / this.opts.font.info.size;
10+
}
11+
12+
if (!opts.font) {
13+
throw 'BMFont required in opts = { font: {} }';
14+
}
15+
16+
this.vertices = [[0,0,0]];
17+
this.texCoords = [[0,0]];
18+
this.faces = [[0,0,0]];
19+
this.edges = [[0,0]];
20+
//this.geometry = new Geometry({ vertices: true, texCoords: true, faces: true });
21+
//this.geometry.computeEdges();
22+
//this.mesh = new Mesh(this.geometry, this.material, { triangles: true });
23+
//
24+
this.mesh = ctx.createMesh([
25+
{ data: this.vertices, location: ctx.ATTRIB_POSITION, size: 3 },
26+
{ data: this.texCoords, location: ctx.ATTRIB_TEX_COORD_0, size: 2 }
27+
], { data: this.faces })
28+
29+
this.debugMesh = ctx.createMesh([
30+
{ data: this.vertices, location: ctx.ATTRIB_POSITION, size: 3 }
31+
], { data: this.edges }, ctx.LINES)
32+
33+
this.rebuild(this.text);
34+
}
35+
36+
SpriteTextBox.prototype.getCharInfo = function(c) {
37+
var charCode = c.charCodeAt(0);
38+
var chars = this.opts.font.chars;
39+
for(var i=0; i<chars.length; i++) {
40+
if (chars[i].id == charCode) {
41+
return chars[i];
42+
}
43+
}
44+
}
45+
46+
SpriteTextBox.prototype.getKerning = function(firstChar, secondChar) {
47+
var firstCharCode = firstChar.charCodeAt(0);
48+
var secondCharCode = secondChar.charCodeAt(0);
49+
var kernings = this.opts.font.kernings;
50+
var kerning = null;
51+
for(var i=0; i<kernings.length; i++) {
52+
if (kernings[i].first == firstCharCode && kernings[i].second == secondCharCode) {
53+
kerning = kernings[i];
54+
break;
55+
}
56+
}
57+
if (kerning) {
58+
return kerning.amount;
59+
}
60+
else {
61+
return 0;
62+
}
63+
}
64+
65+
SpriteTextBox.prototype.setFontSize = function(fontSize) {
66+
if (fontSize != this.opts.fontSize) {
67+
this.opts.fontSize = fontSize;
68+
this.opts.scale = this.opts.fontSize / this.opts.font.info.size;
69+
this.rebuild(this.text);
70+
}
71+
}
72+
73+
SpriteTextBox.prototype.rebuild = function(text) {
74+
var vertices = this.vertices;
75+
var texCoords = this.texCoords;
76+
var faces = this.faces;
77+
var edges = this.edges;
78+
79+
//TODO: we could reuse the existing data
80+
vertices.length = 0;
81+
texCoords.length = 0;
82+
faces.length = 0;
83+
edges.length = 0;
84+
85+
var dx = 0;
86+
var dy = 0;
87+
var textureWidth = this.opts.font.common.scaleW;
88+
var textureHeight = this.opts.font.common.scaleH;
89+
var fontBaseHeight = this.opts.font.common.base;
90+
var lineHeight = this.opts.font.common.lineHeight;
91+
var fontSize = this.opts.font.info.size;
92+
var kernings = this.opts.font.info.kernings;
93+
var index = 0;
94+
95+
function measure(text, start, end, width) {
96+
var dx = 0;
97+
var i = start;
98+
for(; i<end; i++) {
99+
var charInfo = this.getCharInfo(text[i]);
100+
var kerning = 0;
101+
if (i > start) {
102+
kerning = this.getKerning(text[i], text[i-1]);
103+
}
104+
105+
dx += charInfo.xadvance + kerning;
106+
if (dx > width) break;
107+
}
108+
109+
return {
110+
start: start,
111+
end: i
112+
}
113+
}
114+
115+
var lines = this.text;
116+
117+
if (this.opts.wrap) {
118+
lines = wordwrap(this.text, { width: this.opts.wrap / this.opts.scale, measure: measure.bind(this) }).split('\n');
119+
}
120+
121+
lines.forEach(function(line) {
122+
dx = 0;
123+
for(var i=0; i<line.length; i++) {
124+
var charInfo = this.getCharInfo(line[i]);
125+
if (!charInfo) {
126+
charInfo = this.getCharInfo('?');
127+
}
128+
if (!charInfo) {
129+
continue;
130+
}
131+
132+
133+
//texture coords
134+
var tx = charInfo.x / textureWidth;
135+
var ty = charInfo.y / textureHeight;
136+
var tw = charInfo.width / textureWidth;
137+
var th = charInfo.height / textureHeight;
138+
139+
var w = charInfo.width;
140+
var h = charInfo.height;
141+
142+
var kerning = 0;
143+
144+
if (i > 0) {
145+
kerning = this.getKerning(line[i], line[i-1]);
146+
}
147+
148+
//
149+
// 3--------2
150+
// | _/ |
151+
// | __/ |
152+
// | / |
153+
// 0--------1
154+
//
155+
156+
//https://www.mapbox.com/blog/text-signed-distance-fields/
157+
//https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
158+
//http://www.angelcode.com/products/bmfont/doc/render_text.html
159+
160+
vertices.push([dx + charInfo.xoffset + kerning, dy + h + charInfo.yoffset, 0]);
161+
vertices.push([dx + w + charInfo.xoffset + kerning, dy + h + charInfo.yoffset, 0]);
162+
vertices.push([dx + w + charInfo.xoffset + kerning, dy + charInfo.yoffset, 0]);
163+
vertices.push([dx + charInfo.xoffset + kerning, dy + charInfo.yoffset, 0]);
164+
texCoords.push([tx , 1.0 - ty - th]);
165+
texCoords.push([tx + tw, 1.0 - ty - th]);
166+
texCoords.push([tx + tw, 1.0 - ty ]);
167+
texCoords.push([tx , 1.0 - ty ]);
168+
faces.push([index, index + 1, index + 2]);
169+
faces.push([index, index + 2, index + 3]);
170+
edges.push([index, index + 1]);
171+
edges.push([index + 1, index + 2]);
172+
edges.push([index + 2, index + 3]);
173+
edges.push([index + 3, index]);
174+
index += 4;
175+
dx += charInfo.xadvance + kerning;
176+
}
177+
dy += lineHeight * this.opts.lineHeight;
178+
}.bind(this));
179+
180+
var ctx = this.ctx;
181+
this.mesh.updateAttribute(ctx.ATTRIB_POSITION, vertices);
182+
this.mesh.updateAttribute(ctx.ATTRIB_TEX_COORD_0, texCoords);
183+
this.mesh.updateIndices(faces);
184+
185+
this.debugMesh.updateAttribute(ctx.ATTRIB_POSITION, vertices);
186+
this.debugMesh.updateIndices(edges);
187+
188+
ctx.getGL().finish()
189+
}
190+
191+
SpriteTextBox.prototype.dispose = function() {
192+
//this.mesh.dispose();
193+
//this.debugMesh.dispose();
194+
}
195+
196+
197+
module.exports = SpriteTextBox;

sdf-text/assets/SDFFont.frag

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
uniform sampler2D texture;
2+
uniform vec2 scale;
3+
uniform vec4 color;
4+
uniform float smoothing;
5+
uniform float offset;
6+
7+
varying vec2 vTexCoord;
8+
9+
#extension GL_OES_standard_derivatives : enable
10+
#pragma glslify: aastep = require('glsl-aastep')
11+
12+
void main() {
13+
float dist = texture2D(texture, vTexCoord).a;
14+
float alpha = aastep(0.5, dist);
15+
16+
if (alpha < 0.01) {
17+
discard;
18+
}
19+
20+
gl_FragColor = vec4(color.rgb, color.a * alpha);
21+
}

sdf-text/assets/SDFFont.vert

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
attribute vec4 aPosition;
2+
attribute vec2 aTexCoord0;
3+
4+
uniform mat4 uProjectionMatrix;
5+
uniform mat4 uViewMatrix;
6+
uniform mat4 uModelMatrix;
7+
uniform mat3 uNormalMatrix;
8+
9+
varying vec2 vTexCoord;
10+
11+
void main() {
12+
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * aPosition;
13+
vTexCoord = aTexCoord0;
14+
}

sdf-text/assets/SolidColor.frag

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifdef GL_ES
2+
precision highp float;
3+
#endif
4+
5+
uniform vec4 uColor;
6+
7+
void main() {
8+
gl_FragColor = uColor;
9+
}

sdf-text/assets/SolidColor.vert

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
attribute vec4 aPosition;
2+
attribute vec3 aNormal;
3+
4+
uniform mat4 uProjectionMatrix;
5+
uniform mat4 uViewMatrix;
6+
uniform mat4 uModelMatrix;
7+
uniform mat3 uNormalMatrix;
8+
9+
void main() {
10+
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * aPosition;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
info face="Inconsolata Bold" size=32 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=4,4,4,4 spacing=-8,-8
2+
common lineHeight=35 base=28 scaleW=512 scaleH=512 pages=1 packed=0
3+
page id=0 file="InconsolataBold-sdf.png"
4+
chars count=94
5+
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=28 xadvance=16 page=0 chnl=0
6+
char id=41 x=0 y=0 width=20 height=37 xoffset=-2 yoffset=2 xadvance=16 page=0 chnl=0
7+
char id=40 x=20 y=0 width=20 height=36 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0
8+
char id=106 x=40 y=0 width=21 height=36 xoffset=-3 yoffset=2 xadvance=16 page=0 chnl=0
9+
char id=124 x=61 y=0 width=13 height=35 xoffset=2 yoffset=3 xadvance=16 page=0 chnl=0
10+
char id=125 x=74 y=0 width=22 height=35 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
11+
char id=123 x=96 y=0 width=22 height=35 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
12+
char id=93 x=118 y=0 width=20 height=34 xoffset=-2 yoffset=2 xadvance=16 page=0 chnl=0
13+
char id=91 x=138 y=0 width=20 height=34 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=0
14+
char id=81 x=158 y=0 width=25 height=34 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
15+
char id=36 x=183 y=0 width=24 height=32 xoffset=-3 yoffset=2 xadvance=16 page=0 chnl=0
16+
char id=92 x=207 y=0 width=23 height=32 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
17+
char id=47 x=230 y=0 width=23 height=32 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
18+
char id=63 x=253 y=0 width=23 height=32 xoffset=-3 yoffset=2 xadvance=16 page=0 chnl=0
19+
char id=33 x=276 y=0 width=15 height=32 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=0
20+
char id=100 x=291 y=0 width=24 height=32 xoffset=-4 yoffset=2 xadvance=16 page=0 chnl=0
21+
char id=98 x=315 y=0 width=23 height=32 xoffset=-3 yoffset=2 xadvance=16 page=0 chnl=0
22+
char id=38 x=338 y=0 width=25 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
23+
char id=37 x=363 y=0 width=25 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
24+
char id=64 x=388 y=0 width=25 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
25+
char id=48 x=413 y=0 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
26+
char id=57 x=436 y=0 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
27+
char id=56 x=459 y=0 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
28+
char id=54 x=482 y=0 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
29+
char id=53 x=0 y=37 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
30+
char id=51 x=23 y=37 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
31+
char id=108 x=46 y=37 width=21 height=31 xoffset=-2 yoffset=2 xadvance=16 page=0 chnl=0
32+
char id=107 x=67 y=37 width=24 height=31 xoffset=-3 yoffset=2 xadvance=16 page=0 chnl=0
33+
char id=105 x=91 y=37 width=20 height=31 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=0
34+
char id=104 x=111 y=37 width=23 height=31 xoffset=-3 yoffset=2 xadvance=16 page=0 chnl=0
35+
char id=102 x=134 y=37 width=24 height=31 xoffset=-2 yoffset=2 xadvance=16 page=0 chnl=0
36+
char id=85 x=158 y=37 width=23 height=31 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
37+
char id=83 x=181 y=37 width=24 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
38+
char id=79 x=205 y=37 width=25 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
39+
char id=74 x=230 y=37 width=24 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
40+
char id=71 x=254 y=37 width=24 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
41+
char id=67 x=278 y=37 width=25 height=31 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
42+
char id=35 x=303 y=37 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
43+
char id=55 x=328 y=37 width=22 height=30 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
44+
char id=52 x=350 y=37 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
45+
char id=50 x=374 y=37 width=23 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
46+
char id=49 x=397 y=37 width=18 height=30 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
47+
char id=121 x=415 y=37 width=25 height=30 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
48+
char id=116 x=440 y=37 width=23 height=30 xoffset=-3 yoffset=4 xadvance=16 page=0 chnl=0
49+
char id=113 x=463 y=37 width=24 height=30 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
50+
char id=112 x=487 y=37 width=24 height=30 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
51+
char id=103 x=0 y=68 width=24 height=30 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
52+
char id=90 x=24 y=68 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
53+
char id=89 x=48 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
54+
char id=88 x=73 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
55+
char id=87 x=98 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
56+
char id=86 x=123 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
57+
char id=84 x=148 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
58+
char id=82 x=173 y=68 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
59+
char id=80 x=197 y=68 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
60+
char id=78 x=221 y=68 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
61+
char id=77 x=245 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
62+
char id=76 x=270 y=68 width=23 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
63+
char id=75 x=293 y=68 width=25 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
64+
char id=73 x=318 y=68 width=21 height=30 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
65+
char id=72 x=339 y=68 width=23 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
66+
char id=70 x=362 y=68 width=22 height=30 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
67+
char id=69 x=384 y=68 width=23 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
68+
char id=68 x=407 y=68 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
69+
char id=66 x=431 y=68 width=24 height=30 xoffset=-3 yoffset=3 xadvance=16 page=0 chnl=0
70+
char id=65 x=455 y=68 width=25 height=30 xoffset=-4 yoffset=3 xadvance=16 page=0 chnl=0
71+
char id=59 x=480 y=68 width=16 height=29 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=0
72+
char id=62 x=0 y=98 width=24 height=27 xoffset=-3 yoffset=5 xadvance=16 page=0 chnl=0
73+
char id=60 x=24 y=98 width=24 height=27 xoffset=-3 yoffset=5 xadvance=16 page=0 chnl=0
74+
char id=117 x=48 y=98 width=23 height=26 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
75+
char id=111 x=71 y=98 width=25 height=26 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
76+
char id=101 x=96 y=98 width=23 height=26 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
77+
char id=99 x=119 y=98 width=23 height=26 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
78+
char id=97 x=142 y=98 width=23 height=26 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
79+
char id=122 x=165 y=98 width=23 height=25 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
80+
char id=120 x=188 y=98 width=25 height=25 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
81+
char id=119 x=213 y=98 width=25 height=25 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
82+
char id=118 x=238 y=98 width=24 height=25 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
83+
char id=115 x=262 y=98 width=23 height=25 xoffset=-3 yoffset=9 xadvance=16 page=0 chnl=0
84+
char id=114 x=285 y=98 width=22 height=25 xoffset=-2 yoffset=8 xadvance=16 page=0 chnl=0
85+
char id=110 x=307 y=98 width=23 height=25 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
86+
char id=109 x=330 y=98 width=24 height=25 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
87+
char id=42 x=354 y=98 width=24 height=24 xoffset=-3 yoffset=6 xadvance=16 page=0 chnl=0
88+
char id=58 x=378 y=98 width=14 height=24 xoffset=1 yoffset=10 xadvance=16 page=0 chnl=0
89+
char id=43 x=392 y=98 width=23 height=23 xoffset=-3 yoffset=7 xadvance=16 page=0 chnl=0
90+
char id=94 x=415 y=98 width=21 height=20 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
91+
char id=44 x=436 y=98 width=16 height=20 xoffset=0 yoffset=19 xadvance=16 page=0 chnl=0
92+
char id=61 x=452 y=98 width=23 height=19 xoffset=-3 yoffset=8 xadvance=16 page=0 chnl=0
93+
char id=39 x=475 y=98 width=15 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=0
94+
char id=96 x=490 y=98 width=17 height=18 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=0
95+
char id=34 x=0 y=125 width=21 height=18 xoffset=-2 yoffset=3 xadvance=16 page=0 chnl=0
96+
char id=126 x=21 y=125 width=25 height=15 xoffset=-4 yoffset=8 xadvance=16 page=0 chnl=0
97+
char id=46 x=46 y=125 width=15 height=14 xoffset=1 yoffset=20 xadvance=16 page=0 chnl=0
98+
char id=95 x=61 y=125 width=25 height=12 xoffset=-4 yoffset=24 xadvance=16 page=0 chnl=0
99+
char id=45 x=86 y=125 width=22 height=12 xoffset=-2 yoffset=12 xadvance=16 page=0 chnl=0
100+
kernings count=-1

0 commit comments

Comments
 (0)