draw hole in image context?

is it possible to erase data from an image. I draw a rect to an image context, and then try to use noFill() to draw a hole in the rect, but nothing seems to happen. any ideas?

yes, but it’ll be painful.

In a nutshell, draw your image, and when you’re done, you can img:set(x, y, color(0,0,0,0)) to poke a 1-pixel hole in it. The 4th argument to color() is the alpha - 0 is transparent.

So - to poke a circular hole in an image, you’d draw it as normal, then use img:set to remove the pixels you want to be transparent.

maybe draw a circle with the image set to the context and fill set to (0,0,0,0)? Or will that just not change the image at all?

when you draw it under setContext, you’d adding the zero-alpha circle to the image - so the result would be the original image. @tnlogy wants to poke a hole - he wants what you get from png and a spritepack.

the image:set and image:get work on the bitmap - no context.

What we need, maybe, is image:and and/or image:or and/or image:not, that would do booleans on the actual pixels of a bitmap - so you could just make a mask image, then not it.

+1 for image and sprite boolean operation. :slight_smile:

Actually, I tried with image:set also, but it didn’t work. Would be nice with a function to define alpha mask functionality, if it should add values or replace alpha value.

This works for me:


-- demo of shaped image

-- Use this function to perform your initial setup
function setup()
    img = image(100,100)
    setContext(img)
    background(255, 127, 0, 255)
    setContext()
    for x=25,75 do
        for y=25,75 do
            img:set(x,y,color(0,0,0,0))
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
    sprite(img, WIDTH/2, HEIGHT/2)
    
end

A better way to do it might be to make a black and white mask image, draw into it, then use image:get and image:set to cut out your “real” image.

Thanks for the help, any idea why it doesnt work in this example?


function setup()
    displayMode(FULLSCREEN)
    supportedOrientations(LANDSCAPE_ANY)
    strokeWidth(10)
    Degree = 0
    page1 = image(400,600)
    setContext(page1)
    fill(212, 161, 84, 255)
    rect(0,0,400,600)
    sprite("Planet Cute:Star",200,300)
    setContext()
    for i=135,208 do
        for j=101,130 do
            page1:set(i,j,color(0,0,0,0))
        end
    end
    
    page2 = image(400,600)
    setContext(page2)
    fill(210, 68, 211, 255)
    rect(0,0,400,600)
    sprite("Planet Cute:Tree Tall",200,300)
    setContext()
    
    index = {
        [2] = "MÃ¥ndag",
        [4] = "Tisdag",
        [7] = "Onsdag",
        [13] = "Torsdag",
        [16] = "Fredag",
        [18] = "Lördag",
        [19] = "Söndag"
    }
end

function drawPage(d, i)
    local z = d
    local x = (i-z)*0.5
    if z > 90 then z = 90-z end
    d = math.min(math.max(0,d),180)
    pushMatrix()
    translate(x,0,z)
    rotate(-d, 0,1,0)
    if d > 90 then 
        sprite(page2,200,0)
    else
        sprite(page1,200,0)
    end
    local l = index[i]
    if l then
        font("AmericanTypewriter-Bold")
        fontSize(42)
        local w,h = textSize(l)
        translate(400,200-w+40-math.random(0,200))
        strokeWidth(0)
        fill(115, 92, 64, 255)
        rect(0,0,50,w+50)
        fill(255, 255, 255, 255)
        translate(24, (w+50)/2,10)
        rotate(-90)
        text(l,0,0)
    end
    popMatrix()
end

function touched(touch)
    if touch.state == MOVING then
        Degree = Degree - touch.deltaX * 2
    end
end

function draw()
    background(206, 206, 215, 255)
    camera(0,0,1200, 0,0,0)
    perspective(45)
    math.randomseed(10)
    for i=1,20 do
        drawPage(Degree-140*i,i)
    end
end

Hi @tnlogy, tried out your code and it worked, the small rectangle, drawn on the front page changes to the background colour as you change it. Since you ren’t using z layers that’s the effect you can expect.

Bri_G

:slight_smile:

Hi @Bri_G. What do you mean by that I’m not using z layers? If I want the second page to be seen through the first page, any idea how to achieve it?

Hi @tnlogy,

you need to talk to one of the experts - I’m not sure it’s implemented here but in theory you can build up graphics in layers in the same way you can with vector diagrams. This enables you to retain graphics in each layer - so if you make a hole in one you should be able to see the layer below through it. This was used a lot in early scrolling games with graphics and sprites to give the impression of perspective. Here is the info from the on-line documentation:

zLevel( z )
Description

