Avoid try/catch, DOM and workers

This commit is contained in:
Andreas Hocevar
2020-03-21 15:49:01 +01:00
parent 6dcc54bfb8
commit 3f7f999db0
6 changed files with 85 additions and 100 deletions

View File

@@ -4,10 +4,13 @@
/**
* @typedef {Object} FontParameters
* @property {Array<string>} families
* @property {string} style
* @property {string} variant
* @property {string} weight
* @property {string} size
* @property {string} lineHeight
* @property {string} family
* @property {Array<string>} families
*/
@@ -65,42 +68,52 @@ export const CLASS_CONTROL = 'ol-control';
*/
export const CLASS_COLLAPSED = 'ol-collapsed';
/**
* From http://stackoverflow.com/questions/10135697/regex-to-parse-any-css-font
* @type {RegExp}
*/
const fontRegEx = new RegExp([
'^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)',
'(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)',
'(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)',
'(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?',
'(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))',
'(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))',
'?\\s*([-,\\"\\\'\\sa-z]+?)\\s*$'
].join(''), 'i');
const fontRegExMatchIndex = [
'style',
'variant',
'weight',
'size',
'lineHeight',
'family'
];
/**
* Get the list of font families from a font spec. Note that this doesn't work
* for font families that have commas in them.
* @param {string} fontSpec The CSS font property.
* @param {function(FontParameters):void} callback Called with the font families
* (or null if the input spec is invalid).
* @return {FontParameters} The font parameters (or null if the input spec is invalid).
*/
export const getFontParameters = (function() {
/**
* @type {CSSStyleDeclaration}
*/
let style;
/**
* @type {Object<string, FontParameters>}
*/
const cache = {};
return function(fontSpec, callback) {
if (!style) {
style = document.createElement('div').style;
export const getFontParameters = function(fontSpec) {
const match = fontSpec.match(fontRegEx);
if (!match) {
return null;
}
const style = /** @type {FontParameters} */ ({
lineHeight: 'normal',
size: '1.2em',
style: 'normal',
weight: 'normal',
variant: 'normal'
});
for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) {
const value = match[i + 1];
if (value !== undefined) {
style[fontRegExMatchIndex[i]] = value;
}
if (!(fontSpec in cache)) {
style.font = fontSpec;
const family = style.fontFamily;
if (!family) {
callback(null);
}
const families = family.split(/,\s?/);
cache[fontSpec] = {
families: families,
weight: style.fontWeight,
style: style.fontStyle,
lineHeight: style.lineHeight
};
style.font = '';
}
callback(cache[fontSpec]);
};
})();
}
style.families = style.family.split(/,\s?/);
return style;
};

View File

@@ -1,3 +1,5 @@
import {WORKER_OFFSCREEN_CANVAS} from './has.js';
/**
* @module ol/dom
*/
@@ -14,9 +16,9 @@
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
const canvas = opt_canvasPool && opt_canvasPool.length ?
opt_canvasPool.shift() :
'document' in self ?
document.createElement('canvas') :
new OffscreenCanvas(opt_width || 300, opt_height || 300);
WORKER_OFFSCREEN_CANVAS ?
new OffscreenCanvas(opt_width || 300, opt_height || 300) :
document.createElement('canvas');
if (opt_width) {
canvas.width = opt_width;
}

View File

