Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Question: Efficiently clearing images

edited July 2013 in Questions Posts: 2,820

Hello,
In my program, I'm using setContext to draw into an image, but here's the problem: When I do that without setting the image variable to a blank image, it retains the previous image. Here's the other problem: I need to keep the background alpha, so I can't just draw an opaque rectangle behind it.
This image I'm using is the size of the screen, so when I set it to a blank image ("image(WIDTH,HEIGHT)"), it slows down the performance by about 3 times. I've tried to make a template blank image and set it to the image I want to clear every frame, but that doesn't do anything.
Help would be great.
Thanks!

Comments

  • IgnatzIgnatz Mod
    Posts: 5,396

    Maybe a shader will help. What exactly are you doing with the image and the alpha - it isn't clear to me.

  • Posts: 2,820

    @Ignattz - Here's my code I made so far:

    displayMode(FULLSCREEN)
     
    function setup()
        parameter.watch("1/DeltaTime")
        fixtures = {}
        local gSize = vec2(WIDTH,HEIGHT/2)
        local gScale = 5
        local funnleSize = 5
        funnle1 = physics.body(CHAIN,false,vec2(-gSize.x,gSize.y),
                                    vec2(-funnleSize,-gSize.y/3),
                                    vec2(-funnleSize,-gSize.y)
                                    )
        local funnle2 = physics.body(CHAIN,false,vec2(gSize.x,gSize.y),
                                    vec2(funnleSize,-gSize.y/3),
                                    vec2(funnleSize,-gSize.y)
                                    )
        local split = physics.body(CHAIN,false,vec2(-WIDTH/2,-HEIGHT/8),
                                    vec2(0,HEIGHT/80),
                                    vec2(WIDTH/2,-HEIGHT/8)
                                    )
        funnle1.x = WIDTH / 2
        funnle1.y = HEIGHT - HEIGHT / 4
        funnle1.type = STATIC
        funnle2.x = WIDTH / 2
        funnle2.y = HEIGHT - HEIGHT / 4
        funnle2.type = STATIC
        split.x = WIDTH / 2
        split.y = HEIGHT/8
        split.type = STATIC
        split.restitution = .3
        table.insert(fixtures,funnle1)
        table.insert(fixtures,funnle2)
        table.insert(fixtures,split)
     
        blankImage = image(WIDTH,HEIGHT)
        blur = 30
        water = {}
        waterimage = softImage(color(0, 155, 255),5,blur)
        allWater = blankImage
     
        --[[waterMesh = mesh()
        waterMesh.texture = waterimage]]--
     
        mDisplay = mesh()
        mDisplay:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
        mDisplay.shader = shader("Documents:Alpha Threshold")
        mDisplay.shader.smoothness = 0
        mDisplay.shader.threshold = .1
        mDisplay.shader.unpremultiply = 1
    end
     
     
    function draw()
        background(0, 0, 0, 255)
        physics.gravity(Gravity/10)
        smooth()
     
        allWater = image(WIDTH,HEIGHT)
        print(allWater.data)
        setContext(allWater)
        noStroke()         
        smooth()
        for i,v in ipairs(water) do
            sprite(waterimage,v.body.x,v.body.y,blur,blur)
            --waterMesh:addRect(v.body.x,v.body.y,blur*extraSpace,blur*extraSpace)
            if v.body.x <= 0 or v.body.x >= WIDTH or v.body.y <= 0 or v.body.y >= HEIGHT then
                v.body:destroy()
                table.remove(water,i)
            end
        end
     
        --waterMesh:draw()
        --waterMesh:clear()
        setContext()
     
        mDisplay.texture = allWater
        mDisplay:draw()
     
        strokeWidth(3)
        for i,v in ipairs(fixtures) do
            pushMatrix()
            translate(v.x,v.y)
            local points = v.points
            for j = 1,#points-1 do
                a = points[j]
                b = points[(j % #points)+1]
                line(a.x, a.y, b.x, b.y)
            end
            popMatrix()
        end
        popMatrix()
    end
     
    function touched(t)
        addWater(10,t.x,t.y)
    end
     
    function softImage(f,r,s)
        simg = image(r,r)
        setContext(simg)
        pushStyle()
        noStroke()
        fill(f)
        ellipse(simg.width/2,simg.height/2,simg.width)
        popStyle()
        setContext()
        limg = image(s,s)
        setContext(simg)
        pushStyle()
        smooth()
        sprite(simg,0,0,s,s)
        popStyle()
        setContext()
        return simg
    end
     
    function addWater(amnt,x,y)
        local posRand = 20
        local vRand = 50
        for i = 1,amnt do
            local b = physics.body(CIRCLE,2)
            b.x = x + math.random(-posRand,posRand)
            b.y = y + math.random(-posRand,posRand)
            b.linearVelocity = vec2(math.random(-vRand,vRand),math.random(-vRand,vRand))
            b.restitution = .1
            table.insert(water,{body = b})
        end
    end

    Thanks!

  • Posts: 1,595

    I couldnt find a way around this hurdle, i tried to set my image to a screen size image of 0 alpha pixels every way i could imagine but they didnt work, i just stuck with image(WIDTH,HEIGHT)

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Luatee - how about this

    Forget about drawing a fullsize image on each draw, that will always be slow. Instead, add your water drops to a mesh (with the shader attached, of course), and on each draw, just update their x,y position, add and remove them, etc. Then just draw the mesh.

    This avoids drawing the image twice (once to a hidden image and then to the screen).

    PS you forgot to include the shader code, but I found it in another post because I recognised the code.

  • edited July 2013 Posts: 2,820

    @Ignatz - The issue is that if I add the droplets to a mesh as rectangles, then the shader will only apply to each drop individually. Then you won't get the water effect. If I could access the data aspect of the mesh, that'd be great, but I can't. It returns nil.

  • IgnatzIgnatz Mod
    edited July 2013 Posts: 5,396

    @Zoyt - ok, then how about this

    Instead of drawing a full screen image, you draw it exactly the size you need, by first looping through the drops and calculating the min and max x and y values. Then you draw to that size image. Below is my draw routine that does this. The drops don't stay in the funnel for some reason, but it does seem faster

    function draw()
        background(0, 0, 0, 255)
        physics.gravity(Gravity/10)
        smooth()
        --get min and max values
        local minX,minY,maxX,maxY=9999,9999,0,0
        for i,v in ipairs(water) do
            if v.body.x>maxX then maxX=v.body.x end
            if v.body.x<minX then minX=v.body.x end
            if v.body.y>maxY then maxY=v.body.y end
            if v.body.y<minY then minY=v.body.y end
        end
        if minX>0 then
            --min and max values are at centre of drops, allow space for width of drop
            minX,minY=minX-blur/2,minY-blur/2
            maxX,maxY=maxX+blur/2,maxY+blur/2
            --draw exact size of image required
            local w,h=maxX-minX,maxY-minY
            allWater = image(w,h)
            setContext(allWater)
            noStroke()         
            smooth()
            for i,v in ipairs(water) do
                --waterMesh:addRect(v.body.x,v.body.y,blur*extraSpace,blur*extraSpace)
                if v.body.x <= 0 or v.body.x >= WIDTH or v.body.y <= 0 or v.body.y >= HEIGHT then
                    v.body:destroy()
                    table.remove(water,i)
                else
                   --adjust position by minX, minY
                    sprite(waterimage,v.body.x-minX,v.body.y-minY,blur,blur)
                end
            end
            setContext()
            --adjust rectangle to minX, minY
            mDisplay:setRect(1,minX-w/2,minY-h/2,w,h)
            mDisplay.texture = allWater
            mDisplay:draw()
        end
    
        strokeWidth(3)
        for i,v in ipairs(fixtures) do
            pushMatrix()
            translate(v.x,v.y)
            local points = v.points
            for j = 1,#points-1 do
                a = points[j]
                b = points[(j % #points)+1]
                line(a.x, a.y, b.x, b.y)
            end
            popMatrix()
        end
        popMatrix()
    end
    
  • edited July 2013 Posts: 391

    @Ignatz, that works well except for one thing. Change the following:

    mDisplay:setRect(1,minX-w/2,minY-h/2,w,h)
    

    To

    mDisplay:setRect(1,minX+w/2,minY+h/2,w,h)
    

    Now the water stays where it should.

    Edit: something else is a bit funky too. When the first water drop goes off screen, it clears the whole image. Math.min and math.max functions will fix this, also removing the if statement with minx > 0.

  • Posts: 391
    function draw()
        background(0, 0, 0, 255)
        physics.gravity(Gravity/10)
        smooth()
        --get min and max values
        local minX,minY,maxX,maxY=9999,9999,0,0
        for i,v in ipairs(water) do
            if v.body.x>maxX then maxX=v.body.x end
            if v.body.x<minX then minX=v.body.x end
            if v.body.y>maxY then maxY=v.body.y end
            if v.body.y<minY then minY=v.body.y end
        end
        --min and max values are at centre of drops, allow space for width of drop
        minX,minY=math.max(minX-blur/2,0),math.max(minY-blur/2,0)
        maxX,maxY=math.min(maxX+blur/2,WIDTH),math.min(maxY+blur/2,HEIGHT)
        --draw exact size of image required
        local w,h=maxX-minX,maxY-minY
        allWater = image(w,h)
        setContext(allWater)
        noStroke()
        for i,v in ipairs(water) do
            --waterMesh:addRect(v.body.x,v.body.y,blur*extraSpace,blur*extraSpace)
            if v.body.x <= 0 or v.body.x >= WIDTH or v.body.y <= 0 or v.body.y >= HEIGHT then
                v.body:destroy()
                table.remove(water,i)
            else
                --adjust position by minX, minY
                sprite(waterimage,v.body.x-minX,v.body.y-minY,blur,blur)
            end
        end
        setContext()
        --adjust rectangle to minX, minY
        mDisplay:setRect(1,minX+w/2,minY+h/2,w,h)
        mDisplay.texture = allWater
        mDisplay:draw()
    
        strokeWidth(3)
        for i,v in ipairs(fixtures) do
            pushMatrix()
            translate(v.x,v.y)
            local points = v.points
            for j = 1,#points-1 do
                a = points[j]
                b = points[(j % #points)+1]
                line(a.x, a.y, b.x, b.y)
            end
            popMatrix()
        end
    end
    
  • Posts: 2,161

    To answer the original question, use background. This forces everything to the specified colour including alpha. So background(0,0,0,0) makes the image transparent black.

  • Posts: 2,820

    Thanks guys. You've been super helpful. @Andrew_Stacey's answer was perfect.
    Thanks!

  • Posts: 1,595

    @Andrew_Stacey I tried using background(0,0) with setContext before but it didn't work, yours seems to work though

  • IgnatzIgnatz Mod
    Posts: 5,396

    Unless you provide all four values, Codea assumes alpha is 255

  • Posts: 1,595

    Aha, that would be it, even though it comes up with grey,alpha it must ignore the alpha

  • SimeonSimeon Admin Mod
    Posts: 4,956

    @Luatee sounds like a bug. Thanks for the find.

Sign In or Register to comment.