I am rotating an image on the iPhone. It is a full size grayscale image (3264 by 2448 on the 4s). OpenGl appears to be roughly twice as fast as core graphics by my measurements, running about 1.22 seconds as opposed to 2.6 seconds. But this is not fast enough for my needs, I need sub-second rotation, if it is possible. (If not, we go to Plan B which involves rotating a subsection of the image, which is more elegant perhaps but has it's own issues.)
I should make clear that this only for internal processing of the image, not for display purposes.
I would like to make sure that I am doing this correctly and not making any obvious beginner's mistakes. Here is the code, I would appreciate any hints for improvements if possible.
Thank you,
Ken
void LTT_RotateGL(
unsigned char *src,
unsigned char *dst,
int nHeight,
int nWidth,
int nRowBytes,
double radians,
unsigned char borderColor)
{
unsigned char *tmpSrc = (unsigned char *)malloc( (nWidth * nHeight) );
memcpy( tmpSrc, src, (nWidth * nHeight) );
CGContextRef context2 = CGBitmapContextCreate(
tmpSrc, nWidth, nHeight, (size_t)8, nRowBytes,
CGColorSpaceCreateDeviceGray(),
kCGImageAlphaNone );
CGImageRef imageRef2 = CGBitmapContextCreateImage( context2 );
UIImage *image2 = [[UIImage alloc] initWithCGImage:imageRef2];
GLuint width = CGImageGetWidth(image2.CGImage);
GLuint height = CGImageGetHeight(image2.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
void *imageData = malloc( height * width);
CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, width, colorSpace, kCGImageAlphaNone );
CGColorSpaceRelease( colorSpace );
CGContextTranslateCTM( context, 0, height - height );
CGContextRotateCTM(context, -radians);
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image2.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_LUMINANCE, imageData);
CGContextRelease(context);
memcpy( dst, imageData, (nWidth * nHeight) );
free( tmpSrc );
}
Related
I am new to iphone development. Currently I am working on a project where opengl is used and I need to animate a part of the view. For that I have taken the screen shot and then created the texture.
This is my code
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
UIGraphicsBeginImageContext(self.introductionTextLabel.frame.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
GLuint texture[1];
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
GLuint width = CGImageGetWidth(viewImage.CGImage);
GLuint height = CGImageGetHeight(viewImage.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef contextTexture = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( contextTexture, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( contextTexture, 0, height - height );
CGContextDrawImage( contextTexture, CGRectMake( 0, 0, width, height ), viewImage.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CGContextRelease(contextTexture);
free(imageData);
I have searched a lot but could not find a good method for giving an animation for the texture.
Can any one suggest me good method. If I am doing wrong please point out.
Thanks in advance.
The only way I can recommend you to modify the texture is to use glTexImage2D() for full frame update and glTexSubImage2D() for partial update. Of course it will be better if you will use preloaded and compressed type of textures...
Im capturing from the iphone camera device using CVPixelFormatType_420YpCbCr8BiPlanarFullRange for faster processing of the grayscale plane (plane 0).
I am storing an amount of frames in memory for later video creation. Before I was creating the video in grayscale so I was storing only the plane that contains the luminiscense (plane 0).
Now I have to store both planes and also create a color video from them, for storing the frames I use something like this :
bytesInFrame = width * height * 2; //2 bytes per pixel, is that correct?
frameData = (unsigned char*) malloc(bytesInFrame * numbeOfFrames);
The function for creating the image from the grayscale buffer I was using :
- (UIImage *) convertBitmapGrayScaleToUIImage:(unsigned char *) buffer
withWidth:(int) width
withHeight:(int) height {
size_t bufferLength = width * height * 1;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 8;
size_t bytesPerRow = 1 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceGray();
if(colorSpaceRef == NULL) {
DLog(#"Error allocating color space");
CGDataProviderRelease(provider);
return nil;
}
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef iref = CGImageCreate(width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorSpaceRef,
bitmapInfo,
provider, // data provider
NULL, // decode
YES, // should interpolate
renderingIntent);
uint32_t* pixels = (uint32_t*)malloc(bufferLength);
if(pixels == NULL) {
DLog(#"Error: Memory not allocated for bitmap");
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpaceRef);
CGImageRelease(iref);
return nil;
}
CGContextRef context = CGBitmapContextCreate(pixels,
width,
height,
bitsPerComponent,
bytesPerRow,
colorSpaceRef,
bitmapInfo);
if(context == NULL) {
DLog(#"Error context not created");
free(pixels);
}
UIImage *image = nil;
if(context) {
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), iref);
CGImageRef imageRef = CGBitmapContextCreateImage(context);
image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGContextRelease(context);
}
CGColorSpaceRelease(colorSpaceRef);
CGImageRelease(iref);
CGDataProviderRelease(provider);
if(pixels) {
free(pixels);
}
return image;
}
I have seen that this question is similar to what I want to achieve :
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange frame to UIImage conversion
But I think this would add an extra step to my conversion.
There is any way of creating a color UIImage form the buffer directly?
I would appreciate some indications.
I was able to find a method to help me modify the alpha value for a pixel in a UIImage in my app, but I am running into two errors (the second is most certainly caused by the first). I can't, however, figure out what is going on.
My method:
- (void)modifyAlpha:(int)x and:(int)y {
CGContextRef ctx;
CGImageRef imageRef = [scratchOffImage CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);
// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
rawData[byteIndex+3] = (char) (255); //Change the pixel value
ctx = CGBitmapContextCreate(rawData,
CGImageGetWidth( imageRef ),
CGImageGetHeight( imageRef ),
8,
CGImageGetBytesPerRow( imageRef ),
CGImageGetColorSpace( imageRef ),
kCGImageAlphaPremultipliedFirst ); //This line causes an error: incorrect colorspace
imageRef = CGBitmapContextCreateImage (ctx);
UIImage* rawImage = [UIImage imageWithCGImage:imageRef];
CGContextRelease(ctx);
scratchOffImage = rawImage;
[scratchOffImageView setImage:scratchOffImage];
free(rawData);
}
The error:
<Error>: CGBitmapContextCreate: unsupported color space.
is being thrown on the line:
ctx = CGBitmapContextCreate(rawData, ...
and then the second error:
<Error>: CGBitmapContextCreateImage: invalid context 0x0
is being thrown on the line:
imageRef = CGBitmapContextCreateImage (ctx);
My image is included in the bundle I originally made it by outputting it Photoshop's Save for Web function using PNG-8 with transparancy as the format.
When I ran the sample code that the function was first used in, the sample image worked fine. However, my image doesn't. How can I debug this?
Does anyone have any idea how I can debug this? Might my input PNG be formatted incorrectly? How can I check this?
Cheers,
Brett
EDIT 1: The original source code came from the example found here. The sample shows a conversion to greyscale, whereas I only need to change the alpha value.
EDIT 2: I have tried saving my image as a PNG-8 and a PNG-24, both with no luck.
A PNG-8 uses an 8-bit indexed color space. Quartz doesn't support indexed color spaces for CGBitmapContext. The CGBitmapContextCreate documentation says "Note that indexed color spaces are not supported for bitmap graphics contexts."
Instead of passing CGImageGetColorSpace( imageRef ) as the color space in your second call to CGBitmapContextCreate, you want to pass the same color space you used in your first call (your colorSpace variable).
Anyway, there's no reason to even create a second CGBitmapContext. And you're leaking the result of CGBitmapContextCreateImage. Just do this:
- (void)modifyAlpha:(int)x and:(int)y {
CGImageRef imageRef = [scratchOffImage CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
rawData[byteIndex+3] = (char) (255); //Change the pixel value
imageRef = CGBitmapContextCreateImage (context);
CGContextRelease(context);
UIImage* rawImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
scratchOffImage = rawImage;
[scratchOffImageView setImage:scratchOffImage];
free(rawData);
}
Ok, what I'm trying to achieve is to load an image as a single resource and then save different parts of it as a number of different textures (basically chopping the image into smaller squares and then saving them separately).
For my game, simply mapping different sections of the original image to my shapes won't work and being able to have each 'tile' as a separate texture would be awesome.
Below is the code I'm using for my texture loader. I've tried messing round with the width and height of the texture being loaded but getting some weird results.
Any suggestions would be much appreciated.
Thanks
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
NSString *path =
[[NSBundle mainBundle] pathForResource:#"checkerplate" ofType:#"png"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef context = CGBitmapContextCreate(
imageData, width, height, 8, 4 * width, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( context, 0, height - height );
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, imageData);
CGContextRelease(context);
free(imageData);
[image release];
[texData release];
Ok, just in case anyone wants to achieve this, i figured it out. All you need to do is modify the line CGContextDrawImage and alter the width and height parameters.
This results in the whole texture being loaded in but then will only draw the area specified on this line.
Basically,
CGContextDrawImage( context, CGRectMake(0, 0, width*2, height*2 ), image.CGImage );
will draw 1/4 of the image (as its height and width are being doubled past the textures size).
:)
I'm creating an iPhone game and I need to load an image from a PNG file into OpenGL (and bind it as a texture). I'm using function glTexImage2D to achieve this goal.
I know how to load an image into OpenGL using UIImage (by converting it into CGImage and afterwards drawing into a context).
How can I call my Objective-C code from within C++ code (I'm coding in .mm file)?
Here's the code I'm using and that would work in Objective-C:
NSString *path = [[NSBundle mainBundle] pathForResource:#"texture" ofType:#"png"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil)
NSLog(#"Do real error checking here");
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( context, 0, 0 );
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CGContextRelease(context);
free(imageData);
[image release];
[texData release];
Is there a way to call those UI and Core Graphics functions from C++ code? What files do I have to include?
Thank you in advance.
Solved the problem. Had to find manually and add the Core Graphics framework.