Black line above sprites

Hey everyone,
I encountered a little problem/issue while coding today: everytime I create a sprite, import it into Codea and draw it on the screen, I get a black line above it, as shown http://imgur.com/fVia9Ox . It doesn’t really bother me that much as the line is relatively thin, but I would like to know if I am doing something wrong when importing the sprites, or if it is a bug I should submit to the Issue Tracker.
Thank you for taking the time to read through my post :slight_smile:
ColourCoder

If you have trouble telling where the line is, I can post another picture if you want

@ColourCoder I’ve seen this before. I think it’s a one pixel line from the bottom of the sprite, but I don’t remember what I did about it. I was able to determine that because the bottom of my sprite wasn’t a solid color and the line matched the bottom of the sprite.

@ColourCoder Did you pull your sprite from a sprite sheet. I think I was doing something with a sprite sheet when I saw this. I thought maybe it was getting some of the sprite that was above the one I wanted, but the line matched the bottom of my sprite and not the one above it. It was quite awhile ago and I don’t remember what I ended up doing. I’ll look around, maybe something will jog my memory.

Try calling noSmooth() before drawing, it might fix it.

Thanks @SkyTheCoder. I had the same problem.

@SkyTheCoder calling noSmooth() does actually help a little as the line is now even less visible, thanks.
@dave1707 if by sprite sheet you mean a sprite with the several status the Hero might have (e.g: walking, attacking,etc.), I prefer using individual sprite for each of them. However, as you said, the black line appears when the bottom pixels of the sprite being used are black. Maybe it has something to do with the sprite’s dimensions ?

Would you be able to let us see the actual sprite you’re using? Did you already try to crop the image of a couple of pixels from the top? Maybe that line is part of the sprite and you’re somehow filling it with black.

@ColourCoder does the sprite file have power-of-two dimensions? E.g., 128x128, or 256x256 (and so on)?

Codea has special treatment for power-of-two images in that they are tile-able, I wonder if that could be causing the artefact.

@Simeon yes, the sprite is 32x32
@deactive the line cannot be part of the original sprite as the top of the Hero’s head marks the limit of it

@ColourCoder could you try adding a 1px transparent padding to the sprite so it becomes 34x34 and let me know if it still happens?

I tried making my own sprite of various sizes with black at the bottom, but didn’t get a line at the top. Saving and reloading it didn’t make a difference.

Could the file format be a factor?

@Simeon I changed the sprites to 34x34 like you told me and it now seems that the black line has disappeared. Thanks!
@Ignatz since I download my sprites into my iPad’s pictures and then import them into Codea, I think their file is .jpeg. Are your sprites also .jpeg?

@ColourCoder - If your sprite has transparency, which, looking at your picture, I think it does, then the file extension will be .png

@Simeon Not sure if this is like the problem here, but here’s an example of the bottom of the image showing at the top. The image at the lower left is the original image. The image at the upper left is a copy of the upper left corner of the original image. The image on the right is an enlargement of the copied image. Using the slider, I extent the black and red rectangle down in the original image so that it will eventually get copied. When the black/red rectangle reached the bottom of the larger image, you can see some of the image at the top.

supportedOrientations(LANDSCAPE_ANY)

function setup()
    spriteMode(CORNER)
    rectMode(CORNER)    
    parameter.integer("val",55,65,65,change)
end

function draw()
    background(40)
    sprite(img1,50,200)
    sprite(img2,80,300)
    sprite(img2,200,200,500)
end

function change()
    img1=image(80,80)
    setContext(img1)
    background(255)
    -- draw a varying size rectangle with a red stripe
    fill(0)
    rect(6,val,8,75-val)
    fill(255, 0, 0, 255)
    rect(8,val,2,75-val)
    setContext()
    
    -- copy upper left corner of original image
    img2=img1:copy(2,63,16,16)   
end

Good test, @dave1707. And if you use a size that isn’t a power of 2, you don’t get the stripe.

@dave1707 thank you very much for the example. It does appear to be due to the bilinear filtering when smooth() is enabled and you have a power-of-two texture. The sampling window exceeds the texture boundary and so wraps past the edge, noSmooth() or a non-power-of-two texture will resolve this.

