Start implementing basic SVG rendering. Not working yet.
This commit is contained in:
parent
0bae14b326
commit
8e2172f06f
|
@ -25,3 +25,6 @@ body {
|
|||
color: white;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
#pf-svg {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -46,7 +46,6 @@ export interface Meshes<T> {
|
|||
|
||||
export class PathfinderMeshData implements Meshes<ArrayBuffer> {
|
||||
constructor(meshes: any) {
|
||||
console.log(meshes);
|
||||
for (const bufferName of Object.keys(BUFFER_TYPES) as Array<keyof Meshes<void>>)
|
||||
this[bufferName] = base64js.toByteArray(meshes[bufferName]).buffer as ArrayBuffer;
|
||||
|
||||
|
|
|
@ -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<SVGDemoView> {
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<TextDemoView> {
|
|||
})
|
||||
}
|
||||
|
||||
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<TextDemoView> {
|
|||
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<TextDemoView> {
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -375,14 +425,22 @@ export abstract class PathfinderView {
|
|||
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue