I load images in a C++ library using the code below. When loading some images the rotation of the image is wrong. It seems to affect JPEGs that come from the iPhone camera. How do I fix this?
Presumably there is a flag somewhere that gets set for JPEGs captured by the camera. I'm not sure how to access it when loading the images in this way.
CGImageSourceRef source = CGImageSourceCreateWithURL(url, NULL);
CFRelease(url);
if(source==NULL)
return IM_ErrFileError;
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
CFRelease(source);
if(image==NULL)
return IM_ErrFileError;
int w = (int)CGImageGetWidth(image);
int h = (int)CGImageGetHeight(image);
im::Err err = img.Create(im::IntSize(w, h), bands, sizeof(uint8_t), imgtype);
if(IM_FAILED(err))
{
CGImageRelease(image);
return err;
}
img.Clear();
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(colorspacename);
if(colorSpace==NULL)
{
CGImageRelease(image);
return IM_ErrAlloc;
}
// Create RGBA context
CGContextRef context = CGBitmapContextCreate(img.BytePtr(), w, h, 8, img.StrideBytes(), colorSpace, alphainfo);
if(context==NULL)
{
CGColorSpaceRelease(colorSpace);
CGImageRelease(image);
return IM_ErrAlloc;
}
CGColorSpaceRelease(colorSpace);
CGRect rect;
rect.origin.x = 0;
rect.origin.y = 0;
rect.size.width = w;
rect.size.height = h;
CGContextDrawImage(context, rect, image);
CGContextRelease(context);
CGImageRelease(image);
Based on the answer, here is how I modified my code. First get the orientation:
int orientation = 1;
CFDictionaryRef dict = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
if(dict)
{
CFNumberRef imageOrientation;
if(CFDictionaryGetValueIfPresent(dict, kCGImagePropertyOrientation, (const void **)&imageOrientation))
{
if(imageOrientation)
CFNumberGetValue(imageOrientation, kCFNumberIntType, &orientation);
}
CFRelease(dict);
}
Ensure that the sizes are correct:
int width = (int)CGImageGetWidth(image);
int height = (int)CGImageGetHeight(image);
int canvasw, canvash;
if(orientation<=4)
{
canvasw = width;
canvash = height;
}
else
{
canvasw = height;
canvash = width;
}
Use the canvas size to create the bitmap context image. Compensate for the orientation when rendering:
switch(orientation)
{
case 2:
// 2 = 0th row is at the top, and 0th column is on the right - Flip Horizontal
CGContextConcatCTM(context, CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, width, 0.0));
break;
case 3:
// 3 = 0th row is at the bottom, and 0th column is on the right - Rotate 180 degrees
CGContextConcatCTM(context, CGAffineTransformMake(-1.0, 0.0, 0.0, -1.0, width, height));
break;
case 4:
// 4 = 0th row is at the bottom, and 0th column is on the left - Flip Vertical
CGContextConcatCTM(context, CGAffineTransformMake(1.0, 0.0, 0, -1.0, 0.0, height));
break;
case 5:
// 5 = 0th row is on the left, and 0th column is the top - Rotate -90 degrees and Flip Vertical
CGContextConcatCTM(context, CGAffineTransformMake(0.0, -1.0, -1.0, 0.0, height, width));
break;
case 6:
// 6 = 0th row is on the right, and 0th column is the top - Rotate 90 degrees
CGContextConcatCTM(context, CGAffineTransformMake(0.0, -1.0, 1.0, 0.0, 0.0, width));
break;
case 7:
// 7 = 0th row is on the right, and 0th column is the bottom - Rotate 90 degrees and Flip Vertical
CGContextConcatCTM(context, CGAffineTransformMake(0.0, 1.0, 1.0, 0.0, 0.0, 0.0));
break;
case 8:
// 8 = 0th row is on the left, and 0th column is the bottom - Rotate -90 degrees
CGContextConcatCTM(context, CGAffineTransformMake(0.0, 1.0, -1.0, 0.0, height, 0.0));
break;
default:
break;
}
I haven't actually tried this with iPhone images, but I suggest using CGImageSourceCopyPropertiesAtIndex
to get kCGImagePropertyOrientation
. Once you know the proper orientation, you can apply some transformation when you draw it.