From 04e9a15242b558eacd4b428f0b72c01a036b733f Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Thu, 14 Sep 2017 16:50:36 -0700 Subject: [PATCH 1/4] Flip webgl-based canvas pixels before copying them to intermediary buffer --- src/source/canvas_source.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/source/canvas_source.js b/src/source/canvas_source.js index ccb0ea890f6..8bec5480675 100644 --- a/src/source/canvas_source.js +++ b/src/source/canvas_source.js @@ -127,7 +127,11 @@ class CanvasSource extends ImageSource { gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, data); if (!this.secondaryContext) this.secondaryContext = window.document.createElement('canvas').getContext('2d'); const imageData = this.secondaryContext.createImageData(this.width, this.height); - imageData.data.set(data); + const flipped = new Uint8Array(this.width * this.height * 4); + for (let i = this.height - 1, j = 0; i >= 0; i--, j++) { + flipped.set(data.slice(i * this.width * 4, (i + 1) * this.width * 4), j * this.width * 4); + } + imageData.data.set(flipped); return imageData; } } From f53eaadceb341443ed5743e45ef56618462d7a2d Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Fri, 15 Sep 2017 11:39:47 -0700 Subject: [PATCH 2/4] Use subarray instead of slice for more performant Uint8Array copying --- src/source/canvas_source.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/canvas_source.js b/src/source/canvas_source.js index 8bec5480675..2abc00719e1 100644 --- a/src/source/canvas_source.js +++ b/src/source/canvas_source.js @@ -129,7 +129,7 @@ class CanvasSource extends ImageSource { const imageData = this.secondaryContext.createImageData(this.width, this.height); const flipped = new Uint8Array(this.width * this.height * 4); for (let i = this.height - 1, j = 0; i >= 0; i--, j++) { - flipped.set(data.slice(i * this.width * 4, (i + 1) * this.width * 4), j * this.width * 4); + flipped.set(data.subarray(i * this.width * 4, (i + 1) * this.width * 4), j * this.width * 4); } imageData.data.set(flipped); return imageData; From 0be38edab0e4ce19ea322916d71c9e9456769e45 Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Fri, 15 Sep 2017 14:14:32 -0700 Subject: [PATCH 3/4] Don't reread canvas if not animated or resized --- src/source/canvas_source.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/source/canvas_source.js b/src/source/canvas_source.js index 2abc00719e1..33676d0f7ff 100644 --- a/src/source/canvas_source.js +++ b/src/source/canvas_source.js @@ -45,6 +45,7 @@ class CanvasSource extends ImageSource { secondaryContext: ?CanvasRenderingContext2D; width: number; height: number; + canvasData: ?ImageData; play: () => void; pause: () => void; @@ -114,25 +115,29 @@ class CanvasSource extends ImageSource { */ // setCoordinates inherited from ImageSource - readCanvas() { + readCanvas(resize: boolean) { // We *should* be able to use a pure HTMLCanvasElement in // texImage2D/texSubImage2D (in ImageSource#_prepareImage), but for // some reason this breaks the map on certain GPUs (see #4262). if (this.context instanceof CanvasRenderingContext2D) { - return this.context.getImageData(0, 0, this.width, this.height); + this.canvasData = this.context.getImageData(0, 0, this.width, this.height); } else if (this.context instanceof WebGLRenderingContext) { const gl = this.context; const data = new Uint8Array(this.width * this.height * 4); gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, data); + if (!this.secondaryContext) this.secondaryContext = window.document.createElement('canvas').getContext('2d'); - const imageData = this.secondaryContext.createImageData(this.width, this.height); + if (!this.canvasData || resize) { + this.canvasData = this.secondaryContext.createImageData(this.width, this.height); + } + + // WebGL reads pixels bottom to top, but for our ImageData object we need top to bottom: flip here const flipped = new Uint8Array(this.width * this.height * 4); for (let i = this.height - 1, j = 0; i >= 0; i--, j++) { flipped.set(data.subarray(i * this.width * 4, (i + 1) * this.width * 4), j * this.width * 4); } - imageData.data.set(flipped); - return imageData; + this.canvasData.data.set(flipped); } } @@ -149,12 +154,17 @@ class CanvasSource extends ImageSource { if (this._hasInvalidDimensions()) return; if (Object.keys(this.tiles).length === 0) return; // not enough data for current position - const canvasData = this.readCanvas(); - if (!canvasData) { + + const reread = this.animate || !this.canvasData || resize; + if (reread) { + this.readCanvas(resize); + } + + if (!this.canvasData) { this.fire('error', new Error('Could not read canvas data.')); return; } - this._prepareImage(this.map.painter.gl, canvasData, resize); + this._prepareImage(this.map.painter.gl, this.canvasData, resize); } serialize(): Object { From e7a4f1c6fe2a87d953d2cb4e5e02e85b2a85d752 Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Mon, 18 Sep 2017 11:13:25 -0700 Subject: [PATCH 4/4] Eliminate temporary array for flipping webgl canvases --- src/source/canvas_source.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/source/canvas_source.js b/src/source/canvas_source.js index 33676d0f7ff..e73ae5374d0 100644 --- a/src/source/canvas_source.js +++ b/src/source/canvas_source.js @@ -133,11 +133,9 @@ class CanvasSource extends ImageSource { } // WebGL reads pixels bottom to top, but for our ImageData object we need top to bottom: flip here - const flipped = new Uint8Array(this.width * this.height * 4); for (let i = this.height - 1, j = 0; i >= 0; i--, j++) { - flipped.set(data.subarray(i * this.width * 4, (i + 1) * this.width * 4), j * this.width * 4); + this.canvasData.data.set(data.subarray(i * this.width * 4, (i + 1) * this.width * 4), j * this.width * 4); } - this.canvasData.data.set(flipped); } }