Skip to contents

Blend objects within a single layer (geom) or across multiple layers (geoms) using graphical blending modes, such as "multiply", "overlay", etc. Uses the built-in compositing support in graphical devices added in R 4.2.

Usage

blend(object, blend = "over", alpha = 1)

Arguments

object

One of:

  • A layer-like object: applies this operation to the layer.

  • A missing argument: creates an operation

  • A string (character vector of length 1) giving the name of a blend, which takes the place of the blend argument.

blend

The blend mode to use. The default mode, "over", corresponds to the "usual" blend mode of drawing objects on top of each other. The list of supported blend modes depends on your graphical device (see Murrell 2021), and are listed in dev.capabilities()$compositing. Blend modes can include: "clear", "source", "over", "in", "out", "atop", "dest", "dest.over", "dest.in", "dest.out", "dest.atop", "xor", "add", "saturate", "multiply", "screen", "overlay", "darken", "lighten", "color.dodge", "color.burn", "hard.light", "soft.light", "difference", and "exclusion"

Blend modes like "multiply", "darken", and "lighten" are particularly useful as they are commutative: the result is the same whichever order they are applied in.

A warning is issued if the current graphics device does not appear to support the requested blend mode. In some cases this warning may be spurious, so it can be disabled by setting options(ggblend.check_blend = FALSE).

alpha

A numeric between 0 and 1 (inclusive). The opacity of a transparency mask applied to objects prior to blending.

Value

A layer-like object (if object is layer-like) or an operation (if not).

Details

If object is a single layer / geometry and the partition aesthetic is not set, every graphical object (grob()) output by the geometry will be blended together using the blend blend mode. If alpha != 1, a transparency mask with the provided alpha level will be applied to each grob before blending.

If object is a single layer / geometry and the partition aesthetic is set, the geometry will be rendered for each subset of the data defined by the partition aesthetic, a transparency mask with the provided alpha level will be applied to each resulting group as a whole (if alpha != 1), then these groups will be blended together using the blend blend mode.

If object is a list of layers / geometries, those layers will be rendered separately, a transparency mask with the provided alpha level will be applied to each layer as a whole (if alpha != 1), then these layers will be blended together using the blend blend mode.

If a blend() is multiplied by a list of layers using *, it acts on each layer individually (as if each layer were passed to blend()).

Supported devices

Blending is not currently supported by all graphics devices. As of this writing, at least png(type = "cairo"), svg(), and cairo_pdf() are known to support blending.

blend() attempts to auto-detect support for blending using dev.capabilities(). You may receive a warning when using blend() if it appears blending is not supported by the current graphics device. This warning either means (1) your graphics device does not support blending (in which case you should switch to one that does) or (2) your graphics device supports blending but incorrectly reports that it does not. Unfortunately, not all graphics devices that support blending appear to correctly report that they support blending, so even if auto-detection fails, blend() will still attempt to apply the blend, just in case.

If the warning is issued and the output is still correctly blended, this is likely a bug in the graphics device. You can report the bug to the authors of the graphics device if you wish; in the mean time, you can use options(ggblend.check_blend = FALSE) to disable the check.

References

Murrell, Paul (2021): Groups, Compositing Operators, and Affine Transformations in R Graphics. The University of Auckland. Report. doi:10.17608/k6.auckland.17009120.v1 .

See also

operation for a description of layer operations.

Other layer operations: adjust, affine_transform, copy, nop, partition()

Examples

old_options = options(ggblend.check_blend = FALSE)
library(ggplot2)

# create two versions of a dataset, where draw order can affect output
set.seed(1234)
df_a = data.frame(x = rnorm(500, 0), y = rnorm(500, 1), set = "a")
df_b = data.frame(x = rnorm(500, 1), y = rnorm(500, 2), set = "b")
df_ab = rbind(df_a, df_b) |>
  transform(order = "draw a then b")
df_ba = rbind(df_b, df_a) |>
  transform(order = "draw b then a")
df = rbind(df_ab, df_ba)

# Using the "darken" blend mode, draw order does not matter:
df |>
  ggplot(aes(x, y, color = set)) +
  geom_point(size = 3) |> blend("darken") +
  scale_color_brewer(palette = "Set2") +
  facet_grid(~ order)
#> Warning: Group definition failed
#> Warning: Group definition failed


# Using the "multiply" blend mode, we can see density within groups:
df |>
  ggplot(aes(x, y, color = set)) +
  geom_point(size = 3) |> blend("multiply") +
  scale_color_brewer(palette = "Set2") +
  facet_grid(~ order)
#> Warning: Group definition failed
#> Warning: Group definition failed


# blend() on a single geom by default blends all grobs in that geom together
# using the requested blend mode. If we wish to blend within specific data
# subsets using normal blending ("over") but between subsets using the
# requested blend mode, we can set the partition aesthetic. This will
# make "multiply" behave more like "darken":
df |>
  ggplot(aes(x, y, color = set, partition = set)) +
  geom_point(size = 3) |> blend("multiply") +
  scale_color_brewer(palette = "Set2") +
  facet_grid(~ order)
#> Warning: Group definition failed
#> Warning: Group definition failed


# We can also blend lists of geoms together; these geoms are rendered using
# normal ("over") blending (unless a blend() call is applied to a specific
# sub-layer, as in the first layer below) and then blended together using
# the requested blend mode.
df |>
  ggplot(aes(x, y, color = set)) +
  list(
    geom_point(size = 3) |> blend("darken"),
    geom_vline(xintercept = 0, color = "gray75", linewidth = 1.5),
    geom_hline(yintercept = 0, color = "gray75", linewidth = 1.5)
  ) |> blend("hard.light") +
  scale_color_brewer(palette = "Set2") +
  facet_grid(~ order)
#> Warning: Group definition failed
#> Warning: Group definition failed

options(old_options)