Add an experimental implementation of macOS-like font dilation
Following Apple's earlier terminology, this is exposed as "strong" subpixel AA.
This commit is contained in:
parent
99f6f2a104
commit
60ff71be84
|
@ -54,12 +54,14 @@
|
||||||
<option value="ecaa" selected>ECAA</option>
|
<option value="ecaa" selected>ECAA</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-group">
|
||||||
<label class="form-check-label">
|
<label for="pf-subpixel-aa-select">Subpixel AA</label>
|
||||||
<input class="form-check-input" value="" type="checkbox"
|
<select id="pf-subpixel-aa-select"
|
||||||
id="pf-subpixel-aa" checked>
|
class="form-control custom-select">
|
||||||
Subpixel AA
|
<option value="none">None</option>
|
||||||
</label>
|
<option value="medium" selected>Medium</option>
|
||||||
|
<option value="strong">Strong</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pf-hinting-select">Hinting</label>
|
<label for="pf-hinting-select">Hinting</label>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import * as opentype from "opentype.js";
|
||||||
|
|
||||||
import {mat4, vec2} from "gl-matrix";
|
import {mat4, vec2} from "gl-matrix";
|
||||||
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 {DemoAppController} from "./app-controller";
|
||||||
import PathfinderBufferTexture from "./buffer-texture";
|
import PathfinderBufferTexture from "./buffer-texture";
|
||||||
import {PerspectiveCamera} from "./camera";
|
import {PerspectiveCamera} from "./camera";
|
||||||
|
@ -285,7 +286,7 @@ class ThreeDView extends PathfinderDemoView {
|
||||||
|
|
||||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||||
aaLevel: number,
|
aaLevel: number,
|
||||||
subpixelAA: boolean):
|
subpixelAA: SubpixelAAType):
|
||||||
AntialiasingStrategy {
|
AntialiasingStrategy {
|
||||||
if (aaType !== 'ecaa')
|
if (aaType !== 'ecaa')
|
||||||
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
||||||
|
|
|
@ -14,6 +14,8 @@ import {PathfinderDemoView} from './view';
|
||||||
|
|
||||||
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'ecaa';
|
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'ecaa';
|
||||||
|
|
||||||
|
export type SubpixelAAType = 'none' | 'medium' | 'strong';
|
||||||
|
|
||||||
export abstract class AntialiasingStrategy {
|
export abstract class AntialiasingStrategy {
|
||||||
// True if direct rendering should occur.
|
// True if direct rendering should occur.
|
||||||
shouldRenderDirect: boolean;
|
shouldRenderDirect: boolean;
|
||||||
|
@ -51,7 +53,7 @@ export abstract class AntialiasingStrategy {
|
||||||
export class NoAAStrategy extends AntialiasingStrategy {
|
export class NoAAStrategy extends AntialiasingStrategy {
|
||||||
framebufferSize: glmatrix.vec2;
|
framebufferSize: glmatrix.vec2;
|
||||||
|
|
||||||
constructor(level: number, subpixelAA: boolean) {
|
constructor(level: number, subpixelAA: SubpixelAAType) {
|
||||||
super();
|
super();
|
||||||
this.framebufferSize = glmatrix.vec2.create();
|
this.framebufferSize = glmatrix.vec2.create();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
import {AntialiasingStrategyName} from "./aa-strategy";
|
import { AntialiasingStrategyName, SubpixelAAType } from "./aa-strategy";
|
||||||
import {FilePickerView} from "./file-picker";
|
import {FilePickerView} from "./file-picker";
|
||||||
import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader';
|
import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader';
|
||||||
import {expectNotNull, unwrapNull, unwrapUndef} from './utils';
|
import {expectNotNull, unwrapNull, unwrapUndef} from './utils';
|
||||||
|
@ -56,7 +56,7 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
|
||||||
protected shaderSources: ShaderMap<ShaderProgramSource> | null;
|
protected shaderSources: ShaderMap<ShaderProgramSource> | null;
|
||||||
|
|
||||||
private aaLevelSelect: HTMLSelectElement | null;
|
private aaLevelSelect: HTMLSelectElement | null;
|
||||||
private subpixelAASwitch: HTMLInputElement | null;
|
private subpixelAASelect: HTMLSelectElement | null;
|
||||||
private fpsLabel: HTMLElement | null;
|
private fpsLabel: HTMLElement | null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -143,10 +143,10 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
|
||||||
if (this.aaLevelSelect != null)
|
if (this.aaLevelSelect != null)
|
||||||
this.aaLevelSelect.addEventListener('change', () => this.updateAALevel(), false);
|
this.aaLevelSelect.addEventListener('change', () => this.updateAALevel(), false);
|
||||||
|
|
||||||
this.subpixelAASwitch =
|
this.subpixelAASelect =
|
||||||
document.getElementById('pf-subpixel-aa') as HTMLInputElement | null;
|
document.getElementById('pf-subpixel-aa-select') as HTMLSelectElement | null;
|
||||||
if (this.subpixelAASwitch != null)
|
if (this.subpixelAASelect != null)
|
||||||
this.subpixelAASwitch.addEventListener('change', () => this.updateAALevel(), false);
|
this.subpixelAASelect.addEventListener('change', () => this.updateAALevel(), false);
|
||||||
|
|
||||||
this.updateAALevel();
|
this.updateAALevel();
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,11 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
|
||||||
aaLevel = 0;
|
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));
|
this.view.then(view => view.setAntialiasingOptions(aaType, aaLevel, subpixelAA));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import * as glmatrix from 'gl-matrix';
|
||||||
import * as opentype from "opentype.js";
|
import * as opentype from "opentype.js";
|
||||||
|
|
||||||
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
||||||
|
import {SubpixelAAType} from "./aa-strategy";
|
||||||
import {AppController, DemoAppController} from "./app-controller";
|
import {AppController, DemoAppController} from "./app-controller";
|
||||||
import PathfinderBufferTexture from './buffer-texture';
|
import PathfinderBufferTexture from './buffer-texture';
|
||||||
import {OrthographicCamera} from './camera';
|
import {OrthographicCamera} from './camera';
|
||||||
|
@ -221,7 +222,7 @@ class BenchmarkTestView extends MonochromePathfinderView {
|
||||||
|
|
||||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||||
aaLevel: number,
|
aaLevel: number,
|
||||||
subpixelAA: boolean):
|
subpixelAA: SubpixelAAType):
|
||||||
AntialiasingStrategy {
|
AntialiasingStrategy {
|
||||||
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
import * as glmatrix from 'gl-matrix';
|
import * as glmatrix from 'gl-matrix';
|
||||||
|
|
||||||
import {AntialiasingStrategy} from './aa-strategy';
|
import { AntialiasingStrategy, SubpixelAAType } from './aa-strategy';
|
||||||
import PathfinderBufferTexture from './buffer-texture';
|
import PathfinderBufferTexture from './buffer-texture';
|
||||||
import {createFramebuffer, createFramebufferColorTexture} from './gl-utils';
|
import {createFramebuffer, createFramebufferColorTexture} from './gl-utils';
|
||||||
import {createFramebufferDepthTexture, setTextureParameters, UniformMap} 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 supersampledFramebufferSize: glmatrix.vec2;
|
||||||
protected destFramebufferSize: glmatrix.vec2;
|
protected destFramebufferSize: glmatrix.vec2;
|
||||||
|
|
||||||
protected subpixelAA: boolean;
|
protected subpixelAA: SubpixelAAType;
|
||||||
|
|
||||||
private bVertexPositionBufferTexture: PathfinderBufferTexture;
|
private bVertexPositionBufferTexture: PathfinderBufferTexture;
|
||||||
private bVertexPathIDBufferTexture: PathfinderBufferTexture;
|
private bVertexPathIDBufferTexture: PathfinderBufferTexture;
|
||||||
|
@ -47,7 +47,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
|
||||||
private curveVAOs: UpperAndLower<WebGLVertexArrayObject>;
|
private curveVAOs: UpperAndLower<WebGLVertexArrayObject>;
|
||||||
private resolveVAO: WebGLVertexArrayObject;
|
private resolveVAO: WebGLVertexArrayObject;
|
||||||
|
|
||||||
constructor(level: number, subpixelAA: boolean) {
|
constructor(level: number, subpixelAA: SubpixelAAType) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.subpixelAA = subpixelAA;
|
this.subpixelAA = subpixelAA;
|
||||||
|
@ -472,13 +472,13 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get supersampleScale(): glmatrix.vec2 {
|
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 {
|
export class ECAAMonochromeStrategy extends ECAAStrategy {
|
||||||
protected getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram {
|
protected getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram {
|
||||||
if (this.subpixelAA)
|
if (this.subpixelAA !== 'none')
|
||||||
return view.shaderPrograms.ecaaMonoSubpixelResolve;
|
return view.shaderPrograms.ecaaMonoSubpixelResolve;
|
||||||
return view.shaderPrograms.ecaaMonoResolve;
|
return view.shaderPrograms.ecaaMonoResolve;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,14 @@
|
||||||
|
|
||||||
import * as glmatrix from 'gl-matrix';
|
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 {createFramebuffer, createFramebufferDepthTexture, setTextureParameters} from './gl-utils';
|
||||||
import {unwrapNull} from './utils';
|
import {unwrapNull} from './utils';
|
||||||
import {PathfinderDemoView} from './view';
|
import {PathfinderDemoView} from './view';
|
||||||
|
|
||||||
export default class SSAAStrategy extends AntialiasingStrategy {
|
export default class SSAAStrategy extends AntialiasingStrategy {
|
||||||
private level: number;
|
private level: number;
|
||||||
private subpixelAA: boolean;
|
private subpixelAA: SubpixelAAType;
|
||||||
|
|
||||||
private destFramebufferSize: glmatrix.vec2;
|
private destFramebufferSize: glmatrix.vec2;
|
||||||
private supersampledFramebufferSize: glmatrix.vec2;
|
private supersampledFramebufferSize: glmatrix.vec2;
|
||||||
|
@ -25,7 +25,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
|
||||||
private supersampledDepthTexture: WebGLTexture;
|
private supersampledDepthTexture: WebGLTexture;
|
||||||
private supersampledFramebuffer: WebGLFramebuffer;
|
private supersampledFramebuffer: WebGLFramebuffer;
|
||||||
|
|
||||||
constructor(level: number, subpixelAA: boolean) {
|
constructor(level: number, subpixelAA: SubpixelAAType) {
|
||||||
super();
|
super();
|
||||||
this.level = level;
|
this.level = level;
|
||||||
this.subpixelAA = subpixelAA;
|
this.subpixelAA = subpixelAA;
|
||||||
|
@ -96,7 +96,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
|
||||||
|
|
||||||
// Set up the blit program VAO.
|
// Set up the blit program VAO.
|
||||||
let resolveProgram;
|
let resolveProgram;
|
||||||
if (this.subpixelAA)
|
if (this.subpixelAA !== 'none')
|
||||||
resolveProgram = view.shaderPrograms.ssaaSubpixelResolve;
|
resolveProgram = view.shaderPrograms.ssaaSubpixelResolve;
|
||||||
else
|
else
|
||||||
resolveProgram = view.shaderPrograms.blit;
|
resolveProgram = view.shaderPrograms.blit;
|
||||||
|
@ -120,7 +120,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get supersampleScale(): glmatrix.vec2 {
|
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 {
|
private usedSupersampledFramebufferSize(view: PathfinderDemoView): glmatrix.vec2 {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
import * as glmatrix from 'gl-matrix';
|
import * as glmatrix from 'gl-matrix';
|
||||||
import * as _ from 'lodash';
|
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 {DemoAppController} from './app-controller';
|
||||||
import PathfinderBufferTexture from "./buffer-texture";
|
import PathfinderBufferTexture from "./buffer-texture";
|
||||||
import {OrthographicCamera} from "./camera";
|
import {OrthographicCamera} from "./camera";
|
||||||
|
@ -153,7 +153,7 @@ class SVGDemoView extends PathfinderDemoView {
|
||||||
|
|
||||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||||
aaLevel: number,
|
aaLevel: number,
|
||||||
subpixelAA: boolean):
|
subpixelAA: SubpixelAAType):
|
||||||
AntialiasingStrategy {
|
AntialiasingStrategy {
|
||||||
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ import * as _ from 'lodash';
|
||||||
import * as opentype from 'opentype.js';
|
import * as opentype from 'opentype.js';
|
||||||
|
|
||||||
import {Metrics} 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 {DemoAppController} from './app-controller';
|
||||||
import PathfinderBufferTexture from './buffer-texture';
|
import PathfinderBufferTexture from './buffer-texture';
|
||||||
import {OrthographicCamera} from "./camera";
|
import {OrthographicCamera} from "./camera";
|
||||||
|
@ -71,6 +72,11 @@ const INITIAL_FONT_SIZE: number = 72.0;
|
||||||
|
|
||||||
const DEFAULT_FONT: string = 'open-sans';
|
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_POSITION_SIZE: number = 8;
|
||||||
|
|
||||||
const B_PATH_INDEX_SIZE: number = 2;
|
const B_PATH_INDEX_SIZE: number = 2;
|
||||||
|
@ -172,10 +178,6 @@ class TextDemoController extends DemoAppController<TextDemoView> {
|
||||||
window.jQuery(this.editTextModal).modal();
|
window.jQuery(this.editTextModal).modal();
|
||||||
}
|
}
|
||||||
|
|
||||||
createHint(): Hint {
|
|
||||||
return new Hint(this.font, this.pixelsPerUnit, this.useHinting);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createView() {
|
protected createView() {
|
||||||
return new TextDemoView(this,
|
return new TextDemoView(this,
|
||||||
unwrapNull(this.commonShaderSource),
|
unwrapNull(this.commonShaderSource),
|
||||||
|
@ -245,7 +247,7 @@ class TextDemoController extends DemoAppController<TextDemoView> {
|
||||||
this.view.then(view => view.relayoutText());
|
this.view.then(view => view.relayoutText());
|
||||||
}
|
}
|
||||||
|
|
||||||
get pixelsPerUnit(): number {
|
get layoutPixelsPerUnit(): number {
|
||||||
return this._fontSize / this.font.opentypeFont.unitsPerEm;
|
return this._fontSize / this.font.opentypeFont.unitsPerEm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +285,8 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
|
|
||||||
protected depthFunction: number = this.gl.GREATER;
|
protected depthFunction: number = this.gl.GREATER;
|
||||||
|
|
||||||
|
private subpixelAA: SubpixelAAType;
|
||||||
|
|
||||||
constructor(appController: TextDemoController,
|
constructor(appController: TextDemoController,
|
||||||
commonShaderSource: string,
|
commonShaderSource: string,
|
||||||
shaderSources: ShaderMap<ShaderProgramSource>) {
|
shaderSources: ShaderMap<ShaderProgramSource>) {
|
||||||
|
@ -327,6 +331,17 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
this.setDirty();
|
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() {
|
protected initContext() {
|
||||||
super.initContext();
|
super.initContext();
|
||||||
}
|
}
|
||||||
|
@ -348,7 +363,7 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
protected pathTransformsForObject(objectIndex: number): Float32Array {
|
protected pathTransformsForObject(objectIndex: number): Float32Array {
|
||||||
const pathCount = this.appController.pathCount;
|
const pathCount = this.appController.pathCount;
|
||||||
const atlasGlyphs = this.appController.atlasGlyphs;
|
const atlasGlyphs = this.appController.atlasGlyphs;
|
||||||
const pixelsPerUnit = this.appController.pixelsPerUnit;
|
const pixelsPerUnit = this.displayPixelsPerUnit;
|
||||||
|
|
||||||
const transforms = new Float32Array((pathCount + 1) * 4);
|
const transforms = new Float32Array((pathCount + 1) * 4);
|
||||||
|
|
||||||
|
@ -435,8 +450,9 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
|
|
||||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||||
aaLevel: number,
|
aaLevel: number,
|
||||||
subpixelAA: boolean):
|
subpixelAA: SubpixelAAType):
|
||||||
AntialiasingStrategy {
|
AntialiasingStrategy {
|
||||||
|
this.subpixelAA = subpixelAA;
|
||||||
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,6 +460,12 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
this.appController.newTimingsReceived(this.lastTimings);
|
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.
|
/// Lays out glyphs on the canvas.
|
||||||
private layoutText() {
|
private layoutText() {
|
||||||
const layout = this.appController.layout;
|
const layout = this.appController.layout;
|
||||||
|
@ -456,8 +478,9 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
const glyphPositions = new Float32Array(totalGlyphCount * 8);
|
const glyphPositions = new Float32Array(totalGlyphCount * 8);
|
||||||
const glyphIndices = new Uint32Array(totalGlyphCount * 6);
|
const glyphIndices = new Uint32Array(totalGlyphCount * 6);
|
||||||
|
|
||||||
const hint = this.appController.createHint();
|
const hint = this.createHint();
|
||||||
const pixelsPerUnit = this.appController.pixelsPerUnit;
|
const displayPixelsPerUnit = this.displayPixelsPerUnit;
|
||||||
|
const layoutPixelsPerUnit = this.appController.layoutPixelsPerUnit;
|
||||||
|
|
||||||
let globalGlyphIndex = 0;
|
let globalGlyphIndex = 0;
|
||||||
for (const run of layout.textFrame.runs) {
|
for (const run of layout.textFrame.runs) {
|
||||||
|
@ -465,7 +488,8 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
glyphIndex < run.glyphIDs.length;
|
glyphIndex < run.glyphIDs.length;
|
||||||
glyphIndex++, globalGlyphIndex++) {
|
glyphIndex++, globalGlyphIndex++) {
|
||||||
const rect = run.pixelRectForGlyphAt(glyphIndex,
|
const rect = run.pixelRectForGlyphAt(glyphIndex,
|
||||||
pixelsPerUnit,
|
layoutPixelsPerUnit,
|
||||||
|
displayPixelsPerUnit,
|
||||||
hint,
|
hint,
|
||||||
SUBPIXEL_GRANULARITY);
|
SUBPIXEL_GRANULARITY);
|
||||||
glyphPositions.set([
|
glyphPositions.set([
|
||||||
|
@ -495,10 +519,11 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
private buildAtlasGlyphs() {
|
private buildAtlasGlyphs() {
|
||||||
const font = this.appController.font;
|
const font = this.appController.font;
|
||||||
const glyphStore = this.appController.glyphStore;
|
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 textFrame = this.appController.layout.textFrame;
|
||||||
const hint = this.appController.createHint();
|
const hint = this.createHint();
|
||||||
|
|
||||||
// Only build glyphs in view.
|
// Only build glyphs in view.
|
||||||
const translation = this.camera.translation;
|
const translation = this.camera.translation;
|
||||||
|
@ -511,7 +536,8 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
for (const run of textFrame.runs) {
|
for (const run of textFrame.runs) {
|
||||||
for (let glyphIndex = 0; glyphIndex < run.glyphIDs.length; glyphIndex++) {
|
for (let glyphIndex = 0; glyphIndex < run.glyphIDs.length; glyphIndex++) {
|
||||||
const pixelRect = run.pixelRectForGlyphAt(glyphIndex,
|
const pixelRect = run.pixelRectForGlyphAt(glyphIndex,
|
||||||
pixelsPerUnit,
|
layoutPixelsPerUnit,
|
||||||
|
displayPixelsPerUnit,
|
||||||
hint,
|
hint,
|
||||||
SUBPIXEL_GRANULARITY);
|
SUBPIXEL_GRANULARITY);
|
||||||
if (!rectsIntersect(pixelRect, canvasRect))
|
if (!rectsIntersect(pixelRect, canvasRect))
|
||||||
|
@ -523,7 +549,7 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const subpixel = run.subpixelForGlyphAt(glyphIndex,
|
const subpixel = run.subpixelForGlyphAt(glyphIndex,
|
||||||
pixelsPerUnit,
|
layoutPixelsPerUnit,
|
||||||
hint,
|
hint,
|
||||||
SUBPIXEL_GRANULARITY);
|
SUBPIXEL_GRANULARITY);
|
||||||
const glyphKey = new GlyphKey(glyphID, subpixel);
|
const glyphKey = new GlyphKey(glyphID, subpixel);
|
||||||
|
@ -537,7 +563,7 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.appController.atlasGlyphs = atlasGlyphs;
|
this.appController.atlasGlyphs = atlasGlyphs;
|
||||||
this.appController.atlas.layoutGlyphs(atlasGlyphs, font, pixelsPerUnit, hint);
|
this.appController.atlas.layoutGlyphs(atlasGlyphs, font, displayPixelsPerUnit, hint);
|
||||||
|
|
||||||
this.uploadPathTransforms(1);
|
this.uploadPathTransforms(1);
|
||||||
|
|
||||||
|
@ -577,8 +603,9 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
const font = this.appController.font;
|
const font = this.appController.font;
|
||||||
const atlasGlyphs = this.appController.atlasGlyphs;
|
const atlasGlyphs = this.appController.atlasGlyphs;
|
||||||
|
|
||||||
const hint = this.appController.createHint();
|
const hint = this.createHint();
|
||||||
const pixelsPerUnit = this.appController.pixelsPerUnit;
|
const layoutPixelsPerUnit = this.appController.layoutPixelsPerUnit;
|
||||||
|
const displayPixelsPerUnit = this.displayPixelsPerUnit;
|
||||||
|
|
||||||
const atlasGlyphKeys = atlasGlyphs.map(atlasGlyph => atlasGlyph.glyphKey.sortKey);
|
const atlasGlyphKeys = atlasGlyphs.map(atlasGlyph => atlasGlyph.glyphKey.sortKey);
|
||||||
|
|
||||||
|
@ -592,7 +619,7 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
const textGlyphID = run.glyphIDs[glyphIndex];
|
const textGlyphID = run.glyphIDs[glyphIndex];
|
||||||
|
|
||||||
const subpixel = run.subpixelForGlyphAt(glyphIndex,
|
const subpixel = run.subpixelForGlyphAt(glyphIndex,
|
||||||
pixelsPerUnit,
|
layoutPixelsPerUnit,
|
||||||
hint,
|
hint,
|
||||||
SUBPIXEL_GRANULARITY);
|
SUBPIXEL_GRANULARITY);
|
||||||
|
|
||||||
|
@ -608,10 +635,10 @@ class TextDemoView extends MonochromePathfinderView {
|
||||||
if (atlasGlyphMetrics == null)
|
if (atlasGlyphMetrics == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const atlasGlyphPixelOrigin = atlasGlyph.calculateSubpixelOrigin(pixelsPerUnit);
|
const atlasGlyphPixelOrigin = atlasGlyph.calculateSubpixelOrigin(displayPixelsPerUnit);
|
||||||
const atlasGlyphRect = calculatePixelRectForGlyph(atlasGlyphMetrics,
|
const atlasGlyphRect = calculatePixelRectForGlyph(atlasGlyphMetrics,
|
||||||
atlasGlyphPixelOrigin,
|
atlasGlyphPixelOrigin,
|
||||||
pixelsPerUnit,
|
displayPixelsPerUnit,
|
||||||
hint);
|
hint);
|
||||||
const atlasGlyphBL = atlasGlyphRect.slice(0, 2) as glmatrix.vec2;
|
const atlasGlyphBL = atlasGlyphRect.slice(0, 2) as glmatrix.vec2;
|
||||||
const atlasGlyphTR = atlasGlyphRect.slice(2, 4) 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<void> {
|
protected get directInteriorProgramName(): keyof ShaderMap<void> {
|
||||||
return 'directInterior';
|
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 {
|
interface AntialiasingStrategyTable {
|
||||||
|
|
|
@ -99,27 +99,32 @@ export class TextRun {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calculatePixelOriginForGlyphAt(index: number, pixelsPerUnit: number, hint: Hint):
|
calculatePixelOriginForGlyphAt(index: number,
|
||||||
|
layoutPixelsPerUnit: number,
|
||||||
|
hint: Hint):
|
||||||
glmatrix.vec2 {
|
glmatrix.vec2 {
|
||||||
const textGlyphOrigin = glmatrix.vec2.clone(this.origin);
|
const textGlyphOrigin = glmatrix.vec2.clone(this.origin);
|
||||||
textGlyphOrigin[0] += this.advances[index];
|
textGlyphOrigin[0] += this.advances[index];
|
||||||
glmatrix.vec2.scale(textGlyphOrigin, textGlyphOrigin, pixelsPerUnit);
|
glmatrix.vec2.scale(textGlyphOrigin, textGlyphOrigin, layoutPixelsPerUnit);
|
||||||
return textGlyphOrigin;
|
return textGlyphOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
pixelRectForGlyphAt(index: number,
|
pixelRectForGlyphAt(index: number,
|
||||||
pixelsPerUnit: number,
|
layoutPixelsPerUnit: number,
|
||||||
|
displayPixelsPerUnit: number,
|
||||||
hint: Hint,
|
hint: Hint,
|
||||||
subpixelGranularity: number):
|
subpixelGranularity: number):
|
||||||
glmatrix.vec4 {
|
glmatrix.vec4 {
|
||||||
const metrics = unwrapNull(this.font.metricsForGlyph(this.glyphIDs[index]));
|
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;
|
textGlyphOrigin[0] *= subpixelGranularity;
|
||||||
glmatrix.vec2.round(textGlyphOrigin, textGlyphOrigin);
|
glmatrix.vec2.round(textGlyphOrigin, textGlyphOrigin);
|
||||||
textGlyphOrigin[0] /= subpixelGranularity;
|
textGlyphOrigin[0] /= subpixelGranularity;
|
||||||
|
|
||||||
return calculatePixelRectForGlyph(metrics, textGlyphOrigin, pixelsPerUnit, hint);
|
return calculatePixelRectForGlyph(metrics, textGlyphOrigin, displayPixelsPerUnit, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
subpixelForGlyphAt(index: number,
|
subpixelForGlyphAt(index: number,
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
import * as glmatrix from 'gl-matrix';
|
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 PathfinderBufferTexture from './buffer-texture';
|
||||||
import {Camera} from "./camera";
|
import {Camera} from "./camera";
|
||||||
import {QUAD_ELEMENTS, UniformMap} from './gl-utils';
|
import {QUAD_ELEMENTS, UniformMap} from './gl-utils';
|
||||||
|
@ -164,13 +164,13 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
||||||
|
|
||||||
this.wantsScreenshot = false;
|
this.wantsScreenshot = false;
|
||||||
|
|
||||||
this.antialiasingStrategy = new NoAAStrategy(0, false);
|
this.antialiasingStrategy = new NoAAStrategy(0, 'none');
|
||||||
this.antialiasingStrategy.init(this);
|
this.antialiasingStrategy.init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAntialiasingOptions(aaType: AntialiasingStrategyName,
|
setAntialiasingOptions(aaType: AntialiasingStrategyName,
|
||||||
aaLevel: number,
|
aaLevel: number,
|
||||||
subpixelAA: boolean) {
|
subpixelAA: SubpixelAAType) {
|
||||||
this.antialiasingStrategy = this.createAAStrategy(aaType, aaLevel, subpixelAA);
|
this.antialiasingStrategy = this.createAAStrategy(aaType, aaLevel, subpixelAA);
|
||||||
|
|
||||||
const canvas = this.canvas;
|
const canvas = this.canvas;
|
||||||
|
@ -385,7 +385,7 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
||||||
|
|
||||||
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
|
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
|
||||||
aaLevel: number,
|
aaLevel: number,
|
||||||
subpixelAA: boolean):
|
subpixelAA: SubpixelAAType):
|
||||||
AntialiasingStrategy;
|
AntialiasingStrategy;
|
||||||
|
|
||||||
protected abstract compositeIfNecessary(): void;
|
protected abstract compositeIfNecessary(): void;
|
||||||
|
|
Loading…
Reference in New Issue