Log In  

Cart #gipuwesuno-0 | 2020-02-12 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1

We were fiddling with another project and realized that, because we were using sspr() to resize images, we didn't actually know where all the pixels were - and we wanted to know, because we wanted to cast a shadow that was the colors of what was below but in shadow. And we realized that PICO-8 lets you look at the screen in the code, and then the concept of green screen popped into our head, and then we got to work.

It's not minimized and it's not optimized - if you tell it to chromakey the whole screen, it'll chew through the entire CPU budget with change (ask us how we know!) - but I think it's readable enough that people can hack on it. We haven't tested it extensively, but we made sure it respected the current clipping rectangle and restored it before it exited, because that seemed like the correct thing to do.

function chromakey(drawfunc,x,y,w,h,c)
    -- replaces pixels of c in rectangle x,y,w,h with output of drawfunc
    -- c defaults to 3
    if type(c) == "nil" then
        c = 3
    end
    -- load prior clip state
    local clip_x0=peek(0x5f20)
    local clip_y0=peek(0x5f21)
    local clip_x1=peek(0x5f22)
    local clip_y1=peek(0x5f23)

    -- squeeze chromakey rectangle into clipping region
    x = max(x,clip_x0)
    y = max(y,clip_y0)
    w = min(w,clip_x1-x)
    h = min(h,clip_y1-y)

    clip()
    for xs = x,x+w-1 do
        local ys = y
        local y0 = y
        local chroma = false
        while ys < y+h do
            if not chroma and pget(xs,ys) == c then
                -- start detecting
                chroma = true
                y0 = ys
            end
            if chroma and pget(xs,ys) ~= c then
                -- stop detecting and draw rectangle
                clip(xs,y0,1,ys-y0)
                drawfunc()
                chroma = false
                clip()
            end
            ys += 1
        end
        if chroma then
            -- c extended to bottom of column
            clip(xs,y0,1,y+h-y0)
            drawfunc()
            clip()
        end
    end

    -- reset clipping region
    clip(clip_x0,clip_y0,clip_x1-clip_x0,clip_y1-clip_y0)
end
P#73022 2020-02-12 16:36

:: sparr

You're calling drawfunc() for every horizontally adjacent set of chroma pixels, and letting it do its entire draw?

I would propose requiring drawfunc() to take x,y,w,h parameters so you can tell it where it's drawing and it can just draw those pixels.

P#73026 2020-02-12 17:06
:: sparr
1

Here's a version with x2/y2 instead of w/h, plus my proposal above, and some token optimization.

function chromakey(drawfunc,x1,y1,x2,y2,c)
    -- replaces pixels of c in rectangle x1,y1,x2,y2 with output of drawfunc
    -- c defaults to 3
    c = c or 3
    -- load prior clip state
    local clip_x1, clip_y1, clip_x2, clip_y2 = peek(0x5f20), peek(0x5f21), peek(0x5f22), peek(0x5f23)

    -- squeeze chromakey rectangle into clipping region
    x1, y1, x2, y2 = max(x1, clip_x1), max(y1, clip_y1), min(x2, clip_x2 + x1), min(y2, clip_y2 + y1)

    for ys = y1, y2 do
        local x0, chroma = x1
        for xs = x1, x2 do
            if pget(xs, ys) == c then
                if not chroma then
                    -- start detecting
                    chroma = 1
                    x0 = xs
                end
            elseif chroma then
                -- stop detecting and draw rectangle
                clip(x0, ys, xs - x0, 1)
                drawfunc(x0, ys, xs - 1, ys)
                chroma = nil
                clip()
            end
        end
        if chroma then
            -- c extended to end of row
            clip(x0, ys, x2 - x1 - x0, 1)
            drawfunc(x0, ys, x2, ys)
            clip()
        end

    end

    -- reset clipping region
    clip(clip_x1, clip_y1, clip_x2 - clip_x1, clip_y2 - clip_y1)
end
P#73027 2020-02-12 17:43 ( Edited 2020-02-15 23:54)

Ooh, that's really clean - thanks! Quick test suggests it's ~40% faster, which is terrific.

And yeah, that's a good change - I'd tried passing the clipping window info to the drawfunc at first, but it made no perceptible difference for a single sspr() or spr() command so I cut it out - could definitely be a big difference for some draw functions, though. It should definitely be in the library version of the function.

Edit: do you want the cart in the OP updated with your function?

P#73028 2020-02-12 18:20 ( Edited 2020-02-12 18:21)
:: sparr

No preference. Do you mind if I put my version in https://github.com/sparr/pico8lib ?

P#73051 2020-02-13 06:14
1

demo effect is nice but per-pixel operations for masking is really a cpu killer. suggest to look at peek/poke and bitmasking instead...

P#73055 2020-02-13 07:20 ( Edited 2020-02-13 07:27)
:: sparr

peek/poke and bitmasking seem like they would be good for predetermined screening patterns, while the approach here is for arbitrary shapes.

P#73057 2020-02-13 10:19

@sparr Yes, please - we actually originally posted it with the thought that a chroma key function would be a good addition to the library.

@freds72 Yeah, this is a CPU-expensive general case for CPU-unconstrained carts and times when general-case is the best way to do it. A lot of cases, including the one that inspired me to write it, have leaner solutions.

P#73062 2020-02-13 13:06 ( Edited 2020-02-13 13:07)

@sparr: just noticed that the line to squeeze chromakey rectangle into clipping region has min(y2, clip_y2 + x1)

should be min(y2, clip_y2 + y1)

P#73140 2020-02-15 22:01

[Please log in to post a comment]

Follow Lexaloffle:        
Generated 2020-02-21 07:28 | 0.060s | 4194k | Q:51