Premultiplied alpha trouble

I've been working on premultiplying all the El Ballo texture files, and they look just fine in Preview and Photoshop. However, when I load them into the actual game, they blow up and produce very odd color values. Here's a composited screenshot of what's happening: top half is using my pre-multiplied textures and bottom half is using the non-premmed ones. I've tried with glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) as well, but with the same results.
Can anyone guess as to what is going wrong here? It looks as if pure yellow goes into pure cyan, which would mean that (pure red + pure green) becomes (pure green + pure blue) (#FFFF00 -> #00FFFF). Also, if you look at the text in the upper half, right below GL_SRC_ALPHA: that text is black in the texture, but is completely transparent here. Looks to me as if the pixels become badly swizzled somewhere, but the same code works if I just use the non-premultiplied textures instead. Any guesses?

OK, fixed that. Turned out to be a very subtle bug in my file loading code (which ought to have broken every file loaded since the dawn of this game) which caused a pointer offset which, yes, in a way swizzled the channels. Now, however, everything looks great. Except, whenever I draw with an glColor* alpha less than 1, I get a strange glowing effect. Similarily, half-transparent textures look awfully dark. Any hints?

RGB * alpha * one = RGB * alpha. There's no difference between premultipling the alpha, using GL_ONE for the src blending factor and not premultipling the alpha, using GL_SRC_ALPHA for the src blending factor. Except, of course, that premultipling destroys your RGB data so you can't turn the alpha channel off whenever you want to.

If you take some RGB+A image from photoshop, and premultiply it (for example with tiffutil, or programatically) then you build an RGBA texture out of it and blend it into your scene with glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA). That's fine, the resulting framebuffer pixel is src_rgb * src_alpha * one + dst_rgb * (one - src_alpha);

Alternatively you can take some RGB+A image from photoshop as is, build an RGBA texture out of it and blend it into your scene with glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). The resulting framebuffer pixel is src_rgb * src_alpha + dst_rgb * (one - src_alpha).

It's exactly the same. That's what I was saying. I've done it both ways.

As I said, the problem is only when the nonpremultiplied image is bilinearly filtered. halfway between an opaque green texel and a transparent white texel, a semitransparent light-green fragment is generated, which produces the effect of a light halo. Halfway between an opaque green texel and a transparent black texel, a semitransparent dark-green fragment is generated, which produces the effect of a dark halo. Only if the transparent texel is the same shade of green as the opaque one is the halo eliminated.

By contrast, if the image is premultiplied there is only one case -- an opaque green texel next to a transparent black texel. In this case the filtered fragment (semitransparent dark green) is actually a premultiplied pure green, the desired color. No halo is visible.

So, premultiplication isn't the only solution, but it is the most practical solution -- premultiplication of the image is easier than ensuring the transparent texels are the same color as the surrounding opaque texels.

I haven't used premultiplied alpha before (it sounds like a good idea) though correct me if I'm wrong- if you have a particularly transparent amount of color, then your precision goes down provided you are using integral channels (e.g. if you were making a 1/2 transparent image, then you'd only use values 0-127 instead of the whole range of 0-255 of a byte.) I assume that's the downside of using premultiplied alpha?

Though it does sound like the benefits outweigh the downside. In my game I actually intentionally make some of the black haloes because it gives an interesting look to some of the icons. The rest of my images I have a blurred copy of the image in the transparent pixels to make sure surrounding pixels aren't black or white.