Stub integration test functionality some more
This commit is contained in:
parent
bc99fdf02b
commit
0642e65c9d
|
@ -99,7 +99,7 @@ button > svg path {
|
|||
#pf-rendering-options-group {
|
||||
right: 1em;
|
||||
}
|
||||
#pf-canvas {
|
||||
.pf-maximized-canvas {
|
||||
display: block;
|
||||
position: absolute;
|
||||
touch-action: none;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</head>
|
||||
<body class="pf-unscrollable">
|
||||
{{>partials/navbar.html isDemo=true}}
|
||||
<canvas id="pf-canvas" width="400" height="300"></canvas>
|
||||
<canvas id="pf-canvas" class="pf-maximized-canvas" width="400" height="300"></canvas>
|
||||
<div class="fixed-bottom mb-3 d-flex justify-content-between align-items-end pf-pointer-events-none">
|
||||
<div class="rounded invisible container py-1 px-3 ml-3" id="pf-fps-label"></div>
|
||||
<div id="pf-toolbar">
|
||||
|
|
|
@ -28,10 +28,11 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<canvas id="pf-reference-pane" class="pf-draggable col" width="300" height="400">
|
||||
</canvas>
|
||||
<canvas id="pf-rendering-pane" class="pf-draggable col" width="300" height="400">
|
||||
<canvas id="pf-reference-pane" class="pf-draggable pf-pane col" width="300"
|
||||
height="400">
|
||||
</canvas>
|
||||
<canvas id="pf-canvas" class="pf-draggable pf-pane col" width="300"
|
||||
height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
</head>
|
||||
<body class="pf-unscrollable">
|
||||
{{>partials/navbar.html isTool=true}}
|
||||
<canvas id="pf-canvas" class="pf-draggable" width="400" height="300"></canvas>
|
||||
<canvas id="pf-canvas" class="pf-maximized-canvas pf-draggable" width="400"
|
||||
height="300"></canvas>
|
||||
<svg id="pf-svg" xmlns="http://www.w3.org/2000/svg" width="400" height="300"></svg>
|
||||
<div class="fixed-bottom mb-3 d-flex justify-content-end align-items-end pf-pointer-events-none">
|
||||
<div id="pf-toolbar">
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
</head>
|
||||
<body class="pf-unscrollable">
|
||||
{{>partials/navbar.html isDemo=true}}
|
||||
<canvas id="pf-canvas" class="pf-draggable" width="400" height="300"></canvas>
|
||||
<canvas id="pf-canvas" class="pf-maximized-canvas pf-draggable" width="400"
|
||||
height="300"></canvas>
|
||||
<svg id="pf-svg" xmlns="http://www.w3.org/2000/svg" width="400" height="300"></svg>
|
||||
<div class="fixed-bottom mb-3 d-flex justify-content-between align-items-end pf-pointer-events-none">
|
||||
<div class="rounded invisible container py-1 px-3 ml-3" id="pf-fps-label"></div>
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
</head>
|
||||
<body class="pf-unscrollable">
|
||||
{{>partials/navbar.html isDemo=true}}
|
||||
<canvas id="pf-canvas" class="pf-draggable" width="400" height="300"></canvas>
|
||||
<canvas id="pf-canvas" class="pf-maximized-canvas pf-draggable" width="400"
|
||||
height="300"></canvas>
|
||||
<div class="fixed-bottom mb-3 d-flex justify-content-between align-items-end pf-pointer-events-none">
|
||||
<div class="rounded invisible container py-1 px-3 ml-3" id="pf-fps-label"></div>
|
||||
<div id="pf-toolbar">
|
||||
|
|
|
@ -68,9 +68,7 @@ export abstract class AppController {
|
|||
|
||||
protected screenshotButton: HTMLButtonElement | null;
|
||||
|
||||
start() {
|
||||
const canvas = document.getElementById('pf-canvas') as HTMLCanvasElement;
|
||||
}
|
||||
start(): void {}
|
||||
|
||||
protected loadInitialFile(builtinFileURI: string) {
|
||||
const selectFileElement = document.getElementById('pf-select-file') as
|
||||
|
|
|
@ -74,7 +74,7 @@ class BenchmarkAppController extends DemoAppController<BenchmarkTestView> {
|
|||
private elapsedTimes: ElapsedTime[];
|
||||
private partitionTime: number;
|
||||
|
||||
start() {
|
||||
start(): void {
|
||||
super.start();
|
||||
|
||||
this.resultsModal = unwrapNull(document.getElementById('pf-benchmark-results-modal')) as
|
||||
|
|
|
@ -15,18 +15,27 @@ import {SubpixelAAType} from './aa-strategy';
|
|||
import {DemoAppController} from "./app-controller";
|
||||
import {OrthographicCamera} from './camera';
|
||||
import {UniformMap} from './gl-utils';
|
||||
import {PathfinderMeshData} from './meshes';
|
||||
import {Renderer} from "./renderer";
|
||||
import {ShaderMap} from "./shader-loader";
|
||||
import {ShaderMap, ShaderProgramSource} from "./shader-loader";
|
||||
import SSAAStrategy from './ssaa-strategy';
|
||||
import {BUILTIN_FONT_URI, computeStemDarkeningAmount, ExpandedMeshData, GlyphStore} from "./text";
|
||||
import {PathfinderFont, TextFrame, TextRun} from "./text";
|
||||
import {unwrapNull} from "./utils";
|
||||
import {DemoView} from "./view";
|
||||
import {AdaptiveMonochromeXCAAStrategy} from './xcaa-strategy';
|
||||
|
||||
const FONT: string = 'open-sans';
|
||||
const TEXT_COLOR: number[] = [0, 0, 0, 255];
|
||||
|
||||
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
|
||||
none: NoAAStrategy,
|
||||
ssaa: SSAAStrategy,
|
||||
xcaa: AdaptiveMonochromeXCAAStrategy,
|
||||
};
|
||||
|
||||
const STRING: string = "A";
|
||||
|
||||
interface AntialiasingStrategyTable {
|
||||
none: typeof NoAAStrategy;
|
||||
ssaa: typeof SSAAStrategy;
|
||||
|
@ -34,38 +43,183 @@ interface AntialiasingStrategyTable {
|
|||
}
|
||||
|
||||
class IntegrationTestAppController extends DemoAppController<IntegrationTestView> {
|
||||
protected builtinFileURI: string;
|
||||
protected defaultFile: string;
|
||||
protected createView(): IntegrationTestView {
|
||||
throw new Error("Method not implemented.");
|
||||
font: PathfinderFont | null;
|
||||
textRun: TextRun | null;
|
||||
|
||||
protected readonly defaultFile: string = FONT;
|
||||
protected readonly builtinFileURI: string = BUILTIN_FONT_URI;
|
||||
|
||||
private glyphStore: GlyphStore;
|
||||
private baseMeshes: PathfinderMeshData;
|
||||
private expandedMeshes: ExpandedMeshData;
|
||||
|
||||
start(): void {
|
||||
super.start();
|
||||
|
||||
this.loadInitialFile(this.builtinFileURI);
|
||||
}
|
||||
protected fileLoaded(data: ArrayBuffer, builtinName: string | null): void {
|
||||
throw new Error("Method not implemented.");
|
||||
|
||||
protected createView(): IntegrationTestView {
|
||||
return new IntegrationTestView(this,
|
||||
unwrapNull(this.gammaLUT),
|
||||
unwrapNull(this.commonShaderSource),
|
||||
unwrapNull(this.shaderSources));
|
||||
}
|
||||
|
||||
protected fileLoaded(fileData: ArrayBuffer, builtinName: string | null): void {
|
||||
const font = new PathfinderFont(fileData, builtinName);
|
||||
this.font = font;
|
||||
|
||||
const textRun = new TextRun(STRING, [0, 0], font);
|
||||
textRun.layout();
|
||||
this.textRun = textRun;
|
||||
const textFrame = new TextFrame([textRun], font);
|
||||
|
||||
const glyphIDs = textFrame.allGlyphIDs;
|
||||
glyphIDs.sort((a, b) => a - b);
|
||||
this.glyphStore = new GlyphStore(font, glyphIDs);
|
||||
|
||||
this.glyphStore.partition().then(result => {
|
||||
this.baseMeshes = result.meshes;
|
||||
|
||||
const expandedMeshes = textFrame.expandMeshes(this.baseMeshes, glyphIDs);
|
||||
this.expandedMeshes = expandedMeshes;
|
||||
|
||||
this.view.then(view => {
|
||||
view.attachMeshes([expandedMeshes.meshes]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class IntegrationTestView extends DemoView {
|
||||
readonly renderer: IntegrationTestRenderer;
|
||||
readonly appController: IntegrationTestAppController;
|
||||
|
||||
get camera(): OrthographicCamera {
|
||||
return this.renderer.camera;
|
||||
}
|
||||
readonly renderer: IntegrationTestRenderer;
|
||||
|
||||
constructor(appController: IntegrationTestAppController,
|
||||
gammaLUT: HTMLImageElement,
|
||||
commonShaderSource: string,
|
||||
shaderSources: ShaderMap<ShaderProgramSource>) {
|
||||
super(gammaLUT, commonShaderSource, shaderSources);
|
||||
|
||||
this.appController = appController;
|
||||
this.renderer = new IntegrationTestRenderer(this);
|
||||
|
||||
this.resizeToFit(true);
|
||||
}
|
||||
}
|
||||
|
||||
class IntegrationTestRenderer extends Renderer {
|
||||
renderContext: IntegrationTestView;
|
||||
camera: OrthographicCamera;
|
||||
destFramebuffer: WebGLFramebuffer | null;
|
||||
destAllocatedSize: glmatrix.vec2;
|
||||
destUsedSize: glmatrix.vec2;
|
||||
protected objectCount: number;
|
||||
protected usedSizeFactor: glmatrix.vec2;
|
||||
protected worldTransform: glmatrix.mat4;
|
||||
|
||||
private _pixelsPerEm: number = 32.0;
|
||||
|
||||
get destFramebuffer(): WebGLFramebuffer | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
get bgColor(): glmatrix.vec4 {
|
||||
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
|
||||
}
|
||||
|
||||
get fgColor(): glmatrix.vec4 {
|
||||
return glmatrix.vec4.clone([0.0, 0.0, 0.0, 1.0]);
|
||||
}
|
||||
|
||||
get destAllocatedSize(): glmatrix.vec2 {
|
||||
const canvas = this.renderContext.canvas;
|
||||
return glmatrix.vec2.clone([canvas.width, canvas.height]);
|
||||
}
|
||||
|
||||
get destUsedSize(): glmatrix.vec2 {
|
||||
return this.destAllocatedSize;
|
||||
}
|
||||
|
||||
get emboldenAmount(): glmatrix.vec2 {
|
||||
return this.stemDarkeningAmount;
|
||||
}
|
||||
|
||||
protected get objectCount(): number {
|
||||
return this.meshes.length;
|
||||
}
|
||||
|
||||
protected get usedSizeFactor(): glmatrix.vec2 {
|
||||
return glmatrix.vec2.clone([1.0, 1.0]);
|
||||
}
|
||||
|
||||
protected get worldTransform() {
|
||||
const canvas = this.renderContext.canvas;
|
||||
|
||||
const transform = glmatrix.mat4.create();
|
||||
const translation = this.camera.translation;
|
||||
glmatrix.mat4.translate(transform, transform, [-1.0, -1.0, 0.0]);
|
||||
glmatrix.mat4.scale(transform, transform, [2.0 / canvas.width, 2.0 / canvas.height, 1.0]);
|
||||
glmatrix.mat4.translate(transform, transform, [translation[0], translation[1], 0]);
|
||||
glmatrix.mat4.scale(transform, transform, [this.camera.scale, this.camera.scale, 1.0]);
|
||||
|
||||
const pixelsPerUnit = this.pixelsPerUnit;
|
||||
glmatrix.mat4.scale(transform, transform, [pixelsPerUnit, pixelsPerUnit, 1.0]);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
private get pixelsPerUnit(): number {
|
||||
const font = unwrapNull(this.renderContext.appController.font);
|
||||
return this._pixelsPerEm / font.opentypeFont.unitsPerEm;
|
||||
}
|
||||
|
||||
private get stemDarkeningAmount(): glmatrix.vec2 {
|
||||
return computeStemDarkeningAmount(this._pixelsPerEm, this.pixelsPerUnit);
|
||||
}
|
||||
|
||||
constructor(renderContext: IntegrationTestView) {
|
||||
super(renderContext);
|
||||
|
||||
this.camera = new OrthographicCamera(renderContext.canvas);
|
||||
this.camera.onPan = () => renderContext.setDirty();
|
||||
this.camera.onZoom = () => renderContext.setDirty();
|
||||
}
|
||||
|
||||
attachMeshes(meshes: PathfinderMeshData[]): void {
|
||||
super.attachMeshes(meshes);
|
||||
|
||||
this.uploadPathColors(1);
|
||||
this.uploadPathTransforms(1);
|
||||
}
|
||||
|
||||
pathCountForObject(objectIndex: number): number {
|
||||
return STRING.length;
|
||||
}
|
||||
|
||||
pathBoundingRects(objectIndex: number): Float32Array {
|
||||
throw new Error("Method not implemented.");
|
||||
const appController = this.renderContext.appController;
|
||||
const font = unwrapNull(appController.font);
|
||||
|
||||
const boundingRects = new Float32Array((STRING.length + 1) * 4);
|
||||
|
||||
for (let glyphIndex = 0; glyphIndex < STRING.length; glyphIndex++) {
|
||||
const glyphID = unwrapNull(appController.textRun).glyphIDs[glyphIndex];
|
||||
|
||||
const metrics = font.metricsForGlyph(glyphID);
|
||||
if (metrics == null)
|
||||
continue;
|
||||
|
||||
boundingRects[(glyphIndex + 1) * 4 + 0] = metrics.xMin;
|
||||
boundingRects[(glyphIndex + 1) * 4 + 1] = metrics.yMin;
|
||||
boundingRects[(glyphIndex + 1) * 4 + 2] = metrics.xMax;
|
||||
boundingRects[(glyphIndex + 1) * 4 + 3] = metrics.yMax;
|
||||
}
|
||||
|
||||
return boundingRects;
|
||||
}
|
||||
|
||||
setHintsUniform(uniforms: UniformMap): void {
|
||||
throw new Error("Method not implemented.");
|
||||
this.renderContext.gl.uniform4f(uniforms.uHints, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||
|
@ -75,16 +229,38 @@ class IntegrationTestRenderer extends Renderer {
|
|||
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
||||
}
|
||||
|
||||
protected compositeIfNecessary(): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
protected compositeIfNecessary(): void {}
|
||||
|
||||
protected pathColorsForObject(objectIndex: number): Uint8Array {
|
||||
throw new Error("Method not implemented.");
|
||||
const pathColors = new Uint8Array(4 * (STRING.length + 1));
|
||||
for (let pathIndex = 0; pathIndex < STRING.length; pathIndex++)
|
||||
pathColors.set(TEXT_COLOR, (pathIndex + 1) * 4);
|
||||
return pathColors;
|
||||
}
|
||||
|
||||
protected pathTransformsForObject(objectIndex: number): Float32Array {
|
||||
throw new Error("Method not implemented.");
|
||||
const appController = this.renderContext.appController;
|
||||
const canvas = this.renderContext.canvas;
|
||||
const font = unwrapNull(appController.font);
|
||||
|
||||
const pathTransforms = new Float32Array(4 * (STRING.length + 1));
|
||||
|
||||
let currentX = 0, currentY = 0;
|
||||
const availableWidth = canvas.width / this.pixelsPerUnit;
|
||||
const lineHeight = font.opentypeFont.lineHeight();
|
||||
|
||||
for (let glyphIndex = 0; glyphIndex < STRING.length; glyphIndex++) {
|
||||
const glyphID = unwrapNull(appController.textRun).glyphIDs[glyphIndex];
|
||||
pathTransforms.set([1, 1, currentX, currentY], (glyphIndex + 1) * 4);
|
||||
|
||||
currentX += font.opentypeFont.glyphs.get(glyphID).advanceWidth;
|
||||
if (currentX > availableWidth) {
|
||||
currentX = 0;
|
||||
currentY += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return pathTransforms;
|
||||
}
|
||||
|
||||
protected directCurveProgramName(): keyof ShaderMap<void> {
|
||||
|
|
|
@ -63,7 +63,7 @@ export abstract class PathfinderView {
|
|||
window.addEventListener('resize', () => this.resizeToFit(false), false);
|
||||
}
|
||||
|
||||
setDirty() {
|
||||
setDirty(): void {
|
||||
if (this.dirty)
|
||||
return;
|
||||
this.dirty = true;
|
||||
|
@ -101,31 +101,35 @@ export abstract class PathfinderView {
|
|||
this.setDirty();
|
||||
}
|
||||
|
||||
protected redraw() {
|
||||
protected redraw(): void {
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
protected resizeToFit(initialSize: boolean) {
|
||||
const width = window.innerWidth;
|
||||
protected resizeToFit(initialSize: boolean): void {
|
||||
if (!this.canvas.classList.contains('pf-pane')) {
|
||||
const windowWidth = window.innerWidth;
|
||||
const canvasTop = this.canvas.getBoundingClientRect().top;
|
||||
let height = window.scrollY + window.innerHeight - canvasTop;
|
||||
|
||||
let height = window.scrollY + window.innerHeight - this.canvas.getBoundingClientRect().top;
|
||||
const nonoverlappingBottomBar = document.getElementById('pf-nonoverlapping-bottom-bar');
|
||||
if (nonoverlappingBottomBar != null) {
|
||||
const rect = nonoverlappingBottomBar.getBoundingClientRect();
|
||||
height -= window.innerHeight - rect.top;
|
||||
const nonoverlappingBottomBar =
|
||||
document.getElementById('pf-nonoverlapping-bottom-bar');
|
||||
if (nonoverlappingBottomBar != null) {
|
||||
const rect = nonoverlappingBottomBar.getBoundingClientRect();
|
||||
height -= window.innerHeight - rect.top;
|
||||
}
|
||||
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
|
||||
const canvasSize = new Float32Array([windowWidth, height]) as glmatrix.vec2;
|
||||
glmatrix.vec2.scale(canvasSize, canvasSize, devicePixelRatio);
|
||||
glmatrix.vec2.round(canvasSize, canvasSize);
|
||||
|
||||
this.canvas.style.width = windowWidth + 'px';
|
||||
this.canvas.style.height = height + 'px';
|
||||
this.canvas.width = canvasSize[0];
|
||||
this.canvas.height = canvasSize[1];
|
||||
}
|
||||
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
|
||||
const canvasSize = new Float32Array([width, height]) as glmatrix.vec2;
|
||||
glmatrix.vec2.scale(canvasSize, canvasSize, devicePixelRatio);
|
||||
glmatrix.vec2.round(canvasSize, canvasSize);
|
||||
|
||||
this.canvas.style.width = width + 'px';
|
||||
this.canvas.style.height = height + 'px';
|
||||
this.canvas.width = canvasSize[0];
|
||||
this.canvas.height = canvasSize[1];
|
||||
|
||||
this.resized();
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +200,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.quadElementsBuffer);
|
||||
}
|
||||
|
||||
queueScreenshot() {
|
||||
queueScreenshot(): void {
|
||||
this.wantsScreenshot = true;
|
||||
this.setDirty();
|
||||
}
|
||||
|
@ -213,7 +217,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
this.renderer.canvasResized();
|
||||
}
|
||||
|
||||
protected initContext() {
|
||||
protected initContext(): void {
|
||||
// Initialize the OpenGL context.
|
||||
this.gl = expectNotNull(this.canvas.getContext('webgl', { antialias: false, depth: true }),
|
||||
"Failed to initialize WebGL! Check that your browser supports it.");
|
||||
|
@ -243,7 +247,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
this.compositingTimerQuery = this.timerQueryExt.createQueryEXT();
|
||||
}
|
||||
|
||||
protected redraw() {
|
||||
protected redraw(): void {
|
||||
super.redraw();
|
||||
|
||||
this.renderer.redraw();
|
||||
|
@ -304,7 +308,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
return shaderProgramMap as ShaderMap<PathfinderShaderProgram>;
|
||||
}
|
||||
|
||||
private takeScreenshot() {
|
||||
private takeScreenshot(): void {
|
||||
const width = this.canvas.width, height = this.canvas.height;
|
||||
const scratchCanvas = document.createElement('canvas');
|
||||
scratchCanvas.width = width;
|
||||
|
|
Loading…
Reference in New Issue