opengl es 2.0 alpha blending - opengl-es-2.0

I'm using the following to enable alpha blending
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
The transparency is taken from a texture which seems to work, using the simplest of fragment shaders:
void main(void) {
gl_FragColor = texture2D(texture, texCoordOut);
}
While it blends nicely with the background color it doesn't work with other objects using the same alpha blending. I don't really know how to explain this so here's a picture:
What am I doing wrong?

Nothing wrong in your code. My guess is probably the order you draw each image makes the difference.For example if you draw images in the following order BGImage ,image1 , image2 and so on.
What opengl do is, it takes your draw call order for blending images. After drawing BGImage in framebuffer, then it blends image1 and framebuffer, then draws the result again in framebuffer. Then it blends image2 and framebuffer, then draws the result again in framebuffer and so on.
So if image1 overlaps image2, image1 blends with BGimage not with image2.

Related

Blending two images and drawing resized image from two UIImageViews

I have two ImageViews, one called imageView and the other called subView (which is a subview of imageView).
I want to blend the images on these views together, with the user being able to switch the alpha of the blend with a pan. My code works, but right now, the code is slow as we are redrawing the image each time the pan gesture is moved. Is there a faster/more efficient way of doing this?
BONUS Q: I want to allow for my subView image to drawn zoomed in. Currently I've set my subView to be UIViewContentModeCenter, however I can't seem to draw a zoomed in part of my image with this content mode. Is there any way around this?
My drawrect:
- (void)drawRect:(CGRect)rect
{
float xCenter = self.center.x - self.currentImage1.size.width/2.0;
float yCenter = self.center.y - self.currentImage1.size.height/2.0;
subView.alpha = self.blendAmount; // Customize the opacity of the top image.
UIGraphicsBeginImageContext(self.currentImage1.size);
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(c, kCGBlendModeColorBurn);
[imageView.layer renderInContext:c];
self.blendedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.blendedImage drawAtPoint:CGPointMake(xCenter,yCenter)];
}
You need to use GPU for image processing which is far faster than using CPU (as you're doing right now).
You can use Core Image framework which is very fast and easy to use but requires iOS 5, or you can use Open GL directly but you need to be experienced and have some knowledge about Open GL Shading.

Draw rounded linear gradient (or extended radial gradient) with CoreGraphics

I want to do some custom drawing with CoreGraphics. I need a linear gradient on my view, but the thing is that this view is a rounded rectangle so I want my gradient to be also rounded at angles. You can see what I want to achieve on the image below:
So is this possible to implement in CoreGraphics or some other programmatic and easy way?
Thank you.
I don't think there is an API for that, but you can get the same effect if you first draw a radial gradient, say, in an (N+1)x(N+1) size bitmap context, then convert the image from the context to a resizable image with left and right caps set to N.
Pseudocode:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(N+1,N+1), NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
// <draw the gradient into 'context'>
UIImage* gradientBase = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage* gradientImage = [gradientBase resizableImageWithCapInsets:UIEdgeInsetsMake(0,N,0,N)];
In case you want the image to scale vertically as well, you just have to set the caps to UIEdgeInsetsMake(N,N,N,N).
I just want to add more sample code for this technique, as some things weren't obvious for. Maybe it will be useful for somebody:
So, let's say, we have our custom view class and in it's drawRect: method we put this:
// Defining the rect in which to draw
CGRect drawRect=self.bounds;
Float32 gradientSize=drawRect.size.height; // The size of original radial gradient
CGPoint center=CGPointMake(0.5f*gradientSize,0.5f*gradientSize); // Center of gradient
// Creating the gradient
Float32 colors[4]={0.f,1.f,1.f,0.2f}; // From opaque white to transparent black
CGGradientRef gradient=CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceGray(), colors, nil, 2);
// Starting image and drawing gradient into it
UIGraphicsBeginImageContextWithOptions(CGSizeMake(gradientSize, gradientSize), NO, 1.f);
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextDrawRadialGradient(context, gradient, center, 0.f, center, center.x, 0); // Drawing gradient
UIImage* gradientImage=UIGraphicsGetImageFromCurrentImageContext(); // Retrieving image from context
UIGraphicsEndImageContext(); // Ending process
gradientImage=[gradientImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.f, center.x-1.f, 0.f, center.x-1.f)]; // Leaving 2 pixels wide area in center which will be tiled to fill whole area
// Drawing image into view frame
[gradientImage drawInRect:drawRect];
That's all. Also if you're not going to ever change the gradient while app is running, you would want to put everything except last line in awakeFromNib method and then in drawRect: just draw the gradientImage into view's frame. Also don't forget to retain the gradientImage in this case.

