From ed836a05a4a99285db28ce26d1d8ababbee80771 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Mon, 25 Sep 2023 13:08:27 +0200
Subject: [PATCH] Add "How to get a transparent background" page (#3236)
* Add "How to get a transparent background" page
* use `clear = true` in Figure because it's always top-level
* use transparency in glmakie example
* show non-transparent example first
* change title
* change to How-Tos
---
docs/_layout/masthead.html | 1 +
docs/explanations.md | 2 +-
docs/how-to.md | 5 ++
docs/how-to/save-figure-with-transparency.md | 79 ++++++++++++++++++++
src/figures.jl | 2 +-
5 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 docs/how-to.md
create mode 100644 docs/how-to/save-figure-with-transparency.md
diff --git a/docs/_layout/masthead.html b/docs/_layout/masthead.html
index a8e5492f125..665d52fa67a 100644
--- a/docs/_layout/masthead.html
+++ b/docs/_layout/masthead.html
@@ -12,6 +12,7 @@
+
diff --git a/docs/explanations.md b/docs/explanations.md
index 3542f41c3d7..eae0ca075fa 100644
--- a/docs/explanations.md
+++ b/docs/explanations.md
@@ -1,4 +1,4 @@
-@def order = 3
+@def order = 4
# Explanations
diff --git a/docs/how-to.md b/docs/how-to.md
new file mode 100644
index 00000000000..9423ac5e16a
--- /dev/null
+++ b/docs/how-to.md
@@ -0,0 +1,5 @@
+@def order = 3
+
+# How-Tos
+
+{{list_folder how-to}}
diff --git a/docs/how-to/save-figure-with-transparency.md b/docs/how-to/save-figure-with-transparency.md
new file mode 100644
index 00000000000..aec0522f3a1
--- /dev/null
+++ b/docs/how-to/save-figure-with-transparency.md
@@ -0,0 +1,79 @@
+# How to save a `Figure` with transparency
+
+## CairoMakie
+
+In CairoMakie, set the background color to `:transparent` (converts to `RGBAf(0, 0, 0, 0)`) to get a fully transparent background.
+In the following examples, I use a partially transparent blue because a transparent background is indistinguishable from the usual white on a white page.
+
+\begin{examplefigure}{svg = true}
+```julia
+using CairoMakie
+CairoMakie.activate!() # hide
+
+f = Figure(backgroundcolor = (:blue, 0.4))
+Axis(f[1, 1], backgroundcolor = (:tomato, 0.5))
+f
+```
+\end{examplefigure}
+
+## GLMakie
+
+For technical reasons, GLMakie's color buffer does not have an alpha component:
+
+\begin{examplefigure}{}
+```julia
+using GLMakie
+GLMakie.activate!() # hide
+Makie.inline!(true) # hide
+
+f = Figure(backgroundcolor = (:blue, 0.4))
+Axis(f[1, 1], backgroundcolor = (:tomato, 0.5))
+f
+```
+\end{examplefigure}
+
+
+With the following trick you can still save an image with transparent background.
+It works by setting two different background colors and calculating the foreground color with alpha from the difference.
+
+```julia:transparent-glmakie
+using GLMakie
+GLMakie.activate!() # hide
+Makie.inline!(true) # hide
+
+function calculate_rgba(rgb1, rgb2, rgba_bg)::RGBAf
+ rgb1 == rgb2 && return RGBAf(rgb1.r, rgb1.g, rgb1.b, 1)
+ c1 = Float64.((rgb1.r, rgb1.g, rgb1.b))
+ c2 = Float64.((rgb2.r, rgb2.g, rgb2.b))
+ alphas_fg = 1 .+ c1 .- c2
+ alpha_fg = clamp(sum(alphas_fg) / 3, 0, 1)
+ alpha_fg == 0 && return rgba_bg
+ rgb_fg = clamp.((c1 ./ alpha_fg), 0, 1)
+ rgb_bg = Float64.((rgba_bg.r, rgba_bg.g, rgba_bg.b))
+ alpha_final = alpha_fg + (1 - alpha_fg) * rgba_bg.alpha
+ rgb_final = @. 1 / alpha_final * (alpha_fg * rgb_fg + (1 - alpha_fg) * rgba_bg.alpha * rgb_bg)
+ return RGBAf(rgb_final..., alpha_final)
+end
+
+function alpha_colorbuffer(figure)
+ scene = figure.scene
+ bg = scene.backgroundcolor[]
+ scene.backgroundcolor[] = RGBAf(0, 0, 0, 1)
+ b1 = copy(colorbuffer(scene))
+ scene.backgroundcolor[] = RGBAf(1, 1, 1, 1)
+ b2 = colorbuffer(scene)
+ scene.backgroundcolor[] = bg
+ return map(b1, b2) do b1, b2
+ calculate_rgba(b1, b2, bg)
+ end
+end
+
+f = Figure(backgroundcolor = (:blue, 0.4))
+Axis(f[1, 1], backgroundcolor = (:tomato, 0.5))
+f
+
+save(joinpath(@OUTPUT, "transparent.png"), alpha_colorbuffer(f)) # hide
+save("transparent.png", alpha_colorbuffer(f))
+```
+
+\fig{transparent.png}
\ No newline at end of file
diff --git a/src/figures.jl b/src/figures.jl
index b7d60ac8c6d..6590571dfec 100644
--- a/src/figures.jl
+++ b/src/figures.jl
@@ -98,7 +98,7 @@ function Figure(; kwargs...)
kwargs_dict = Dict(kwargs)
padding = pop!(kwargs_dict, :figure_padding, theme(:figure_padding))
- scene = Scene(; camera=campixel!, kwargs_dict...)
+ scene = Scene(; camera=campixel!, clear = true, kwargs_dict...)
padding = convert(Observable{Any}, padding)
alignmode = lift(Outside ∘ to_rectsides, padding)