diff --git a/demo/client/html/text-demo.html.hbs b/demo/client/html/text-demo.html.hbs
index ea9b5539..39d17ce1 100644
--- a/demo/client/html/text-demo.html.hbs
+++ b/demo/client/html/text-demo.html.hbs
@@ -54,12 +54,14 @@
-
-
+
+
+
diff --git a/demo/client/src/3d-demo.ts b/demo/client/src/3d-demo.ts
index 07b0366b..13fb822d 100644
--- a/demo/client/src/3d-demo.ts
+++ b/demo/client/src/3d-demo.ts
@@ -14,6 +14,7 @@ import * as opentype from "opentype.js";
import {mat4, vec2} from "gl-matrix";
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
+import {SubpixelAAType} from "./aa-strategy";
import {DemoAppController} from "./app-controller";
import PathfinderBufferTexture from "./buffer-texture";
import {PerspectiveCamera} from "./camera";
@@ -285,7 +286,7 @@ class ThreeDView extends PathfinderDemoView {
protected createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number,
- subpixelAA: boolean):
+ subpixelAA: SubpixelAAType):
AntialiasingStrategy {
if (aaType !== 'ecaa')
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
diff --git a/demo/client/src/aa-strategy.ts b/demo/client/src/aa-strategy.ts
index bc2e9667..64b2c66c 100644
--- a/demo/client/src/aa-strategy.ts
+++ b/demo/client/src/aa-strategy.ts
@@ -14,6 +14,8 @@ import {PathfinderDemoView} from './view';
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'ecaa';
+export type SubpixelAAType = 'none' | 'medium' | 'strong';
+
export abstract class AntialiasingStrategy {
// True if direct rendering should occur.
shouldRenderDirect: boolean;
@@ -51,7 +53,7 @@ export abstract class AntialiasingStrategy {
export class NoAAStrategy extends AntialiasingStrategy {
framebufferSize: glmatrix.vec2;
- constructor(level: number, subpixelAA: boolean) {
+ constructor(level: number, subpixelAA: SubpixelAAType) {
super();
this.framebufferSize = glmatrix.vec2.create();
}
diff --git a/demo/client/src/app-controller.ts b/demo/client/src/app-controller.ts
index d7c18fe4..fc7390d6 100644
--- a/demo/client/src/app-controller.ts
+++ b/demo/client/src/app-controller.ts
@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-import {AntialiasingStrategyName} from "./aa-strategy";
+import { AntialiasingStrategyName, SubpixelAAType } from "./aa-strategy";
import {FilePickerView} from "./file-picker";
import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader';
import {expectNotNull, unwrapNull, unwrapUndef} from './utils';
@@ -56,7 +56,7 @@ export abstract class DemoAppController extends
protected shaderSources: ShaderMap | null;
private aaLevelSelect: HTMLSelectElement | null;
- private subpixelAASwitch: HTMLInputElement | null;
+ private subpixelAASelect: HTMLSelectElement | null;
private fpsLabel: HTMLElement | null;
constructor() {
@@ -143,10 +143,10 @@ export abstract class DemoAppController extends
if (this.aaLevelSelect != null)
this.aaLevelSelect.addEventListener('change', () => this.updateAALevel(), false);
- this.subpixelAASwitch =
- document.getElementById('pf-subpixel-aa') as HTMLInputElement | null;
- if (this.subpixelAASwitch != null)
- this.subpixelAASwitch.addEventListener('change', () => this.updateAALevel(), false);
+ this.subpixelAASelect =
+ document.getElementById('pf-subpixel-aa-select') as HTMLSelectElement | null;
+ if (this.subpixelAASelect != null)
+ this.subpixelAASelect.addEventListener('change', () => this.updateAALevel(), false);
this.updateAALevel();
}
@@ -191,7 +191,11 @@ export abstract class DemoAppController extends
aaLevel = 0;
}
- const subpixelAA = this.subpixelAASwitch == null ? false : this.subpixelAASwitch.checked;
+ let subpixelAA: SubpixelAAType;
+ if (this.subpixelAASelect != null)
+ subpixelAA = this.subpixelAASelect.selectedOptions[0].value as SubpixelAAType;
+ else
+ subpixelAA = 'none';
this.view.then(view => view.setAntialiasingOptions(aaType, aaLevel, subpixelAA));
}
diff --git a/demo/client/src/benchmark.ts b/demo/client/src/benchmark.ts
index 6eb16aa4..a400588b 100644
--- a/demo/client/src/benchmark.ts
+++ b/demo/client/src/benchmark.ts
@@ -12,6 +12,7 @@ import * as glmatrix from 'gl-matrix';
import * as opentype from "opentype.js";
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
+import {SubpixelAAType} from "./aa-strategy";
import {AppController, DemoAppController} from "./app-controller";
import PathfinderBufferTexture from './buffer-texture';
import {OrthographicCamera} from './camera';
@@ -221,7 +222,7 @@ class BenchmarkTestView extends MonochromePathfinderView {
protected createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number,
- subpixelAA: boolean):
+ subpixelAA: SubpixelAAType):
AntialiasingStrategy {
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
}
diff --git a/demo/client/src/ecaa-strategy.ts b/demo/client/src/ecaa-strategy.ts
index 9da89593..f2034f86 100644
--- a/demo/client/src/ecaa-strategy.ts
+++ b/demo/client/src/ecaa-strategy.ts
@@ -10,7 +10,7 @@
import * as glmatrix from 'gl-matrix';
-import {AntialiasingStrategy} from './aa-strategy';
+import { AntialiasingStrategy, SubpixelAAType } from './aa-strategy';
import PathfinderBufferTexture from './buffer-texture';
import {createFramebuffer, createFramebufferColorTexture} from './gl-utils';
import {createFramebufferDepthTexture, setTextureParameters, UniformMap} from './gl-utils';
@@ -35,7 +35,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
protected supersampledFramebufferSize: glmatrix.vec2;
protected destFramebufferSize: glmatrix.vec2;
- protected subpixelAA: boolean;
+ protected subpixelAA: SubpixelAAType;
private bVertexPositionBufferTexture: PathfinderBufferTexture;
private bVertexPathIDBufferTexture: PathfinderBufferTexture;
@@ -47,7 +47,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
private curveVAOs: UpperAndLower;
private resolveVAO: WebGLVertexArrayObject;
- constructor(level: number, subpixelAA: boolean) {
+ constructor(level: number, subpixelAA: SubpixelAAType) {
super();
this.subpixelAA = subpixelAA;
@@ -472,13 +472,13 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
}
protected get supersampleScale(): glmatrix.vec2 {
- return glmatrix.vec2.fromValues(this.subpixelAA ? 3.0 : 1.0, 1.0);
+ return glmatrix.vec2.fromValues(this.subpixelAA !== 'none' ? 3.0 : 1.0, 1.0);
}
}
export class ECAAMonochromeStrategy extends ECAAStrategy {
protected getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram {
- if (this.subpixelAA)
+ if (this.subpixelAA !== 'none')
return view.shaderPrograms.ecaaMonoSubpixelResolve;
return view.shaderPrograms.ecaaMonoResolve;
}
diff --git a/demo/client/src/ssaa-strategy.ts b/demo/client/src/ssaa-strategy.ts
index 89a58c2c..28c36d30 100644
--- a/demo/client/src/ssaa-strategy.ts
+++ b/demo/client/src/ssaa-strategy.ts
@@ -10,14 +10,14 @@
import * as glmatrix from 'gl-matrix';
-import {AntialiasingStrategy} from './aa-strategy';
+import {AntialiasingStrategy, SubpixelAAType} from './aa-strategy';
import {createFramebuffer, createFramebufferDepthTexture, setTextureParameters} from './gl-utils';
import {unwrapNull} from './utils';
import {PathfinderDemoView} from './view';
export default class SSAAStrategy extends AntialiasingStrategy {
private level: number;
- private subpixelAA: boolean;
+ private subpixelAA: SubpixelAAType;
private destFramebufferSize: glmatrix.vec2;
private supersampledFramebufferSize: glmatrix.vec2;
@@ -25,7 +25,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
private supersampledDepthTexture: WebGLTexture;
private supersampledFramebuffer: WebGLFramebuffer;
- constructor(level: number, subpixelAA: boolean) {
+ constructor(level: number, subpixelAA: SubpixelAAType) {
super();
this.level = level;
this.subpixelAA = subpixelAA;
@@ -96,7 +96,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
// Set up the blit program VAO.
let resolveProgram;
- if (this.subpixelAA)
+ if (this.subpixelAA !== 'none')
resolveProgram = view.shaderPrograms.ssaaSubpixelResolve;
else
resolveProgram = view.shaderPrograms.blit;
@@ -120,7 +120,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
}
private get supersampleScale(): glmatrix.vec2 {
- return glmatrix.vec2.fromValues(this.subpixelAA ? 3 : 2, this.level === 2 ? 1 : 2);
+ return glmatrix.vec2.clone([this.subpixelAA !== 'none' ? 3 : 2, this.level === 2 ? 1 : 2]);
}
private usedSupersampledFramebufferSize(view: PathfinderDemoView): glmatrix.vec2 {
diff --git a/demo/client/src/svg-demo.ts b/demo/client/src/svg-demo.ts
index c2147a48..4e968c01 100644
--- a/demo/client/src/svg-demo.ts
+++ b/demo/client/src/svg-demo.ts
@@ -11,7 +11,7 @@
import * as glmatrix from 'gl-matrix';
import * as _ from 'lodash';
-import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
+import { AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy, SubpixelAAType } from "./aa-strategy";
import {DemoAppController} from './app-controller';
import PathfinderBufferTexture from "./buffer-texture";
import {OrthographicCamera} from "./camera";
@@ -153,7 +153,7 @@ class SVGDemoView extends PathfinderDemoView {
protected createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number,
- subpixelAA: boolean):
+ subpixelAA: SubpixelAAType):
AntialiasingStrategy {
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
}
diff --git a/demo/client/src/text-demo.ts b/demo/client/src/text-demo.ts
index 70b143b6..0053ca8e 100644
--- a/demo/client/src/text-demo.ts
+++ b/demo/client/src/text-demo.ts
@@ -14,7 +14,8 @@ import * as _ from 'lodash';
import * as opentype from 'opentype.js';
import {Metrics} from 'opentype.js';
-import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from './aa-strategy';
+import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
+import {SubpixelAAType} from './aa-strategy';
import {DemoAppController} from './app-controller';
import PathfinderBufferTexture from './buffer-texture';
import {OrthographicCamera} from "./camera";
@@ -71,6 +72,11 @@ const INITIAL_FONT_SIZE: number = 72.0;
const DEFAULT_FONT: string = 'open-sans';
+const FONT_DILATION_FACTOR: number = 1.0242;
+
+/// In pixels.
+const MAX_FONT_DILATION: number = 0.3;
+
const B_POSITION_SIZE: number = 8;
const B_PATH_INDEX_SIZE: number = 2;
@@ -172,10 +178,6 @@ class TextDemoController extends DemoAppController {
window.jQuery(this.editTextModal).modal();
}
- createHint(): Hint {
- return new Hint(this.font, this.pixelsPerUnit, this.useHinting);
- }
-
protected createView() {
return new TextDemoView(this,
unwrapNull(this.commonShaderSource),
@@ -245,7 +247,7 @@ class TextDemoController extends DemoAppController {
this.view.then(view => view.relayoutText());
}
- get pixelsPerUnit(): number {
+ get layoutPixelsPerUnit(): number {
return this._fontSize / this.font.opentypeFont.unitsPerEm;
}
@@ -283,6 +285,8 @@ class TextDemoView extends MonochromePathfinderView {
protected depthFunction: number = this.gl.GREATER;
+ private subpixelAA: SubpixelAAType;
+
constructor(appController: TextDemoController,
commonShaderSource: string,
shaderSources: ShaderMap) {
@@ -327,6 +331,17 @@ class TextDemoView extends MonochromePathfinderView {
this.setDirty();
}
+ setAntialiasingOptions(aaType: AntialiasingStrategyName,
+ aaLevel: number,
+ subpixelAA: SubpixelAAType) {
+ super.setAntialiasingOptions(aaType, aaLevel, subpixelAA);
+
+ // Need to relayout because changing AA options can cause font dilation to change...
+ this.layoutText();
+ this.buildAtlasGlyphs();
+ this.setDirty();
+ }
+
protected initContext() {
super.initContext();
}
@@ -348,7 +363,7 @@ class TextDemoView extends MonochromePathfinderView {
protected pathTransformsForObject(objectIndex: number): Float32Array {
const pathCount = this.appController.pathCount;
const atlasGlyphs = this.appController.atlasGlyphs;
- const pixelsPerUnit = this.appController.pixelsPerUnit;
+ const pixelsPerUnit = this.displayPixelsPerUnit;
const transforms = new Float32Array((pathCount + 1) * 4);
@@ -435,8 +450,9 @@ class TextDemoView extends MonochromePathfinderView {
protected createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number,
- subpixelAA: boolean):
+ subpixelAA: SubpixelAAType):
AntialiasingStrategy {
+ this.subpixelAA = subpixelAA;
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
}
@@ -444,6 +460,12 @@ class TextDemoView extends MonochromePathfinderView {
this.appController.newTimingsReceived(this.lastTimings);
}
+ private createHint(): Hint {
+ return new Hint(this.appController.font,
+ this.displayPixelsPerUnit,
+ this.appController.useHinting);
+ }
+
/// Lays out glyphs on the canvas.
private layoutText() {
const layout = this.appController.layout;
@@ -456,8 +478,9 @@ class TextDemoView extends MonochromePathfinderView {
const glyphPositions = new Float32Array(totalGlyphCount * 8);
const glyphIndices = new Uint32Array(totalGlyphCount * 6);
- const hint = this.appController.createHint();
- const pixelsPerUnit = this.appController.pixelsPerUnit;
+ const hint = this.createHint();
+ const displayPixelsPerUnit = this.displayPixelsPerUnit;
+ const layoutPixelsPerUnit = this.appController.layoutPixelsPerUnit;
let globalGlyphIndex = 0;
for (const run of layout.textFrame.runs) {
@@ -465,7 +488,8 @@ class TextDemoView extends MonochromePathfinderView {
glyphIndex < run.glyphIDs.length;
glyphIndex++, globalGlyphIndex++) {
const rect = run.pixelRectForGlyphAt(glyphIndex,
- pixelsPerUnit,
+ layoutPixelsPerUnit,
+ displayPixelsPerUnit,
hint,
SUBPIXEL_GRANULARITY);
glyphPositions.set([
@@ -495,10 +519,11 @@ class TextDemoView extends MonochromePathfinderView {
private buildAtlasGlyphs() {
const font = this.appController.font;
const glyphStore = this.appController.glyphStore;
- const pixelsPerUnit = this.appController.pixelsPerUnit;
+ const layoutPixelsPerUnit = this.appController.layoutPixelsPerUnit;
+ const displayPixelsPerUnit = this.displayPixelsPerUnit;
const textFrame = this.appController.layout.textFrame;
- const hint = this.appController.createHint();
+ const hint = this.createHint();
// Only build glyphs in view.
const translation = this.camera.translation;
@@ -511,7 +536,8 @@ class TextDemoView extends MonochromePathfinderView {
for (const run of textFrame.runs) {
for (let glyphIndex = 0; glyphIndex < run.glyphIDs.length; glyphIndex++) {
const pixelRect = run.pixelRectForGlyphAt(glyphIndex,
- pixelsPerUnit,
+ layoutPixelsPerUnit,
+ displayPixelsPerUnit,
hint,
SUBPIXEL_GRANULARITY);
if (!rectsIntersect(pixelRect, canvasRect))
@@ -523,7 +549,7 @@ class TextDemoView extends MonochromePathfinderView {
continue;
const subpixel = run.subpixelForGlyphAt(glyphIndex,
- pixelsPerUnit,
+ layoutPixelsPerUnit,
hint,
SUBPIXEL_GRANULARITY);
const glyphKey = new GlyphKey(glyphID, subpixel);
@@ -537,7 +563,7 @@ class TextDemoView extends MonochromePathfinderView {
return;
this.appController.atlasGlyphs = atlasGlyphs;
- this.appController.atlas.layoutGlyphs(atlasGlyphs, font, pixelsPerUnit, hint);
+ this.appController.atlas.layoutGlyphs(atlasGlyphs, font, displayPixelsPerUnit, hint);
this.uploadPathTransforms(1);
@@ -577,8 +603,9 @@ class TextDemoView extends MonochromePathfinderView {
const font = this.appController.font;
const atlasGlyphs = this.appController.atlasGlyphs;
- const hint = this.appController.createHint();
- const pixelsPerUnit = this.appController.pixelsPerUnit;
+ const hint = this.createHint();
+ const layoutPixelsPerUnit = this.appController.layoutPixelsPerUnit;
+ const displayPixelsPerUnit = this.displayPixelsPerUnit;
const atlasGlyphKeys = atlasGlyphs.map(atlasGlyph => atlasGlyph.glyphKey.sortKey);
@@ -592,7 +619,7 @@ class TextDemoView extends MonochromePathfinderView {
const textGlyphID = run.glyphIDs[glyphIndex];
const subpixel = run.subpixelForGlyphAt(glyphIndex,
- pixelsPerUnit,
+ layoutPixelsPerUnit,
hint,
SUBPIXEL_GRANULARITY);
@@ -608,10 +635,10 @@ class TextDemoView extends MonochromePathfinderView {
if (atlasGlyphMetrics == null)
continue;
- const atlasGlyphPixelOrigin = atlasGlyph.calculateSubpixelOrigin(pixelsPerUnit);
+ const atlasGlyphPixelOrigin = atlasGlyph.calculateSubpixelOrigin(displayPixelsPerUnit);
const atlasGlyphRect = calculatePixelRectForGlyph(atlasGlyphMetrics,
atlasGlyphPixelOrigin,
- pixelsPerUnit,
+ displayPixelsPerUnit,
hint);
const atlasGlyphBL = atlasGlyphRect.slice(0, 2) as glmatrix.vec2;
const atlasGlyphTR = atlasGlyphRect.slice(2, 4) as glmatrix.vec2;
@@ -678,6 +705,18 @@ class TextDemoView extends MonochromePathfinderView {
protected get directInteriorProgramName(): keyof ShaderMap {
return 'directInterior';
}
+
+ /// Pixels per unit, including dilation.
+ private get displayPixelsPerUnit(): number {
+ // FIXME(pcwalton): Check against Cocoa and make sure this is what they do.
+ const layoutPixelsPerUnit = this.appController.layoutPixelsPerUnit;
+ if (this.subpixelAA !== 'strong')
+ return layoutPixelsPerUnit;
+
+ const ascender = this.appController.font.opentypeFont.ascender * layoutPixelsPerUnit;
+ const maxFontDilationFactor = (ascender + MAX_FONT_DILATION) / ascender;
+ return Math.min(maxFontDilationFactor, FONT_DILATION_FACTOR) * layoutPixelsPerUnit;
+ }
}
interface AntialiasingStrategyTable {
diff --git a/demo/client/src/text.ts b/demo/client/src/text.ts
index eb812ae5..1d026ba5 100644
--- a/demo/client/src/text.ts
+++ b/demo/client/src/text.ts
@@ -99,27 +99,32 @@ export class TextRun {
}
}
- calculatePixelOriginForGlyphAt(index: number, pixelsPerUnit: number, hint: Hint):
+ calculatePixelOriginForGlyphAt(index: number,
+ layoutPixelsPerUnit: number,
+ hint: Hint):
glmatrix.vec2 {
const textGlyphOrigin = glmatrix.vec2.clone(this.origin);
textGlyphOrigin[0] += this.advances[index];
- glmatrix.vec2.scale(textGlyphOrigin, textGlyphOrigin, pixelsPerUnit);
+ glmatrix.vec2.scale(textGlyphOrigin, textGlyphOrigin, layoutPixelsPerUnit);
return textGlyphOrigin;
}
pixelRectForGlyphAt(index: number,
- pixelsPerUnit: number,
+ layoutPixelsPerUnit: number,
+ displayPixelsPerUnit: number,
hint: Hint,
subpixelGranularity: number):
glmatrix.vec4 {
const metrics = unwrapNull(this.font.metricsForGlyph(this.glyphIDs[index]));
- const textGlyphOrigin = this.calculatePixelOriginForGlyphAt(index, pixelsPerUnit, hint);
+ const textGlyphOrigin = this.calculatePixelOriginForGlyphAt(index,
+ layoutPixelsPerUnit,
+ hint);
textGlyphOrigin[0] *= subpixelGranularity;
glmatrix.vec2.round(textGlyphOrigin, textGlyphOrigin);
textGlyphOrigin[0] /= subpixelGranularity;
- return calculatePixelRectForGlyph(metrics, textGlyphOrigin, pixelsPerUnit, hint);
+ return calculatePixelRectForGlyph(metrics, textGlyphOrigin, displayPixelsPerUnit, hint);
}
subpixelForGlyphAt(index: number,
diff --git a/demo/client/src/view.ts b/demo/client/src/view.ts
index 1769ac2f..7c4d3741 100644
--- a/demo/client/src/view.ts
+++ b/demo/client/src/view.ts
@@ -10,7 +10,7 @@
import * as glmatrix from 'gl-matrix';
-import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
+import { AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy, SubpixelAAType } from "./aa-strategy";
import PathfinderBufferTexture from './buffer-texture';
import {Camera} from "./camera";
import {QUAD_ELEMENTS, UniformMap} from './gl-utils';
@@ -164,13 +164,13 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.wantsScreenshot = false;
- this.antialiasingStrategy = new NoAAStrategy(0, false);
+ this.antialiasingStrategy = new NoAAStrategy(0, 'none');
this.antialiasingStrategy.init(this);
}
setAntialiasingOptions(aaType: AntialiasingStrategyName,
aaLevel: number,
- subpixelAA: boolean) {
+ subpixelAA: SubpixelAAType) {
this.antialiasingStrategy = this.createAAStrategy(aaType, aaLevel, subpixelAA);
const canvas = this.canvas;
@@ -385,7 +385,7 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number,
- subpixelAA: boolean):
+ subpixelAA: SubpixelAAType):
AntialiasingStrategy;
protected abstract compositeIfNecessary(): void;