library(grid)

HersheyLabel <- function(x, y=unit(.5, "npc")) {
    lines <- strsplit(x, "\n")[[1]]
    if (!is.unit(y))
        y <- unit(y, "npc")
    n <- length(lines)
    if (n > 1) {
        y <- y + unit(rev(seq(n)) - mean(seq(n)), "lines")
    }
    grid.text(lines, y=y, gp=gpar(fontfamily="HersheySans"))
}

r1 <- rectGrob(x=1/3, y=2/3, width=.5, height=.5,
               gp=gpar(col=NA, fill=rgb(.7, 0, 0, .8)))
r2 <- rectGrob(x=2/3, y=1/3, width=.5, height=.5,
               gp=gpar(col=NA, fill=rgb(0, 0, .9, .4)))

group <- function(op) {
    grid.rect()
    grid.group(r2, op, r1)
    HersheyLabel(op, y=1/4)
}
porterDuff <- function() {
    grid.newpage()
    pushViewport(viewport(layout=grid.layout(3, 4, respect=TRUE)))
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=1))
    group("over")
    popViewport()
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=2))
    group("clear")
    popViewport()
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=3))
    group("source")
    popViewport()
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=4))
    group("in")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=1))
    group("out")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=2))
    group("atop")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=3))
    group("dest")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=4))
    group("dest.over")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=1))
    group("dest.in")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=2))
    group("dest.out")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=3))
    group("dest.atop")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=4))
    group("xor")
    popViewport()
}
blendModes <- function() {
    grid.newpage()
    pushViewport(viewport(layout=grid.layout(3, 4, respect=TRUE)))
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=1))
    group("add")
    popViewport()
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=2))
    group("saturate")
    popViewport()
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=3))
    group("multiply")
    popViewport()
    pushViewport(viewport(layout.pos.row=1, layout.pos.col=4))
    group("screen")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=1))
    group("overlay")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=2))
    group("darken")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=3))
    group("lighten")
    popViewport()
    pushViewport(viewport(layout.pos.row=2, layout.pos.col=4))
    group("color.dodge")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=1))
    group("color.burn")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=2))
    group("hard.light")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=3))
    group("soft.light")
    popViewport()
    pushViewport(viewport(layout.pos.row=3, layout.pos.col=4))
    group("difference")
    popViewport()
}

porterDuff()
blendModes()