Graphics Aren't the Same on Different iPads

Hey, so I’ve been working on a project on my iPad Mini, and I decided to test it on my iPad Air too. The code was the same, but the image was different. Here’s my code:

-- Scale Test

function setup()
    size = 7
    img = image(size,size)
    setContext(img)
    noSmooth()
    ellipse(size/2,size/2,size)
    setContext()
    for i = 1,size do
        for j = 1,size do
            r,g,b,a = img:get(i,j)
            if a > 0 then
                img:set(i,j,color(255))
            end
        end
    end
end

function draw() 
    background(40, 40, 50)
    strokeWidth(5)
    sprite(img,WIDTH/2,HEIGHT/2,size*32)
end

And here are the images:
iPad Mini
iPad Mini Test
iPad Air
iPad Air Test

Thanks!

The IPad mini doesn’t have Retina display while the iPad Air does. I would say that’s the reason. The image you’re creating in img doesn’t have the same alpha values in the same positions on both devices. Just my guess since I don’t have a mini to examine the alpha values.

Here’s a program that prints the alpha values in the 7x7 image created by the ellipse. How do they compare on the Air and the mini.

function setup()
    size = 7
    img = image(size,size)
    setContext(img)
    noSmooth()
    ellipse(size/2,size/2,size)
    setContext()
end

function draw() 
    background(40, 40, 50)
    fill(255)
    for i = 1,size do
        for j = 1,size do
            r,g,b,a=img:get(i,j)
            text(a,i*40+100,j*40+300)
        end
    end
end

Hi @Dwins,

What happens if you use the following code?


-- Scale Test

function setup()
    size = 8
    img = image(size,size)
    setContext(img)
    noSmooth()
    ellipse(size/2,size/2,size)
    setContext()
    for i = 1,size do
        for j = 1,size do
            r,g,b,a = img:get(i,j)
             Img:set(i,j,r,g,b,a)
        end
    end
end

function draw() 
    background(40, 40, 50)
    strokeWidth(0)
    sprite(img,WIDTH/2,HEIGHT/2,size*32)
    fill(0, 206, 255, 255)
    ellipse(size/2,size/2,size)
end

Bri_G

This shows the gradient produced in making the Sprite. I also changed the value of size so it’s a multiple of 2 and the loop size 1 to 8 will work properly.

Let us know what the image is like on each of your devices.
Bri_G

@Dwins Here’s how to get the same image on a Retina display.

-- Scale Test

function setup()
    size = 7   
    img = image(size,size)
    setContext(img)
    noSmooth()
    ellipse(size/4,size/4,size/2)
    setContext()
    for i = 1,size do
        for j = 1,size do
            r,g,b,a = img:rawGet(i,j)
            if a > 0 then
                img:rawSet(i,j,color(255))
            end
        end
    end
end

function draw() 
    background(40, 40, 50)
    strokeWidth(5)
    sprite(img,WIDTH/2,HEIGHT/2,size*64)
end

@Dwins Just in case anyone doesn’t understand what the problem was, here’s an example and explanation. On a non Retina display, you can only img:get() integer x,y positions. On a retina device img:get() also only gets the integer x,y positions, but img:rawGet() will get the integer and equivalent .5 position values. In the print area, I show the values for img which has a regular size of 7 and a raw size of 14. The raw size includes the .5 values. The top square of text shows the alpha values of img at the integer positions. The bottom square of text shows the alpha values at the integer and .5 positions. In the bottom section, you’ll see that the alpha values are symmetrical, but in the top section, it’s not symmetrical because you’re not getting the .5 positions. The top integer values correspond with the lower integer positions. The green line of text just shows the integer and .5 position of the squares. This is why your image on the non Retina display is different than the Retina display. It’s easier to understand than its is to explain it.

supportedOrientations(PORTRAIT_ANY)

function setup()
    ellipseMode(CENTER)
    size = 7  
    img = image(size,size)
    print(img)
    setContext(img)
    noSmooth()
    ellipse(size/2,size/2,size)
    setContext()
end