Codea purposely interprets power-of-two textures with wrapping enabled on the UV coordinates — so if the texture coordinates exceed 1.0 or go less than 0.0 the texture will wrap. This is handy on meshes where you want a tiled texture on a single quad.

(Cargo-Bot uses this effect to do the infinite scrolling star background, below is an adaptation of it as a demo)

--# Main
-- ScrollingStars
function setup()
    texDemo = ScrollingTexture("Cargo Bot:Starry Background")
end

function draw()
    texDemo:setRect(WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
    
    texDemo:setOffset( (texDemo.offset + vec2(DeltaTime, DeltaTime)):unpack() )
    
    texDemo:draw()
end


--# ScrollingTexture
ScrollingTexture = class()

local function imageOrSpriteSize(imgOrKey)
    local w,h = 0,0
    if type(imgOrKey) == "string" then
        w, h = spriteSize(imgOrKey)
    else
        w = imgOrKey.width
        h = imgOrKey.height
    end
    
    return w,h
end

function ScrollingTexture:init(tex)
    -- note: requires a power-of-two image    
    self.mesh = mesh()
    self.mesh.texture = tex
    self.offset = vec2()
    self.tw, self.th = imageOrSpriteSize(tex)
    self.rect = self.mesh:addRect(0,0,0,0)
    self:setRect(0, 0, 100, 100)
end

function ScrollingTexture:setRect(x, y, w, h)
    self.mw, self.mh = w, h
    self.mesh:setRect(self.rect, x, y, w, h)
    self:setOffset(self.offset:unpack())
end

function ScrollingTexture:setOffset(x, y)
    self.offset = vec2(x,y)
    
    local w = self.mw / self.tw
    local h = self.mh / self.th
    
    self.mesh:setRectTex(self.rect, x, y, w, h)
end

function ScrollingTexture:draw()
    self.mesh:draw()
end

I found that example a bit complicated, so I tried to create a simpler one, below, which tiles an image across a surface.

I note two things.

A. My example draws a fractional number of copies of the original image, which looks a bit untidy.

You can make it an integer number by simply adjusting the numbers in setRectTex. For example, if we use 60 (instead of 64) as the width and height of the image, then it fits exactly 5x across and 10x up, so m2:setRectTex(r,0,0,5,10) will look neater.

You may find the image is the wrong scale for the rest of your scene, so you want to make the tiles smaller. You can halve their size, by simply doubling the numbers in setRectTex.

This works because Codea only requires the image size to be a power of two. You can tile it as many times as you want, to get the effect you want.

Also, it works on all meshes of all shapes and sizes, not just rectangles.

B. This approach requires a power of two square image. What do you do if it’s not a power of two, or not square? The answer is a shader which can handle any size image, and it’s not difficult to use.

function setup()
    --read an image
    img=readImage("Platformer Art:Block Brick")
    --this is the size we want to draw
    local w,h=300,600
    
    --First, set up a normal mesh
    --this will stretch our image across the whole mesh
    m1=mesh()
    m1:addRect(200,400,w,h)
    m1.texture=img
    
    --Second, set up another mesh which tiles the image repeatedly
    --The image needs to be a power of 2 size, ie 4,8,16,... pixels square
    --Our image is 70x70, so we need to convert it to a 
    --  power of two - 64 is closest
    img2=image(64,64) --create a 64x64 image and copy our picture 
    setContext(img2)
    sprite(img,32,32,64) --copy our image and shrink it to 64 pixels
    setContext()

    m2=mesh()
    m2.texture=img2

    --add rectangle as before (store id number of rectangle in r)
    local r=m2:addRect(500,400,w,h)
    --we set texture coordinates as multiples of image size
    --normal texture coords are (0,0) at bottom left to (1,1) top right
    --we calculate them as multiples of actual image size
    --so in this case, width=300 and image size=64, so it fits 4.6875 times
    --and height=600 and image height is 64, so it fits 9.375 times
    --set new texture coordinates
    m2:setRectTex(r,0,0,w/img2.width,h/img2.height) 
end

function draw()
    background(40)
    m1:draw()
    m2:draw()
end