Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c6558ce
Introduce descender+ascender, fix fonts dis-alignment
zmiao Sep 20, 2019
800bff7
Fix failing tests
zmiao Sep 20, 2019
85224db
Add glyph baseine checker
zmiao Sep 23, 2019
4cc4ad6
Update render test
zmiao Sep 23, 2019
13f1bdb
Update font baseline, make vertical mode applying with font baseline
zmiao Sep 24, 2019
ba51c90
Fix failing tests
zmiao Sep 24, 2019
6d9700f
Update render test case source position
zmiao Sep 25, 2019
fa6eb09
Move ascender/descender to font level attributes, remove non-necessar…
zmiao Sep 30, 2019
539560d
Add new glyph pbfs
zmiao May 17, 2021
06f66a9
rename the inner callback parameter naming
zmiao May 18, 2021
938e485
gl-native parity: Extend Ideographs rasterization range to include CJ…
zmiao May 18, 2021
4fa30d0
Adjust text shaping
zmiao May 18, 2021
1d93720
Fix shaping.test contents parsing
zmiao May 19, 2021
6bfd132
Update render tests expectation
zmiao May 19, 2021
e5dc140
Fix vertical text rendering with 'text-offset' property
zmiao May 24, 2021
4cb41f2
Fix text shaping for characters that have very big part below baselin…
zmiao May 24, 2021
909259a
Update test expectations
zmiao May 24, 2021
84bf3d5
Fix text rendering when both 'text-rotate' and 'text-offset' is set
zmiao May 25, 2021
7690c5e
Update render tests
zmiao May 25, 2021
57efbc4
Fix text rendering for line labels
zmiao May 25, 2021
c94c3a9
Enable ignored render tests regarding text rendering
zmiao May 25, 2021
e4900ca
Make ascender and descender optional
zmiao May 26, 2021
61882cb
Update render tests
zmiao May 26, 2021
d64640a
Update shaping.js logic
zmiao May 28, 2021
a226cfc
Update render test expectations
zmiao May 28, 2021
9c1e02a
Update shaping.test
zmiao May 28, 2021
cf8627c
Fix review comments
zmiao Jun 3, 2021
d02ec2d
fix typo
zmiao Jun 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 16 additions & 19 deletions src/render/glyph_atlas.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {AlphaImage} from '../util/image.js';
import {register} from '../util/web_worker_transfer.js';
import potpack from 'potpack';

import type {GlyphMetrics, StyleGlyph} from '../style/style_glyph.js';
import type {StyleGlyph} from '../style/style_glyph.js';

const glyphPadding = 1;
/*
Expand All @@ -17,34 +17,31 @@ const glyphPadding = 1;
*/
const localGlyphPadding = glyphPadding * SDF_SCALE;

