Skip to content

Commit 4c9588c

Browse files
committed
improved(display): Horizontal text positioning accuracy enhanced, especially with [textAnchor](https://heremaps.github.io/xyz-maps/docs/interfaces/core.textstyle.html#textanchor) enabled
Signed-off-by: Tim Deubler <tim.deubler@here.com>
1 parent 06b5ae1 commit 4c9588c

File tree

3 files changed

+71
-40
lines changed

3 files changed

+71
-40
lines changed

packages/display/src/displays/webgl/GlyphManager.ts

+40-19
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,28 @@ export type Glyph = {
4242
direction: number; // 1(LTR)|0(NEUTRAL)|-1(RTL)
4343
}
4444

45+
type Font = {
46+
canvas: HTMLCanvasElement;
47+
textMetricsCache: Map<string, TextMetrics>;
48+
ctx: CanvasRenderingContext2D;
49+
scale: number;
50+
glyphs: Map<string, Glyph>;
51+
baselineOffset: number;
52+
paddingX: number;
53+
offsetX: number;
54+
size: number;
55+
name: string;
56+
width: number;
57+
style: FontStyle;
58+
letterHeight: number;
59+
letterHeightBottom: number;
60+
spaceWidth: number;
61+
paddingY: number;
62+
rowHeight: number
63+
};
64+
4565
class GlyphManager {
46-
fonts = {};
66+
private fonts: { [id: string]: Font } = {};
4767

4868
static instance: GlyphManager;
4969

@@ -60,7 +80,7 @@ class GlyphManager {
6080
// return `${style.font || DEFAULT_FONT}${style.strokeWidth || DEFAULT_STROKE_WIDTH}${style.textAlign || DEFAULT_TEXT_ALIGN}${scale}`;
6181
}
6282

63-
initFont(style: FontStyle, scale: number = 1) {
83+
initFont(style: FontStyle, scale: number = 1): Font {
6484
const {fonts} = this;
6585
const styleId = this.getFontId(style, scale);
6686

@@ -100,7 +120,7 @@ class GlyphManager {
100120
offsetX: 2 * lineWidth * scale,
101121
scale,
102122
style,
103-
charWidthCache: new Map<string, number>(),
123+
textMetricsCache: new Map<string, TextMetrics>(),
104124
rowHeight: rowHeight * scale,
105125
letterHeightBottom: letterHeightBottom,
106126
letterHeight: letterHeight,
@@ -117,7 +137,7 @@ class GlyphManager {
117137
return font.glyphs.has(char);
118138
}
119139

120-
getGlyph(char: string, font): Glyph {
140+
getGlyph(char: string, font: Font): Glyph {
121141
let glyph = font.glyphs.get(char);
122142

123143
if (!glyph) {
@@ -128,15 +148,15 @@ class GlyphManager {
128148

129149
drawCharacter(font.ctx, char, font.paddingX, font.paddingY, font.style);
130150

131-
let charWidth = font.charWidthCache.get(char);
132-
if (charWidth == undefined) {
133-
charWidth = font.ctx.measureText(char).width;
151+
let metrics = font.textMetricsCache.get(char);
152+
153+
if (!metrics) {
154+
metrics = font.ctx.measureText(char);
134155
} else {
135-
font.charWidthCache.delete(char);
156+
font.textMetricsCache.delete(char);
136157
}
137-
138-
139-
let width = Math.round((charWidth || 0) + 2 * font.paddingX) * scale;
158+
const {width} = metrics;
159+
const imgWidth = Math.round((width || 0) + 2 * font.paddingX) * scale;
140160

141161
// debug only
142162
// let lw = font.ctx.lineWidth;
@@ -146,14 +166,15 @@ class GlyphManager {
146166
// font.ctx.lineWidth = lw;
147167
// font.ctx.strokeStyle = GLYPH_STROKE;
148168

149-
let imgData = font.ctx.getImageData(0, 0, width, font.rowHeight);
169+
const imgData = font.ctx.getImageData(0, 0, imgWidth, font.rowHeight);
150170

151171
glyph = {
172+
// metrics,
152173
char: char,
153-
width: charWidth,
174+
width,
154175
data: imgData,
155176
direction: getDirection(char.charCodeAt(0)),
156-
advanceX: charWidth ? imgData.width - offsetX : 0
177+
advanceX: width ? imgData.width - offsetX : 0
157178
};
158179

159180
font.glyphs.set(char, glyph);
@@ -171,12 +192,12 @@ class GlyphManager {
171192
if (glyph) {
172193
width += glyph.width;
173194
} else {
174-
let w = font.charWidthCache.get(char);
175-
if (w == undefined) {
176-
w = ctx.measureText(char).width;
177-
font.charWidthCache.set(char, w);
195+
let metrics = font.textMetricsCache.get(char);
196+
if (!metrics) {
197+
metrics = ctx.measureText(char);
198+
font.textMetricsCache.set(char, metrics);
178199
}
179-
width += w;
200+
width += metrics.width;
180201
}
181202
}
182203
return width;

packages/display/src/displays/webgl/buffer/addText.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ const addText = (
8686

8787
for (let text of lines) {
8888
const textData = createTextData(text, glyphAtlas, offsets, textureCoordinates, rotationZ, rotationY);
89-
const tx = textData.width * anchorOffset.x * glyphAtlas.scale * OFFSET_SCALE;
9089
const vertexCnt = textData.count * dim;
90+
const tx = (textData.width * anchorOffset.x * glyphAtlas.scale + textData.leftSideBearing) * OFFSET_SCALE;
9191

9292
vertex.reserve(vertexCnt);
9393

packages/display/src/displays/webgl/buffer/createText.ts

+30-20
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const findNextDir = (text, i, glyphAtlas) => {
4040
}
4141
};
4242

43-
type TextData = { x: number; x2: number; offset: number; }
43+
type TextData = { x: number; boxWidth: number; offset: number; textWidth: number }
4444

4545
const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotationY: number | undefined, positions: FlexArray, texcoords: FlexArray, data: TextData) => {
4646
let {offset, x} = data;
@@ -54,10 +54,9 @@ const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotation
5454
let p = positions.length;
5555
const texcoordData = texcoords.data;
5656
let t = texcoords.length;
57-
58-
5957
let rotationHi = rotationZ >> 5;
6058
let rotationLow = (rotationZ & 31);
59+
let glyphWidth = 0;
6160

6261
if (glyphInfo) {
6362
// let {u1, v1, u2, v2, glyph} = glyphInfo;
@@ -67,48 +66,53 @@ const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotation
6766
let v1 = glyphInfo.v1 << 5 | rotationHi;
6867
let v2 = glyphInfo.v2 << 5 | rotationHi;
6968
let {advanceX} = glyph;
69+
glyphWidth = glyph.width;
70+
7071
let {width, height} = glyph.data;
71-
let sx = x * OFFSET_SCALE;
72+
73+
x2 = x + width;
74+
75+
const scaledX = x * OFFSET_SCALE;
76+
const scaledX2 = x2 * OFFSET_SCALE;
7277

7378
height *= OFFSET_SCALE;
74-
x2 = sx + OFFSET_SCALE * width;
7579

76-
positionData[p++] = sx;
80+
positionData[p++] = scaledX;
7781
positionData[p++] = 0;
7882

7983
if (hasRotY) {
8084
positionData[p++] = rotationY;
8185
}
8286

83-
positionData[p++] = x2;
87+
positionData[p++] = scaledX2;
8488
positionData[p++] = height;
8589

8690
if (hasRotY) {
8791
positionData[p++] = rotationY;
8892
}
8993

90-
positionData[p++] = sx;
94+
positionData[p++] = scaledX;
9195
positionData[p++] = height;
9296

9397
if (hasRotY) {
9498
positionData[p++] = rotationY;
9599
}
96100

97-
positionData[p++] = x2;
101+
positionData[p++] = scaledX2;
98102
positionData[p++] = 0;
99103

100104
if (hasRotY) {
101105
positionData[p++] = rotationY;
102106
}
103107

104-
positionData[p++] = x2;
108+
positionData[p++] = scaledX2;
105109
positionData[p++] = height;
106110

107111
if (hasRotY) {
108112
positionData[p++] = rotationY;
109113
}
110114

111-
positionData[p++] = sx;
115+
positionData[p++] = scaledX;
112116
positionData[p++] = 0;
113117

114118
if (hasRotY) {
@@ -144,7 +148,8 @@ const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotation
144148

145149
data.x = x;
146150
data.offset = offset;
147-
data.x2 = x2;
151+
data.boxWidth = x2;
152+
data.textWidth += glyphWidth;
148153
};
149154

150155
const addText = (
@@ -214,10 +219,11 @@ export const createTextData = (
214219
}
215220

216221

217-
const txtData = {
222+
const txtData: TextData = {
218223
x: 0,
219-
x2: 0,
220-
offset: 0
224+
boxWidth: 0,
225+
offset: 0,
226+
textWidth: 0
221227
};
222228
let baseDirection;
223229
let prevDirection;
@@ -231,7 +237,11 @@ export const createTextData = (
231237
let char = text.charAt(i);
232238
let glyphInfo = glyphAtlas.glyphInfos[char];
233239
let isLast = i == len - 1;
234-
let curDirection = glyphInfo?.glyph?.direction || 0; // -1,0,+1
240+
let curDirection = 0;
241+
const glyph = glyphInfo?.glyph;
242+
if (glyph) {
243+
curDirection = glyph.direction; // -1,0,+1
244+
}
235245

236246
if (!baseDirection) {
237247
if (char == ' ') { // neutral
@@ -282,13 +292,13 @@ export const createTextData = (
282292
prevChar = char;
283293
}
284294

285-
const {offset, x2} = txtData;
295+
const width = txtData.boxWidth / glyphAtlas.scale;
286296

287297
return {
288-
count: offset / 2,
298+
count: txtData.offset / 2,
289299
position: positions.data,
290300
texcoord: texcoords.data,
291-
width: x2 / OFFSET_SCALE / glyphAtlas.scale
292-
// height: glyphAtlas.letterHeight
301+
width,
302+
leftSideBearing: ( width - txtData.textWidth ) / 2
293303
};
294304
};

0 commit comments

Comments
 (0)