From 17b34685a1c190d6483219b752ceb622327e5866 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 26 Sep 2017 18:55:47 -0700 Subject: [PATCH] Factor mesh expansion out of the glyph store --- demo/client/src/meshes.ts | 152 ++++++++++++++++++++++++++++++++++- demo/client/src/text-demo.ts | 2 + demo/client/src/text.ts | 152 ++--------------------------------- 3 files changed, 158 insertions(+), 148 deletions(-) diff --git a/demo/client/src/meshes.ts b/demo/client/src/meshes.ts index f83bf075..acea3cf5 100644 --- a/demo/client/src/meshes.ts +++ b/demo/client/src/meshes.ts @@ -10,7 +10,8 @@ import * as base64js from 'base64-js'; -import {PathfinderError, expectNotNull, panic} from './utils'; +import { PathfinderError, expectNotNull, panic, UINT32_SIZE, UINT32_MAX } from './utils'; +import * as _ from 'lodash'; const BUFFER_TYPES: Meshes = { bQuads: 'ARRAY_BUFFER', @@ -69,6 +70,125 @@ export class PathfinderMeshData implements Meshes { this.edgeLowerCurveIndexCount = this.edgeLowerCurveIndices.byteLength / 16; } + expand(pathIDs: number[]): PathfinderMeshData { + const bQuads = _.chunk(new Uint32Array(this.bQuads), B_QUAD_SIZE / UINT32_SIZE); + const bVertexPositions = new Float32Array(this.bVertexPositions); + const bVertexPathIDs = new Uint16Array(this.bVertexPathIDs); + const bVertexLoopBlinnData = new Uint32Array(this.bVertexLoopBlinnData); + + const edgeUpperCurveIndices = new Uint32Array(this.edgeUpperCurveIndices); + const edgeLowerCurveIndices = new Uint32Array(this.edgeLowerCurveIndices); + for (let indexIndex = 3; indexIndex < edgeUpperCurveIndices.length; indexIndex += 4) + edgeUpperCurveIndices[indexIndex] = 0; + for (let indexIndex = 3; indexIndex < edgeLowerCurveIndices.length; indexIndex += 4) + edgeLowerCurveIndices[indexIndex] = 0; + + const expandedBQuads: number[] = []; + const expandedBVertexPositions: number[] = []; + const expandedBVertexPathIDs: number[] = []; + const expandedBVertexLoopBlinnData: number[] = []; + const expandedCoverInteriorIndices: number[] = []; + const expandedCoverCurveIndices: number[] = []; + const expandedEdgeUpperCurveIndices: number[] = []; + const expandedEdgeUpperLineIndices: number[] = []; + const expandedEdgeLowerCurveIndices: number[] = []; + const expandedEdgeLowerLineIndices: number[] = []; + + let textGlyphIndex = 0; + for (const pathID of pathIDs) { + const firstBVertexIndex = _.sortedIndex(bVertexPathIDs, pathID); + if (firstBVertexIndex < 0) + continue; + + // Copy over vertices. + let bVertexIndex = firstBVertexIndex; + const firstExpandedBVertexIndex = expandedBVertexPathIDs.length; + while (bVertexIndex < bVertexPathIDs.length && + bVertexPathIDs[bVertexIndex] === pathID) { + expandedBVertexPositions.push(bVertexPositions[bVertexIndex * 2 + 0], + bVertexPositions[bVertexIndex * 2 + 1]); + expandedBVertexPathIDs.push(textGlyphIndex + 1); + expandedBVertexLoopBlinnData.push(bVertexLoopBlinnData[bVertexIndex]); + bVertexIndex++; + } + + // Copy over indices. + copyIndices(expandedCoverInteriorIndices, + new Uint32Array(this.coverInteriorIndices), + firstExpandedBVertexIndex, + firstBVertexIndex, + bVertexIndex); + copyIndices(expandedCoverCurveIndices, + new Uint32Array(this.coverCurveIndices), + firstExpandedBVertexIndex, + firstBVertexIndex, + bVertexIndex); + + copyIndices(expandedEdgeUpperLineIndices, + new Uint32Array(this.edgeUpperLineIndices), + firstExpandedBVertexIndex, + firstBVertexIndex, + bVertexIndex); + copyIndices(expandedEdgeUpperCurveIndices, + new Uint32Array(edgeUpperCurveIndices), + firstExpandedBVertexIndex, + firstBVertexIndex, + bVertexIndex, + indexIndex => indexIndex % 4 < 3); + copyIndices(expandedEdgeLowerLineIndices, + new Uint32Array(this.edgeLowerLineIndices), + firstExpandedBVertexIndex, + firstBVertexIndex, + bVertexIndex); + copyIndices(expandedEdgeLowerCurveIndices, + new Uint32Array(edgeLowerCurveIndices), + firstExpandedBVertexIndex, + firstBVertexIndex, + bVertexIndex, + indexIndex => indexIndex % 4 < 3); + + // Copy over B-quads. + let firstBQuadIndex = _.findIndex(bQuads, + bQuad => bVertexPathIDs[bQuad[0]] === pathID); + if (firstBQuadIndex < 0) + firstBQuadIndex = bQuads.length; + const indexDelta = firstExpandedBVertexIndex - firstBVertexIndex; + for (let bQuadIndex = firstBQuadIndex; bQuadIndex < bQuads.length; bQuadIndex++) { + const bQuad = bQuads[bQuadIndex]; + if (bVertexPathIDs[bQuad[0]] !== pathID) + break; + for (let indexIndex = 0; indexIndex < B_QUAD_SIZE / UINT32_SIZE; indexIndex++) { + const srcIndex = bQuad[indexIndex]; + if (srcIndex === UINT32_MAX) + expandedBQuads.push(srcIndex); + else + expandedBQuads.push(srcIndex + indexDelta); + } + } + + textGlyphIndex++; + } + + return new PathfinderMeshData({ + bQuads: new Uint32Array(expandedBQuads).buffer as ArrayBuffer, + bVertexPositions: new Float32Array(expandedBVertexPositions).buffer as ArrayBuffer, + bVertexPathIDs: new Uint16Array(expandedBVertexPathIDs).buffer as ArrayBuffer, + bVertexLoopBlinnData: new Uint32Array(expandedBVertexLoopBlinnData).buffer as + ArrayBuffer, + coverInteriorIndices: new Uint32Array(expandedCoverInteriorIndices).buffer as + ArrayBuffer, + coverCurveIndices: new Uint32Array(expandedCoverCurveIndices).buffer as ArrayBuffer, + edgeUpperCurveIndices: new Uint32Array(expandedEdgeUpperCurveIndices).buffer as + ArrayBuffer, + edgeUpperLineIndices: new Uint32Array(expandedEdgeUpperLineIndices).buffer as + ArrayBuffer, + edgeLowerCurveIndices: new Uint32Array(expandedEdgeLowerCurveIndices).buffer as + ArrayBuffer, + edgeLowerLineIndices: new Uint32Array(expandedEdgeLowerLineIndices).buffer as + ArrayBuffer, + }) + } + readonly bQuads: ArrayBuffer; readonly bVertexPositions: ArrayBuffer; readonly bVertexPathIDs: ArrayBuffer; @@ -109,3 +229,33 @@ export class PathfinderMeshBuffers implements Meshes { readonly edgeLowerLineIndices: WebGLBuffer; readonly edgeLowerCurveIndices: WebGLBuffer; } + +function copyIndices(destIndices: number[], + srcIndices: Uint32Array, + firstExpandedIndex: number, + firstIndex: number, + lastIndex: number, + validateIndex?: (indexIndex: number) => boolean) { + if (firstIndex === lastIndex) + return; + + // FIXME(pcwalton): Use binary search instead of linear search. + let indexIndex = _.findIndex(srcIndices, srcIndex => { + return srcIndex >= firstIndex && srcIndex < lastIndex; + }); + if (indexIndex < 0) + return; + + const indexDelta = firstExpandedIndex - firstIndex; + while (indexIndex < srcIndices.length) { + const index = srcIndices[indexIndex]; + if (validateIndex == null || validateIndex(indexIndex)) { + if (index < firstIndex || index >= lastIndex) + break; + destIndices.push(index + indexDelta); + } else { + destIndices.push(index); + } + indexIndex++; + } +} diff --git a/demo/client/src/text-demo.ts b/demo/client/src/text-demo.ts index de616951..e2d4f02e 100644 --- a/demo/client/src/text-demo.ts +++ b/demo/client/src/text-demo.ts @@ -74,6 +74,8 @@ const B_POSITION_SIZE: number = 8; const B_PATH_INDEX_SIZE: number = 2; +const SUBPIXEL_GRANULARITY: number = 4; + const ATLAS_SIZE: glmatrix.vec2 = glmatrix.vec2.fromValues(2048, 4096); const MIN_SCALE: number = 0.001; diff --git a/demo/client/src/text.ts b/demo/client/src/text.ts index 345019c4..e33a5ab8 100644 --- a/demo/client/src/text.ts +++ b/demo/client/src/text.ts @@ -84,130 +84,18 @@ export class TextFrame { } expandMeshes(uniqueGlyphs: Glyph[], meshes: PathfinderMeshData): ExpandedMeshData { - const bQuads = _.chunk(new Uint32Array(meshes.bQuads), B_QUAD_SIZE / UINT32_SIZE); - const bVertexPositions = new Float32Array(meshes.bVertexPositions); - const bVertexPathIDs = new Uint16Array(meshes.bVertexPathIDs); - const bVertexLoopBlinnData = new Uint32Array(meshes.bVertexLoopBlinnData); - - const edgeUpperCurveIndices = new Uint32Array(meshes.edgeUpperCurveIndices); - const edgeLowerCurveIndices = new Uint32Array(meshes.edgeLowerCurveIndices); - for (let indexIndex = 3; indexIndex < edgeUpperCurveIndices.length; indexIndex += 4) - edgeUpperCurveIndices[indexIndex] = 0; - for (let indexIndex = 3; indexIndex < edgeLowerCurveIndices.length; indexIndex += 4) - edgeLowerCurveIndices[indexIndex] = 0; - - const expandedBQuads: number[] = []; - const expandedBVertexPositions: number[] = []; - const expandedBVertexPathIDs: number[] = []; - const expandedBVertexLoopBlinnData: number[] = []; - const expandedCoverInteriorIndices: number[] = []; - const expandedCoverCurveIndices: number[] = []; - const expandedEdgeUpperCurveIndices: number[] = []; - const expandedEdgeUpperLineIndices: number[] = []; - const expandedEdgeLowerCurveIndices: number[] = []; - const expandedEdgeLowerLineIndices: number[] = []; - - let textGlyphIndex = 0; + const pathIDs = []; for (const textRun of this.runs) { for (const textGlyph of textRun.glyphs) { const uniqueGlyphIndex = _.sortedIndexBy(uniqueGlyphs, textGlyph, 'index'); - if (uniqueGlyphIndex < 0) - continue; - const firstBVertexIndex = _.sortedIndex(bVertexPathIDs, uniqueGlyphIndex + 1); - if (firstBVertexIndex < 0) - continue; - - // Copy over vertices. - let bVertexIndex = firstBVertexIndex; - const firstExpandedBVertexIndex = expandedBVertexPathIDs.length; - while (bVertexIndex < bVertexPathIDs.length && - bVertexPathIDs[bVertexIndex] === uniqueGlyphIndex + 1) { - expandedBVertexPositions.push(bVertexPositions[bVertexIndex * 2 + 0], - bVertexPositions[bVertexIndex * 2 + 1]); - expandedBVertexPathIDs.push(textGlyphIndex + 1); - expandedBVertexLoopBlinnData.push(bVertexLoopBlinnData[bVertexIndex]); - bVertexIndex++; - } - - // Copy over indices. - copyIndices(expandedCoverInteriorIndices, - new Uint32Array(meshes.coverInteriorIndices), - firstExpandedBVertexIndex, - firstBVertexIndex, - bVertexIndex); - copyIndices(expandedCoverCurveIndices, - new Uint32Array(meshes.coverCurveIndices), - firstExpandedBVertexIndex, - firstBVertexIndex, - bVertexIndex); - - copyIndices(expandedEdgeUpperLineIndices, - new Uint32Array(meshes.edgeUpperLineIndices), - firstExpandedBVertexIndex, - firstBVertexIndex, - bVertexIndex); - copyIndices(expandedEdgeUpperCurveIndices, - new Uint32Array(edgeUpperCurveIndices), - firstExpandedBVertexIndex, - firstBVertexIndex, - bVertexIndex, - indexIndex => indexIndex % 4 < 3); - copyIndices(expandedEdgeLowerLineIndices, - new Uint32Array(meshes.edgeLowerLineIndices), - firstExpandedBVertexIndex, - firstBVertexIndex, - bVertexIndex); - copyIndices(expandedEdgeLowerCurveIndices, - new Uint32Array(edgeLowerCurveIndices), - firstExpandedBVertexIndex, - firstBVertexIndex, - bVertexIndex, - indexIndex => indexIndex % 4 < 3); - - // Copy over B-quads. - let firstBQuadIndex = - _.findIndex(bQuads, bQuad => bVertexPathIDs[bQuad[0]] == uniqueGlyphIndex + 1); - if (firstBQuadIndex < 0) - firstBQuadIndex = bQuads.length; - const indexDelta = firstExpandedBVertexIndex - firstBVertexIndex; - for (let bQuadIndex = firstBQuadIndex; bQuadIndex < bQuads.length; bQuadIndex++) { - const bQuad = bQuads[bQuadIndex]; - if (bVertexPathIDs[bQuad[0]] !== uniqueGlyphIndex + 1) - break; - for (let indexIndex = 0; indexIndex < B_QUAD_SIZE / UINT32_SIZE; indexIndex++) { - const srcIndex = bQuad[indexIndex]; - if (srcIndex === UINT32_MAX) - expandedBQuads.push(srcIndex); - else - expandedBQuads.push(srcIndex + indexDelta); - } - } - - textGlyphIndex++; + if (uniqueGlyphIndex >= 0) + pathIDs.push(uniqueGlyphIndex + 1); } } return { - meshes: new PathfinderMeshData({ - bQuads: new Uint32Array(expandedBQuads).buffer as ArrayBuffer, - bVertexPositions: new Float32Array(expandedBVertexPositions).buffer as ArrayBuffer, - bVertexPathIDs: new Uint16Array(expandedBVertexPathIDs).buffer as ArrayBuffer, - bVertexLoopBlinnData: new Uint32Array(expandedBVertexLoopBlinnData).buffer as - ArrayBuffer, - coverInteriorIndices: new Uint32Array(expandedCoverInteriorIndices).buffer as - ArrayBuffer, - coverCurveIndices: new Uint32Array(expandedCoverCurveIndices).buffer as - ArrayBuffer, - edgeUpperCurveIndices: new Uint32Array(expandedEdgeUpperCurveIndices).buffer as - ArrayBuffer, - edgeUpperLineIndices: new Uint32Array(expandedEdgeUpperLineIndices).buffer as - ArrayBuffer, - edgeLowerCurveIndices: new Uint32Array(expandedEdgeLowerCurveIndices).buffer as - ArrayBuffer, - edgeLowerLineIndices: new Uint32Array(expandedEdgeLowerLineIndices).buffer as - ArrayBuffer, - }) - } + meshes: meshes.expand(pathIDs), + }; } get bounds(): glmatrix.vec4 { @@ -464,33 +352,3 @@ export class Hint { readonly hintedXHeight: number; private useHinting: boolean; } - -function copyIndices(destIndices: number[], - srcIndices: Uint32Array, - firstExpandedIndex: number, - firstIndex: number, - lastIndex: number, - validateIndex?: (indexIndex: number) => boolean) { - if (firstIndex === lastIndex) - return; - - // FIXME(pcwalton): Use binary search instead of linear search. - let indexIndex = _.findIndex(srcIndices, srcIndex => { - return srcIndex >= firstIndex && srcIndex < lastIndex; - }); - if (indexIndex < 0) - return; - - const indexDelta = firstExpandedIndex - firstIndex; - while (indexIndex < srcIndices.length) { - const index = srcIndices[indexIndex]; - if (validateIndex == null || validateIndex(indexIndex)) { - if (index < firstIndex || index >= lastIndex) - break; - destIndices.push(index + indexDelta); - } else { - destIndices.push(index); - } - indexIndex++; - } -}