@@ -37,27 +37,15 @@ export const MAC = ua.indexOf('macintosh') !== -1;
* @type {number}
* @api
*/
export const DEVICE_PIXEL_RATIO = (function() {
try {
return self.devicePixelRatio;
} catch (e) {
return window.devicePixelRatio || 1;
}
})();
export const DEVICE_PIXEL_RATIO = (typeof self !== 'undefined' ? self.devicePixelRatio : window.devicePixelRatio) || 1;
/**
* The execution context is a window.
* The execution context is a worker with OffscreenCanvas available.
* @const
* @type {boolean}
*/
export const WINDOW = (function() {
try {
return 'document' in self;
} catch (e) {
// ancient browsers don't have `self`
return true;
}
})();
export const WORKER_OFFSCREEN_CANVAS = typeof WorkerGlobalScope !== 'undefined' && typeof OffscreenCanvas !== 'undefined' &&
self instanceof WorkerGlobalScope; //eslint-disable-line
/**
* Image.prototype.decode() is supported.

View File

@@ -6,7 +6,7 @@ import {createCanvasContext2D} from '../dom.js';
import {clear} from '../obj.js';
import BaseObject from '../Object.js';
import EventTarget from '../events/Target.js';
import {WINDOW} from '../has.js';
import {WORKER_OFFSCREEN_CANVAS} from '../has.js';
import {toString} from '../transform.js';
@@ -266,7 +266,8 @@ export const registerFont = (function() {
}
}
function fontCallback(font) {
return function(fontSpec) {
const font = getFontParameters(fontSpec);
if (!font) {
return;
}
@@ -284,31 +285,6 @@ export const registerFont = (function() {
}
}
}
}
return function(fontSpec) {
if (WINDOW) {
getFontParameters(fontSpec, fontCallback);
} else if (self.postMessage) {
/** @type {any} */
const worker = self;
worker.postMessage({
action: 'getFontParameters',
font: fontSpec
});
worker.addEventListener('message', function handler(event) {
if (event.data.action === 'getFontParameters') {
worker.removeEventListener('message', handler);
const font = event.data.font;
fontCallback(font);
if (!textHeights[fontSpec]) {
const metrics = measureText(fontSpec, 'Žg');
const lineHeight = isNaN(font.lineHeight) ? 1.2 : Number(font.lineHeight);
textHeights[fontSpec] = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
}
}
});
}
};
})();
@@ -323,22 +299,29 @@ export const measureTextHeight = (function() {
*/
let div;
const heights = textHeights;
return function(font) {
let height = heights[font];
return function(fontSpec) {
let height = heights[fontSpec];
if (height == undefined) {
if (!div) {
div = document.createElement('div');
div.innerHTML = 'M';
div.style.margin = '0 !important';
div.style.padding = '0 !important';
div.style.position = 'absolute !important';
div.style.left = '-99999px !important';
if (WORKER_OFFSCREEN_CANVAS) {
const font = getFontParameters(fontSpec);
const metrics = measureText(fontSpec, 'Žg');
const lineHeight = isNaN(Number(font.lineHeight)) ? 1.2 : Number(font.lineHeight);
textHeights[fontSpec] = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
} else {
if (!div) {
div = document.createElement('div');
div.innerHTML = 'M';
div.style.margin = '0 !important';
div.style.padding = '0 !important';
div.style.position = 'absolute !important';
div.style.left = '-99999px !important';
}
div.style.font = fontSpec;
document.body.appendChild(div);
height = div.offsetHeight;
heights[fontSpec] = height;
document.body.removeChild(div);
}
div.style.font = font;
document.body.appendChild(div);
height = div.offsetHeight;
heights[font] = height;
document.body.removeChild(div);
}
return height;
};
@@ -369,7 +352,6 @@ export function measureTextWidth(font, text) {
return measureText(font, text).width;
}
/**
* Measure text width using a cache.
* @param {string} font The font.
@@ -484,13 +466,13 @@ let createTransformStringCanvas = null;
* @return {string} CSS transform.
*/
export function createTransformString(transform) {
if (WINDOW) {
if (WORKER_OFFSCREEN_CANVAS) {
return toString(transform);
} else {
if (!createTransformStringCanvas) {
createTransformStringCanvas = createCanvasContext2D(1, 1).canvas;
}
createTransformStringCanvas.style.transform = toString(transform);
return createTransformStringCanvas.style.transform;
} else {
return toString(transform);
}
}

View File

@@ -18,7 +18,7 @@ import {
} from '../../transform.js';
import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
import RBush from 'rbush/rbush.js';
import {WINDOW} from '../../has.js';
import {WORKER_OFFSCREEN_CANVAS} from '../../has.js';
/**
@@ -206,7 +206,7 @@ class Executor {
contextInstructions.push('lineJoin', strokeState.lineJoin);
contextInstructions.push('miterLimit', strokeState.miterLimit);
// eslint-disable-next-line
const Context = WINDOW ? CanvasRenderingContext2D : OffscreenCanvasRenderingContext2D;
const Context = WORKER_OFFSCREEN_CANVAS ? OffscreenCanvasRenderingContext2D : CanvasRenderingContext2D;
if (Context.prototype.setLineDash) {
contextInstructions.push('setLineDash', [strokeState.lineDash]);
contextInstructions.push('lineDashOffset', strokeState.lineDashOffset);