Decouple text renderers from HTML canvas elements.

This allows for offscreen rendering of text.
This commit is contained in:
Patrick Walton 2017-10-17 12:10:20 -07:00
parent 562851fd6e
commit 32de1d3441
3 changed files with 46 additions and 22 deletions

View File

@ -63,10 +63,23 @@ interface PerspectiveMovementKeys {
[keyCode: number]: boolean;
}
export abstract class Camera {
protected canvas: HTMLCanvasElement;
export interface CameraView {
readonly width: number;
readonly height: number;
readonly classList: DOMTokenList | null;
constructor(canvas: HTMLCanvasElement) {
addEventListener<K extends keyof HTMLElementEventMap>(type: K,
listener: (this: HTMLCanvasElement,
ev: HTMLElementEventMap[K]) =>
any,
useCapture?: boolean): void;
getBoundingClientRect(): ClientRect;
}
export abstract class Camera {
protected canvas: CameraView;
constructor(canvas: CameraView) {
this.canvas = canvas;
}
@ -88,7 +101,7 @@ export class OrthographicCamera extends Camera {
private readonly scaleBounds: boolean;
private readonly ignoreBounds: boolean;
constructor(canvas: HTMLCanvasElement, options?: OrthographicCameraOptions) {
constructor(canvas: CameraView, options?: OrthographicCameraOptions) {
super(canvas);
if (options == null)
@ -104,16 +117,21 @@ export class OrthographicCamera extends Camera {
this._bounds = glmatrix.vec4.create();
this.canvas.addEventListener('wheel', event => this.onWheel(event), false);
this.canvas.addEventListener('mousedown', event => this.onMouseDown(event), false);
this.canvas.addEventListener('mouseup', event => this.onMouseUp(event), false);
this.canvas.addEventListener('mousemove', event => this.onMouseMove(event), false);
if (this.canvas != null) {
this.canvas.addEventListener('wheel', event => this.onWheel(event), false);
this.canvas.addEventListener('mousedown', event => this.onMouseDown(event), false);
this.canvas.addEventListener('mouseup', event => this.onMouseUp(event), false);
this.canvas.addEventListener('mousemove', event => this.onMouseMove(event), false);
}
this.onPan = null;
this.onZoom = null;
}
onWheel(event: MouseWheelEvent): void {
if (this.canvas == null)
throw new Error("onWheel() with no canvas?!");
event.preventDefault();
if (!event.ctrlKey) {
@ -161,11 +179,13 @@ export class OrthographicCamera extends Camera {
}
private onMouseDown(event: MouseEvent): void {
this.canvas.classList.add('pf-grabbing');
if (this.canvas.classList != null)
this.canvas.classList.add('pf-grabbing');
}
private onMouseUp(event: MouseEvent): void {
this.canvas.classList.remove('pf-grabbing');
if (this.canvas.classList != null)
this.canvas.classList.remove('pf-grabbing');
}
private onMouseMove(event: MouseEvent): void {
@ -243,6 +263,8 @@ export class OrthographicCamera extends Camera {
}
export class PerspectiveCamera extends Camera {
canvas: HTMLCanvasElement;
onChange: (() => void) | null;
translation: glmatrix.vec3;

View File

@ -19,7 +19,7 @@ import {StemDarkeningMode, SubpixelAAType} from './aa-strategy';
import {DemoAppController} from './app-controller';
import {Atlas, AtlasGlyph, SUBPIXEL_GRANULARITY} from './atlas';
import PathfinderBufferTexture from './buffer-texture';
import {OrthographicCamera} from "./camera";
import {CameraView, OrthographicCamera} from "./camera";
import {createFramebuffer, createFramebufferColorTexture} from './gl-utils';
import {createFramebufferDepthTexture, QUAD_ELEMENTS, setTextureParameters} from './gl-utils';
import {UniformMap} from './gl-utils';
@ -260,6 +260,10 @@ class TextDemoView extends DemoView implements TextRenderContext {
appController: TextDemoController;
get cameraView(): CameraView {
return this.canvas;
}
get atlasGlyphs(): AtlasGlyph[] {
return this.appController.atlasGlyphs;
}

View File

@ -14,7 +14,7 @@ import * as _ from 'lodash';
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from './aa-strategy';
import {StemDarkeningMode, SubpixelAAType} from './aa-strategy';
import {Atlas, ATLAS_SIZE, AtlasGlyph, GlyphKey, SUBPIXEL_GRANULARITY} from './atlas';
import {OrthographicCamera} from './camera';
import {CameraView, OrthographicCamera} from './camera';
import {createFramebuffer, createFramebufferDepthTexture, QUAD_ELEMENTS} from './gl-utils';
import {UniformMap} from './gl-utils';
import {Renderer} from './renderer';
@ -44,6 +44,7 @@ const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
export interface TextRenderContext extends RenderContext {
atlasGlyphs: AtlasGlyph[];
readonly cameraView: CameraView;
readonly atlas: Atlas;
readonly layout: SimpleTextLayout;
readonly glyphStore: GlyphStore;
@ -53,9 +54,6 @@ export interface TextRenderContext extends RenderContext {
readonly layoutPixelsPerUnit: number;
readonly useHinting: boolean;
// TODO(pcwalton): Remove this.
readonly canvas: HTMLCanvasElement;
newTimingsReceived(timings: Timings): void;
}
@ -139,7 +137,7 @@ export class TextRenderer extends Renderer {
constructor(renderContext: TextRenderContext) {
super(renderContext);
this.camera = new OrthographicCamera(this.renderContext.canvas, {
this.camera = new OrthographicCamera(this.renderContext.cameraView, {
maxScale: MAX_SCALE,
minScale: MIN_SCALE,
});
@ -244,8 +242,8 @@ export class TextRenderer extends Renderer {
this.renderContext.gl.bindFramebuffer(this.renderContext.gl.FRAMEBUFFER, null);
this.renderContext.gl.viewport(0,
0,
this.renderContext.canvas.width,
this.renderContext.canvas.height);
this.renderContext.cameraView.width,
this.renderContext.cameraView.height);
this.renderContext.gl.disable(this.renderContext.gl.DEPTH_TEST);
this.renderContext.gl.disable(this.renderContext.gl.SCISSOR_TEST);
this.renderContext.gl.blendEquation(this.renderContext.gl.FUNC_ADD);
@ -288,8 +286,8 @@ export class TextRenderer extends Renderer {
const transform = glmatrix.mat4.create();
glmatrix.mat4.fromTranslation(transform, [-1.0, -1.0, 0.0]);
glmatrix.mat4.scale(transform, transform, [
2.0 / this.renderContext.canvas.width,
2.0 / this.renderContext.canvas.height,
2.0 / this.renderContext.cameraView.width,
2.0 / this.renderContext.cameraView.height,
1.0,
]);
glmatrix.mat4.translate(transform,
@ -439,8 +437,8 @@ export class TextRenderer extends Renderer {
const canvasRect = glmatrix.vec4.clone([
-translation[0],
-translation[1],
-translation[0] + this.renderContext.canvas.width,
-translation[1] + this.renderContext.canvas.height,
-translation[0] + this.renderContext.cameraView.width,
-translation[1] + this.renderContext.cameraView.height,
]);
let atlasGlyphs = [];