image:copy, image:set, and setContext weirdness?

First off, I think the doc for image:copy is wrong. It says the x and y params are top left corner of the copy area. It’s in fact the bottom left. Or maybe it’s affected by xxxxMode()?

Second, there’s something funky here. Maybe it’s something I shouldn’t do and I don’t know about it.

... -- call to several draw operations
copiedArea = img:copy(...)
sprite(copiedArea, ...)
... -- more drawing calls

The img above is displayed correctly with the changes in all draw loop. But then as soon as I do img:set(...), right after that (e.g as part of reacting to touch), whatever changes I had made in the code above to img is strangely reverted like nothing had happened.

What did I not know about these combination of calls?

@tsukit I’m not sure the img:copy inside the setContext pair will work. I don’t think img is complete until the closing setContext.

@tsukit Can you give a brief description of what you’re trying to do. Maybe I can give you an example based on that. From what you show above, I’m not sure what’s going on.

@tsukit I don’t think you can use raw pixel access during a setContext call on the image you are rendering to. Because when the image is put up in setContext it is used as an OpenGL buffer, in order to access the raw pixel data that buffer has to be flushed from the GPU memory copied back into main memory.

The closest you could get would be to save the image from the previous frame and access that when needed for the current frame’s image.

Thanks @Simeon and @dave1707. I think at this point I want to understand how it happens, not as much what caused it. Here’s a sample code that demonstrates the problem. After the first touch, you see the copied area shows up correctly. But then after the second touch, the copied area is gone

function setup()
    img = image(100,100)
    background(255, 255, 255, 255)
    step = 0

function touched(touch)
    if touch.state == BEGAN then
        if step == 0 then
            setContext(img) -- if i move this line to after img:copy below, then no problem
            local copiedArea = img:copy(0, 0, 50, 50)
            sprite(copiedArea, 0, 50)
            step = 1
            img:set(1, 1, 0, 0, 0) -- this cause the content of the image to revert

function draw()
    background(40, 40, 50)

    sprite(img, WIDTH/2, HEIGHT/2, HEIGHT/2, HEIGHT/2)

@tsukit I think there’s a difference between the img that was originally created in setup() and the img used with setContext(img). If you put setContext(img) and setContext() around the img:set code and change the 1’s in img:set to 10 so the black square shows farther into the image, then you’ll see the black square along with the correct image. But when using img:set without setContext, then I think its reverting to the original img created in setup. I’m still trying to figure out a way to prove this.

Thanks @dave1707 . If they are indeed different img, that would be very interesting.

@tsukit I don’t think they’re different in a way that you can use one or the other. In you’re code above, doing an img:set with and without the setContext statements around it showed different images. So depending on how img is used was giving a different result.

Just to be clear. Anything to do with manually copying or setting pixels within a setContext() call is undefined.

We maintain a local copy of an image as an array of bytes, at certain points data is copied into and out of the GPU when required (like after setContext is used, or after set is used). Trying to access those bytes while also drawing to the image means that those two things are not in sync and so changes may be overwritten, ignored or discarded.

@John Thanks for the explanation. I think that’s what I was trying to explain but I wasn’t sure how things were working within the setContext pair.

@dave1707 and @John thanks for the explanation. I think that’s something important to add into the reference doc so everyone is aware of it – in addition to the correction on the description of x, y params of image:copy() as I mentioned in the original post.