function draw() 
    background(40, 40, 50)
    fill(255)
    
    -- show integer position values    (top)
    for i = 1,size do
        for j = 1,size do
            r,g,b,a = img:get(i,j)
            text(a,i*60,j*60+550)
        end
    end
    
    -- show integer and .5 position values    (bottom)
    for i = 1,size*2 do
        for j = 1,size*2 do
            r,g,b,a = img:rawGet(i,j)
            text(a,i*30+30,j*30+20)
        end
    end

    -- show number line
    fill(160, 255, 0, 255)
    for i=1,size+.5,.5 do
        text(i,i*60,530)
    end

end


(Non native english speaker warning)
Can I ask if hardcoding coordinates works to all iPads? I started to code on XCode, and tested some graphic UI with hardcoded X and Y on iPad simulator (also on iPad Pro sim) and it works. I want to know if Codea scales its UI to any iPad. And, why it doesn’t work for iPhone?

@Bri_G the gradient on the Mini looks fine, but on the Air the gradient is shifted slightly up to the right

@dave1707 thanks for sharing the img:rawGet() code. The only problem is that it doesn’t work on my Mini. I’m still trying to find a way for the code to work the same on both iPads

@Dwins Because you’re working with individual pixel values, I don’t think you can have the same code on a non Retina and a Retina display. You could have code to identify which device you’re using and call a routine that use img:get on the non retina device and img:rawGet on the retina device. I may be wrong, but from what I see using my example, I don’t think the same code will work.

I think I finally got this code to work. @dave1707 I thought about how img:rawGet() gets the 0.5 values, or something like that. Basically this code scales the image up 16 times and checks the 9/16th pixel, which is the same for Retina and Non-Retina. It’s a bit more complicated, but now I don’t need to check if it’s Retina display or not. Thanks for the help everyone!

-- Scale Test

function setup()
    size = 16
    scal = 16
    img = image(size*scal,size*scal)
    img2 = image(size,size)
    setContext(img)
    noSmooth()
    ellipse(size*scal/2,size*scal/2,size*scal)
    setContext()
    for i = 1,size do
        for j = 1,size do
            local r,g,b,a = img:get(i*scal-7,j*scal-7)
            if a > 0 then
                img2:set(i,j,color(255))
            end
        end
    end
end

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

    sprite(img2,300,300,400)
end

@Dwins @dave1707 is right to suggest rawGet / rawSet

The image is different on your iPad mini / iPad Air because you are rendering (and subsequently analysing) fewer pixels on the mini. If you want to analyse all pixels on both devices. Then you can use the following code:

(This will produce a higher fidelity, but still pixellated, circle on the iPad Air.)

function setup()
    size = 7
    img = image(size,size)
    setContext(img)
    noSmooth()
    ellipse(size/2,size/2,size)
    setContext()

    -- The raw size on non-retina devices will
    -- be the same as the size
    local rawSize = size * ContentScaleFactor

    for i = 1,rawSize do
        for j = 1,rawSize do
            r,g,b,a = img:rawGet(i,j)
            if a > 0 then
                img:rawSet(i,j,color(255))
            end
        end
    end
end

function draw() 
    background(40, 40, 50)
    strokeWidth(5)
    sprite(img,WIDTH/2,HEIGHT/2,size*32)
end

There is a global variable called ContentScaleFactor. On a non-retina display, this will be 1 where on a retina display it will be 2 (or 3 for iPhone 6+).

I expect you want the same result (same number of pixels) on both devices. In that case we need to start with an image size that is evenly divisible by all our possible scale factors, the closest one we have is 12 (divisible by 2 and 3).

function setup()
    size = 12.0 -- Divisble by 2 and 3
    
    -- Divide the size by the content scale factor
    -- This means our image will be:
    --  12 points, 12 pixels on non-retina
    --  6 points, 12 pixels on 2x retina
    --  4 points, 12 pixels on 3x retina
    local imgSize = size / ContentScaleFactor
    
    -- Create an image with the same number of pixels
    -- regardless of device screen density
    img = image(imgSize,imgSize)
    setContext(img)
    noSmooth()
    ellipse(imgSize/2,imgSize/2,imgSize)
    setContext()
    
    -- The raw size is just the undivided size
    local rawSize = size
    
    for i = 1,rawSize do
        for j = 1,rawSize do
            r,g,b,a = img:rawGet(i,j)
            if a > 0 then
                img:rawSet(i,j,color(255))
            end
        end
    end
end

function draw() 
    background(40, 40, 50)
    strokeWidth(5)
    sprite(img,WIDTH/2,HEIGHT/2,size*32)
end