From e41634316f24edc9658905d905936ce62e252269 Mon Sep 17 00:00:00 2001 From: Justin Novosad Date: Wed, 19 Jan 2022 13:48:14 -0500 Subject: [PATCH 1/4] Add willReadFrequently to 2d canvas contexts, where appropriate. This chang add the willReadFrequently:true 2d context creation attribute to call sites where canvases will be read from (i.e getImagData or toDataURL). This is a performance hint that tells the browser to optimize for readbacks. See: https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-will-read-frequently The new API will soon be launched in chromium-based web browsers. The objective of this change is to optimize performance and prevent possible performance regressions when this new feature is launched by major web browser in the near future. --- src/components/images/draw.js | 2 +- src/plots/gl2d/scene2d.js | 2 +- src/plots/gl3d/scene.js | 2 +- src/snapshot/svgtoimg.js | 2 +- src/traces/image/hover.js | 2 +- src/traces/image/plot.js | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/images/draw.js b/src/components/images/draw.js index b08cdebbd09..cd6dfeb698b 100644 --- a/src/components/images/draw.js +++ b/src/components/images/draw.js @@ -89,7 +89,7 @@ module.exports = function draw(gd) { canvas.width = this.width; canvas.height = this.height; - var ctx = canvas.getContext('2d'); + var ctx = canvas.getContext('2d', {willReadFrequently: true}); ctx.drawImage(this, 0, 0); var dataURL = canvas.toDataURL('image/png'); diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 9df5db9618b..d988b9bbe12 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -211,7 +211,7 @@ proto.toImage = function(format) { canvas.width = w; canvas.height = h; - var context = canvas.getContext('2d'); + var context = canvas.getContext('2d', {willReadFrequently: true}); var imageData = context.createImageData(w, h); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 6bbae1c4aa8..78be9c070d0 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -1084,7 +1084,7 @@ proto.toImage = function(format) { var canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; - var context = canvas.getContext('2d'); + var context = canvas.getContext('2d', {willReadFrequently: true}); var imageData = context.createImageData(w, h); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); diff --git a/src/snapshot/svgtoimg.js b/src/snapshot/svgtoimg.js index 51968b136f6..ee8ad39e695 100644 --- a/src/snapshot/svgtoimg.js +++ b/src/snapshot/svgtoimg.js @@ -33,7 +33,7 @@ function svgToImg(opts) { var w1 = scale * w0; var h1 = scale * h0; - var ctx = canvas.getContext('2d'); + var ctx = canvas.getContext('2d', {willReadFrequently: true}); var img = new Image(); var svgBlob, url; diff --git a/src/traces/image/hover.js b/src/traces/image/hover.js index 80977b7e76b..9b4e132d3dc 100644 --- a/src/traces/image/hover.js +++ b/src/traces/image/hover.js @@ -24,7 +24,7 @@ module.exports = function hoverPoints(pointData, xval, yval) { if(trace._hasZ) { pixel = cd0.z[ny][nx]; } else if(trace._hasSource) { - pixel = trace._canvas.el.getContext('2d').getImageData(nx, ny, 1, 1).data; + pixel = trace._canvas.el.getContext('2d', {willReadFrequently: true}).getImageData(nx, ny, 1, 1).data; } // return early if pixel is undefined diff --git a/src/traces/image/plot.js b/src/traces/image/plot.js index e182df8db81..0e6845438e8 100644 --- a/src/traces/image/plot.js +++ b/src/traces/image/plot.js @@ -92,7 +92,7 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { var canvas = document.createElement('canvas'); canvas.width = imageWidth; canvas.height = imageHeight; - var context = canvas.getContext('2d'); + var context = canvas.getContext('2d', {willReadFrequently: true}); var ipx = function(i) {return Lib.constrain(Math.round(xa.c2p(x0 + i * dx) - left), 0, imageWidth);}; var jpx = function(j) {return Lib.constrain(Math.round(ya.c2p(y0 + j * dy) - top), 0, imageHeight);}; @@ -167,7 +167,7 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { var canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; - var context = canvas.getContext('2d'); + var context = canvas.getContext('2d', {willReadFrequently: true}); trace._image = trace._image || new Image(); var image = trace._image; @@ -192,7 +192,7 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { if(realImage) { href = trace.source; } else { - var context = trace._canvas.el.getContext('2d'); + var context = trace._canvas.el.getContext('2d', {willReadFrequently:true}); var data = context.getImageData(0, 0, w, h).data; canvas = drawMagnifiedPixelsOnCanvas(function(i, j) { var index = 4 * (j * w + i); From d2918c2237a1ed09dec3440453d3898b6f69cb92 Mon Sep 17 00:00:00 2001 From: Justin Novosad Date: Wed, 19 Jan 2022 14:07:41 -0500 Subject: [PATCH 2/4] add draftlog --- draftlogs/6084_change.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/6084_change.md diff --git a/draftlogs/6084_change.md b/draftlogs/6084_change.md new file mode 100644 index 00000000000..1609607b7c9 --- /dev/null +++ b/draftlogs/6084_change.md @@ -0,0 +1 @@ + - Use the willReadFrequently 2d context creation attribute to optimize readback performance [[#6084](https://github.com/plotly/plotly.js/pull/6084)] From ed5e8715035b335d79180e265ff662989018c65f Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi <33888540+archmoj@users.noreply.github.com> Date: Wed, 23 Mar 2022 19:27:45 -0400 Subject: [PATCH 3/4] Update src/traces/image/plot.js --- src/traces/image/plot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/image/plot.js b/src/traces/image/plot.js index 0e6845438e8..a8a9e182fc7 100644 --- a/src/traces/image/plot.js +++ b/src/traces/image/plot.js @@ -192,7 +192,7 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { if(realImage) { href = trace.source; } else { - var context = trace._canvas.el.getContext('2d', {willReadFrequently:true}); + var context = trace._canvas.el.getContext('2d', {willReadFrequently: true}); var data = context.getImageData(0, 0, w, h).data; canvas = drawMagnifiedPixelsOnCanvas(function(i, j) { var index = 4 * (j * w + i); From 26920a068c1ba7a801980aac7daee6a7c96efd00 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi <33888540+archmoj@users.noreply.github.com> Date: Wed, 23 Mar 2022 19:30:12 -0400 Subject: [PATCH 4/4] Update draftlogs/6084_change.md --- draftlogs/6084_change.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/draftlogs/6084_change.md b/draftlogs/6084_change.md index 1609607b7c9..2172e2e4172 100644 --- a/draftlogs/6084_change.md +++ b/draftlogs/6084_change.md @@ -1 +1,2 @@ - - Use the willReadFrequently 2d context creation attribute to optimize readback performance [[#6084](https://github.com/plotly/plotly.js/pull/6084)] + - Use the willReadFrequently 2d context creation attribute to optimize readback performance [[#6084](https://github.com/plotly/plotly.js/pull/6084)], + with thanks to @junov for the contribution!