How can I get the color of a screen pixel x,y?

i want to make some sort of color picker. Where you touch the screen and it reads the color at the touch coordinates.
Get.image only seems to read colors in an image.

You need to draw everything into an image in order to be able to get a color. In setup, create an image

screen = image(WIDTH, HEIGHT)

at the start of draw, use setContext to route drawing into it:

setContext(screen)

Everything you draw after this line will now be put inside of the screen image variable.

At the end of draw, call setContext with no parameters to restore drawing to the actual screen, and sprite the screen image to display everything you drew.

setContext()
sprite(screen, WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT)

Note that if you change orientations this might break, you can listen for orientation changes like this to fix it by re-making the screen variable:

function orientationChanged(newOrientation)
   screen = image(WIDTH, HEIGHT)
end

Once you’ve got that, you can get a color at the touch point using something like this:

function touched(touch)
    local col  = color(screen:get(math.floor(touch.x), math.floor(touch.y)))
    -- `color` is now the color of the screen at the touched point
end

Look at :
http://gregory.thomas.free.fr/codeautil.html/gui.html#

This is how I suggest you do it, drawing to an image in memory as SkyTheCoder suggests.

function setup()
    img=image(WIDTH,HEIGHT) --create image to draw on
end

function draw()
    DrawScreen() --see note underneath
end

function DrawScreen()
    --do all your drawing here
    background(100)
    sprite("Planet Cute:Character Princess Girl",WIDTH/2,HEIGHT/2,500)
    --prove the touch works by showing a coloured circle
    if touchColour then 
        fill(touchColour)
        ellipse(100,100,100)
    end
end

function touched(t)
    if t.state==ENDED then 
        setContext(img)
        DrawScreen()
        setContext()
        local r,g,b=img:get(math.floor(t.x),math.floor(t.y))
        touchColour=color(r,g,b)
    end
end

You can’t read a pixel colour off the screen because Codea doesn’t remember what it draws. So you need to redraw the screen, in an image in memory, then you can read the pixel colour.

This means drawing everything twice. So I put all the drawing code in its own function DrawScreen, to make redrawing easy.

looks like you can even do this (shorter) with your principle:

function setup()
    img=image(WIDTH,HEIGHT) --create image to draw on
end

function draw()
    --do all your drawing here
    background(100)
    sprite("Planet Cute:Character Princess Girl",WIDTH/2,HEIGHT/2,500)
    --prove the touch works by showing a coloured circle
    if touchColour then 
        fill(touchColour)
        ellipse(100,100,100)
    end
end

function touched(t)
    if t.state==ENDED then 
        setContext(img)
        draw()
        setContext()
        local r,g,b=img:get(math.floor(t.x),math.floor(t.y))
        touchColour=color(r,g,b)
    end
end

lol, of course, draw is just a function

I think there is some value making a separate drawing function though, because the draw function doesn’t just draw, it manages the app, so there may be other code in there that you don’t want to run twice.

Incidentally, you can make this run faster by only drawing to a 3x3 image, like so (changes are marked)

function setup()
    img=image(3,3) --CHANGED
end

function draw()
    --same as before
end

function touched(t)
    if t.state==ENDED then 
        setContext(img)
        translate(-t.x+1,-t.y+1) --CHANGED
        draw()
        setContext()
        local r,g,b=img:get(2,2) --CHANGED
        touchColour=color(r,g,b)
    end
end

Or you can use clip() to restrict the drawing area (but I’m sure tyxs doesn’t need anything this fancy).

nice trick

Thank you all for the thorough explanations and the generous snippets of code!
The screen handling in Codea is a bit unusual for me, but I get the thought behind it.
I always assumed that devices store the screen in a chunk of data where the video processor grabs it x times per second to beam it on the screen. what I’m learning here is that the hardware doesn’t need such a intermediate step and Codea gains speed by not storing screen data by default.
Thanks for the elegant solutions suggested! In particular I like the idea of using one cycle of the draw function to rebuild the screen in an image.

@Ignatz: on your last suggestion. I might need to get fancy if I needed the speed. For instance when I want to update the color in a drag screen touch instead of a tap.

One other thing that surprises me is that the touch coordinates are expressed in floating point numbers instead of integers. Can anyone explain why that is?

touch coords probably come that way in the Apple SDK.

For speed, I would use the 3 pixel image code above.

If you want to learn more about Codea works, I’ve written an ebook on it, here.

Thanks Ignatz. Your ebooks are very helpful. Now, I just have to continue coding and get used to the syntax. I’m still blundering through every line of code.
:slight_smile:

Don’t worry, I still do that now, but instead of syntax, it’s the math…

(When you get settled down, have a look at shaders. They’re awesome. I have an ebook on them too).

I know what you mean. I’m plowing through school geometry lessons on YouTube all the time. I haven’t used that stuff in 30 years!
I already plunged into the shaders ebook. I’m in way over my head but that’s okay.

@tyxs Here’s an example I had laying around.

EDIT: Slide your finger around the triangle.

supportedOrientations(LANDSCAPE_ANY)

function setup()
    rectMode(CENTER)
    parameter.integer("red",0,255,128)
    parameter.integer("green",0,255,128)
    parameter.integer("blue",0,255,128)
    parameter.integer("alpha",0,255,255)
    tx,ty=0,0
    img=image(WIDTH,HEIGHT)
    m=mesh()
    m.vertices={vec2(10,100),vec2(WIDTH-10,100),vec2((WIDTH-20)/2,HEIGHT/2+100)}
    m:setColors(255,255,255,255)
    m:color(1,255,0,0)
    m:color(2,0,255,0)
    m:color(3,0,0,255)
end

function draw()
    background(0)
    setContext(img)
    m:draw()  
    setContext()
    sprite(img,WIDTH/2,HEIGHT/2)
    fill(red,green,blue,alpha)
    rect(WIDTH/2,HEIGHT-150,400,200)
    fill(255)
    ellipse(tx,ty,10)
end

function touched(t)
    tx=math.floor(t.x)
    ty=math.floor(t.y)
    red,green,blue,alpha=img:get(tx,ty) 
end

@tyxs touch is floating point, because with retina devices Apple decided not to increase the number of points, but to make half point coordinates. Inceasing resolution means floating point then.

i couldnt understand translate(-t.x+1,-t.y+1) --CHANGED

to be faster, ignatz draws into a 3x3 image. Then the region you touch draws outide the image => by shifting the draw as he does, you draw the touched region right in the middle of the 3x3 image. So now you can read the pixel value.
Got it?

i could understand other statements?but translate statement means from what position to what positon?

Why not try it for yourself?

Draw a circle, then translate a little and draw it again. That way you will understand for yourself.

@dave1707: Great! That works like a charm!

@Jmv38: I get it! I noticed that the values are either whole numbers or .5

why not be so?

function setup()
    img=image(1,1) --CHANGED
end

function draw()
    --same as before
end

function touched(t)
    if t.state==ENDED then 
        setContext(img)
        translate(-t.x,-t.y) --CHANGED
        draw()
        setContext()
        local r,g,b=img:get(1,1) --CHANGED
        touchColour=color(r,g,b)
    end
end