Scalar
Scalar

Reputation: 225

Black borders around PNG image, drawn with OpenGL

When I'm trying to draw a PNG image with transparency, using OpenGL, I get a strange black border around it:

Two images with borders

But the original images are clean and normal:

Normal icons

My code:

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

UIImage* allIcons = [[appDelegate load_image_from_zip: pl.icons[ico_index]] retain];

                CGRect rect = CGRectMake( 0, 0, allIcons.size.width/nIcons, allIcons.size.height );
                UIGraphicsBeginImageContext(rect.size);

                CGContextRef currentContext = UIGraphicsGetCurrentContext();

                CGContextTranslateCTM ( currentContext, 0, allIcons.size.height );
                CGContextScaleCTM ( currentContext, 1, -1 );

                CGRect clippedRect = CGRectMake(0, 0, rect.size.width, rect.size.height);
                CGContextClipToRect( currentContext, clippedRect);
                CGRect drawRect = CGRectMake(rect.origin.x * -1, rect.origin.y * -1, allIcons.size.width, allIcons.size.height);
                CGContextDrawImage(currentContext, drawRect, allIcons.CGImage);

Upvotes: 2

Views: 2413

Answers (4)

Roger Gilbrat
Roger Gilbrat

Reputation: 3835

This is the dreaded premultiplied alpha issue. I struggled with this for a good week before figuring it out. Telling your project to not compress .png files does not solve the problem as various API calls will re-multiply the alpha. This is how i solved it.

When setting blend modes and color:

blendFuncSource =  premultAlpha ? GL_ONE : GL_SRC_ALPHA;
blendFuncDestination = GL_ONE_MINUS_SRC_ALPHA;

if (premultAlpha)
{
    glColor4f(colorfilter.red*colorfilter.alpha, colorfilter.green*colorfilter.alpha, colorfilter.blue*colorfilter.alpha, colorfilter.alpha);
}
else
{
    glColor4f(colorfilter.red, colorfilter.green, colorfilter.blue, colorfilter.alpha);
}

You'll need to figure out of you images are using premultiplied alpha. You can do this my checking the png headers when they are loaded:

CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo( image );

...

switch( bitmapInfo & kCGBitmapAlphaInfoMask )
{
case kCGImageAlphaPremultipliedFirst:
    premultAlpha = YES;
    srcFormat = GL_BGRA;
    break;

case kCGImageAlphaFirst:
    srcFormat = GL_BGRA;
    break;

case kCGImageAlphaNoneSkipFirst:
    srcFormat = GL_BGRA;
    break;

default:
    srcFormat = GL_RGBA;
}

I'm paraphrasing a lot of code here, hopefully it's still helpful.

Upvotes: 3

Lijiayu
Lijiayu

Reputation: 7

try these

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Upvotes: 0

Noah Witherspoon
Noah Witherspoon

Reputation: 57139

The issue is that your images have premultiplied alpha, where the color of each pixel has been multiplied by its alpha value. UIKit on the iPhone prefers premultiplied-alpha images—blending them is faster—but OpenGL doesn’t assume that, so the transparency effect of your alpha channel is sort of getting applied twice, which results in the semitransparent pixels in your image appearing darker than they actually are. Try turning off the “Compress PNG Files” option in your project’s build settings.

Upvotes: 1

genpfault
genpfault

Reputation: 52083

Assuming your TexEnv is the default GL_MODULEATE try a glColor4ub(255, 255, 255, 255) before drawing.

Upvotes: 1

Related Questions