Partial transparency bug (when drawing quad to canvas?)

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
Benedict Ide
Prole
Posts: 2
Joined: Fri Nov 11, 2016 6:54 pm

Partial transparency bug (when drawing quad to canvas?)

Post by Benedict Ide »

Hi,

I've written a custom animation handling system for a game I've been working on, and it seems to be having some trouble with partial transparency.

Image

This character's got glasses, and where those glasses don't overlap the face, they're white with ~50% alpha. This gets drawn over the game's background. When I'm just drawing a static single frame (when the dialogue portrait has finished talking), this works just fine. You can see that on the right there (with the lightened black outlines highlighted to distinguish them from the gray floor background.) On the left, though, is what the same sprite looks like when an animation is playing. You can see the pixels underneath the glasses aren't affected as much by the partially transparent glasses. I've messed around in an image editor and determined that there is an effect- the pixels underneath the glasses are slightly lightened- but despite the animation images having the exact same pixels in those locations, it comes out looking different when drawn in-game. (This produces an undesirable flicker effect when transitioning between the static and animated sprites.)

The way my animation code works (github link here) is that it takes a spritesheet, then slices that up into frames by drawing quads of that spritesheet to individual frame canvases, which it pushes to an array. (My custom array class is just a table with some utility functions, nothing suspicious there.) When an AnimatedThing calls its draw, it determines the appropriate frame canvas based on time, and just draws that, same as a static sprite. So, I'm pretty sure the problem here is happening when I'm slicing up the spritesheet and drawing the individual frames to the canvases. Here's the code that does this when the animation is created:

Code: Select all

AnimatedThing = function(xp,yp,zp,filename)
	local base = CanvasThing(xp,yp,zp,love.graphics.newCanvas(10,10)); --just setting up a game object
	base.thingType = "AnimatedThing"; --and making it a different type
	base.jsonstring = love.filesystem.read("json/animations/" .. filename .. ".json"); --reading the animation data
	base.data = json.decode(base.jsonstring);
	base.anims = {}; --creating a table of separate arrays for different animations the game object can play
	base.currentAnim = base.data.default; 
	--create individual animation frame canvases for each animation
	for k,v in pairs(base.data.animations) do
		local animdata = base.data.animations[k];
		local sheet = love.graphics.newImage(animdata.filepath); --reads the spritesheet image data
		base.anims[k] = {}; 
		base.anims[k].fps = animdata.fps;
		base.anims[k].frames = Array(); --create the frames array used to store the individual canvases
		base.anims[k].framecount = animdata.frames;
		base.anims[k].playOnce = animdata.playOnce;
		for i = 1, base.anims[k].framecount, 1 do --okay, here's the juicy part where things go wrong somehow
			local canv = love.graphics.newCanvas(animdata.width,animdata.height); --create a new canvas to draw to
			love.graphics.pushCanvas(canv); --custom method for setting the canvas and pushing it to a stack to be removed after. does not affect the canvas itself.
			local code = i - 1;
			local cx = math.floor(code%base.anims[k].framecount);
			local cy = math.floor(code/base.anims[k].framecount);
			local quad = love.graphics.newQuad(cx*animdata.width,cy*animdata.height,animdata.width,animdata.height,sheet:getWidth(),sheet:getHeight());
			--^all that is working fine, it's just calculating which frame to slice out of the sheet
			love.graphics.draw(sheet,quad,0,0); --here's the problem, i think- looks like this draw is drawing the transparency wrong
			love.graphics.popCanvas(); --sets LOVE's active canvas to whatever it was before
			base.anims[k].frames.push(canv); --stores the new frame canvas
			if k == base.data.default then 
				base.canvas = canv; --if you just created the default animation, set the game object's animation to that
			end
		end
	end
	--a bunch of timing code you can check out at the github
end
And here are the sprites in question- though this happens with all her sprites that have such partial transparency, not just these:
Image <static image, game draws image data
Image <animation, game draws canvas data

Somewhere along the line, the partially transparent pixels in the spritesheet... are becoming either more transparent than they should be, or the color is getting darker, or something. Not sure what, not sure why, only kinda sure where. There's only two places those get drawn- the slicing code right there, and when the canvas itself is drawn to the screen. (Some difference in how LOVE handles transparency when drawing image data vs canvas data?)

Any idea what could cause this sort of thing to happen?
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Partial transparency bug (when drawing quad to canvas?)

Post by zorg »

The default blending is alpha blending, so when you're drawing something to a canvas, that's fine, but you should set the blendmodes to premultiplied accordingly just before drawing the canvas contents to the screen, otherwise things like this may happen.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Benedict Ide
Prole
Posts: 2
Joined: Fri Nov 11, 2016 6:54 pm

Re: Partial transparency bug (when drawing quad to canvas?)

Post by Benedict Ide »

zorg wrote: Wed Apr 11, 2018 3:50 pm The default blending is alpha blending, so when you're drawing something to a canvas, that's fine, but you should set the blendmodes to premultiplied accordingly just before drawing the canvas contents to the screen, otherwise things like this may happen.
Just set my canvas drawing code to use premultiplied, and that totally fixed the issue (as well as another smaller related issue that I'd just learned to live with because it wasn't too far off from what I wanted.) Thanks!
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 1 guest