From d814dc230925a969bd1f2eb0772b67081b15509e Mon Sep 17 00:00:00 2001 From: David Manthey Date: Thu, 31 May 2018 16:17:36 -0400 Subject: [PATCH 1/2] Add a parameter to the text feature to limit what gets rendered. If specified, this skips rendering text that is outside of the current viewport by a specified distance. If you have very large text or text with offsets, this could fail to render the text. However, in well-formed cases, this can skip rendering text that would not be visible in any case. --- src/canvas/textFeature.js | 20 ++++++++++++++------ src/textFeature.js | 6 ++++++ tests/cases/textFeature.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/canvas/textFeature.js b/src/canvas/textFeature.js index e85930182c..dd0aeeda3a 100644 --- a/src/canvas/textFeature.js +++ b/src/canvas/textFeature.js @@ -99,11 +99,13 @@ var canvas_textFeature = function (arg) { this._renderOnCanvas = function (context2d, map) { var data = m_this.data(), posFunc = m_this.style.get('position'), + renderedZone = m_this.style.get('renderedZone')(data), textFunc = m_this.style.get('text'), mapRotation = map.rotation(), mapZoom = map.zoom(), - fontFromSubValues, text, pos, visible, color, blur, stroke, width, - rotation, rotateWithMap, scale, offset, + mapSize = map.size(), + fontFromSubValues, text, posArray, pos, visible, color, blur, stroke, + width, rotation, rotateWithMap, scale, offset, transform, lastTransform = util.mat3AsArray(); /* If any of the font styles other than `font` have values, then we need to @@ -117,7 +119,17 @@ var canvas_textFeature = function (arg) { }); /* Clear the canvas property buffer */ m_this._canvasProperty(); + posArray = m_this.featureGcsToDisplay(data.map(posFunc)); data.forEach(function (d, i) { + /* If the position is far enough outside of the map viewport, don't + * render it, even if the offset of size would be sufficient to make it + * appear in the viewport. */ + pos = posArray[i]; + if (renderedZone > 0 && ( + pos.x < -renderedZone || pos.x > mapSize.width + renderedZone || + pos.y < -renderedZone || pos.y > mapSize.height + renderedZone)) { + return; + } visible = m_this.style.get('visible')(d, i); if (!visible && visible !== undefined) { return; @@ -130,10 +142,6 @@ var canvas_textFeature = function (arg) { return; } m_this._canvasProperty(context2d, 'fillStyle', util.convertColorToRGBA(color)); - // TODO: get the position position without transform. If it is outside - // of the map to an extent that there is no chance of text showing, - // skip further processing. - pos = m_this.featureGcsToDisplay(posFunc(d, i)); text = textFunc(d, i); m_this._canvasProperty(context2d, 'font', m_this.getFontFromStyles(fontFromSubValues, d, i)); m_this._canvasProperty(context2d, 'textAlign', m_this.style.get('textAlign')(d, i) || 'center'); diff --git a/src/textFeature.js b/src/textFeature.js index 8b2db4a31d..750260619f 100644 --- a/src/textFeature.js +++ b/src/textFeature.js @@ -60,6 +60,12 @@ var feature = require('./feature'); * stroke color. May include opacity. * @property {geo.geoColor|function} [style.textStrokeWidth=0] Text stroke * width in pixels. + * @property {number|function} [style.renderedZone] If this is a positive + * number, text elements may not be rendered if their base position + * (before offset and font effects are applied) is more than this distance + * in pixels outside of the current viewport. If it is known that such + * text elements cannot affect the current viewport, setting this can + * speed up rendering. This is computed once for the whole feature. */ /** diff --git a/tests/cases/textFeature.js b/tests/cases/textFeature.js index a44c5d8b34..c56ed9eac0 100644 --- a/tests/cases/textFeature.js +++ b/tests/cases/textFeature.js @@ -198,5 +198,36 @@ describe('geo.textFeature', function () { expect(text.getFontFromStyles(true, text.data()[3], 3)).toBe('italic small-caps bold condensed 40px/60px serif'); expect(text.getFontFromStyles(true, text.data()[11], 11)).toBe('bold 16px/60px sans-serif'); }); + it('renderedZone', function () { + mockAnimationFrame(); + logCanvas2D(); + map = createMap(); + layer = map.createLayer('feature', {renderer: 'canvas'}); + text = layer.createFeature('text').data(testText); + text.draw(); + stepAnimationFrame(); + var count1 = $.extend({}, window._canvasLog.counts).fillText; + text.style({renderedZone: 0}); + text.draw(); + stepAnimationFrame(); + var count2 = $.extend({}, window._canvasLog.counts).fillText; + expect(count2 - count1).toBe(14); + text.style({renderedZone: 1}); + text.draw(); + stepAnimationFrame(); + var count3 = $.extend({}, window._canvasLog.counts).fillText; + expect(count3 - count2).toBe(6); + text.style({renderedZone: 20}); + text.draw(); + stepAnimationFrame(); + var count4 = $.extend({}, window._canvasLog.counts).fillText; + expect(count4 - count3).toBe(8); + text.style({renderedZone: 0}); + text.draw(); + stepAnimationFrame(); + var count5 = $.extend({}, window._canvasLog.counts).fillText; + expect(count5 - count4).toBe(14); + unmockAnimationFrame(); + }); }); }); From 95f10c9ec9f4276a97c50ec1825f423a162b6f60 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Wed, 27 Jun 2018 15:55:03 -0400 Subject: [PATCH 2/2] Change the name of renderedZone to renderThreshold. --- src/canvas/textFeature.js | 8 ++++---- src/textFeature.js | 2 +- tests/cases/textFeature.js | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/canvas/textFeature.js b/src/canvas/textFeature.js index dd0aeeda3a..620e6a893a 100644 --- a/src/canvas/textFeature.js +++ b/src/canvas/textFeature.js @@ -99,7 +99,7 @@ var canvas_textFeature = function (arg) { this._renderOnCanvas = function (context2d, map) { var data = m_this.data(), posFunc = m_this.style.get('position'), - renderedZone = m_this.style.get('renderedZone')(data), + renderThreshold = m_this.style.get('renderThreshold')(data), textFunc = m_this.style.get('text'), mapRotation = map.rotation(), mapZoom = map.zoom(), @@ -125,9 +125,9 @@ var canvas_textFeature = function (arg) { * render it, even if the offset of size would be sufficient to make it * appear in the viewport. */ pos = posArray[i]; - if (renderedZone > 0 && ( - pos.x < -renderedZone || pos.x > mapSize.width + renderedZone || - pos.y < -renderedZone || pos.y > mapSize.height + renderedZone)) { + if (renderThreshold > 0 && ( + pos.x < -renderThreshold || pos.x > mapSize.width + renderThreshold || + pos.y < -renderThreshold || pos.y > mapSize.height + renderThreshold)) { return; } visible = m_this.style.get('visible')(d, i); diff --git a/src/textFeature.js b/src/textFeature.js index 750260619f..cddd10cda2 100644 --- a/src/textFeature.js +++ b/src/textFeature.js @@ -60,7 +60,7 @@ var feature = require('./feature'); * stroke color. May include opacity. * @property {geo.geoColor|function} [style.textStrokeWidth=0] Text stroke * width in pixels. - * @property {number|function} [style.renderedZone] If this is a positive + * @property {number|function} [style.renderThreshold] If this is a positive * number, text elements may not be rendered if their base position * (before offset and font effects are applied) is more than this distance * in pixels outside of the current viewport. If it is known that such diff --git a/tests/cases/textFeature.js b/tests/cases/textFeature.js index c56ed9eac0..b9cf4afdef 100644 --- a/tests/cases/textFeature.js +++ b/tests/cases/textFeature.js @@ -198,7 +198,7 @@ describe('geo.textFeature', function () { expect(text.getFontFromStyles(true, text.data()[3], 3)).toBe('italic small-caps bold condensed 40px/60px serif'); expect(text.getFontFromStyles(true, text.data()[11], 11)).toBe('bold 16px/60px sans-serif'); }); - it('renderedZone', function () { + it('renderThreshold', function () { mockAnimationFrame(); logCanvas2D(); map = createMap(); @@ -207,22 +207,22 @@ describe('geo.textFeature', function () { text.draw(); stepAnimationFrame(); var count1 = $.extend({}, window._canvasLog.counts).fillText; - text.style({renderedZone: 0}); + text.style({renderThreshold: 0}); text.draw(); stepAnimationFrame(); var count2 = $.extend({}, window._canvasLog.counts).fillText; expect(count2 - count1).toBe(14); - text.style({renderedZone: 1}); + text.style({renderThreshold: 1}); text.draw(); stepAnimationFrame(); var count3 = $.extend({}, window._canvasLog.counts).fillText; expect(count3 - count2).toBe(6); - text.style({renderedZone: 20}); + text.style({renderThreshold: 20}); text.draw(); stepAnimationFrame(); var count4 = $.extend({}, window._canvasLog.counts).fillText; expect(count4 - count3).toBe(8); - text.style({renderedZone: 0}); + text.style({renderThreshold: 0}); text.draw(); stepAnimationFrame(); var count5 = $.extend({}, window._canvasLog.counts).fillText;