Adding a shadow or glow: Do I have the right concept?

Hello,
I love the glow on @John’s awsome Stack3r app. So I though I might as well make a shadow/glow class for images. But I want to make sure I’ve got the concept right. Se here’s some really hacky code that I made to confirm that:



-- Use this function to perform your initial setup
function setup()
    img = image(5,5)
    fill(0, 0, 0, 118)
    strokeWidth(0)
    setContext(img)
    ellipse(2.5,2.5,5,5)
    setContext()
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,CurrentTouch.x-20,CurrentTouch.y-20,250,250)
    fill(0, 0, 0, 255)
    strokeWidth(0)
    ellipse(CurrentTouch.x,CurrentTouch.y,200,200)
end

Is that correct? Thanks!

I must admit I have no real idea what this code is supposed to do …

Yes, I can imagine it, but it’s …

Let’s start again, I got stuck.

I think John is using special purpose code for shadows or glows. He knows that he is drawing rectangles, so he can draw a shadow by drawing a set of overlaid rectangles, starting with a wide and light stroke, then narrowing it down and making it darker.

Maybe you’re asking for some kind of universal function (and I really mean function, no need for a class, if that detail is of some importance), so I tried this little demo.

There are two functions, one blurs an image, the other lightens the color, both make up for a nice shadow. Move a bulb with your finger and see the shadow of the prepared image wander.

Note that I’ve only written demo functions, but they might be a good start if you want to use them in some real project.

Another note: I have to admit that I didn’t run it on a real iPad, so I don’t know how fast the blur algorithm is. Setting blur_q = 4 makes a nicer blur but takes longer to calculate.


-- increase blur_q for more blur
blur_q = 2
fill_circle = false

function setup()
    img = makeMasterImage()
    shadow = lighten(blur(img))
end

-- Returns a (filled) circle as an image
function makeMasterImage()
    local img = image(60, 60)
    if fill_circle then
        fill(255, 255, 255)
    else
        fill(255, 255, 255, 0)
    end
    setContext(img)
    strokeWidth(3)
    stroke(0)
    -- leave some space for blurring
    ellipse(30, 30, 50, 50)
    setContext()
    return img
end

-- Not very precise blur algorithm.
-- In img   Image to blur
-- Returns  Blurred image
function blur(img)
    local w = img:getWidth()
    local h = img:getHeight()
    local blr = image(w, h)
    local q = blur_q
    local div = q * q

    -- start at 0 because of xi, yi
    for x = 0, w-q do
        for y = 0, h-q do
            -- sum of color components
            local sr, sg, sb, sa = 0, 0, 0, 0
            for xi = 1, q do
                for yi = 1, q do
                    local r, g, b, a = img:get(x + xi, y + yi)
                    sr = sr + r
                    sg = sg + g
                    sb = sb + b
                    sa = sa + a
                end
            end
            sr = sr / div
            sg = sg / div
            sb = sb / div
            sa = sa / div
            blr:set(x, y, sr, sg, sb, sa)
        end
    end

    return blr
end

-- Lighten an image by reducing the color distance to white by half.
-- In img   Image to lighten
-- Returns  Lightened image
function lighten(img)
    local w = img:getWidth()
    local h = img:getHeight()
    local li = image(w, h)

    for x = 1, w do
        for y = 1, h do
            local r, g, b, a = img:get(x, y)
            r = 255 - (255 - r) / 2
            g = 255 - (255 - g) / 2
            b = 255 - (255 - b) / 2
            -- don't do this with alpha
            li:set(x, y, r, g, b, a)
        end
    end

    return li
end

function draw()
    background(255, 255, 255)
    local mainx, mainy = WIDTH / 2, HEIGHT / 2
    -- A bulb
    local bulbx, bulby = CurrentTouch.x, CurrentTouch.y
    -- Distance of bulb to shape, scaled down a bit
    local dx = (bulbx - mainx) / 10
    local dy = (bulby - mainy) / 10
    local d = math.sqrt(dx * dx + dy * dy)
    -- f: scale factor of shadow, shadow grows with bulb distance
    local f = 1 + d / 100
    -- Move shadow away from bulb
    sprite(shadow, mainx - dx, mainy - dy, shadow:getWidth() * f, shadow:getHeight() * f)
    sprite(img, mainx, mainy)
    -- bulb
    fill(255, 255, 100)
    strokeWidth(0)
    ellipse(bulbx, bulby, 50, 50)
end

Is this of your liking?

On an iPad you get

error: [string "..."]:33: attempt to call method 'getWidth' (a nil value)
Pausing playback

instead of img:getWidth use img.width, that Works! Looks nice

@Codeslinger - You need to do what @Inviso said. But anyways, that looks awesome, but your method is not how I would do it in my program. So, to get a smooth shadow no matter how large it is, you need to use smooth(). But smooth() wont work when just scaling an image, so you have to redraw the image over and over again to get a smooth shadow. But I will take this code into my class. Thanks a lot!