-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Improve heatmap rendering performance when zsmooth
is false
#6574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
ab756a1
e19f15d
7b8106f
7ea2082
85bc5e4
0510f2f
1e9d6bd
6fb171f
c25484b
4eaf62e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
- Improve heatmap rendering performance when zsmooth is false [[#6574](https://github.com/plotly/plotly.js/pull/6574)] | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,50 @@ var LINE_SPACING = alignmentConstants.LINE_SPACING; | |
|
||
var labelClass = 'heatmap-label'; | ||
|
||
// Pixelated image rendering | ||
// The actual declaration is prepended with fallbacks for older browsers. | ||
// https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering | ||
// https://caniuse.com/?search=image-rendering | ||
var pixelatedImageCSS = [ | ||
'image-rendering: optimizeSpeed', | ||
'image-rendering: -moz-crisp-edges', | ||
'image-rendering: -o-crisp-edges', | ||
'image-rendering: -webkit-optimize-contrast', | ||
'image-rendering: optimize-contrast', | ||
'image-rendering: crisp-edges', | ||
'image-rendering: pixelated' | ||
]; | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var _supportsPixelated = null; | ||
function supportsPixelatedImage() { | ||
if(_supportsPixelated !== null) { // only run the feature detection once | ||
return _supportsPixelated; | ||
} | ||
if(Lib.isIE()) { | ||
// `-ms-interpolation-mode` works only with <img> not with SVG <image> | ||
_supportsPixelated = false; | ||
} else { | ||
var declarations = Array.from(pixelatedImageCSS).reverse(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please elaborate why we need to reverse the list here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
var supports = window.CSS && window.CSS.supports || window.supportsCSS; | ||
if(typeof supports === 'function') { | ||
_supportsPixelated = declarations.some(function(d) { | ||
return supports.apply(null, d.split(': ')); | ||
}); | ||
} else { | ||
var image3 = Drawing.tester.append('image'); | ||
var cStyles = window.getComputedStyle(image3.node()); | ||
image3.attr('style', pixelatedImageCSS.join('; ') + ';'); | ||
_supportsPixelated = declarations.some(function(d) { | ||
var value = d.split(': ')[1]; | ||
return cStyles.imageRendering === value || | ||
cStyles.imageRendering === value.toLowerCase(); | ||
}); | ||
image3.remove(); | ||
} | ||
} | ||
return _supportsPixelated; | ||
} | ||
|
||
function selectLabels(plotGroup) { | ||
return plotGroup.selectAll('g.' + labelClass); | ||
} | ||
|
@@ -109,11 +153,18 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
y = cd0.yfill; | ||
} | ||
|
||
var drawingMethod = 'default'; | ||
if(zsmooth) { | ||
drawingMethod = zsmooth === 'best' ? 'smooth' : 'fast'; | ||
} else if(trace._islinear && xGap === 0 && yGap === 0 && supportsPixelatedImage()) { | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
drawingMethod = 'fast'; | ||
} | ||
|
||
// make an image that goes at most half a screen off either side, to keep | ||
// time reasonable when you zoom in. if zsmooth is true/fast, don't worry | ||
// time reasonable when you zoom in. if drawingMethod is fast, don't worry | ||
// about this, because zooming doesn't increase number of pixels | ||
// if zsmooth is best, don't include anything off screen because it takes too long | ||
if(zsmooth !== 'fast') { | ||
if(drawingMethod !== 'fast') { | ||
var extra = zsmooth === 'best' ? 0 : 0.5; | ||
left = Math.max(-extra * xa._length, left); | ||
right = Math.min((1 + extra) * xa._length, right); | ||
|
@@ -140,7 +191,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
// generate image data | ||
|
||
var canvasW, canvasH; | ||
if(zsmooth === 'fast') { | ||
if(drawingMethod === 'fast') { | ||
canvasW = n; | ||
canvasH = m; | ||
} else { | ||
|
@@ -158,7 +209,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
// map brick boundaries to image pixels | ||
var xpx, | ||
ypx; | ||
if(zsmooth === 'fast') { | ||
if(drawingMethod === 'fast') { | ||
xpx = xrev ? | ||
function(index) { return n - 1 - index; } : | ||
Lib.identity; | ||
|
@@ -235,7 +286,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
return setColor(z00 + xinterp.frac * dx + yinterp.frac * (dy + xinterp.frac * dxy)); | ||
} | ||
|
||
if(zsmooth) { // best or fast, works fastest with imageData | ||
if(drawingMethod !== 'default') { // works fastest with imageData | ||
var pxIndex = 0; | ||
var pixels; | ||
|
||
|
@@ -245,7 +296,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
pixels = new Array(canvasW * canvasH * 4); | ||
} | ||
|
||
if(zsmooth === 'best') { | ||
if(drawingMethod === 'smooth') { // zsmooth="best" | ||
var xForPx = xc || x; | ||
var yForPx = yc || y; | ||
var xPixArray = new Array(xForPx.length); | ||
|
@@ -273,7 +324,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
putColor(pixels, pxIndex, c); | ||
} | ||
} | ||
} else { // zsmooth = fast | ||
} else { // drawingMethod = "fast" (zsmooth = "fast"|false) | ||
for(j = 0; j < m; j++) { | ||
row = z[j]; | ||
yb = ypx(j); | ||
|
@@ -297,7 +348,8 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
} | ||
|
||
context.putImageData(imageData, 0, 0); | ||
} else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect | ||
} else { // rawingMethod = "default" (zsmooth = false) | ||
// filling potentially large bricks works fastest with fillRect | ||
// gaps do not need to be exact integers, but if they *are* we will get | ||
// cleaner edges by rounding at least one edge | ||
var xGapLeft = Math.floor(xGap / 2); | ||
|
@@ -353,6 +405,10 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { | |
'xlink:href': canvas.toDataURL('image/png') | ||
}); | ||
|
||
if(drawingMethod === 'fast' && !zsmooth) { | ||
image3.attr('style', pixelatedImageCSS.join('; ') + ';'); | ||
} | ||
|
||
removeLabels(plotGroup); | ||
|
||
var texttemplate = trace.texttemplate; | ||
|
Uh oh!
There was an error while loading. Please reload this page.