From 3557271e5a5292abff52146b770cb57dcc755d18 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2019 14:06:14 +0200 Subject: [PATCH 1/4] Use Image.prototype.decode also in Safari --- src/ol/Image.js | 15 +++++++++------ src/ol/has.js | 6 ++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ol/Image.js b/src/ol/Image.js index ec5385f772..bf4601d6fb 100644 --- a/src/ol/Image.js +++ b/src/ol/Image.js @@ -6,7 +6,7 @@ import ImageState from './ImageState.js'; import {listenOnce, unlistenByKey} from './events.js'; import EventType from './events/EventType.js'; import {getHeight} from './extent.js'; -import {SAFARI} from './has.js'; +import {IMAGE_DECODE} from './has.js'; /** @@ -159,10 +159,7 @@ class ImageWrapper extends ImageBase { export function listenImage(image, loadHandler, errorHandler) { const img = /** @type {HTMLImageElement} */ (image); - // The decode function is supported by Safari but not when the image is a svg file. - // FIXME: remove `!SAFARI` in the test when this bug is fixed upstream. - // See: https://bugs.webkit.org/show_bug.cgi?id=198527 - if (!SAFARI && img.decode) { + if (IMAGE_DECODE) { const promise = img.decode(); let listening = true; const unlisten = function() { @@ -174,7 +171,13 @@ export function listenImage(image, loadHandler, errorHandler) { } }).catch(function(error) { if (listening) { - errorHandler(); + // FIXME: Unconditionally call errorHandler() when this bug is fixed upstream: + // https://bugs.webkit.org/show_bug.cgi?id=198527 + if (error.name === 'EncodingError' && error.message === 'Invalid image type.') { + loadHandler(); + } else { + errorHandler(); + } } }); return unlisten; diff --git a/src/ol/has.js b/src/ol/has.js index 84e7fca05f..c71bc12d1c 100644 --- a/src/ol/has.js +++ b/src/ol/has.js @@ -38,3 +38,9 @@ export const MAC = ua.indexOf('macintosh') !== -1; * @api */ export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1; + +/** + * Image.prototype.decode() is supported. + * @type {boolean} + */ +export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode; From f864c05070f3228df42ff37770b8f1800f34e591 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2019 14:07:22 +0200 Subject: [PATCH 2/4] Less frame budget restrictions --- src/ol/PluggableMap.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index fe088e7cd9..abbc34a575 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -22,7 +22,7 @@ import {listen, unlistenByKey, unlisten} from './events.js'; import EventType from './events/EventType.js'; import {createEmpty, clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js'; import {TRUE} from './functions.js'; -import {DEVICE_PIXEL_RATIO} from './has.js'; +import {DEVICE_PIXEL_RATIO, IMAGE_DECODE} from './has.js'; import LayerGroup from './layer/Group.js'; import {hasArea} from './size.js'; import {DROP} from './structs/PriorityQueue.js'; @@ -967,7 +967,7 @@ class PluggableMap extends BaseObject { if (frameState) { const hints = frameState.viewHints; if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) { - const lowOnFrameBudget = Date.now() - frameState.time > 8; + const lowOnFrameBudget = !IMAGE_DECODE && Date.now() - frameState.time > 8; maxTotalLoading = lowOnFrameBudget ? 0 : 8; maxNewLoads = lowOnFrameBudget ? 0 : 2; } From 4d2fa476a3872b9b42bf32ae8497cdbed9cfafa7 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2019 14:22:56 +0200 Subject: [PATCH 3/4] Simpler z sort --- src/ol/renderer/canvas/TileLayer.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index b2a5509804..5480262ec6 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -264,15 +264,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { this.renderedTiles.length = 0; /** @type {Array} */ let zs = Object.keys(tilesToDrawByZ).map(Number); - zs.sort(function(a, b) { - if (a === z) { - return 1; - } else if (b === z) { - return -1; - } else { - return a > b ? 1 : a < b ? -1 : 0; - } - }); + zs.sort(numberSafeCompareFunction); let clips, clipZs, currentClip; if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) { From 9cd35d67a99c5243541c46fe95385d0c1aa858ac Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2019 14:23:32 +0200 Subject: [PATCH 4/4] Fix clipping and clearing --- src/ol/renderer/canvas/TileLayer.js | 50 ++++++++++++++++------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index 5480262ec6..55df226224 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -7,6 +7,7 @@ import TileState from '../../TileState.js'; import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js'; import CanvasLayerRenderer from './Layer.js'; import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js'; +import {numberSafeCompareFunction} from '../../array.js'; /** * @classdesc @@ -303,32 +304,37 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { const h = nextY - y; const transition = z === currentZ; - if (clips && (!transition || tile.getAlpha(getUid(this), frameState.time) === 1)) { - // Clip mask for regions in this tile that already filled by a higher z tile - context.save(); - currentClip = [x, y, x + w, y, x + w, y + h, x, y + h]; - for (let i = 0, ii = clips.length; i < ii; ++i) { - if (z !== currentZ && currentZ < clipZs[i]) { - const clip = clips[i]; - context.beginPath(); - // counter-clockwise (outer ring) for current tile - context.moveTo(currentClip[0], currentClip[1]); - context.lineTo(currentClip[2], currentClip[3]); - context.lineTo(currentClip[4], currentClip[5]); - context.lineTo(currentClip[6], currentClip[7]); - // clockwise (inner ring) for higher z tile - context.moveTo(clip[6], clip[7]); - context.lineTo(clip[4], clip[5]); - context.lineTo(clip[2], clip[3]); - context.lineTo(clip[0], clip[1]); - context.clip(); + const inTransition = transition && tile.getAlpha(getUid(this), frameState.time) !== 1; + if (!inTransition) { + if (clips) { + // Clip mask for regions in this tile that already filled by a higher z tile + context.save(); + currentClip = [x, y, x + w, y, x + w, y + h, x, y + h]; + for (let i = 0, ii = clips.length; i < ii; ++i) { + if (z !== currentZ && currentZ < clipZs[i]) { + const clip = clips[i]; + context.beginPath(); + // counter-clockwise (outer ring) for current tile + context.moveTo(currentClip[0], currentClip[1]); + context.lineTo(currentClip[2], currentClip[3]); + context.lineTo(currentClip[4], currentClip[5]); + context.lineTo(currentClip[6], currentClip[7]); + // clockwise (inner ring) for higher z tile + context.moveTo(clip[6], clip[7]); + context.lineTo(clip[4], clip[5]); + context.lineTo(clip[2], clip[3]); + context.lineTo(clip[0], clip[1]); + context.clip(); + } } + clips.push(currentClip); + clipZs.push(currentZ); + } else { + context.clearRect(x, y, w, h); } - clips.push(currentClip); - clipZs.push(currentZ); } this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity); - if (clips) { + if (clips && !inTransition) { context.restore(); } this.renderedTiles.push(tile);