Stub integration test functionality some more

This commit is contained in:
Patrick Walton 2017-11-13 15:26:46 -08:00
parent bc99fdf02b
commit 0642e65c9d
10 changed files with 239 additions and 57 deletions

View File

@ -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;

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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

View File

@ -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

View File

@ -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> {

View File

@ -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;