Only render glyphs in view
This commit is contained in:
parent
c0914a0c87
commit
5f347087a7
|
@ -34,7 +34,7 @@ const B_QUAD_SIZE: number = 4 * 8;
|
||||||
const B_QUAD_UPPER_INDICES_OFFSET: number = 0;
|
const B_QUAD_UPPER_INDICES_OFFSET: number = 0;
|
||||||
const B_QUAD_LOWER_INDICES_OFFSET: number = 4 * 4;
|
const B_QUAD_LOWER_INDICES_OFFSET: number = 4 * 4;
|
||||||
|
|
||||||
const ATLAS_SIZE: glmatrix.vec2 = glmatrix.vec2.fromValues(4096, 4096);
|
const ATLAS_SIZE: glmatrix.vec2 = glmatrix.vec2.fromValues(3072, 3072);
|
||||||
|
|
||||||
const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
|
const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
|
||||||
blit: {
|
blit: {
|
||||||
|
@ -223,6 +223,11 @@ function unwrapUndef<T>(value: T | undefined): T {
|
||||||
return expectNotUndef(value, "Unexpected `undefined`!");
|
return expectNotUndef(value, "Unexpected `undefined`!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The separating axis theorem.
|
||||||
|
function rectsIntersect(a: glmatrix.vec4, b: glmatrix.vec4): boolean {
|
||||||
|
return a[2] > b[0] && a[3] > b[1] && a[0] < b[2] && a[1] < b[3];
|
||||||
|
}
|
||||||
|
|
||||||
class PathfinderError extends Error {
|
class PathfinderError extends Error {
|
||||||
constructor(message?: string | undefined) {
|
constructor(message?: string | undefined) {
|
||||||
super(message);
|
super(message);
|
||||||
|
@ -452,20 +457,21 @@ class AppController {
|
||||||
throw new PathfinderError("The font type is unsupported.");
|
throw new PathfinderError("The font type is unsupported.");
|
||||||
|
|
||||||
// Lay out the text.
|
// Lay out the text.
|
||||||
this.textGlyphs = this.font.stringToGlyphs(TEXT);
|
this.textGlyphs = this.font.stringToGlyphs(TEXT).map(glyph => new TextGlyph(glyph));
|
||||||
|
|
||||||
this.atlasGlyphs = this.textGlyphs.map(glyph => new PathfinderGlyph(glyph));
|
// Determine all glyphs potentially needed.
|
||||||
this.atlasGlyphs.sort((a, b) => a.index - b.index);
|
this.uniqueGlyphs = this.textGlyphs.map(textGlyph => textGlyph.glyph);
|
||||||
this.atlasGlyphs = _.sortedUniqBy(this.atlasGlyphs, glyph => glyph.index);
|
this.uniqueGlyphs.sort((a, b) => a.getIndex() - b.getIndex());
|
||||||
|
this.uniqueGlyphs = _.sortedUniqBy(this.uniqueGlyphs, glyph => glyph.getIndex());
|
||||||
|
|
||||||
// Build the partitioning request to the server.
|
// Build the partitioning request to the server.
|
||||||
const request = {
|
const request = {
|
||||||
otf: base64js.fromByteArray(new Uint8Array(this.fontData)),
|
otf: base64js.fromByteArray(new Uint8Array(this.fontData)),
|
||||||
fontIndex: 0,
|
fontIndex: 0,
|
||||||
glyphs: this.atlasGlyphs.map(glyph => {
|
glyphs: this.uniqueGlyphs.map(glyph => {
|
||||||
const metrics = glyph.metrics;
|
const metrics = glyph.getMetrics();
|
||||||
return {
|
return {
|
||||||
id: glyph.index,
|
id: glyph.getIndex(),
|
||||||
transform: [1, 0, 0, 1, 0, 0],
|
transform: [1, 0, 0, 1, 0, 0],
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
@ -483,9 +489,9 @@ class AppController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private meshesReceived() {
|
private meshesReceived() {
|
||||||
this.rebuildAtlas();
|
|
||||||
this.view.then(view => {
|
this.view.then(view => {
|
||||||
view.uploadPathData(this.atlasGlyphs.length);
|
view.attachText();
|
||||||
|
view.uploadPathData(this.uniqueGlyphs.length);
|
||||||
view.attachMeshes(this.meshes);
|
view.attachMeshes(this.meshes);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -496,8 +502,7 @@ class AppController {
|
||||||
|
|
||||||
private setFontSize(newPixelsPerEm: number) {
|
private setFontSize(newPixelsPerEm: number) {
|
||||||
this.fontSize = newPixelsPerEm;
|
this.fontSize = newPixelsPerEm;
|
||||||
|
this.view.then(view => view.attachText());
|
||||||
this.rebuildAtlas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTiming(newTimes: {atlasRendering: number, compositing: number}) {
|
updateTiming(newTimes: {atlasRendering: number, compositing: number}) {
|
||||||
|
@ -505,17 +510,6 @@ class AppController {
|
||||||
`${newTimes.atlasRendering} ms atlas, ${newTimes.compositing} ms compositing`;
|
`${newTimes.atlasRendering} ms atlas, ${newTimes.compositing} ms compositing`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private rebuildAtlas() {
|
|
||||||
this._atlas.layoutGlyphs(this.atlasGlyphs, this.fontSize, this.font.unitsPerEm);
|
|
||||||
|
|
||||||
this.view.then(view => {
|
|
||||||
view.attachText(this.textGlyphs,
|
|
||||||
this.atlasGlyphs,
|
|
||||||
this.fontSize,
|
|
||||||
this.font.unitsPerEm);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get atlas(): Atlas {
|
get atlas(): Atlas {
|
||||||
return this._atlas;
|
return this._atlas;
|
||||||
}
|
}
|
||||||
|
@ -526,11 +520,12 @@ class AppController {
|
||||||
private fpsLabel: HTMLElement;
|
private fpsLabel: HTMLElement;
|
||||||
|
|
||||||
private fontData: ArrayBuffer;
|
private fontData: ArrayBuffer;
|
||||||
private font: opentype.Font;
|
font: opentype.Font;
|
||||||
private textGlyphs: opentype.Glyph[];
|
textGlyphs: TextGlyph[];
|
||||||
|
uniqueGlyphs: opentype.Glyph[];
|
||||||
|
|
||||||
private _atlas: Atlas;
|
private _atlas: Atlas;
|
||||||
private atlasGlyphs: PathfinderGlyph[];
|
atlasGlyphs: AtlasGlyph[];
|
||||||
|
|
||||||
private meshes: PathfinderMeshData;
|
private meshes: PathfinderMeshData;
|
||||||
|
|
||||||
|
@ -696,41 +691,143 @@ class PathfinderView {
|
||||||
this.setDirty();
|
this.setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTextBuffers(textGlyphs: opentype.Glyph[], atlasGlyphs: PathfinderGlyph[]) {
|
/// Lays out glyphs on the canvas.
|
||||||
|
private layoutGlyphs() {
|
||||||
|
const textGlyphs = this.appController.textGlyphs;
|
||||||
|
|
||||||
|
this.pixelsPerUnit = this.appController.fontSize / this.appController.font.unitsPerEm;
|
||||||
|
|
||||||
this.textGlyphCount = textGlyphs.length;
|
this.textGlyphCount = textGlyphs.length;
|
||||||
|
|
||||||
const atlasGlyphIndices = atlasGlyphs.map(atlasGlyph => atlasGlyph.index);
|
|
||||||
|
|
||||||
const glyphPositions = new Float32Array(textGlyphs.length * 8);
|
const glyphPositions = new Float32Array(textGlyphs.length * 8);
|
||||||
const glyphTexCoords = new Float32Array(textGlyphs.length * 8);
|
|
||||||
const glyphIndices = new Uint32Array(textGlyphs.length * 6);
|
const glyphIndices = new Uint32Array(textGlyphs.length * 6);
|
||||||
|
|
||||||
const currentPosition = glmatrix.vec2.create();
|
const currentPosition = glmatrix.vec2.create();
|
||||||
|
|
||||||
for (let textGlyphIndex = 0; textGlyphIndex < textGlyphs.length; textGlyphIndex++) {
|
for (let glyphIndex = 0; glyphIndex < textGlyphs.length; glyphIndex++) {
|
||||||
const textGlyph = textGlyphs[textGlyphIndex];
|
const textGlyph = textGlyphs[glyphIndex];
|
||||||
const textGlyphMetrics = textGlyph.getMetrics();
|
const glyphMetrics = textGlyph.glyph.getMetrics();
|
||||||
|
|
||||||
const atlasGlyphIndex = _.sortedIndexOf(atlasGlyphIndices, textGlyph.getIndex());
|
// Determine the atlas size.
|
||||||
const atlasGlyph = atlasGlyphs[atlasGlyphIndex];
|
const atlasSize = glmatrix.vec2.fromValues(glyphMetrics.xMax - glyphMetrics.xMin,
|
||||||
|
glyphMetrics.yMax - glyphMetrics.yMin);
|
||||||
|
glmatrix.vec2.scale(atlasSize, atlasSize, this.pixelsPerUnit);
|
||||||
|
glmatrix.vec2.ceil(atlasSize, atlasSize);
|
||||||
|
|
||||||
// Set positions.
|
// Set positions.
|
||||||
const textGlyphBL = glmatrix.vec2.create(), textGlyphTR = glmatrix.vec2.create();
|
const textGlyphBL = glmatrix.vec2.create(), textGlyphTR = glmatrix.vec2.create();
|
||||||
const offset = glmatrix.vec2.fromValues(textGlyphMetrics.leftSideBearing,
|
const offset = glmatrix.vec2.fromValues(glyphMetrics.leftSideBearing,
|
||||||
textGlyphMetrics.yMin);
|
glyphMetrics.yMin);
|
||||||
glmatrix.vec2.scale(offset, offset, this.pixelsPerUnit);
|
glmatrix.vec2.scale(offset, offset, this.pixelsPerUnit);
|
||||||
glmatrix.vec2.add(textGlyphBL, currentPosition, offset);
|
glmatrix.vec2.add(textGlyphBL, currentPosition, offset);
|
||||||
glmatrix.vec2.round(textGlyphBL, textGlyphBL);
|
glmatrix.vec2.round(textGlyphBL, textGlyphBL);
|
||||||
glmatrix.vec2.add(textGlyphTR, textGlyphBL, atlasGlyph.atlasSize);
|
glmatrix.vec2.add(textGlyphTR, textGlyphBL, atlasSize);
|
||||||
|
|
||||||
glyphPositions.set([
|
glyphPositions.set([
|
||||||
textGlyphBL[0], textGlyphTR[1],
|
textGlyphBL[0], textGlyphTR[1],
|
||||||
textGlyphTR[0], textGlyphTR[1],
|
textGlyphTR[0], textGlyphTR[1],
|
||||||
textGlyphBL[0], textGlyphBL[1],
|
textGlyphBL[0], textGlyphBL[1],
|
||||||
textGlyphTR[0], textGlyphBL[1],
|
textGlyphTR[0], textGlyphBL[1],
|
||||||
], textGlyphIndex * 8);
|
], glyphIndex * 8);
|
||||||
|
|
||||||
|
textGlyph.canvasRect = glmatrix.vec4.fromValues(textGlyphBL[0], textGlyphBL[1],
|
||||||
|
textGlyphTR[0], textGlyphTR[1]);
|
||||||
|
|
||||||
|
// Set indices.
|
||||||
|
glyphIndices.set(QUAD_ELEMENTS.map(elementIndex => elementIndex + 4 * glyphIndex),
|
||||||
|
glyphIndex * 6);
|
||||||
|
|
||||||
|
// Advance.
|
||||||
|
currentPosition[0] += textGlyph.glyph.advanceWidth * this.pixelsPerUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.glyphPositionsBuffer = unwrapNull(this.gl.createBuffer());
|
||||||
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glyphPositionsBuffer);
|
||||||
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, glyphPositions, this.gl.STATIC_DRAW);
|
||||||
|
this.glyphElementsBuffer = unwrapNull(this.gl.createBuffer());
|
||||||
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.glyphElementsBuffer);
|
||||||
|
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, glyphIndices, this.gl.STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildAtlasGlyphs() {
|
||||||
|
const textGlyphs = this.appController.textGlyphs;
|
||||||
|
|
||||||
|
// Only build glyphs in view.
|
||||||
|
const canvasRect = glmatrix.vec4.fromValues(-this.translation[0],
|
||||||
|
-this.translation[1],
|
||||||
|
-this.translation[0] + this.canvas.width,
|
||||||
|
-this.translation[1] + this.canvas.height);
|
||||||
|
|
||||||
|
let atlasGlyphs =
|
||||||
|
textGlyphs.filter(textGlyph => rectsIntersect(textGlyph.canvasRect, canvasRect))
|
||||||
|
.map(textGlyph => new AtlasGlyph(textGlyph.glyph));
|
||||||
|
atlasGlyphs.sort((a, b) => a.index - b.index);
|
||||||
|
atlasGlyphs = _.sortedUniqBy(atlasGlyphs, glyph => glyph.index);
|
||||||
|
this.appController.atlasGlyphs = atlasGlyphs;
|
||||||
|
|
||||||
|
const fontSize = this.appController.fontSize;
|
||||||
|
const unitsPerEm = this.appController.font.unitsPerEm;
|
||||||
|
|
||||||
|
this.appController.atlas.layoutGlyphs(atlasGlyphs, fontSize, unitsPerEm);
|
||||||
|
|
||||||
|
const uniqueGlyphIndices = this.appController.uniqueGlyphs.map(glyph => glyph.getIndex());
|
||||||
|
uniqueGlyphIndices.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
// TODO(pcwalton): Regenerate the IBOs to include only the glyphs we care about.
|
||||||
|
const transforms = new Float32Array((this.appController.uniqueGlyphs.length + 1) * 4);
|
||||||
|
for (let glyphIndex = 0; glyphIndex < atlasGlyphs.length; glyphIndex++) {
|
||||||
|
const glyph = atlasGlyphs[glyphIndex];
|
||||||
|
|
||||||
|
let pathID = _.sortedIndexOf(uniqueGlyphIndices, glyph.index);
|
||||||
|
assert(pathID >= 0, "No path ID!");
|
||||||
|
pathID++;
|
||||||
|
|
||||||
|
const atlasLocation = glyph.atlasRect;
|
||||||
|
const metrics = glyph.metrics;
|
||||||
|
const left = metrics.xMin * this.pixelsPerUnit;
|
||||||
|
const bottom = metrics.yMin * this.pixelsPerUnit;
|
||||||
|
|
||||||
|
transforms[pathID * 4 + 0] = this.pixelsPerUnit;
|
||||||
|
transforms[pathID * 4 + 1] = this.pixelsPerUnit;
|
||||||
|
transforms[pathID * 4 + 2] = atlasLocation[0] - left;
|
||||||
|
transforms[pathID * 4 + 3] = atlasLocation[1] - bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.atlasTransformBuffer.upload(this.gl, transforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createAtlasFramebuffer() {
|
||||||
|
const atlasColorTexture = this.appController.atlas.ensureTexture(this.gl);
|
||||||
|
this.atlasDepthTexture = createFramebufferDepthTexture(this.gl, ATLAS_SIZE);
|
||||||
|
this.atlasFramebuffer = createFramebuffer(this.gl,
|
||||||
|
this.drawBuffersExt,
|
||||||
|
[atlasColorTexture],
|
||||||
|
this.atlasDepthTexture);
|
||||||
|
|
||||||
|
// Allow the antialiasing strategy to set up framebuffers as necessary.
|
||||||
|
this.antialiasingStrategy.setFramebufferSize(this, ATLAS_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setGlyphTexCoords() {
|
||||||
|
const textGlyphs = this.appController.textGlyphs;
|
||||||
|
const atlasGlyphs = this.appController.atlasGlyphs;
|
||||||
|
|
||||||
|
const atlasGlyphIndices = atlasGlyphs.map(atlasGlyph => atlasGlyph.index);
|
||||||
|
|
||||||
|
const glyphTexCoords = new Float32Array(textGlyphs.length * 8);
|
||||||
|
|
||||||
|
const currentPosition = glmatrix.vec2.create();
|
||||||
|
|
||||||
|
for (let textGlyphIndex = 0; textGlyphIndex < textGlyphs.length; textGlyphIndex++) {
|
||||||
|
const textGlyph = textGlyphs[textGlyphIndex];
|
||||||
|
const textGlyphMetrics = textGlyph.glyph.getMetrics();
|
||||||
|
|
||||||
|
let atlasGlyphIndex = _.sortedIndexOf(atlasGlyphIndices, textGlyph.glyph.getIndex());
|
||||||
|
if (atlasGlyphIndex < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Set texture coordinates.
|
// Set texture coordinates.
|
||||||
|
const atlasGlyph = atlasGlyphs[atlasGlyphIndex];
|
||||||
const atlasGlyphRect = atlasGlyph.atlasRect;
|
const atlasGlyphRect = atlasGlyph.atlasRect;
|
||||||
const atlasGlyphBL = atlasGlyphRect.slice(0, 2) as glmatrix.vec2;
|
const atlasGlyphBL = atlasGlyphRect.slice(0, 2) as glmatrix.vec2;
|
||||||
const atlasGlyphTR = atlasGlyphRect.slice(2, 4) as glmatrix.vec2;
|
const atlasGlyphTR = atlasGlyphRect.slice(2, 4) as glmatrix.vec2;
|
||||||
|
@ -743,64 +840,25 @@ class PathfinderView {
|
||||||
atlasGlyphBL[0], atlasGlyphBL[1],
|
atlasGlyphBL[0], atlasGlyphBL[1],
|
||||||
atlasGlyphTR[0], atlasGlyphBL[1],
|
atlasGlyphTR[0], atlasGlyphBL[1],
|
||||||
], textGlyphIndex * 8);
|
], textGlyphIndex * 8);
|
||||||
|
|
||||||
// Set indices.
|
|
||||||
glyphIndices.set(QUAD_ELEMENTS.map(elementIndex => elementIndex + 4 * textGlyphIndex),
|
|
||||||
textGlyphIndex * 6);
|
|
||||||
|
|
||||||
// Advance.
|
|
||||||
currentPosition[0] += textGlyph.advanceWidth * this.pixelsPerUnit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.glyphPositionsBuffer = unwrapNull(this.gl.createBuffer());
|
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glyphPositionsBuffer);
|
|
||||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, glyphPositions, this.gl.STATIC_DRAW);
|
|
||||||
this.glyphTexCoordsBuffer = unwrapNull(this.gl.createBuffer());
|
this.glyphTexCoordsBuffer = unwrapNull(this.gl.createBuffer());
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glyphTexCoordsBuffer);
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glyphTexCoordsBuffer);
|
||||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, glyphTexCoords, this.gl.STATIC_DRAW);
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, glyphTexCoords, this.gl.STATIC_DRAW);
|
||||||
this.glyphElementsBuffer = unwrapNull(this.gl.createBuffer());
|
|
||||||
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.glyphElementsBuffer);
|
|
||||||
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, glyphIndices, this.gl.STATIC_DRAW);
|
|
||||||
|
|
||||||
this.setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attachText(textGlyphs: opentype.Glyph[],
|
attachText() {
|
||||||
atlasGlyphs: PathfinderGlyph[],
|
if (this.atlasFramebuffer == null)
|
||||||
fontSize: number,
|
this.createAtlasFramebuffer();
|
||||||
unitsPerEm: number) {
|
this.layoutGlyphs();
|
||||||
this.pixelsPerUnit = fontSize / unitsPerEm;
|
|
||||||
|
|
||||||
const transforms = new Float32Array(_.concat([0, 0, 0, 0],
|
this.rebuildAtlasIfNecessary();
|
||||||
_.flatMap(atlasGlyphs, glyph => {
|
}
|
||||||
const atlasLocation = glyph.atlasRect;
|
|
||||||
const metrics = glyph.metrics;
|
|
||||||
const left = metrics.xMin * this.pixelsPerUnit;
|
|
||||||
const bottom = metrics.yMin * this.pixelsPerUnit;
|
|
||||||
return [
|
|
||||||
this.pixelsPerUnit,
|
|
||||||
this.pixelsPerUnit,
|
|
||||||
atlasLocation[0] - left,
|
|
||||||
atlasLocation[1] - bottom,
|
|
||||||
];
|
|
||||||
})));
|
|
||||||
|
|
||||||
this.atlasTransformBuffer.upload(this.gl, transforms);
|
private rebuildAtlasIfNecessary() {
|
||||||
|
this.buildAtlasGlyphs();
|
||||||
// Create the atlas framebuffer if necessary.
|
this.setGlyphTexCoords();
|
||||||
if (this.atlasFramebuffer == null) {
|
this.setDirty();
|
||||||
const atlasColorTexture = this.appController.atlas.ensureTexture(this.gl);
|
|
||||||
this.atlasDepthTexture = createFramebufferDepthTexture(this.gl, ATLAS_SIZE);
|
|
||||||
this.atlasFramebuffer = createFramebuffer(this.gl,
|
|
||||||
this.drawBuffersExt,
|
|
||||||
[atlasColorTexture],
|
|
||||||
this.atlasDepthTexture);
|
|
||||||
|
|
||||||
// Allow the antialiasing strategy to set up framebuffers as necessary.
|
|
||||||
this.antialiasingStrategy.setFramebufferSize(this, ATLAS_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createTextBuffers(textGlyphs, atlasGlyphs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setDirty() {
|
private setDirty() {
|
||||||
|
@ -841,7 +899,8 @@ class PathfinderView {
|
||||||
const delta = glmatrix.vec2.fromValues(-event.deltaX, event.deltaY);
|
const delta = glmatrix.vec2.fromValues(-event.deltaX, event.deltaY);
|
||||||
glmatrix.vec2.scale(delta, delta, window.devicePixelRatio);
|
glmatrix.vec2.scale(delta, delta, window.devicePixelRatio);
|
||||||
glmatrix.vec2.add(this.translation, this.translation, delta);
|
glmatrix.vec2.add(this.translation, this.translation, delta);
|
||||||
this.setDirty();
|
|
||||||
|
this.rebuildAtlasIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
private resizeToFit() {
|
private resizeToFit() {
|
||||||
|
@ -1996,7 +2055,24 @@ interface AntialiasingStrategyTable {
|
||||||
ecaa: typeof ECAAStrategy;
|
ecaa: typeof ECAAStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathfinderGlyph {
|
class TextGlyph {
|
||||||
|
constructor(glyph: opentype.Glyph) {
|
||||||
|
this.glyph = glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canvasRect() {
|
||||||
|
return this._canvasRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
set canvasRect(rect: Rect) {
|
||||||
|
this._canvasRect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph: opentype.Glyph;
|
||||||
|
private _canvasRect: Rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtlasGlyph {
|
||||||
constructor(glyph: opentype.Glyph) {
|
constructor(glyph: opentype.Glyph) {
|
||||||
this.glyph = glyph;
|
this.glyph = glyph;
|
||||||
}
|
}
|
||||||
|
@ -2005,6 +2081,10 @@ class PathfinderGlyph {
|
||||||
return this._atlasRect;
|
return this._atlasRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set atlasRect(rect: Rect) {
|
||||||
|
this._atlasRect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
get atlasSize(): Size2D {
|
get atlasSize(): Size2D {
|
||||||
let atlasSize = glmatrix.vec2.create();
|
let atlasSize = glmatrix.vec2.create();
|
||||||
glmatrix.vec2.sub(atlasSize,
|
glmatrix.vec2.sub(atlasSize,
|
||||||
|
@ -2013,10 +2093,6 @@ class PathfinderGlyph {
|
||||||
return atlasSize;
|
return atlasSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAtlasRect(rect: Rect) {
|
|
||||||
this._atlasRect = rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
get index(): number {
|
get index(): number {
|
||||||
return this.glyph.getIndex();
|
return this.glyph.getIndex();
|
||||||
}
|
}
|
||||||
|
@ -2035,7 +2111,7 @@ class Atlas {
|
||||||
this._usedSize = glmatrix.vec2.create();
|
this._usedSize = glmatrix.vec2.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutGlyphs(glyphs: PathfinderGlyph[], fontSize: number, unitsPerEm: number) {
|
layoutGlyphs(glyphs: AtlasGlyph[], fontSize: number, unitsPerEm: number) {
|
||||||
const pixelsPerUnit = fontSize / unitsPerEm;
|
const pixelsPerUnit = fontSize / unitsPerEm;
|
||||||
|
|
||||||
let nextOrigin = glmatrix.vec2.create();
|
let nextOrigin = glmatrix.vec2.create();
|
||||||
|
@ -2059,7 +2135,7 @@ class Atlas {
|
||||||
nextOrigin[0] + glyphSize[0] + 1,
|
nextOrigin[0] + glyphSize[0] + 1,
|
||||||
nextOrigin[1] + glyphSize[1] + 1);
|
nextOrigin[1] + glyphSize[1] + 1);
|
||||||
|
|
||||||
glyph.setAtlasRect(glyphRect);
|
glyph.atlasRect = glyphRect;
|
||||||
|
|
||||||
nextOrigin[0] = glyphRect[2] + 1;
|
nextOrigin[0] = glyphRect[2] + 1;
|
||||||
shelfBottom = Math.max(shelfBottom, glyphRect[3]);
|
shelfBottom = Math.max(shelfBottom, glyphRect[3]);
|
||||||
|
|
Loading…
Reference in New Issue