export type Rect = {
export type GlyphRect = {
x: number,
y: number,
w: number,
h: number
};
// {glyphID: glyphRect}
export type GlyphPositionMap = { [_: number]: GlyphRect };

export type GlyphPosition = {
rect: Rect,
metrics: GlyphMetrics
};

export type GlyphPositions = {[_: string]: {[_: number]: GlyphPosition } }
// {fontStack: glyphPoistionMap}
export type GlyphPositions = { [_: string]: GlyphPositionMap };

export default class GlyphAtlas {
image: AlphaImage;
positions: GlyphPositions;

constructor(stacks: {[_: string]: {[_: number]: ?StyleGlyph } }) {
constructor(stacks: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph }, ascender?: number, descender?: number }}) {
const positions = {};
const bins = [];

for (const stack in stacks) {
const glyphs = stacks[stack];
const stackPositions = positions[stack] = {};
const glyphData = stacks[stack];
const glyphPositionMap = positions[stack] = {};

for (const id in glyphs) {
const src = glyphs[+id];
for (const id in glyphData.glyphs) {
const src = glyphData.glyphs[+id];
if (!src || src.bitmap.width === 0 || src.bitmap.height === 0) continue;

const padding = src.metrics.localGlyph ? localGlyphPadding : glyphPadding;
Expand All @@ -55,20 +52,20 @@ export default class GlyphAtlas {
h: src.bitmap.height + 2 * padding
};
bins.push(bin);
stackPositions[id] = {rect: bin, metrics: src.metrics};
glyphPositionMap[id] = bin;
}
}

const {w, h} = potpack(bins);
const image = new AlphaImage({width: w || 1, height: h || 1});

for (const stack in stacks) {
const glyphs = stacks[stack];
const glyphData = stacks[stack];

for (const id in glyphs) {
const src = glyphs[+id];
for (const id in glyphData.glyphs) {
const src = glyphData.glyphs[+id];
if (!src || src.bitmap.width === 0 || src.bitmap.height === 0) continue;
const bin = positions[stack][id].rect;
const bin = positions[stack][id];
const padding = src.metrics.localGlyph ? localGlyphPadding : glyphPadding;
AlphaImage.copy(src.bitmap, image, {x: 0, y: 0}, {x: bin.x + padding, y: bin.y + padding}, src.bitmap);
}
Expand Down
48 changes: 30 additions & 18 deletions src/render/glyph_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ export const SDF_SCALE = 2;
type Entry = {
// null means we've requested the range, but the glyph wasn't included in the result.
glyphs: {[id: number]: StyleGlyph | null},
requests: {[range: number]: Array<Callback<{[_: number]: StyleGlyph | null}>>},
requests: {[range: number]: Array<Callback<{glyphs: {[number]: StyleGlyph | null}, ascender?: number, descender?: number}>>},
ranges: {[range: number]: boolean | null},
tinySDF?: TinySDF
tinySDF?: TinySDF,
ascender?: number,
descender?: number
};

export const LocalGlyphMode = {
Expand All @@ -55,7 +57,7 @@ class GlyphManager {
entries: {[_: string]: Entry};
// Multiple fontstacks may share the same local glyphs, so keep an index
// into the glyphs based soley on font weight
localGlyphs: {[_: string]: {[id: number]: StyleGlyph | null}};
localGlyphs: {[_: string]: {glyphs: {[id: number]: StyleGlyph | null}, ascender: ?number, descender: ?number}};
url: ?string;

// exposed as statics to enable stubbing in unit tests
Expand All @@ -80,7 +82,7 @@ class GlyphManager {
this.url = url;
}

getGlyphs(glyphs: {[stack: string]: Array<number>}, callback: Callback<{[stack: string]: {[id: number]: ?StyleGlyph}}>) {
getGlyphs(glyphs: {[stack: string]: Array<number>}, callback: Callback<{[stack: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}>) {
const all = [];

for (const stack in glyphs) {
Expand All @@ -89,49 +91,53 @@ class GlyphManager {
}
}

asyncAll(all, ({stack, id}, callback: Callback<{stack: string, id: number, glyph: ?StyleGlyph}>) => {
asyncAll(all, ({stack, id}, fnCallback: Callback<{stack: string, id: number, glyph: ?StyleGlyph}>) => {
let entry = this.entries[stack];
if (!entry) {
entry = this.entries[stack] = {
glyphs: {},
requests: {},
ranges: {}
ranges: {},
ascender: undefined,
descender: undefined
};
}

let glyph = entry.glyphs[id];
if (glyph !== undefined) {
callback(null, {stack, id, glyph});
fnCallback(null, {stack, id, glyph});
return;
}

glyph = this._tinySDF(entry, stack, id);
if (glyph) {
entry.glyphs[id] = glyph;
callback(null, {stack, id, glyph});
fnCallback(null, {stack, id, glyph});
return;
}

const range = Math.floor(id / 256);
if (range * 256 > 65535) {
callback(new Error('glyphs > 65535 not supported'));
fnCallback(new Error('glyphs > 65535 not supported'));
return;
}

if (entry.ranges[range]) {
callback(null, {stack, id, glyph});
fnCallback(null, {stack, id, glyph});
return;
}

let requests = entry.requests[range];
if (!requests) {
requests = entry.requests[range] = [];
GlyphManager.loadGlyphRange(stack, range, (this.url: any), this.requestManager,
(err, response: ?{[_: number]: StyleGlyph | null}) => {
(err, response: ?{glyphs: {[_: number]: StyleGlyph | null}, ascender?: number, descender?: number}) => {
if (response) {
for (const id in response) {
entry.ascender = response.ascender;
entry.descender = response.descender;
for (const id in response.glyphs) {
if (!this._doesCharSupportLocalGlyph(+id)) {
entry.glyphs[+id] = response[+id];
entry.glyphs[+id] = response.glyphs[+id];
}
}
entry.ranges[range] = true;
Expand All @@ -143,11 +149,11 @@ class GlyphManager {
});
}

requests.push((err, result: ?{[_: number]: StyleGlyph | null}) => {
requests.push((err, result: ?{glyphs: {[_: number]: StyleGlyph | null}, ascender?: number, descender?: number}) => {
if (err) {
callback(err);
fnCallback(err);
} else if (result) {
callback(null, {stack, id, glyph: result[id] || null});
fnCallback(null, {stack, id, glyph: result.glyphs[id] || null});
}
});
}, (err, glyphs: ?Array<{stack: string, id: number, glyph: ?StyleGlyph}>) => {
Expand All @@ -158,11 +164,15 @@ class GlyphManager {

for (const {stack, id, glyph} of glyphs) {
// Clone the glyph so that our own copy of its ArrayBuffer doesn't get transferred.
(result[stack] || (result[stack] = {}))[id] = glyph && {
if (result[stack] === undefined) result[stack] = {};
if (result[stack].glyphs === undefined) result[stack].glyphs = {};
result[stack].glyphs[id] = glyph && {
id: glyph.id,
bitmap: glyph.bitmap.clone(),
metrics: glyph.metrics
};
result[stack].ascender = this.entries[stack].ascender;
result[stack].descender = this.entries[stack].descender;
}

callback(null, result);
Expand All @@ -181,7 +191,9 @@ class GlyphManager {
(isChar['CJK Unified Ideographs'](id) ||
isChar['Hangul Syllables'](id) ||
isChar['Hiragana'](id) ||
isChar['Katakana'](id));
isChar['Katakana'](id)) ||
// gl-native parity: Extend Ideographs rasterization range to include CJK symbols and punctuations
isChar['CJK Symbols and Punctuation'](id);
/* eslint-enable new-cap */
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/source/worker_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export type WorkerTileResult = {
rawTileData?: ArrayBuffer,
resourceTiming?: Array<PerformanceResourceTiming>,
// Only used for benchmarking:
glyphMap?: {[_: string]: {[_: number]: ?StyleGlyph}} | null,
glyphMap?: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}} | null,
iconMap?: {[_: string]: StyleImage} | null,
glyphPositions?: GlyphPositions | null
};
Expand Down
2 changes: 1 addition & 1 deletion src/source/worker_tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class WorkerTile {
lineAtlas.trim();

let error: ?Error;
let glyphMap: ?{[_: string]: {[_: number]: ?StyleGlyph}};
let glyphMap: ?{[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}};
let iconMap: ?{[_: string]: StyleImage};
let patternMap: ?{[_: string]: StyleImage};
const taskMetadata = {type: 'maybePrepare', isSymbolTile: this.isSymbolTile, zoom: this.zoom};
Expand Down
9 changes: 4 additions & 5 deletions src/style/load_glyph_range.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function (fontstack: string,
range: number,
urlTemplate: string,
requestManager: RequestManager,
callback: Callback<{[_: number]: StyleGlyph | null}>) {
callback: Callback<{glyphs: {[number]: StyleGlyph | null}, ascender?: number, descender?: number}>) {
const begin = range * 256;
const end = begin + 255;

Expand All @@ -27,12 +27,11 @@ export default function (fontstack: string,
callback(err);
} else if (data) {
const glyphs = {};

for (const glyph of parseGlyphPBF(data)) {
const glyphData = parseGlyphPBF(data);
for (const glyph of glyphData.glyphs) {
glyphs[glyph.id] = glyph;
}

callback(null, glyphs);
callback(null, {glyphs, ascender: glyphData.ascender, descender: glyphData.descender});
}
});
}
17 changes: 11 additions & 6 deletions src/style/parse_glyph_pbf.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,28 @@ const border = 3;

import type {StyleGlyph} from './style_glyph.js';

function readFontstacks(tag: number, glyphs: Array<StyleGlyph>, pbf: Protobuf) {
function readFontstacks(tag: number, glyphData: {glyphs: Array<StyleGlyph>, ascender?: number, descender?: number}, pbf: Protobuf) {
glyphData.glyphs = [];
if (tag === 1) {
pbf.readMessage(readFontstack, glyphs);
pbf.readMessage(readFontstack, glyphData);
}
}

function readFontstack(tag: number, glyphs: Array<StyleGlyph>, pbf: Protobuf) {
function readFontstack(tag: number, glyphData: {glyphs: Array<StyleGlyph>, ascender?: number, descender?: number}, pbf: Protobuf) {
if (tag === 3) {
const {id, bitmap, width, height, left, top, advance} = pbf.readMessage(readGlyph, {});
glyphs.push({
glyphData.glyphs.push({
id,
bitmap: new AlphaImage({
width: width + 2 * border,
height: height + 2 * border
}, bitmap),
metrics: {width, height, left, top, advance}
});
} else if (tag === 4) {
glyphData.ascender = pbf.readSVarint();
} else if (tag === 5) {
glyphData.descender = pbf.readSVarint();
}
}

Expand All @@ -37,8 +42,8 @@ function readGlyph(tag: number, glyph: Object, pbf: Protobuf) {
else if (tag === 7) glyph.advance = pbf.readVarint();
}

export default function (data: ArrayBuffer | Uint8Array): Array<StyleGlyph> {
return new Protobuf(data).readFields(readFontstacks, []);
export default function (data: ArrayBuffer | Uint8Array): {glyphs: Array<StyleGlyph>, ascender?: number, descender?: number} {
return new Protobuf(data).readFields(readFontstacks, {});
}

export const GLYPH_PBF_BORDER = border;
2 changes: 1 addition & 1 deletion src/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -1723,7 +1723,7 @@ class Style extends Evented {
setDependencies(this._symbolSourceCaches[params.source]);
}

getGlyphs(mapId: string, params: {stacks: {[_: string]: Array<number>}}, callback: Callback<{[_: string]: {[_: number]: ?StyleGlyph}}>) {
getGlyphs(mapId: string, params: {stacks: {[_: string]: Array<number>}}, callback: Callback<{[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}>) {
this.glyphManager.getGlyphs(params.stacks, callback);
}

Expand Down
Loading