diff --git a/demo/client/css/pathfinder.css b/demo/client/css/pathfinder.css index 8eee3714..7324a004 100644 --- a/demo/client/css/pathfinder.css +++ b/demo/client/css/pathfinder.css @@ -25,3 +25,6 @@ body { color: white; background: rgba(0, 0, 0, 0.75); } +#pf-svg { + visibility: hidden; +} diff --git a/demo/client/package.json b/demo/client/package.json index 33bdda20..6f21f879 100644 --- a/demo/client/package.json +++ b/demo/client/package.json @@ -19,6 +19,7 @@ "gl-matrix": "^2.4.0", "lodash": "^4.17.4", "opentype.js": "^0.7.3", + "parse-color": "^1.0.0", "path-data-polyfill.js": "^1.0.2", "ts-loader": "^2.3.2", "typescript": "^2.4.2", diff --git a/demo/client/src/ecaa-strategy.ts b/demo/client/src/ecaa-strategy.ts index b18f9536..b7bae6b9 100644 --- a/demo/client/src/ecaa-strategy.ts +++ b/demo/client/src/ecaa-strategy.ts @@ -88,10 +88,12 @@ export abstract class ECAAStrategy implements AntialiasingStrategy { null); setTextureParameters(view.gl, view.gl.NEAREST); + this.aaDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize); + this.aaFramebuffer = createFramebuffer(view.gl, view.drawBuffersExt, [this.aaAlphaTexture], - view.destDepthTexture); + this.aaDepthTexture); } private createCoverVAO(view: MonochromePathfinderView) { @@ -421,6 +423,7 @@ export abstract class ECAAStrategy implements AntialiasingStrategy { protected directColorTexture: WebGLTexture; protected directPathIDTexture: WebGLTexture; + protected aaDepthTexture: WebGLTexture; protected framebufferSize: glmatrix.vec2; } @@ -483,7 +486,7 @@ export class ECAAMulticolorStrategy extends ECAAStrategy { this.edgeDetectFramebuffer = createFramebuffer(view.gl, view.drawBuffersExt, [this.bgColorTexture, this.fgColorTexture], - view.destDepthTexture); + this.aaDepthTexture); } protected createEdgeDetectVAO(view: MonochromePathfinderView) { diff --git a/demo/client/src/meshes.ts b/demo/client/src/meshes.ts index 20193ca4..5589bbc9 100644 --- a/demo/client/src/meshes.ts +++ b/demo/client/src/meshes.ts @@ -46,7 +46,6 @@ export interface Meshes { export class PathfinderMeshData implements Meshes { constructor(meshes: any) { - console.log(meshes); for (const bufferName of Object.keys(BUFFER_TYPES) as Array>) this[bufferName] = base64js.toByteArray(meshes[bufferName]).buffer as ArrayBuffer; diff --git a/demo/client/src/svg.ts b/demo/client/src/svg.ts index 7ad01737..3d893edf 100644 --- a/demo/client/src/svg.ts +++ b/demo/client/src/svg.ts @@ -20,6 +20,8 @@ import {PathfinderView, Timings} from './view'; import AppController from './app-controller'; import SSAAStrategy from "./ssaa-strategy"; +const parseColor = require('parse-color'); + const SVG_NS: string = "http://www.w3.org/2000/svg"; const PARTITION_SVG_PATHS_ENDPOINT_URL: string = "/partition-svg-paths"; @@ -35,8 +37,10 @@ declare class SVGPathSegment { values: number[]; } -declare class SVGPathElement { - getPathData(settings: any): SVGPathSegment[]; +declare global { + interface SVGPathElement { + getPathData(settings: any): SVGPathSegment[]; + } } interface AntialiasingStrategyTable { @@ -117,7 +121,7 @@ class SVGDemoController extends AppController { private meshesReceived() { this.view.then(view => { - // TODO(pcwalton): Upload path color data. + view.uploadPathData(this.pathElements); view.attachMeshes(this.meshes); }) } @@ -136,7 +140,7 @@ class SVGDemoView extends PathfinderView { this.appController = appController; - this.resized(false); + this._scale = 1.0; } protected resized(initialSize: boolean) {} @@ -145,24 +149,25 @@ class SVGDemoView extends PathfinderView { return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height); } - get destDepthTexture() { - return panic("TODO"); - } - - get destFramebuffer() { - return panic("TODO"); + get destFramebuffer(): WebGLFramebuffer | null { + return null; } get destUsedSize(): glmatrix.vec2 { return this.destAllocatedSize; } - setTransformAndTexScaleUniformsForDest() { - panic("TODO"); - } + protected panned(): void {} - setTransformSTAndTexScaleUniformsForDest() { - panic("TODO"); + uploadPathData(elements: SVGPathElement[]) { + const pathColors = new Uint8Array(4 * (elements.length + 1)); + for (let pathIndex = 0; pathIndex < elements.length; pathIndex++) { + const style = window.getComputedStyle(elements[pathIndex]); + const fillColor = style.fill === 'none' ? [0, 0, 0, 0] : parseColor(style.fill).rgba; + pathColors.set(fillColor, (pathIndex + 1) * 4); + } + + this.pathColorsBufferTexture.upload(this.gl, pathColors); } protected createAAStrategy(aaType: AntialiasingStrategyName, aaLevel: number): @@ -176,6 +181,21 @@ class SVGDemoView extends PathfinderView { // TODO(pcwalton) } + protected get usedSizeFactor(): glmatrix.vec2 { + return glmatrix.vec2.create(); + } + + protected get scale(): number { + return this._scale; + } + + protected set scale(newScale: number) { + this._scale = newScale; + this.setDirty(); + } + + private _scale: number; + private appController: SVGDemoController; } diff --git a/demo/client/src/text.ts b/demo/client/src/text.ts index 8d97c941..b720f269 100644 --- a/demo/client/src/text.ts +++ b/demo/client/src/text.ts @@ -64,8 +64,6 @@ And the mome raths outgrabe.`; const INITIAL_FONT_SIZE: number = 72.0; -const SCALE_FACTOR: number = 1.0 / 100.0; - const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font"; const B_POSITION_SIZE: number = 8; @@ -207,15 +205,6 @@ class TextDemoController extends AppController { }) } - scaleFontSize(scale: number) { - this.setFontSize(scale * this.fontSize); - } - - private setFontSize(newPixelsPerEm: number) { - this.fontSize = newPixelsPerEm; - this.view.then(view => view.attachText()); - } - updateTimings(newTimes: Timings) { this.fpsLabel.innerHTML = `${newTimes.atlasRendering} ms atlas, ${newTimes.compositing} ms compositing`; @@ -225,6 +214,17 @@ class TextDemoController extends AppController { return this._atlas; } + /// The font size in pixels per em. + get fontSize(): number { + return this._fontSize; + } + + /// The font size in pixels per em. + set fontSize(newFontSize: number) { + this._fontSize = newFontSize; + this.view.then(view => view.attachText()); + } + private aaLevelSelect: HTMLSelectElement; private fpsLabel: HTMLElement; @@ -238,8 +238,7 @@ class TextDemoController extends AppController { private meshes: PathfinderMeshData; - /// The font size in pixels per em. - fontSize: number; + private _fontSize: number; } class TextDemoView extends MonochromePathfinderView { @@ -250,10 +249,6 @@ class TextDemoView extends MonochromePathfinderView { super(canvas, commonShaderSource, shaderSources); this.appController = appController; - - this.translation = glmatrix.vec2.create(); - - this.canvas.addEventListener('wheel', event => this.onWheel(event), false); } protected initContext() { @@ -453,38 +448,7 @@ class TextDemoView extends MonochromePathfinderView { this.setDirty(); } - private onWheel(event: WheelEvent) { - event.preventDefault(); - - if (event.ctrlKey) { - // Zoom event: see https://developer.mozilla.org/en-US/docs/Web/Events/wheel - const mouseLocation = glmatrix.vec2.fromValues(event.clientX, event.clientY); - const canvasLocation = this.canvas.getBoundingClientRect(); - mouseLocation[0] -= canvasLocation.left; - mouseLocation[1] = canvasLocation.bottom - mouseLocation[1]; - glmatrix.vec2.scale(mouseLocation, mouseLocation, window.devicePixelRatio); - - const absoluteTranslation = glmatrix.vec2.create(); - glmatrix.vec2.sub(absoluteTranslation, this.translation, mouseLocation); - glmatrix.vec2.scale(absoluteTranslation, - absoluteTranslation, - 1.0 / this.appController.fontSize); - - const scale = 1.0 - event.deltaY * window.devicePixelRatio * SCALE_FACTOR; - this.appController.scaleFontSize(scale); - - glmatrix.vec2.scale(absoluteTranslation, - absoluteTranslation, - this.appController.fontSize); - glmatrix.vec2.add(this.translation, absoluteTranslation, mouseLocation); - return; - } - - // Pan event. - const delta = glmatrix.vec2.fromValues(-event.deltaX, event.deltaY); - glmatrix.vec2.scale(delta, delta, window.devicePixelRatio); - glmatrix.vec2.add(this.translation, this.translation, delta); - + protected panned() { this.rebuildAtlasIfNecessary(); } @@ -494,33 +458,16 @@ class TextDemoView extends MonochromePathfinderView { this.setDirty(); } - setIdentityTexScaleUniform(uniforms: UniformMap) { + private setIdentityTexScaleUniform(uniforms: UniformMap) { this.gl.uniform2f(uniforms.uTexScale, 1.0, 1.0); } - private get usedSizeFactor(): glmatrix.vec2 { + protected get usedSizeFactor(): glmatrix.vec2 { const usedSize = glmatrix.vec2.create(); glmatrix.vec2.div(usedSize, this.appController.atlas.usedSize, ATLAS_SIZE); return usedSize; } - setTransformSTAndTexScaleUniformsForDest(uniforms: UniformMap) { - const usedSize = this.usedSizeFactor; - this.gl.uniform4f(uniforms.uTransformST, 2.0 * usedSize[0], 2.0 * usedSize[1], -1.0, -1.0); - this.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); - } - - setTransformAndTexScaleUniformsForDest(uniforms: UniformMap) { - const usedSize = this.usedSizeFactor; - - const transform = glmatrix.mat4.create(); - glmatrix.mat4.fromTranslation(transform, [-1.0, -1.0, 0.0]); - glmatrix.mat4.scale(transform, transform, [2.0 * usedSize[0], 2.0 * usedSize[1], 1.0]); - this.gl.uniformMatrix4fv(uniforms.uTransform, false, transform); - - this.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); - } - protected compositeIfNecessary() { // Set up composite state. this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); @@ -579,10 +526,6 @@ class TextDemoView extends MonochromePathfinderView { return this.atlasFramebuffer; } - get destDepthTexture(): WebGLTexture { - return this.atlasDepthTexture; - } - get destAllocatedSize(): glmatrix.vec2 { return ATLAS_SIZE; } @@ -591,6 +534,14 @@ class TextDemoView extends MonochromePathfinderView { return this.appController.atlas.usedSize; } + protected get scale(): number { + return this.appController.fontSize; + } + + protected set scale(newScale: number) { + this.appController.fontSize = newScale; + } + protected createAAStrategy(aaType: AntialiasingStrategyName, aaLevel: number): AntialiasingStrategy { return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel); @@ -600,8 +551,6 @@ class TextDemoView extends MonochromePathfinderView { this.appController.updateTimings(timings); } - private translation: glmatrix.vec2; - atlasFramebuffer: WebGLFramebuffer; atlasDepthTexture: WebGLTexture; diff --git a/demo/client/src/view.ts b/demo/client/src/view.ts index 8163b124..29a0fbf8 100644 --- a/demo/client/src/view.ts +++ b/demo/client/src/view.ts @@ -18,6 +18,8 @@ import {ShaderProgramSource, UnlinkedShaderProgram} from './shader-loader'; import {PathfinderError, UINT32_SIZE, expectNotNull, unwrapNull} from './utils'; import PathfinderBufferTexture from './buffer-texture'; +const SCALE_FACTOR: number = 1.0 / 100.0; + const TIME_INTERVAL_DELAY: number = 32; const B_LOOP_BLINN_DATA_SIZE: number = 4; @@ -53,6 +55,8 @@ export abstract class PathfinderView { this.initContext(); + this.translation = glmatrix.vec2.create(); + const shaderSource = this.compileShaders(commonShaderSource, shaderSources); this.shaderPrograms = this.linkShaders(shaderSource); @@ -64,6 +68,8 @@ export abstract class PathfinderView { window.addEventListener('resize', () => this.resizeToFit(false), false); this.resizeToFit(true); + + this.canvas.addEventListener('wheel', event => this.onWheel(event), false); } setAntialiasingOptions(aaType: AntialiasingStrategyName, aaLevel: number) { @@ -365,8 +371,52 @@ export abstract class PathfinderView { window.requestAnimationFrame(() => this.redraw()); } - abstract setTransformAndTexScaleUniformsForDest(uniforms: UniformMap): void; - abstract setTransformSTAndTexScaleUniformsForDest(uniforms: UniformMap): void; + private onWheel(event: WheelEvent) { + event.preventDefault(); + + if (event.ctrlKey) { + // Zoom event: see https://developer.mozilla.org/en-US/docs/Web/Events/wheel + const mouseLocation = glmatrix.vec2.fromValues(event.clientX, event.clientY); + const canvasLocation = this.canvas.getBoundingClientRect(); + mouseLocation[0] -= canvasLocation.left; + mouseLocation[1] = canvasLocation.bottom - mouseLocation[1]; + glmatrix.vec2.scale(mouseLocation, mouseLocation, window.devicePixelRatio); + + const absoluteTranslation = glmatrix.vec2.create(); + glmatrix.vec2.sub(absoluteTranslation, this.translation, mouseLocation); + glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, 1.0 / this.scale); + + this.scale *= 1.0 - event.deltaY * window.devicePixelRatio * SCALE_FACTOR; + + glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, this.scale); + glmatrix.vec2.add(this.translation, absoluteTranslation, mouseLocation); + return; + } + + // Pan event. + const delta = glmatrix.vec2.fromValues(-event.deltaX, event.deltaY); + glmatrix.vec2.scale(delta, delta, window.devicePixelRatio); + glmatrix.vec2.add(this.translation, this.translation, delta); + + this.panned(); + } + + setTransformSTAndTexScaleUniformsForDest(uniforms: UniformMap) { + const usedSize = this.usedSizeFactor; + this.gl.uniform4f(uniforms.uTransformST, 2.0 * usedSize[0], 2.0 * usedSize[1], -1.0, -1.0); + this.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); + } + + setTransformAndTexScaleUniformsForDest(uniforms: UniformMap) { + const usedSize = this.usedSizeFactor; + + const transform = glmatrix.mat4.create(); + glmatrix.mat4.fromTranslation(transform, [-1.0, -1.0, 0.0]); + glmatrix.mat4.scale(transform, transform, [2.0 * usedSize[0], 2.0 * usedSize[1], 1.0]); + this.gl.uniformMatrix4fv(uniforms.uTransform, false, transform); + + this.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); + } protected abstract createAAStrategy(aaType: AntialiasingStrategyName, aaLevel: number): AntialiasingStrategy; @@ -374,15 +424,23 @@ export abstract class PathfinderView { protected abstract compositeIfNecessary(): void; protected abstract updateTimings(timings: Timings): void; - - abstract get destFramebuffer(): WebGLFramebuffer; - abstract get destDepthTexture(): WebGLTexture; + + protected abstract panned(): void; + + abstract get destFramebuffer(): WebGLFramebuffer | null; abstract get destAllocatedSize(): glmatrix.vec2; abstract get destUsedSize(): glmatrix.vec2; + protected abstract get usedSizeFactor(): glmatrix.vec2; + + protected abstract get scale(): number; + protected abstract set scale(newScale: number); + protected antialiasingStrategy: AntialiasingStrategy; + protected translation: glmatrix.vec2; + protected canvas: HTMLCanvasElement; gl: WebGLRenderingContext;