Add support for full 3D transforms, including rotation, to SVG in XCAA mode.

This commit is contained in:
Patrick Walton 2017-11-22 21:10:51 -08:00
parent 0e9144286e
commit 6942bb51ca
14 changed files with 340 additions and 324 deletions

View File

@ -21,7 +21,7 @@ import {CompositingOperation, RenderTaskType} from './render-task';
import {ShaderMap} from './shader-loader';
import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull, unwrapUndef} from './utils';
import {RenderContext, Timings} from "./view";
import {MCAAMulticolorStrategy} from './xcaa-strategy';
import {ECAAMulticolorStrategy} from './xcaa-strategy';
const MAX_PATHS: number = 65535;
@ -232,6 +232,12 @@ export abstract class Renderer {
gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
}
setTransformUniform(uniforms: UniformMap, objectIndex: number) {
const transform = glmatrix.mat4.clone(this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
}
setTransformSTUniform(uniforms: UniformMap, objectIndex: number): void {
// FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an ST matrix is ugly and fragile.
// Refactor.
@ -444,7 +450,7 @@ export abstract class Renderer {
this.pathTransformBufferTextures[meshIndex]
.bind(gl, directInteriorProgram.uniforms, 1);
if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
const strategy = antialiasingStrategy as ECAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2);
}
const coverInteriorRange = getMeshIndexRange(meshes.coverInteriorIndexRanges, pathRange);
@ -486,7 +492,7 @@ export abstract class Renderer {
this.setEmboldenAmountUniform(objectIndex, directCurveProgram.uniforms);
this.pathTransformBufferTextures[meshIndex].bind(gl, directCurveProgram.uniforms, 1);
if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
const strategy = antialiasingStrategy as ECAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directCurveProgram.uniforms, 2);
}
const coverCurveRange = getMeshIndexRange(meshes.coverCurveIndexRanges, pathRange);
@ -687,12 +693,6 @@ export abstract class Renderer {
gl.bufferData(gl.ARRAY_BUFFER, pathIDs, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
private setTransformUniform(uniforms: UniformMap, objectIndex: number) {
const transform = glmatrix.mat4.clone(this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
}
}
function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range {

View File

@ -147,11 +147,11 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
},
xcaaMultiEdgeMaskCurve: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-curve.fs.glsl",
vertex: "/glsl/gles2/mcaa-curve.vs.glsl",
vertex: "/glsl/gles2/ecaa-curve.vs.glsl",
},
xcaaMultiEdgeMaskLine: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-line.fs.glsl",
vertex: "/glsl/gles2/mcaa-line.vs.glsl",
vertex: "/glsl/gles2/ecaa-line.vs.glsl",
},
xcaaMultiResolve: {
fragment: "/glsl/gles2/xcaa-multi-resolve.fs.glsl",
@ -198,10 +198,13 @@ export class PathfinderShaderProgram {
readonly uniforms: UniformMap;
readonly attributes: AttributeMap;
readonly program: WebGLProgram;
readonly programName: string;
constructor(gl: WebGLRenderingContext,
programName: string,
unlinkedShaderProgram: UnlinkedShaderProgram) {
this.programName = programName;
this.program = expectNotNull(gl.createProgram(), "Failed to create shader program!");
for (const compiledShader of Object.values(unlinkedShaderProgram))
gl.attachShader(this.program, compiledShader);

View File

@ -25,7 +25,7 @@ import SSAAStrategy from "./ssaa-strategy";
import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader';
import {panic, Range, unwrapNull} from './utils';
import {DemoView, Timings} from './view';
import {MCAAMulticolorStrategy, XCAAStrategy} from "./xcaa-strategy";
import {ECAAMulticolorStrategy, XCAAStrategy} from "./xcaa-strategy";
const parseColor = require('parse-color');
@ -36,7 +36,7 @@ const DEFAULT_FILE: string = 'tiger';
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy,
ssaa: SSAAStrategy,
xcaa: MCAAMulticolorStrategy,
xcaa: ECAAMulticolorStrategy,
};
interface AntialiasingStrategyTable {
@ -82,7 +82,7 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
private meshesReceived(): void {
this.view.then(view => {
view.attachMeshes([this.meshes]);
view.initCameraBounds(this.loader.bounds);
view.initCameraBounds(this.loader.svgBounds);
});
}
}
@ -157,8 +157,11 @@ class SVGDemoRenderer extends Renderer {
}
pathBoundingRects(objectIndex: number): Float32Array {
// TODO
return new Float32Array(0);
const loader = this.renderContext.appController.loader;
const boundingRectsBuffer = new Float32Array((loader.pathBounds.length + 1) * 4);
for (let pathIndex = 0; pathIndex < loader.pathBounds.length; pathIndex++)
boundingRectsBuffer.set(loader.pathBounds[pathIndex], (pathIndex + 1) * 4);
return boundingRectsBuffer;
}
attachMeshes(meshes: PathfinderMeshData[]): void {
@ -203,13 +206,11 @@ class SVGDemoRenderer extends Renderer {
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]);
if (!(this.antialiasingStrategy instanceof XCAAStrategy)) {
const centerPoint = glmatrix.vec3.clone([canvas.width * 0.5, canvas.height * 0.5, 0.0]);
glmatrix.mat4.translate(transform, transform, centerPoint);
glmatrix.mat4.rotateZ(transform, transform, this.camera.rotationAngle);
glmatrix.vec3.negate(centerPoint, centerPoint);
glmatrix.mat4.translate(transform, transform, centerPoint);
}
const centerPoint = glmatrix.vec3.clone([canvas.width * 0.5, canvas.height * 0.5, 0.0]);
glmatrix.mat4.translate(transform, transform, centerPoint);
glmatrix.mat4.rotateZ(transform, transform, this.camera.rotationAngle);
glmatrix.vec3.negate(centerPoint, centerPoint);
glmatrix.mat4.translate(transform, transform, centerPoint);
const translation = this.camera.translation;
glmatrix.mat4.translate(transform, transform, [translation[0], translation[1], 0]);

View File

@ -74,7 +74,8 @@ export class SVGLoader {
renderTasks: RenderTask[];
pathInstances: SVGPath[];
scale: number;
bounds: glmatrix.vec4;
pathBounds: glmatrix.vec4[];
svgBounds: glmatrix.vec4;
private svg: SVGSVGElement;
private fileData: ArrayBuffer;
@ -86,8 +87,8 @@ export class SVGLoader {
this.scale = 1.0;
this.renderTasks = [];
this.pathInstances = [];
this.paths = [];
this.bounds = glmatrix.vec4.create();
this.pathBounds = [];
this.svgBounds = glmatrix.vec4.create();
this.svg = unwrapNull(document.getElementById('pf-svg')) as Element as SVGSVGElement;
}
@ -133,10 +134,10 @@ export class SVGLoader {
this.scanElement(this.svg);
this.popTopRenderTaskIfEmpty();
let minX = 0, minY = 0, maxX = 0, maxY = 0;
this.paths = [];
// Extract, normalize, and transform the path data.
const svgBottomLeft = glmatrix.vec2.create(), svgTopRight = glmatrix.vec2.create();
for (const instance of this.pathInstances) {
const element = instance.element;
const svgCTM = element.getCTM();
@ -146,15 +147,16 @@ export class SVGLoader {
glmatrix.mat2d.scale(ctm, ctm, [1.0, -1.0]);
glmatrix.mat2d.scale(ctm, ctm, [this.scale, this.scale]);
const bottomLeft = glmatrix.vec2.create();
const topRight = glmatrix.vec2.create();
const segments = element.getPathData({ normalize: true }).map(segment => {
const newValues = _.flatMap(_.chunk(segment.values, 2), coords => {
const point = glmatrix.vec2.create();
glmatrix.vec2.transformMat2d(point, coords, ctm);
minX = Math.min(point[0], minX);
minY = Math.min(point[1], minY);
maxX = Math.max(point[0], maxX);
maxY = Math.max(point[1], maxY);
glmatrix.vec2.min(bottomLeft, bottomLeft, point);
glmatrix.vec2.max(topRight, topRight, point);
return [point[0], point[1]];
});
@ -164,17 +166,30 @@ export class SVGLoader {
};
});
const pathBounds = glmatrix.vec4.clone([
bottomLeft[0], bottomLeft[1],
topRight[0], topRight[1],
]);
glmatrix.vec2.min(svgBottomLeft, svgBottomLeft, bottomLeft);
glmatrix.vec2.max(svgTopRight, svgTopRight, topRight);
if (instance instanceof SVGFill) {
this.paths.push({ segments: segments, kind: 'Fill' });
this.pathBounds.push(pathBounds);
} else if (instance instanceof SVGStroke) {
this.paths.push({
kind: { Stroke: Math.max(HAIRLINE_STROKE_WIDTH, instance.width) },
segments: segments,
});
this.pathBounds.push(pathBounds);
}
}
this.bounds = glmatrix.vec4.clone([minX, minY, maxX, maxY]);
this.svgBounds = glmatrix.vec4.clone([
svgBottomLeft[0], svgBottomLeft[1],
svgTopRight[0], svgTopRight[1],
]);
}
private scanElement(element: Element): void {

View File

@ -18,7 +18,7 @@ import {createFramebufferDepthTexture, setTextureParameters, UniformMap} from '.
import {WebGLVertexArrayObject} from './gl-utils';
import {B_QUAD_LOWER_INDICES_OFFSET, B_QUAD_SIZE, B_QUAD_UPPER_INDICES_OFFSET} from './meshes';
import {Renderer} from './renderer';
import {PathfinderShaderProgram} from './shader-loader';
import {PathfinderShaderProgram, ShaderMap} from './shader-loader';
import {computeStemDarkeningAmount} from './text';
import {assert, FLOAT32_SIZE, lerp, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull} from './utils';
import {unwrapUndef} from './utils';
@ -36,6 +36,8 @@ const DIRECTIONS: Direction[] = ['upper', 'lower'];
export abstract class XCAAStrategy extends AntialiasingStrategy {
abstract readonly directRenderingMode: DirectRenderingMode;
protected abstract get usesDilationTransforms(): boolean;
protected pathBoundsBufferTexture: PathfinderBufferTexture;
protected supersampledFramebufferSize: glmatrix.vec2;
@ -225,7 +227,11 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
renderer.setTransformSTUniform(uniforms, 0);
if (this.usesDilationTransforms)
renderer.setTransformSTUniform(uniforms, 0);
else
renderer.setTransformUniform(uniforms, 0);
gl.uniform2i(uniforms.uFramebufferSize,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
@ -746,9 +752,12 @@ export abstract class MCAAStrategy extends XCAAStrategy {
}
}
export class ECAAStrategy extends XCAAStrategy {
private lineVAO: WebGLVertexArrayObject;
private curveVAO: WebGLVertexArrayObject;
export abstract class ECAAStrategy extends XCAAStrategy {
protected abstract get lineShaderProgramNames(): Array<keyof ShaderMap<void>>;
protected abstract get curveShaderProgramNames(): Array<keyof ShaderMap<void>>;
private lineVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
private curveVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
get directRenderingMode(): DirectRenderingMode {
return 'none';
@ -757,16 +766,23 @@ export class ECAAStrategy extends XCAAStrategy {
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
this.createLineVAO(renderer);
this.createCurveVAO(renderer);
this.createLineVAOs(renderer);
this.createCurveVAOs(renderer);
}
antialiasObject(renderer: Renderer, objectIndex: number): void {
super.antialiasObject(renderer, objectIndex);
// Antialias.
this.antialiasLinesOfObject(renderer, objectIndex);
this.antialiasCurvesOfObject(renderer, objectIndex);
const shaderPrograms = renderer.renderContext.shaderPrograms;
this.setAAState(renderer);
this.setBlendModeForAA(renderer);
this.antialiasLinesOfObjectWithProgram(renderer,
objectIndex,
this.lineShaderProgramNames[0]);
this.antialiasCurvesOfObjectWithProgram(renderer,
objectIndex,
this.curveShaderProgramNames[0]);
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): void {
@ -804,140 +820,10 @@ export class ECAAStrategy extends XCAAStrategy {
gl.clear(gl.COLOR_BUFFER_BIT);
}
private setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
private createLineVAO(renderer: Renderer): void {
if (renderer.meshes == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const lineProgram = renderContext.shaderPrograms.ecaaLine;
const attributes = lineProgram.attributes;
const vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const lineVertexPositionsBuffer = renderer.meshes[0].segmentLines;
const linePathIDsBuffer = renderer.meshes[0].segmentLinePathIDs;
const lineNormalsBuffer = renderer.meshes[0].segmentLineNormals;
gl.useProgram(lineProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition, 2, gl.FLOAT, false, FLOAT32_SIZE * 4, 0);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 2);
gl.bindBuffer(gl.ARRAY_BUFFER, linePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, lineNormalsBuffer);
gl.vertexAttribPointer(attributes.aLeftNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE * 2,
0);
gl.vertexAttribPointer(attributes.aRightNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE * 2,
FLOAT32_SIZE);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
gl.enableVertexAttribArray(attributes.aLeftNormalAngle);
gl.enableVertexAttribArray(attributes.aRightNormalAngle);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftNormalAngle, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRightNormalAngle, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
this.lineVAO = vao;
}
private createCurveVAO(renderer: Renderer): void {
if (renderer.meshes == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const curveProgram = renderContext.shaderPrograms.ecaaCurve;
const attributes = curveProgram.attributes;
const vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const curveVertexPositionsBuffer = renderer.meshes[0].segmentCurves;
const curvePathIDsBuffer = renderer.meshes[0].segmentCurvePathIDs;
const curveNormalsBuffer = renderer.meshes[0].segmentCurveNormals;
gl.useProgram(curveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition, 2, gl.FLOAT, false, FLOAT32_SIZE * 6, 0);
gl.vertexAttribPointer(attributes.aControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 4);
gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveNormalsBuffer);
gl.vertexAttribPointer(attributes.aNormalAngles, 3, gl.FLOAT, false, FLOAT32_SIZE * 3, 0);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aControlPointPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
gl.enableVertexAttribArray(attributes.aNormalAngles);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aNormalAngles, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
this.curveVAO = vao;
}
private antialiasLinesOfObject(renderer: Renderer, objectIndex: number): void {
protected antialiasLinesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
programName: keyof ShaderMap<void>):
void {
if (renderer.meshData == null)
return;
@ -947,19 +833,14 @@ export class ECAAStrategy extends XCAAStrategy {
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.setAAState(renderer);
const lineProgram = renderContext.shaderPrograms.ecaaLine;
const lineProgram = renderContext.shaderPrograms[programName];
gl.useProgram(lineProgram.program);
const uniforms = lineProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
const vao = this.lineVAO;
const vao = this.lineVAOs[programName];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
// FIXME(pcwalton): Only render the appropriate instances.
const count = renderer.meshData[meshIndex].segmentLineCount;
renderContext.instancedArraysExt
@ -968,7 +849,10 @@ export class ECAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private antialiasCurvesOfObject(renderer: Renderer, objectIndex: number): void {
protected antialiasCurvesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
programName: keyof ShaderMap<void>):
void {
if (renderer.meshData == null)
return;
@ -978,19 +862,14 @@ export class ECAAStrategy extends XCAAStrategy {
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.setAAState(renderer);
const curveProgram = renderContext.shaderPrograms.ecaaCurve;
const curveProgram = renderContext.shaderPrograms[programName];
gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
const vao = this.curveVAO;
const vao = this.curveVAOs[programName];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
// FIXME(pcwalton): Only render the appropriate instances.
const count = renderer.meshData[meshIndex].segmentCurveCount;
renderContext.instancedArraysExt
@ -998,9 +877,198 @@ export class ECAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
private createLineVAOs(renderer: Renderer): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
this.lineVAOs = {};
for (const programName of this.lineShaderProgramNames) {
const lineProgram = renderContext.shaderPrograms[programName];
const attributes = lineProgram.attributes;
const vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const lineVertexPositionsBuffer = renderer.meshes[0].segmentLines;
const linePathIDsBuffer = renderer.meshes[0].segmentLinePathIDs;
const lineNormalsBuffer = renderer.meshes[0].segmentLineNormals;
gl.useProgram(lineProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
0);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 2);
gl.bindBuffer(gl.ARRAY_BUFFER, linePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, 0, 0);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
if (renderer.meshData[0].segmentLineNormals.byteLength > 0) {
gl.bindBuffer(gl.ARRAY_BUFFER, lineNormalsBuffer);
gl.vertexAttribPointer(attributes.aLeftNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE * 2,
0);
gl.vertexAttribPointer(attributes.aRightNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE * 2,
FLOAT32_SIZE);
gl.enableVertexAttribArray(attributes.aLeftNormalAngle);
gl.enableVertexAttribArray(attributes.aRightNormalAngle);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLeftNormalAngle, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aRightNormalAngle, 1);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
this.lineVAOs[programName] = vao;
}
}
private createCurveVAOs(renderer: Renderer): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
this.curveVAOs = {};
for (const programName of this.curveShaderProgramNames) {
const curveProgram = renderContext.shaderPrograms[programName];
const attributes = curveProgram.attributes;
const vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const curveVertexPositionsBuffer = renderer.meshes[0].segmentCurves;
const curvePathIDsBuffer = renderer.meshes[0].segmentCurvePathIDs;
const curveNormalsBuffer = renderer.meshes[0].segmentCurveNormals;
gl.useProgram(curveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
0);
gl.vertexAttribPointer(attributes.aControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 4);
gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, 0, 0);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aControlPointPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
if (renderer.meshData[0].segmentCurveNormals.byteLength > 0) {
gl.bindBuffer(gl.ARRAY_BUFFER, curveNormalsBuffer);
gl.vertexAttribPointer(attributes.aNormalAngles,
3,
gl.FLOAT,
false,
FLOAT32_SIZE * 3,
0);
gl.enableVertexAttribArray(attributes.aNormalAngles);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aNormalAngles, 1);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
this.curveVAOs[programName] = vao;
}
}
}
export class ECAAMonochromeStrategy extends ECAAStrategy {
protected get usesDilationTransforms(): boolean {
return false;
}
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine'];
}
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaCurve'];
}
}
export class MCAAMonochromeStrategy extends MCAAStrategy {
protected get usesDilationTransforms(): boolean {
return true;
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
if (this.subpixelAA !== 'none')
return renderContext.shaderPrograms.xcaaMonoSubpixelResolve;
@ -1049,7 +1117,7 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
constructor(level: number, subpixelAA: SubpixelAAType) {
this.mcaaStrategy = new MCAAMonochromeStrategy(level, subpixelAA);
this.ecaaStrategy = new ECAAStrategy(level, subpixelAA);
this.ecaaStrategy = new ECAAMonochromeStrategy(level, subpixelAA);
}
init(renderer: Renderer): void {
@ -1106,7 +1174,19 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
}
}
export class MCAAMulticolorStrategy extends MCAAStrategy {
export class ECAAMulticolorStrategy extends ECAAStrategy {
protected get usesDilationTransforms(): boolean {
return false;
}
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine', 'xcaaMultiEdgeMaskLine'];
}
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaCurve', 'xcaaMultiEdgeMaskCurve'];
}
private edgeMaskVAO: WebGLVertexArrayObject;
bindEdgeDepthTexture(gl: WebGLRenderingContext, uniforms: UniformMap, textureUnit: number):
@ -1136,7 +1216,7 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
gl.colorMask(false, false, false, false);
gl.colorMask(true, true, true, true);
gl.depthMask(true);
gl.depthFunc(gl.GREATER);
gl.enable(gl.DEPTH_TEST);
@ -1147,12 +1227,9 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Perform edge masking.
const edgeMaskLineProgram = renderer.renderContext.shaderPrograms.xcaaMultiEdgeMaskLine;
gl.useProgram(edgeMaskLineProgram.program);
this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, edgeMaskLineProgram);
const edgeMaskCurveProgram = renderer.renderContext.shaderPrograms.xcaaMultiEdgeMaskCurve;
gl.useProgram(edgeMaskCurveProgram.program);
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, edgeMaskCurveProgram);
gl.colorMask(false, false, false, false);
this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, 'xcaaMultiEdgeMaskLine');
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, 'xcaaMultiEdgeMaskCurve');
gl.colorMask(true, true, true, true);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
@ -1160,17 +1237,19 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
protected setCoverDepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.depthMask(false);
renderContext.gl.depthFunc(renderContext.gl.EQUAL);
renderContext.gl.enable(renderContext.gl.DEPTH_TEST);
const gl = renderContext.gl;
gl.depthMask(false);
gl.depthFunc(gl.EQUAL);
gl.enable(gl.DEPTH_TEST);
}
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
/*const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.clear(gl.COLOR_BUFFER_BIT);*/
}
protected setAADepthState(renderer: Renderer): void {

View File

@ -425,8 +425,9 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
let mut path_buffer = PathBuffer::new();
let mut paths = vec![];
let mut last_point = Point2D::zero();
let mut library = MeshLibrary::new();
for path in &request.paths {
for (path_index, path) in request.paths.iter().enumerate() {
let mut stream = vec![];
let first_subpath_index = path_buffer.subpaths.len() as u32;
@ -463,7 +464,9 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
match path.kind {
PartitionSvgPathKind::Fill => {
path_buffer.add_stream(MonotonicPathCommandStream::new(stream.into_iter()))
let stream = MonotonicPathCommandStream::new(stream.into_iter());
library.push_segments((path_index + 1) as u16, stream.clone());
path_buffer.add_stream(stream)
}
PartitionSvgPathKind::Stroke(stroke_width) => {
let mut temp_path_buffer = PathBuffer::new();
@ -471,6 +474,7 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
let stream = PathBufferStream::new(&temp_path_buffer);
let stream = MonotonicPathCommandStream::new(stream);
library.push_segments((path_index + 1) as u16, stream.clone());
path_buffer.add_stream(stream)
}
}
@ -481,7 +485,7 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
}
// Partition the paths.
let mut partitioner = Partitioner::new(MeshLibrary::new());
let mut partitioner = Partitioner::new(library);
partitioner.init_with_path_buffer(&path_buffer);
let path_partitioning_result = PathPartitioningResult::compute(&mut partitioner, &paths);

View File

@ -120,6 +120,7 @@ impl PathBuffer {
}
}
#[derive(Clone)]
pub struct PathBufferStream<'a> {
path_buffer: &'a PathBuffer,
endpoint_index: u32,

View File

@ -15,6 +15,7 @@ use std::mem;
use PathCommand;
use curve::Curve;
#[derive(Clone)]
pub struct MonotonicPathCommandStream<I> {
inner: I,
queue: ArrayVec<[PathCommand; 2]>,

View File

@ -160,48 +160,60 @@ bool computeECAAQuadPosition(out vec2 outPosition,
vec2 quadPosition,
ivec2 framebufferSize,
vec4 localTransformST,
vec4 globalTransformST,
mat4 globalTransform,
vec4 hints,
vec4 bounds,
float leftNormalAngle,
float rightNormalAngle,
vec2 emboldenAmount) {
vec2 edgeBL = bounds.xy, edgeTL = bounds.xw, edgeTR = bounds.zw, edgeBR = bounds.zy;
leftPosition = dilatePosition(leftPosition, leftNormalAngle, emboldenAmount);
rightPosition = dilatePosition(rightPosition, rightNormalAngle, emboldenAmount);
leftPosition = hintPosition(leftPosition, hints);
rightPosition = hintPosition(rightPosition, hints);
vec2 edgePosition = hintPosition(bounds.zw, hints);
leftPosition = transformVertexPositionST(leftPosition, localTransformST);
rightPosition = transformVertexPositionST(rightPosition, localTransformST);
edgePosition = transformVertexPositionST(edgePosition, localTransformST);
edgeBL = transformVertexPositionST(edgeBL, localTransformST);
edgeTL = transformVertexPositionST(edgeTL, localTransformST);
edgeBR = transformVertexPositionST(edgeBR, localTransformST);
edgeTR = transformVertexPositionST(edgeTR, localTransformST);
leftPosition = transformVertexPositionST(leftPosition, globalTransformST);
rightPosition = transformVertexPositionST(rightPosition, globalTransformST);
edgePosition = transformVertexPositionST(edgePosition, globalTransformST);
leftPosition = transformVertexPosition(leftPosition, globalTransform);
rightPosition = transformVertexPosition(rightPosition, globalTransform);
edgeBL = transformVertexPosition(edgeBL, globalTransform);
edgeTL = transformVertexPosition(edgeTL, globalTransform);
edgeBR = transformVertexPosition(edgeBR, globalTransform);
edgeTR = transformVertexPosition(edgeTR, globalTransform);
leftPosition = convertClipToScreenSpace(leftPosition, framebufferSize);
rightPosition = convertClipToScreenSpace(rightPosition, framebufferSize);
edgePosition = convertClipToScreenSpace(edgePosition, framebufferSize);
float winding = sign(leftPosition.x - rightPosition.x);
outWinding = winding;
if (winding > 0.0) {
vec2 tmp = leftPosition;
leftPosition = rightPosition;
rightPosition = tmp;
}
outWinding = winding;
if (rightPosition.x - leftPosition.x <= EPSILON) {
outPosition = vec2(0.0);
return false;
}
// Find the bottom of the path, and convert to clip space.
//
// FIXME(pcwalton): Speed this up somehow?
float pathBottomY = max(max(edgeBL.y, edgeBR.y), max(edgeTL.y, edgeTR.y));
pathBottomY = (pathBottomY + 1.0) * 0.5 * float(framebufferSize.y);
vec4 roundedExtents = vec4(floor(leftPosition.x),
floor(min(leftPosition.y, rightPosition.y)),
ceil(rightPosition.x),
ceil(edgePosition.y));
ceil(pathBottomY));
vec2 position = mix(roundedExtents.xy, roundedExtents.zw, quadPosition);
outPosition = convertScreenToClipSpace(position, framebufferSize);

View File

@ -10,7 +10,7 @@
precision highp float;
uniform vec4 uTransformST;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformDimensions;
@ -23,8 +23,8 @@ attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aControlPointPosition;
attribute vec2 aRightPosition;
attribute vec3 aNormalAngles;
attribute float aPathID;
attribute vec3 aNormalAngles;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
@ -52,7 +52,7 @@ void main() {
aQuadPosition,
uFramebufferSize,
transform,
uTransformST,
uTransform,
uHints,
bounds,
leftNormalAngle,
@ -63,7 +63,7 @@ void main() {
uEmboldenAmount);
controlPointPosition = hintPosition(controlPointPosition, uHints);
controlPointPosition = transformVertexPositionST(controlPointPosition, transform);
controlPointPosition = transformVertexPositionST(controlPointPosition, uTransformST);
controlPointPosition = transformVertexPosition(controlPointPosition, uTransform);
controlPointPosition = convertClipToScreenSpace(controlPointPosition, uFramebufferSize);
}

View File

@ -10,7 +10,7 @@
precision highp float;
uniform vec4 uTransformST;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformDimensions;
@ -49,7 +49,7 @@ void main() {
aQuadPosition,
uFramebufferSize,
transform,
uTransformST,
uTransform,
uHints,
bounds,
leftNormalAngle,

View File

@ -1,79 +0,0 @@
// pathfinder/shaders/gles2/xcaa-edge-detect.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathID;
uniform sampler2D uPathColors;
varying vec2 vTexCoord;
void checkFG(out vec2 fgPosition, out int fgPathID, vec2 queryPosition, int queryPathID) {
if (queryPathID > fgPathID) {
fgPosition = queryPosition;
fgPathID = queryPathID;
}
}
void updateMinMaxInt(inout ivec2 minMax, int value) {
if (value < minMax.x)
minMax.x = value;
if (value > minMax.y)
minMax.y = value;
}
ivec2 minMaxIVec4(ivec4 values) {
ivec2 minMax = ivec2(values.x);
updateMinMaxInt(minMax, values.y);
updateMinMaxInt(minMax, values.z);
updateMinMaxInt(minMax, values.w);
return minMax;
}
void main() {
// Unpack.
vec2 position = vTexCoord;
// Compute positions.
vec2 onePixel = 1.0 / vec2(uFramebufferSize);
vec2 positionL = position + vec2(-onePixel.x, 0.0);
vec2 positionR = position + vec2( onePixel.x, 0.0);
vec2 positionB = position + vec2(0.0, -onePixel.y);
vec2 positionT = position + vec2(0.0, onePixel.y);
// Determine the topmost and bottommost paths.
int centerPathID = unpackPathID(texture2D(uPathID, position).rg);
ivec4 neighborPathIDs = ivec4(unpackPathID(texture2D(uPathID, positionL).rg),
unpackPathID(texture2D(uPathID, positionR).rg),
unpackPathID(texture2D(uPathID, positionB).rg),
unpackPathID(texture2D(uPathID, positionT).rg));
ivec2 pathIDsBGFG = minMaxIVec4(neighborPathIDs);
// Determine the depth.
//
// If all colors are the same, avoid touching this pixel in any further passes.
float outDepth;
if (pathIDsBGFG.x == pathIDsBGFG.y)
outDepth = 1.0;
else
outDepth = convertPathIndexToWindowDepthValue(pathIDsBGFG.y);
// FIXME(pcwalton): Fetch the background color.
// FIXME(pcwalton): Output path ID for debugging. Switch to BG color.
//vec2 color = pathIDsBGFG.x == pathIDsBGFG.y ? vec2(1.0) : packPathID(pathIDsBGFG.y);
//vec4 color = fetchFloat4Data(uPathColors, pathIDsBGFG.x, uPathColorsDimensions);
// Output results.
//gl_FragColor = vec4(packPathID(pathIDsBGFG.x), 0.0, 1.0);
gl_FragColor = vec4(packPathID(pathIDsBGFG.x), packPathID(pathIDsBGFG.y));
gl_FragDepthEXT = outDepth;
}

View File

@ -1,21 +0,0 @@
// pathfinder/shaders/gles2/xcaa-edge-detect.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
attribute vec2 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), aPosition), 0.0, 1.0);
vTexCoord = aTexCoord;
}

View File

@ -21,7 +21,7 @@ void main() {
float edgeDepth = texture2D(uAADepth, vTexCoord).r;
int edgePathID = convertWindowDepthValueToPathIndex(edgeDepth);
vec4 edgeColor = fetchFloat4Data(uPathColors, edgePathID, uPathColorsDimensions);
float edgeAlpha = texture2D(uAAAlpha, vTexCoord).r;
float edgeAlpha = abs(texture2D(uAAAlpha, vTexCoord).r);
gl_FragColor = vec4(edgeColor.rgb, edgeColor.a * edgeAlpha);
gl_FragDepthEXT = edgeDepth;
}