Plotting into buffer

Is it possible and how to plot into an existing buffer with Makie.jl ? My guess is that Cairomakie.jl would be the choice, but I can’t find a solution for it.

The reason for this task is, that I want to hand over the buffer to a C library several times.

Here is an example for a 3D plot (from gallery Beautiful Makie) without the buffer:

using ImageView
using CairoMakie

CairoMakie.activate!()
x = -2:0.005:2
y = -2:0.005:2
f(z) = (z^2 + 1) / (z^2 - 1)
fvals = [f(u + 1im * v) for u in x, v in y]
fvalues = abs.(fvals)
fargs = angle.(fvals)
indxCut = fvalues .> 3
fvalues[indxCut] .= 3.01
fig, ax, pltobj = surface(x, y, fvalues, color = fargs,
    colormap = :roma, colorrange = (-π, π),
    backlight = 1.0f0, highclip = :black,
    figure = (; size = (800, 600), fontsize = 22));
display(fig)
imshow(colorbuffer(fig.scene))

Here is some pseudocode for what I want to do

using CairoMakie

CairoMakie.activate!()

buffer=zeros(RGB24, 800, 600)

function draw_into_buffer!(buffer)
	x = -2:0.005:2
	y = -2:0.005:2
	f(z) = (z^2 + 1) / (z^2 - 1)
	fvals = [f(u + 1im * v) for u in x, v in y]
	fvalues = abs.(fvals)
	fargs = angle.(fvals)
	indxCut = fvalues .> 3
	fvalues[indxCut] .= 3.01

	#PSEUDOCODE: plot into existing buffer:
	surface_into_buffer(buffer, x, y, fvalues, color = fargs,
		colormap = :roma, colorrange = (-π, π),
		backlight = 1.0f0, highclip = :black,
		figure = (; size = (1200, 800), fontsize = 22));
end

draw_into_buffer!(buffer);

#provide buffer to some C library as pointer(buffer) 

How can I tell CairoMakie to plot into an existing buffer for image pixels?

The prefered idea is not to copy again and again from colorbuffer(fig.scene) to buffer for performance.

You have to go a bit into internals but you can construct a CairoScreen from a CairoARGBSurface that is backed by an array that you create. But the memory layout must be the same.

I can’t send an example now but will try it out in a bit.

1 Like

Thanks a lot, I am waiting for it… I see the idea already but for today I can’t go into my own experiments…

using CairoMakie
using CairoMakie.Colors

sz = (400, 300)
px_per_unit = 2
img = fill(ARGB32(1, 0, 0, 1), ((sz .* px_per_unit) .+ (200, 0))...)
surf = CairoMakie.Cairo.CairoImageSurface(img)

f = Figure(size = sz)
scatter(f[1, 1], cumsum(randn(100)))

conf = Makie.merge_screen_config(CairoMakie.ScreenConfig, Dict(:px_per_unit => px_per_unit))
scr = CairoMakie.Screen(f.scene, conf, surf)
CairoMakie.cairo_draw(scr, f.scene)

save("test.png", img')

The red section on the side is just to visualize that I mutated the original array filled with red

3 Likes

Thanks for the code.

Yet I am still not happy because the line CairoMakie.cairo_draw(scr, f.scene) is so slow, that it doesn’t work very good in my interactive scenario.
The code I tried now is:

using CairoMakie
using CairoMakie.Colors

buffer = zeros(UInt32, 800, 600)

function draw()
	amplitude = 5.0
	sz = (800, 600)
	px_per_unit = 1
	surf = CairoMakie.Cairo.CairoImageSurface(buffer, CairoMakie.Cairo.FORMAT_ARGB32; flipxy=false)
	conf = Makie.merge_screen_config(CairoMakie.ScreenConfig, Dict(:px_per_unit => px_per_unit))

	x = -2:0.005:2
	y = -2:0.005:2
	f(z) = (amplitude/5.0) * (z^2 + 1) / (z^2 - 1)
	fvals = [f(u + 1im * v) for u in x, v in y]
	fvalues = abs.(fvals)
	fargs = angle.(fvals)
	indxCut = fvalues .> 3
	fvalues[indxCut] .= 3.01
	fig, ax, pltobj = surface(x, y, fvalues, color = fargs,
		colormap = :roma, colorrange = (-π, π),
		backlight = 1.0f0, highclip = :black,
		figure = (; size = sz, fontsize = 22));

	resize_to_layout!(fig)
	scr = CairoMakie.Screen(fig.scene, conf, surf)
	CairoMakie.cairo_draw(scr, fig.scene)
end
draw()

The last line in the draw() function, CairoMakie.cairo_draw(scr, fig.scene), uses several seconds, which makes interactivity very annoying.

In general it is working quite well: the buffer can be passed to the C library and it shows up correctly.

Perhaps CairoMakie wasn’t the best choice for a Julia plotting library to plot a 3d function into an existing buffer. What I additionally need is fast rendering into the buffer.

Any suggestions?

Ah yeah that’s because 3D mesh drawing is the weak point in Cairo, and you’re doing a fairly detailed surface with 640k vertices. Not sure if @sdanisch has a good idea how to do this with GLMakie. But the buffer would be on the GPU there, so a copy would have to be made, whether into an existing buffer or as a separate array like colorbuffer does it. I don’t think OpenGL would draw directly into a RAM buffer.

1 Like

In GLMakie we already draw in a pre-allocated frame cache, which I guess you could just use?

1 Like

Thanks, will try…

Something is not as expected.
The plot renders nicely but the framecache is not the full picture:

using GLMakie
using GLMakie.Colors

amplitude = 5.0
sz = (800, 600)
px_per_unit = 1

x = -2:0.005:2
y = -2:0.005:2
f(z) = (amplitude/5.0) * (z^2 + 1) / (z^2 - 1)
fvals = [f(u + 1im * v) for u in x, v in y]
fvalues = abs.(fvals)
fargs = angle.(fvals)
indxCut = fvalues .> 3
fvalues[indxCut] .= 3.01
fig, ax, pltobj = surface(x, y, fvalues, color = fargs,
	colormap = :roma, colorrange = (-π, π),
	backlight = 1.0f0, highclip = :black,
	figure = (; size = sz, fontsize = 22));

resize_to_layout!(fig)

scr = GLMakie.Screen(fig.scene);
julia> scr.framecache
10×10 Matrix{RGB{FixedPointNumbers.N0f8}}:
 RGB(0.004, 0.0, 0.0)  RGB(0.0, 0.0, 0.02)   RGB(0.0, 0.0, 0.0) ...
...

Perhaps scr.framebuffer can be used, but it’s a more complex structure so I can’t see how?

For those late in the thread:
How to repeatedly plot (e.g. a nice 3D function) directly into a (predefined) buffer with good performance without displaying the plot? The raw buffer is repeatedly passed to a C library.

It only gets filled when calling colorbuffer(screen) .
You can call that or exctract the relevant bits from the linked source!

1 Like

Thanks for all your efforts, but I have trouble doing what I want with Makie:

CairoMakie: CairoMakie.cairo_draw needs several seconds => bad interactivity.

GLMakie: colorbuffer and GLMakie.Screen stalls, probably because the C GUI library uses OpenGL too.

WGLMakie: can’t get the pixel buffer without showing the plot, a browser screen must be open.