diff --git a/NAMESPACE b/NAMESPACE index 1be75dec..60690e2a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -68,6 +68,7 @@ importFrom(grDevices, dev.list, dev.new, dev.off, + dev.size, extendrange, gray.colors, hcl, diff --git a/NEWS.md b/NEWS.md index 70257988..7f61fbc3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -313,7 +313,7 @@ not 'at'`. The free-facet code path listed the eligible types by name, so ### Internals -- Performance improvemnts. (#723 @grantmcdermott) +- Performance improvements. (#723, #724 @grantmcdermott) ## v0.7.0 diff --git a/R/legend.R b/R/legend.R index 0dff41d1..9abc71f3 100644 --- a/R/legend.R +++ b/R/legend.R @@ -258,8 +258,14 @@ tinylegend = function(legend_env) { legend_env$args[["text.width"]] = NULL } - # Re-measure legend dimensions (device size may have changed on resize) - legend_env$dims = measure_fake_legend(legend_env) + # Re-measure legend dimensions, but only when the device has actually + # changed. draw_legend() already measured on this device during setup, so on + # the initial draw the result is still current and a measuring pass costs + # about as much as drawing the legend itself. A resize, or a replay onto a + # different device, fails the key comparison and forces the re-measure. + if (is.null(legend_env$dims) || !identical(legend_env$dims_dev, legend_dev_key())) { + legend_env$dims = measure_fake_legend(legend_env) + } # Calculate and apply soma (outer margin adjustment based on legend size) # When soma_target is set (multi-legend), use it directly so all legends @@ -362,6 +368,17 @@ tinylegend = function(legend_env) { # Measure legend dimensions using a fake (non-plotted) legend +# Identity of the device a legend measurement belongs to. Size alone is not +# enough: replaying or copying a display list onto a same-sized device with a +# different backend (dev.copy(), dev.print(), an IDE's plot export) yields +# different text metrics, so the device itself has to be part of the key. +# dev.cur() is a named integer, so this captures the backend as well as the +# device number. +legend_dev_key = function() { + list(dev = dev.cur(), size = dev.size()) +} + + measure_fake_legend = function(legend_env) { fklgnd.args = modifyList( legend_env$args, @@ -389,6 +406,10 @@ measure_fake_legend = function(legend_env) { } } + # Record which device this measurement was taken on, so callers can tell + # whether a cached result is still valid (see tinylegend()). + legend_env$dims_dev = legend_dev_key() + do.call("legend", fklgnd.args) } @@ -929,7 +950,7 @@ build_legend_env = function( #' with a legend in the margin. #' #' @importFrom graphics grconvertX grconvertY rasterImage strheight strwidth xinch -#' @importFrom grDevices as.raster recordGraphics +#' @importFrom grDevices as.raster dev.size recordGraphics #' @importFrom utils modifyList #' #' @examples