Hello,
I'm almost done with my current project, but somehow can't solve the following problem.
My app uses parts of the GLPaint demo app, to allow the user to draw on the iPhone screen. The EAGLview is set to transparent, using
Code:
eaglLayer.opaque = NO;
Below I have a UIImageView, which holds an image. This all looks great, but I haven't been able to merge both into one image, that can be saved to the photo library.
Here is what I tried so far:
1) Trying to make a simple screenshot
Code:
UIGraphicsBeginImageContext(self.window.bounds.size);
[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
This doesn't work, as it will only grab contents of the UIImageView, which is below my EAGLview.
2) Grabbing content of the EAGLview and putting it in a new UIImageView, which is positioned above the old UIImageView:
Code:
- (CGImageRef) glToUIImage {
NSInteger myDataLength = 320 * 480 * 4;
// allocate array and read pixels into it.
GLubyte *buffer = (GLubyte *) malloc(myDataLength);
glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// gl renders "upside down" so swap top to bottom into new array.
// there's gotta be a better way, but this works.
GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
for(int y = 0; y <480; y++)
{
for(int x = 0; x <320 * 4; x++)
{
buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
}
}
// make data provider with data.
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
// prep the ingredients
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * 320;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// make the cgimage
CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// then make the uiimage from that
UIImage *myImage = [UIImage imageWithCGImage:imageRef];
return myImage;
}
This returns content of the EAGLview in a UIImage. Works fine, except for one major problem: There is no transparency. So, parts that are transparent are black.
3) Grabbing content of EAGLview (like before) and merging it with content of my UIImageView, using quartz.
Code:
CGImageRef brushImage;
CGContextRef brushContext;
GLubyte *brushData;
size_t width, height;
brushImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"imageOfUIImageView" ofType:@"png"]].CGImage;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
CGColorSpaceRef colorSpace;
bitmapData = malloc(bitmapByteCount);
bitmapBytesPerRow = (320 * 4);// 1
bitmapByteCount = (bitmapBytesPerRow * 480);
colorSpace = CGImageGetColorSpace(image);
width = CGImageGetWidth(brushImage);
height = CGImageGetHeight(brushImage);
brushData = (GLubyte *) malloc(320 * 480 * 4);
// Use the bitmatp creation function provided by the Core Graphics framework.
brushContext = CGBitmapContextCreate (bitmapData,
320,
480,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
// After you create the context, you can draw the image to the context.
// image is a UIImage, that holds the image, returned from the glToUIImage method above
// Draw the EAGLview content (now in image) to the context
CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)320, (CGFloat)480), image.CGImage);
//Set the blend mode
CGContextSetBlendMode (brushContext, kCGBlendModeLighten); //lighten seems to give the "best" result
//draw the background to the context
CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)320, (CGFloat)480), brushImage);
//create a new image, that combines both EAGLview and my background image
CGImageRef mergedImage = CGBitmapContextCreateImage(brushContext);
CGContextRelease(brushContext);
//write it to photolibrary
UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:image], self, nil, nil);
This doesn't work either. Depending on the blend mode I choose, the result looks sometimes better, sometimes worse, but is never close to what I need. On the other hand, if I simply push the home and on/off button to make a screenshot manually, the result is exactly what I need.
Maybe you have some ideas, how this can be achieved. It's really the final touch for my app and I definitely don't want the user to make the screenshot manually.
Any help appreciated! Thanks in advance.