Sets the z level of future drawing operations. Negative values mean the drawing will occur behind (further into the screen), positive values will cause drawing to happen in front. By default all drawing will occur above previous drawing operations.

This property is pushed onto the matrix stack with pushMatrix()
Syntax

zLevel( z )

Parameters
Name Description
z float, the amount of depth for future drawing operations, use positive values to draw in front, and negative values to draw behind.

I think you should try to play around with this - let us know how you get on as I think this could be a powerful tool.

Bri_G

:slight_smile:

Oh, but isnt that the same as translate(0,0,z)? This small example works, but havent figured out the difference…



-- demo of shaped image

-- Use this function to perform your initial setup
function setup()
    img = image(100,100)
    setContext(img)
    fill(255, 127, 0, 255)
    rect(0,0,100,100)
    setContext()
    for x=25,75 do
        for y=25,75 do
            img:set(x,y,color(0,0,0,0))
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(19, 19, 209, 255)
    

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
    camera(0,0,1200, 0,0,0)
    perspective(45)
    pushMatrix()
    translate(0,0,0)
    rotate(-45, 0,1,0)
    sprite(img, 0, 0)
    popMatrix()
    translate(0,0,20)
    rotate(-20, 0,1,0)
    sprite(img, 40, 40)
    
end

In draw(), change your for statement from “1,20” to “20,1,-1” and your effect works.

The issue you’re seeing is due to the fact that depth in 3d graphics is still very much an illusion - if you want it to handle transparency correctly, you need to draw things furthest from the camera first. When you draw your stack from to to bottom, the first card shows what’s behind the hole when it’s drawn - the other 19 cards aren’t there yet, so your hole just shows background. Drawing the next card, behind the first one, the 3d engine clips things properly - but it doesn’t know to update the hole, because that’s in a region already drawn - the new card is behind that, so it clips. To put it another way, you know about the transparency - but the 3d engine does not. As far as it knows, you drew a rectangle.

If you drew each card as 4 regions - a top rectangle, two side rectangles, and a bottom rectangle (ie. leaving a hole, rather than carving one out), you might see the effect you’re after, but it would only work for rectangular holes, and managing draw order is probably easier.

i had this same issue once, maybe that clears it for you…
i had trees (pngs with alpha a the corners) and you could rotate the view…

so when the camera was in the right position, all was fine. the trees were behind each other and the alpha was nice… but when you turned the camera to the other side, all the pngs werent “cut out” properly, you saw the rectangles and it wouldnt draw the tree behind another tree…
so if you can change the code that the last page is drawn first and then the first pages, you’ll get the desired effect.

in opengl i think you can turn something like zBuffer on, the it would work too, but i don’t know if lua/codea supports that?

@Inviso Codea uses a depth buffer for rendering, but as @Bortels explains, this only works for solid geometry. Any polygons rendered with transparency will need to be drawn back-to-front (this is just how OpenGL works — the depth buffer, or z buffer, doesn’t know about the transparency of individual pixels.)

@Simeon ah yes, i remember, i think i did something like glEnable(GL_ALPHA_TEST) with glAlphaFunc(GL_GREATER, 0.1f) or so… don’t have the code at hand, but i remember my “workaround” was the same as Bortels describes, i used more primitives…

Thanks for the explanations! Nice to get a logical answer. :slight_smile:

Another solution would be to discard pixels in SpriteShader, like on this page? If the alpha is just true or false. http://www.opengl.org/wiki/Transparency_Sorting or maybe that’s not possible on opengl es?

@tnlogy use of discard in shaders on iOS (including GL_ALPHA_TEST) is discouraged by Apple. From my understanding it incurs a performance penalty on PowerVR hardware due to its tile-based rendering.

For reference, here are Apple’s notes on the subject. (From: https://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/Performance/Performance.html)

Avoid Alpha Test and Discard

Graphics hardware often performs depth testing early in the graphics pipeline, before calculating the fragment’s color value. If your application uses an alpha test in OpenGL ES 1.1 or the discard instruction in an OpenGL ES 2.0 fragment shader, some hardware depth-buffer optimizations must be disabled. In particular, this may require a fragment’s color to be completely calculated only to be discarded because the fragment is not visible.

An alternative to using alpha test or discard to kill pixels is to use alpha blending with alpha forced to zero. This effectively eliminates any contribution to the framebuffer color while retaining the Z-buffer optimizations. This does change the value stored in the depth buffer and so may require back-to-front sorting of the transparent primitives.

If you need to use alpha testing or a discard instruction, draw these objects separately in the scene after processing any primitives that do not require it. Place the discard instruction early in the fragment shader to avoid performing calculations whose results are unused.

Ah, there is an explanation for everyhing! Thanks!