Given an image, add white to all surrounding areas within a region - iOS

Given an image G with dimensions w x h, how can I surround the image with white to produce a new image that is 320 x 480 where G is centered, surrounded by white. The width and height of the incoming image might be larger than 320 or 480, repsecitively, so first I would need to scale it to ensure that it is less than 320x480 (or equal to).
-(UIImage *)whiteVersion:(UIImage *)img {
}
I would use these steps:
Firstly, scale image if needed...then:
1. Create a UIImage of size 320x480
2. Paint the whole thing white
3. Paint G (the image) in the center by using some simple math to find the location
However, I can't figure out step 1 or 2.
You don't draw into images. You draw into graphics contexts. There are different kinds of graphics contexts. An image graphics context lets you take a snapshot of its contents as an image.
- (UIImage *)whiteVersion:(UIImage *)myImage {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(320, 480), YES, myImage.scale);
[[UIColor whiteColor] setFill];
UIRectFill(CGRectInfinite);
[myImage drawAtPoint:CGPointMake((320 - myImage.size.width) / 2, (480 - myImage.size.height) / 2)];
UIImage *myPaddedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return myPaddedImage;
}
You can learn more about drawing on iOS by perusing the Drawing and Printing Guide for iOS and the Quartz 2D Programming Guide.

How do I implement a soft eraser stroke in a CGBitmapContext

I am trying to erase an image with my touch in iOS. By setting the blend mode to kCGBlendModeClear I am able to do this - but only with hard edges. I could draw my stroke with varying line widths and alphas, but it appears that CGContextSetAlpha does not have an effect with kCGBlendModeClear.
How do I go about this?
I would use a transparency layer compositing with kCGBlendModeDestinationOut (Da * (1 - Sa), Dc * (1 - Sa).) Something like this:
CGPathRef pathToErase = ...; // The path you want erased
// could also be an image or (nearly) anything else
// that can be drawn in a bitmap context
CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
CGContextBeginTransparencyLayer(ctx, NULL);
{
CGContextSetGrayFillColor(ctx, 0.0, 1.0); // solid black
CGContextAddPath(ctx, pathToErase);
CGContextFillPath(ctx);
// the above two lines could instead be CGContextDrawImage()
// or whatever else you're using to clear
}
CGContextEndTransparencyLayer(ctx);
Note that you should also save and restore the gstate (CGContextSaveGState()/CGContextRestoreGState()) before/after the transparency layer to ensure that the blend mode and any other gstate changes do not persist.
Note: This is brain-compiled and it's possible that transparency layers don't play nice with all blend modes. If so, try drawing the path/image into a second bitmap context, then drawing the contents of that context with the above blend mode.
You can also experiment with other blend modes for different effects.

Erasing after drawing with CGContext

I'm trying to do a simple drawing app for the iPad where you can draw on a picture, and I'm using CGContext stuff to do it but the way I originally planned on handling erasing was to just draw over stuff with white...except I just realized today that it doesn't work when you're drawing onto another image because then when you "erase" you'll also "erase" the background image as well.
Is there any way to support actual erasing?
Thanks!
I also needed erasing functionality. Based on #Jeremy's answer, here is what worked for me:
CGContextRef cgref = UIGraphicsGetCurrentContext();
if(erase == TRUE) // Erase to show background
{
CGContextSetBlendMode(cgref, kCGBlendModeClear);
}
else // Draw with color
{
CGContextSetBlendMode(cgref, kCGBlendModeNormal);
}
Display the user's drawing in a layer above the image. Then erasing is as simple as drawing a transparent patch on the drawing layer in order to let the image pixels below it show through.
Clear all CGContextRef drawings:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, self.bounds);
[self setNeedsDisplay];