JuliaGraphics / Luxor.jl

Simple drawings using vector graphics; Cairo "for tourists!"

Home Page:http://juliagraphics.github.io/Luxor.jl/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Drawing method which calls Cairo.CairoImageSurface(img)

oheil opened this issue · comments

commented

Playing around with MiniFB and Luxor the currently only available Drawing methods turned out not to be the perfect match for MiniFB because the image buffer is copied a lot. But in Cairo.jl there is also:

CairoImageSurface(img::Matrix{T}) where {T<:Union{RGB24,ARGB32}}

where the image buffer is provided by the user.

So I forked Luxor and added this method to drawings.jl :

mutable struct Drawing
    ....
    function Drawing(img::Matrix{T}; strokescale=false) where {T<:Union{RGB24,ARGB32}}
        w,h = size(img)
        bufdata = UInt8[]
        iobuf = IOBuffer(bufdata, read=true, write=true)
        the_surfacetype = :image
        f = ""
        the_surface = Cairo.CairoImageSurface(img)
        the_cr  = Cairo.CairoContext(the_surface)
        currentdrawing      = new(w, h, f, the_surface, the_cr, the_surfacetype, 0.0, 0.0, 0.0, 1.0, iobuf, bufdata, strokescale)
        if isempty(CURRENTDRAWING)
            push!(CURRENTDRAWING, currentdrawing)
        else
            CURRENTDRAWING[1] = currentdrawing
        end
        return currentdrawing
    end

    function Drawing(w, h, stype::Symbol, f::AbstractString=""; strokescale=false)
        ...
    end
    ...
end

The following example shows an example which wasn't possible as smooth as it is now.
First, I wanted to have a MiniFB window which is updated automatically in the background and let's me put Luxor graphic commands at the REPL together with a FPS display as a layer on top without overwriting the image buffer:

using MiniFB, Luxor, Colors

WIDTH=800
HEIGHT=600

mutable struct MfbState
	state::mfb_update_state
	mousePressed::Int	
end
stateArray=[MfbState( MiniFB.STATE_OK, 0 )]

window = mfb_open_ex("MiniFB", WIDTH, HEIGHT, MiniFB.WF_RESIZABLE)

function windowUpdateTask(window,buffer,stateArray,showFPS=false,singleCall=false)
	sb=buffer[1:105,1:55]
    stateArray[1].state=mfb_update(window,buffer)
	updateCount=0
	startTime=floor(Int,time())
	fps="0"
	while stateArray[1].state == MiniFB.STATE_OK && ! singleCall
		if showFPS
			elapsedTime=floor(Int,time())-startTime
			if elapsedTime > 1
				fps=string(round(Int,updateCount/elapsedTime))
				startTime=floor(Int,time())
				updateCount=0
			end
			sb.=buffer[1:105,1:55]
			@layer begin
				(dx,dy)=Point(0.0, 0.0)-getworldposition(Point(0.0, 0.0);centered=false)
				setcolor((1.0, 0, 0, 0.5))
				fontsize(50)
				text(fps, Point(5+dx,5+dy), halign=:left, valign = :top)
			end
		end
		stateArray[1].state=mfb_update(window,buffer)
		if showFPS
			buffer[1:105,1:55].=sb
		end
        sleep(1.0/120.0)
		updateCount+=1
    end
	println("\nWindow closed\n")
end

buffer=zeros(ARGB32, WIDTH, HEIGHT)
d=Drawing(buffer)

@async windowUpdateTask(window,buffer,stateArray,true)

Now doing some graphics on the image buffer rendered directly in the MiniFB window.
Pressing "q" + "return" stops the while loop and brings you back to the REPL.

mutable struct Ball
    position::Point
    velocity::Point
end
origin()
function stick(w, h)
    channel = Channel(10)
    @async while true
        kb = readline(stdin)
        if contains(kb, "q")
            put!(channel, 1)
            break
        end
    end
	colors=[rand(1:255),rand(1:255),rand(1:255)]
	newcolors=[rand(1:255),rand(1:255),rand(1:255)]
	c=ARGB(colors[1]/255,colors[2]/255,colors[3]/255,1.0)
	balls=[Ball( rand(BoundingBox(Point(-w/2, -h/2), Point(w/2, h/2))), rand(BoundingBox(Point(-10, -10), Point(10, 10))) ) for _ in 1:2] 
	while true
		background(0,0,0,0.05)
		if colors == newcolors
			newcolors=[rand(1:255),rand(1:255),rand(1:255)]
		end
		for (index,(col,newcol)) in enumerate(zip(colors,newcolors))
			if col != newcol
				col > newcol ? col-=1 : col+=1
				colors[index]=col
			end
		end
		c=ARGB(colors[1]/255,colors[2]/255,colors[3]/255,1.0)
		for ball in balls
			if !(-w/2 < ball.position.x < w/2)
				ball.velocity = Point(-ball.velocity.x,ball.velocity.y)
			end
			if !(-h/2 < ball.position.y < h/2)
				ball.velocity = Point(ball.velocity.x,-ball.velocity.y)
			end
			ball.position = ball.position + ball.velocity
		end
		setcolor(c)
		line(balls[1].position,balls[2].position,:stroke)
		sleep(1.0/120.0)
        if isready(channel)
            break
        end
	end
end
stick(WIDTH, HEIGHT)

What do you think? Do you see what I mean and why I think this is a nice way to expand on Luxor?
I can do a PR but for now, the state of my proposal is still a bit rough and needs some discussion.

It looks great! I don't use this bit of the package at all (except for answering queries 😄) so anything that might add useful features for other people is great. If it doesn't affect current functionality (image buffers, snapshots, etc) then adding it would be straightforward and hopefully not breaking. However, adding tests and documentation is required! 😛

You might like to search through some of the old issues for "buffer" where people have asked about image buffer functionality, thread-safety, etc, just in case there are other considerations you haven't thought of.

commented

Hm, thread-safety is surely nothing I can address on the way, snapshot and preview is something which would be nice to have if MiniFB is not used... I will have a look...

Closed via #233 - thanks!