micha is on the right track, but making it transparent black just makes the outline black, although that might be less noticeable. This behaviour comes from the alpha blend mode and how it uses the alpha of the pixel in the framebuffer.
To get rid of it properly, you have to use the premultiplied blend mode. This blend mode will blend the image in such a way that you get your expected result. However, you have to prepare your images for it. The red, green, and blue channels have to be multiplied by the alpha channel (hence premultiplied). You don't have to do this operation at runtime, my following example just shows how it would work.
This example draws four images. The two on the left with alpha blend mode and the two on the right with the prepared images and the premultiplied blend mode. With the two on the left, you can see how it interpolates the color of the transparent pixel. The images on the right do appear as we all would expect.
Code: Select all
function love.load()
imgd1 = love.image.newImageData(128, 128)
imgd2 = love.image.newImageData(128, 128)
-- Black rectangle with a red circle and everything else being transparent white.
imgd1:mapPixel(function(x, y, r, g, b, a)
if x > 50 and x < 100 and y > 30 and y < 80 then
if ((x - 74)^2 + (y - 54)^2)^0.5 < 20 then
return 255, 0, 0, 255
end
return 0, 0, 0, 255
end
return 255, 255, 255, 0
end)
-- White rectangle with a red circle and everything else being transparent black.
imgd2:mapPixel(function(x, y, r, g, b, a)
if x > 50 and x < 100 and y > 30 and y < 80 then
if ((x - 74)^2 + (y - 54)^2)^0.5 < 20 then
return 255, 0, 0, 255
end
return 255, 255, 255, 255
end
return 0, 0, 0, 0
end)
img1 = love.graphics.newImage(imgd1)
img2 = love.graphics.newImage(imgd2)
-- Multiplying the red, green, and blue channels with the alpha channel.
imgd1:mapPixel(premutlipliedFunction)
imgd2:mapPixel(premutlipliedFunction)
img3 = love.graphics.newImage(imgd1)
img4 = love.graphics.newImage(imgd2)
x1, y1 = 0, 0
x2, y2 = 0, 0
end
function premutlipliedFunction(x, y, r, g, b, a)
return (r * a) / 256, (g * a) / 256, (b * a) / 256, a
end
function love.update(dt)
x1 = (x1 + dt * 5) % 400
y1 = (y1 + dt * 3) % 200
end
function love.draw()
-- Making the lower half of the window white.
love.graphics.rectangle("fill", 0, 300, 800, 300)
-- Drawing the images with alpha blend mode.
love.graphics.setBlendMode("alpha")
love.graphics.draw(img1, 20 + x1, 100 + y1)
love.graphics.draw(img2, 20 + x1, 400 + y1)
-- Drawing the premultiplied images with premultiplied blend mode.
love.graphics.setBlendMode("premultiplied")
love.graphics.draw(img3, 200 + x1, 100 + y1)
love.graphics.draw(img4, 200 + x1, 400 + y1)
end
Of course you can also just always draw at integer coordinates as suggested.
The alpha blend mode in the upcoming LÖVE 0.9.0 was slightly changed so this operation is not necessary anymore.
Edit: Oh, I see you use the multiplicative blend mode. This one has a bit of a weird behaviour in 0.8.0. I need to do some more poking before I can explain that.
Edit2: I think I get it now. The 0.8.0 multiplicative blend mode uses the alpha in
a specific way. This gives probably unexpected behaviour if the image that gets drawn has translucent parts. I would recommend to only blend opaque images with this mode, which is a bit tricky if stuff has been drawn with the alpha blend mode. You already do make the 'scene' canvas opaque with that grass color. All you have to do now is to premutliply the images and draw them with the premultiplied blend mode.
Oh, and before i forget it. There's an issue with the premultiplied blend mode in 0.8.0 with fonts and smooth lines. They don't really work with it. This all gets better in 0.9.0 with the changed alpha blend mode.