diff --git a/DESCRIPTION b/DESCRIPTION index e862328..d82dd48 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: gridpattern Type: Package Title: 'grid' Pattern Grobs -Version: 1.2.0-4 +Version: 1.2.0-5 Authors@R: c(person("Mike", "FC", role = "aut", comment = "Code/docs adapted from ggpattern"), person("Trevor L.", "Davis", role=c("aut", "cre"), email="trevor.l.davis@gmail.com", comment = c(ORCID = "0000-0001-6341-4639")), diff --git a/NAMESPACE b/NAMESPACE index 734b99c..ae75e1f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -55,6 +55,7 @@ import(grid) importFrom(glue,glue) importFrom(grDevices,col2rgb) importFrom(grDevices,dev.capabilities) +importFrom(grDevices,dev.capture) importFrom(grDevices,dev.off) importFrom(grDevices,png) importFrom(grDevices,rgb) diff --git a/NEWS.md b/NEWS.md index 672396c..dbaaf2a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -34,6 +34,19 @@ New Features * For completeness there is now a `grid.pattern_none()` corresponding to the previously supported "none" pattern which draws nothing. +Bug fixes and minor improvements +-------------------------------- + +* `clippingPathGrob()` can now more efficiently compute a `rasterGrob()` approximation + via `ragg::agg_capture()` and for `png_device` functions that support + the clipping path feature such as`png(type = "cairo")`(#74). +* `alphaMaskGrob()` can now more efficiently compute a `rasterGrob()` approximation + for `png_device` functions that support + the alpha mask feature such as`png(type = "cairo")`(#75). +* `alphaMaskGrob()` and `clippingPathGrob()` now + switch back to the previously open graphics device if + they open and close any new graphics devices. + gridpattern v1.1.1 ================== diff --git a/R/alphaMaskGrob.R b/R/alphaMaskGrob.R index 008bea5..496c90e 100644 --- a/R/alphaMaskGrob.R +++ b/R/alphaMaskGrob.R @@ -48,14 +48,14 @@ #' } #' @export alphaMaskGrob <- function(maskee, masker, - use_R4.1_masks = getOption("ggpattern_use_R4.1_masks", - getOption("ggpattern_use_R4.1_features")), - png_device = NULL, res = getOption("ggpattern_res", 72), - name = NULL, gp = gpar(), vp = NULL) { + use_R4.1_masks = getOption("ggpattern_use_R4.1_masks", + getOption("ggpattern_use_R4.1_features")), + png_device = NULL, res = getOption("ggpattern_res", 72), + name = NULL, gp = gpar(), vp = NULL) { gTree(maskee = maskee, masker = masker, use_R4.1_masks = use_R4.1_masks, res = res, png_device = png_device, - name=name, gp=gp, vp=vp, cl="alpha_mask") + name = name, gp = gp, vp = vp, cl = "alpha_mask") } # Avoid R CMD check WARNING on R 4.0 which lacks `mask` argument @@ -82,52 +82,90 @@ makeContent.alpha_mask <- function(x) { getRversion() >= '4.1.0' && requireNamespace("ragg", quietly = TRUE) && packageVersion("ragg") >= '1.2.0') { - grob <- gridpattern_mask_agg_capture(x) + grob <- gridpattern_mask_agg_capture(x$maskee, x$masker, x$res) } else { - grob <- gridpattern_mask_raster(x) + png_device <- x$png_device %||% default_png_device() + if (device_supports_masks(png_device)) { + grob <- gridpattern_mask_raster_straight(x$maskee, x$masker, x$res, png_device) + } else { + grob <- gridpattern_mask_raster_manual(x$maskee, x$masker, x$res, png_device) + } } gl <- gList(grob) setChildren(x, gl) } -gridpattern_mask_agg_capture <- function(x) { - height <- x$res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) - width <- x$res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) +device_supports_masks <- function(png_device) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + png_file <- tempfile(fileext = ".png") + on.exit(unlink(png_file), add = TRUE) + png_device(png_file) + value <- guess_has_R4.1_features("masks") + dev.off() + value +} + +gridpattern_mask_agg_capture <- function(maskee, masker, res) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + height <- res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) + width <- res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) - f_masked <- ragg::agg_capture(height = height, width = width, res = x$res, bg = "transparent") - grob <- alphaMaskGrob(x$maskee, x$masker, use_R4.1_masks = TRUE) + ragg::agg_capture(height = height, width = width, res = res, bg = "transparent") + grob <- alphaMaskGrob(maskee, masker, use_R4.1_masks = TRUE) grid.draw(grob) - raster_masked <- f_masked(native = FALSE) + raster_masked <- dev.capture(native = FALSE) dev.off() grid::rasterGrob(raster_masked) } -gridpattern_mask_raster <- function(x) { - height <- x$res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) - width <- x$res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) - png_device <- x$png_device - if (is.null(png_device)) { - if (requireNamespace("ragg", quietly = TRUE)) { - png_device <- ragg::agg_png - } else { - stopifnot(capabilities("png")) - png_device <- grDevices::png - } +default_png_device <- function() { + if (requireNamespace("ragg", quietly = TRUE)) { + ragg::agg_png + } else { + stopifnot(capabilities("png")) + grDevices::png } +} + +gridpattern_mask_raster_straight <- function(maskee, masker, res, png_device) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + height <- res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) + width <- res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) + + png_masked <- tempfile(fileext = ".png") + on.exit(unlink(png_masked), add = TRUE) + png_device(png_masked, height = height, width = width, + res = res, bg = "transparent") + grob <- alphaMaskGrob(maskee, masker, use_R4.1_masks = TRUE) + grid.draw(grob) + dev.off() + + raster_masked <- png::readPNG(png_masked, native = FALSE) + grid::rasterGrob(raster_masked) +} + +gridpattern_mask_raster_manual <- function(maskee, masker, res, png_device) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + height <- res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) + width <- res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) png_maskee <- tempfile(fileext = ".png") - on.exit(unlink(png_maskee)) + on.exit(unlink(png_maskee), add = TRUE) png_device(png_maskee, height = height, width = width, - res = x$res, bg = "transparent") - grid.draw(x$maskee) + res = res, bg = "transparent") + grid.draw(maskee) dev.off() png_masker <- tempfile(fileext = ".png") - on.exit(unlink(png_masker)) + on.exit(unlink(png_masker), add = TRUE) png_device(png_masker, height = height, width = width, - res = x$res, bg = "transparent") - grid.draw(x$masker) + res = res, bg = "transparent") + grid.draw(masker) dev.off() raster_maskee <- png::readPNG(png_maskee, native = FALSE) diff --git a/R/clippingPathGrob.R b/R/clippingPathGrob.R index 8da997a..68da7be 100644 --- a/R/clippingPathGrob.R +++ b/R/clippingPathGrob.R @@ -9,9 +9,12 @@ #' If `NULL` try to guess an appropriate choice. #' Note not all graphic devices support the grid clipping path feature #' and the grid clipping path feature does not nest. -#' @param png_device \dQuote{png} graphics device to use if `use_R4.1_clipping` is `FALSE`. -#' If `NULL` (default) will use `ragg::agg_png()` if the -#' suggested package `ragg` is available else `grDevices::png()`. +#' @param png_device \dQuote{png} graphics device to save intermediate raster data with if `use_R4.1_clipping` is `FALSE`. +#' If `NULL` and suggested package `ragg` is available +#' and versions are high enough we directly capture clipped raster via [ragg::agg_capture()]. +#' Otherwise we will use `png_device` +#' (default [ragg::agg_png()] if available else [grDevices::png()]) and [png::readPNG()] +#' to manually compute a clipped raster. #' @param res Resolution of desired `rasterGrob` in pixels per inch if `use_R4.1_clipping` is `FALSE`. #' @return A `grid` grob #' @inheritParams grid::polygonGrob @@ -41,7 +44,7 @@ clippingPathGrob <- function(clippee, clipper, gTree(clippee = clippee, clipper = clipper, use_R4.1_clipping = use_R4.1_clipping, res = res, png_device = png_device, - name=name, gp=gp, vp=vp, cl="clipping_path") + name = name, gp = gp, vp = vp, cl = "clipping_path") } #' @export @@ -61,40 +64,86 @@ makeContent.clipping_path <- function(x) { grob <- grobTree(x$clippee, vp = viewport(clip = x$clipper), name = "clip") + } else if (is.null(x$png_device) && + getRversion() >= '4.1.0' && + requireNamespace("ragg", quietly = TRUE) && + packageVersion("ragg") >= '1.2.0') { + grob <- gridpattern_clip_agg_capture(x$clippee, x$clipper, x$res) } else { - grob <- gridpattern_clip_raster(x) + png_device <- x$png_device %||% default_png_device() + if (device_supports_clipping(png_device)) { + grob <- gridpattern_clip_raster_straight(x$clippee, x$clipper, x$res, png_device) + } else { + grob <- gridpattern_clip_raster_manual(x$clippee, x$clipper, x$res, png_device) + } } gl <- gList(grob) setChildren(x, gl) } -gridpattern_clip_raster <- function(x) { - height <- x$res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) - width <- x$res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) - png_device <- x$png_device - if (is.null(png_device)) { - if (requireNamespace("ragg", quietly = TRUE)) { - png_device <- ragg::agg_png - } else { - stopifnot(capabilities("png")) - png_device <- grDevices::png - } - } +device_supports_clipping <- function(png_device) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + png_file <- tempfile(fileext = ".png") + on.exit(unlink(png_file), add = TRUE) + png_device(png_file) + value <- guess_has_R4.1_features("clippingPaths") + dev.off() + value +} + +gridpattern_clip_agg_capture <- function(clippee, clipper, res) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + height <- res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) + width <- res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) + + ragg::agg_capture(height = height, width = width, res = res, bg = "transparent") + grob <- clippingPathGrob(clippee, clipper, use_R4.1_clipping = TRUE) + grid.draw(grob) + raster_clipped <- dev.capture(native = FALSE) + dev.off() + grid::rasterGrob(raster_clipped) +} + +gridpattern_clip_raster_straight <- function(clippee, clipper, res, png_device) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + height <- res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) + width <- res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) + + png_clipped <- tempfile(fileext = ".png") + on.exit(unlink(png_clipped), add = TRUE) + png_device(png_clipped, height = height, width = width, + res = res, bg = "transparent") + grob <- clippingPathGrob(clippee, clipper, use_R4.1_clipping = TRUE) + grid.draw(grob) + dev.off() + + raster_clipped <- png::readPNG(png_clipped, native = FALSE) + grid::rasterGrob(raster_clipped) +} + +gridpattern_clip_raster_manual <- function(clippee, clipper, res, png_device) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) + height <- res * convertHeight(unit(1, "npc"), "in", valueOnly = TRUE) + width <- res * convertWidth(unit(1, "npc"), "in", valueOnly = TRUE) png_clippee <- tempfile(fileext = ".png") - on.exit(unlink(png_clippee)) + on.exit(unlink(png_clippee), add = TRUE) png_device(png_clippee, height = height, width = width, - res = x$res, bg = "transparent") - grid.draw(x$clippee) + res = res, bg = "transparent") + grid.draw(clippee) dev.off() png_clipper <- tempfile(fileext = ".png") - on.exit(unlink(png_clipper)) + on.exit(unlink(png_clipper), add = TRUE) png_device(png_clipper, height = height, width = width, - res = x$res, bg = "transparent") + res = res, bg = "transparent") pushViewport(viewport(gp = gpar(lwd = 0, col = NA, fill = "black"))) - grid.draw(x$clipper) + grid.draw(clipper) popViewport() dev.off() diff --git a/R/gridpattern-package.R b/R/gridpattern-package.R index 81c48f1..6f15c62 100644 --- a/R/gridpattern-package.R +++ b/R/gridpattern-package.R @@ -17,7 +17,7 @@ #' If `FALSE` do a `rasterGrob` approximation of the masked pattern. #' If `NULL` try to guess an appropriate choice.} #' \item{ggpattern_use_R4.1_patterns}{If `TRUE` use the grid pattern feature introduced in R v4.1.0. -#' Currently unused by this package.} +#' Currently only used by a couple of examples.} #' } #' Note to use the R v4.1.0 features one needs R be (at least) version 4.1 and not all graphic devices #' support any/all these features. See \url{https://www.stat.auckland.ac.nz/~paul/Reports/GraphicsEngine/definitions/definitions.html} for more information on these features. diff --git a/R/utils-polygon_df.R b/R/utils-polygon_df.R index fd0335b..834fa5e 100644 --- a/R/utils-polygon_df.R +++ b/R/utils-polygon_df.R @@ -148,6 +148,8 @@ get_poly_lengths <- function(sf_object) { #' #' @noRd convert_polygon_df_to_alpha_channel <- function(polygon_df, width, height) { + current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Convert the polygon to an actual grob, coloured 'black' @@ -155,6 +157,8 @@ convert_polygon_df_to_alpha_channel <- function(polygon_df, width, height) { gp <- gpar(fill = 'black') boundary_grob <- convert_polygon_df_to_polygon_grob(polygon_df, gp=gp) + # Note `ragg::agg_capture()`'s non-"native" format is a matrix of color strings + # while `png::readPNG()`'s non-"native" format is an array of numeric values #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Save the grob as an image of the given size #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -167,7 +171,7 @@ convert_polygon_df_to_alpha_channel <- function(polygon_df, width, height) { # Load the file and convert o a numeric matrix with values 0/1 depending # on whether the pixel is white or black. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - alpha_channel <- png::readPNG(png_file) + alpha_channel <- png::readPNG(png_file, native = FALSE) alpha_channel <- alpha_channel[,,1] < 0.5 storage.mode(alpha_channel) <- 'numeric' diff --git a/R/zzz.R b/R/zzz.R index 55d9a0b..c70f7de 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -14,7 +14,7 @@ img_read_memoised <- img_read #' @import grid #' @importFrom glue glue -#' @importFrom grDevices col2rgb dev.capabilities dev.off png rgb +#' @importFrom grDevices col2rgb dev.capture dev.capabilities dev.off png rgb #' @importFrom rlang %||% abort inform warn #' @importFrom utils hasName head packageVersion tail NULL diff --git a/man/clippingPathGrob.Rd b/man/clippingPathGrob.Rd index f7ca8ff..ba61fab 100644 --- a/man/clippingPathGrob.Rd +++ b/man/clippingPathGrob.Rd @@ -27,9 +27,12 @@ If \code{NULL} try to guess an appropriate choice. Note not all graphic devices support the grid clipping path feature and the grid clipping path feature does not nest.} -\item{png_device}{\dQuote{png} graphics device to use if \code{use_R4.1_clipping} is \code{FALSE}. -If \code{NULL} (default) will use \code{ragg::agg_png()} if the -suggested package \code{ragg} is available else \code{grDevices::png()}.} +\item{png_device}{\dQuote{png} graphics device to save intermediate raster data with if \code{use_R4.1_clipping} is \code{FALSE}. +If \code{NULL} and suggested package \code{ragg} is available +and versions are high enough we directly capture clipped raster via \code{\link[ragg:agg_capture]{ragg::agg_capture()}}. +Otherwise we will use \code{png_device} +(default \code{\link[ragg:agg_png]{ragg::agg_png()}} if available else \code{\link[grDevices:png]{grDevices::png()}}) and \code{\link[png:readPNG]{png::readPNG()}} +to manually compute a clipped raster.} \item{res}{Resolution of desired \code{rasterGrob} in pixels per inch if \code{use_R4.1_clipping} is \code{FALSE}.} diff --git a/man/gridpattern-package.Rd b/man/gridpattern-package.Rd index 57e7a58..0b50743 100644 --- a/man/gridpattern-package.Rd +++ b/man/gridpattern-package.Rd @@ -30,7 +30,7 @@ If \code{NULL} try to guess an appropriate choice.} If \code{FALSE} do a \code{rasterGrob} approximation of the masked pattern. If \code{NULL} try to guess an appropriate choice.} \item{ggpattern_use_R4.1_patterns}{If \code{TRUE} use the grid pattern feature introduced in R v4.1.0. -Currently unused by this package.} +Currently only used by a couple of examples.} } Note to use the R v4.1.0 features one needs R be (at least) version 4.1 and not all graphic devices support any/all these features. See \url{https://www.stat.auckland.ac.nz/~paul/Reports/GraphicsEngine/definitions/definitions.html} for more information on these features. diff --git a/tests/figs/array/alphaMaskGrob_transparent.png b/tests/figs/array/alphaMaskGrob_feature.png similarity index 100% rename from tests/figs/array/alphaMaskGrob_transparent.png rename to tests/figs/array/alphaMaskGrob_feature.png diff --git a/tests/figs/array/alphaMaskGrob_manual.png b/tests/figs/array/alphaMaskGrob_manual.png new file mode 100644 index 0000000..e41a05e Binary files /dev/null and b/tests/figs/array/alphaMaskGrob_manual.png differ diff --git a/tests/figs/array/alphaMaskGrob.png b/tests/figs/array/alphaMaskGrob_ragg.png similarity index 100% rename from tests/figs/array/alphaMaskGrob.png rename to tests/figs/array/alphaMaskGrob_ragg.png diff --git a/tests/figs/array/clipGrob_cairo.png b/tests/figs/array/clipGrob_cairo.png new file mode 100644 index 0000000..3d75ab0 Binary files /dev/null and b/tests/figs/array/clipGrob_cairo.png differ diff --git a/tests/figs/array/clipGrob_feature.png b/tests/figs/array/clipGrob_feature.png new file mode 100644 index 0000000..7364b0a Binary files /dev/null and b/tests/figs/array/clipGrob_feature.png differ diff --git a/tests/figs/array/clipGrob.png b/tests/figs/array/clipGrob_manual.png similarity index 100% rename from tests/figs/array/clipGrob.png rename to tests/figs/array/clipGrob_manual.png diff --git a/tests/figs/array/clipGrob_ragg.png b/tests/figs/array/clipGrob_ragg.png new file mode 100644 index 0000000..6c77fea Binary files /dev/null and b/tests/figs/array/clipGrob_ragg.png differ diff --git a/tests/testthat/test_array.R b/tests/testthat/test_array.R index b08e8bf..c48d779 100644 --- a/tests/testthat/test_array.R +++ b/tests/testthat/test_array.R @@ -24,11 +24,10 @@ test_raster <- function(ref_png, fn, update = FALSE) { my_png <- function(f, fn) { current_dev <- grDevices::dev.cur() + if (current_dev > 1) on.exit(grDevices::dev.set(current_dev)) grDevices::png(f, type = "cairo", width = 240, height = 240) - val <- fn() + fn() grDevices::dev.off() - if (current_dev > 1) grDevices::dev.set(current_dev) - invisible(val) } test_that("array patterns works as expected", { @@ -122,26 +121,43 @@ test_that("array patterns works as expected", { y = c(y_hex_outer, y_hex_inner), id = rep(1:2, each = 7), rule = "evenodd") + + clipped <- clippingPathGrob(clippee, clipper, use_R4.1_clipping = FALSE, + png_device = grDevices::png) + test_raster("clipGrob_cairo.png", function() grid.draw(clipped)) + + clipped <- clippingPathGrob(clippee, clipper, use_R4.1_clipping = NULL) + test_raster("clipGrob_feature.png", function() grid.draw(clipped)) + + png_device <- default_png_device() + test_raster("clipGrob_manual.png", function() { + clipped <- gridpattern_clip_raster_manual(clippee, clipper, 72, png_device) + grid.draw(clipped) + }) + clipped <- clippingPathGrob(clippee, clipper, use_R4.1_clipping = FALSE) - test_raster("clipGrob.png", function() grid.draw(clipped)) + test_raster("clipGrob_ragg.png", function() grid.draw(clipped)) # alphaMaskGrob() - clipper <- editGrob(clipper, gp = gpar(col = NA, fill = "black")) - masked <- alphaMaskGrob(clippee, clipper, use_R4.1_masks = FALSE) - test_raster("alphaMaskGrob.png", function() grid.draw(masked)) - - clippee <- rectGrob(gp = gpar(fill = "blue", col = NA)) - clipper <- editGrob(clipper, gp = gpar(col = "black", lwd=20, fill = rgb(0, 0, 0, 0.5))) - masked <- alphaMaskGrob(clippee, clipper, use_R4.1_masks = NULL) - test_raster("alphaMaskGrob_transparent.png", function() grid.draw(masked)) - - clippee <- rectGrob(gp = gpar(fill = "blue", col = NA)) - clipper <- editGrob(clipper, gp = gpar(col = "black", lwd=20, fill = rgb(0, 0, 0, 0.5))) - bitmapType = getOption("bitmapType") - options(bitmapType = "cairo") - masked <- alphaMaskGrob(clippee, clipper, use_R4.1_masks = FALSE, png_device = grDevices::png) - test_raster("alphaMaskGrob_cairo.png", function() grid.draw(masked)) - options(bitmapType = bitmapType) + clippee2 <- rectGrob(gp = gpar(fill = "blue", col = NA)) + clipper2 <- editGrob(clipper, gp = gpar(col = NA, fill = "black")) + clipper3 <- editGrob(clipper2, gp = gpar(col = "black", lwd=20, fill = rgb(0, 0, 0, 0.5))) + + masked <- alphaMaskGrob(clippee2, clipper3, use_R4.1_masks = FALSE, png_device = grDevices::png) + test_raster("alphaMaskGrob_cairo.png", function() { + grid.draw(masked) + }) + + masked <- alphaMaskGrob(clippee2, clipper3, use_R4.1_masks = NULL) + test_raster("alphaMaskGrob_feature.png", function() grid.draw(masked)) + + test_raster("alphaMaskGrob_manual.png", function() { + masked <- gridpattern_mask_raster_manual(clippee2, clipper3, 72, png_device) + grid.draw(masked) + }) + + masked <- alphaMaskGrob(clippee, clipper2, use_R4.1_masks = FALSE) + test_raster("alphaMaskGrob_ragg.png", function() grid.draw(masked)) # ambient skip_if_not_installed("ambient")