Skip to content

Instantly share code, notes, and snippets.

@PaulSolt
Created December 13, 2010 15:54
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save PaulSolt/739132 to your computer and use it in GitHub Desktop.
Save PaulSolt/739132 to your computer and use it in GitHub Desktop.
A simple UIImage to RGBA8 conversion function
/*
* The MIT License
*
* Copyright (c) 2011 Paul Solt, PaulSolt@gmail.com
*
* https://github.com/PaulSolt/UIImage-Conversion/blob/master/MITLicense.txt
*
*/
#import <Foundation/Foundation.h>
@interface ImageHelper : NSObject {
}
/** Converts a UIImage to RGBA8 bitmap.
@param image - a UIImage to be converted
@return a RGBA8 bitmap, or NULL if any memory allocation issues. Cleanup memory with free() when done.
*/
+ (unsigned char *) convertUIImageToBitmapRGBA8:(UIImage *)image;
/** A helper routine used to convert a RGBA8 to UIImage
@return a new context that is owned by the caller
*/
+ (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef)image;
/** Converts a RGBA8 bitmap to a UIImage.
@param buffer - the RGBA8 unsigned char * bitmap
@param width - the number of pixels wide
@param height - the number of pixels tall
@return a UIImage that is autoreleased or nil if memory allocation issues
*/
+ (UIImage *) convertBitmapRGBA8ToUIImage:(unsigned char *)buffer
withWidth:(int)width
withHeight:(int)height;
@end
/*
* The MIT License
*
* Copyright (c) 2011 Paul Solt, PaulSolt@gmail.com
*
* https://github.com/PaulSolt/UIImage-Conversion/blob/master/MITLicense.txt
*
*/
#import "ImageHelper.h"
@implementation ImageHelper
+ (unsigned char *) convertUIImageToBitmapRGBA8:(UIImage *) image {
CGImageRef imageRef = image.CGImage;
// Create a bitmap context to draw the uiimage into
CGContextRef context = [self newBitmapRGBA8ContextFromImage:imageRef];
if(!context) {
return NULL;
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGRect rect = CGRectMake(0, 0, width, height);
// Draw image into the context to get the raw image data
CGContextDrawImage(context, rect, imageRef);
// Get a pointer to the data
unsigned char *bitmapData = (unsigned char *)CGBitmapContextGetData(context);
// Copy the data and release the memory (return memory allocated with new)
size_t bytesPerRow = CGBitmapContextGetBytesPerRow(context);
size_t bufferLength = bytesPerRow * height;
unsigned char *newBitmap = NULL;
if(bitmapData) {
newBitmap = (unsigned char *)malloc(sizeof(unsigned char) * bytesPerRow * height);
if(newBitmap) { // Copy the data
for(int i = 0; i < bufferLength; ++i) {
newBitmap[i] = bitmapData[i];
}
}
free(bitmapData);
} else {
NSLog(@"Error getting bitmap pixel data\n");
}
CGContextRelease(context);
return newBitmap;
}
+ (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef) image {
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
uint32_t *bitmapData;
size_t bitsPerPixel = 32;
size_t bitsPerComponent = 8;
size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t bytesPerRow = width * bytesPerPixel;
size_t bufferLength = bytesPerRow * height;
colorSpace = CGColorSpaceCreateDeviceRGB();
if(!colorSpace) {
NSLog(@"Error allocating color space RGB\n");
return NULL;
}
// Allocate memory for image data
bitmapData = (uint32_t *)malloc(bufferLength);
if(!bitmapData) {
NSLog(@"Error allocating memory for bitmap\n");
CGColorSpaceRelease(colorSpace);
return NULL;
}
//Create bitmap context
context = CGBitmapContextCreate(bitmapData,
width,
height,
bitsPerComponent,
bytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast); // RGBA
if(!context) {
free(bitmapData);
NSLog(@"Bitmap context not created");
}
CGColorSpaceRelease(colorSpace);
return context;
}
+ (UIImage *) convertBitmapRGBA8ToUIImage:(unsigned char *) buffer
withWidth:(int) width
withHeight:(int) height {
size_t bufferLength = width * height * 4;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 32;
size_t bytesPerRow = 4 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
if(colorSpaceRef == NULL) {
NSLog(@"Error allocating color space");
CGDataProviderRelease(provider);
return nil;
}
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
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) {
NSLog(@"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) {
NSLog(@"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);
// Support both iPad 3.2 and iPhone 4 Retina displays with the correct scale
if([UIImage respondsToSelector:@selector(imageWithCGImage:scale:orientation:)]) {
float scale = [[UIScreen mainScreen] scale];
image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
} else {
image = [UIImage imageWithCGImage:imageRef];
}
CGImageRelease(imageRef);
CGContextRelease(context);
}
CGColorSpaceRelease(colorSpaceRef);
CGImageRelease(iref);
CGDataProviderRelease(provider);
if(pixels) {
free(pixels);
}
return image;
}
@end
@PaulSolt
Copy link
Author

Fixed the issue with alpha transparency not being preserved.

@visualication
Copy link

hi,
i need to convert rgb data to uiimage.
using your class always producing
EXC_BAD_ACCESS at CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), iref); in line 173.
any ideas?
thx

@abhay-bhave
Copy link

I also ran into same problem that @visualication did.

Getting EXC_BAD_ACCESS code=1

Many suggestions point to releasing resources prematurely, but this code seems to release everything after this CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), iref);

It still crashes at the line, so it can't be the problem with premature release of resources.

@abhay-bhave
Copy link

abhay-bhave commented Feb 6, 2017

By the way, I tweaked it a little as follows:

ImageHelper.h line 21
+ (unsigned char *) convertUIImageToBitmapARGB8:(UIImage *)image OutLength:(size_t*)length;

ImageHelper.m implementation for the function changed to

+ (unsigned char *) convertUIImageToBitmapARGB8:(UIImage *) image OutLength:(size_t*)length{
	
	CGImageRef imageRef = image.CGImage;
	
	// Create a bitmap context to draw the uiimage into
	CGContextRef context = [self newBitmapARGB8ContextFromImage:imageRef];
	
	if(!context) {
        *length=0;
		return NULL;
	}
	
	size_t width = CGImageGetWidth(imageRef);
	size_t height = CGImageGetHeight(imageRef);
	
	CGRect rect = CGRectMake(0, 0, width, height);
	
	// Draw image into the context to get the raw image data
	CGContextDrawImage(context, rect, imageRef);
	
	// Get a pointer to the data	
	unsigned char *bitmapData = (unsigned char *)CGBitmapContextGetData(context);
	
	// Copy the data and release the memory (return memory allocated with new)
	size_t bytesPerRow = CGBitmapContextGetBytesPerRow(context);
	size_t bufferLength = bytesPerRow * height;
	
    size_t argb8Length=0;
    
	unsigned char *newBitmap = NULL;
	
	if(bitmapData) {
		newBitmap = (unsigned char *)malloc(sizeof(unsigned char) * bytesPerRow * height);
		
		if(newBitmap) {	// Copy the data
			for(int i = 0; i < bufferLength; ++i) {
				newBitmap[i] = bitmapData[i];
                argb8Length++;
			}
		}
		
		free(bitmapData);
		
	} else {
		NSLog(@"Error getting bitmap pixel data\n");
	}
	CGContextRelease(context);
	
    *length = argb8Length;
	return newBitmap;	
}

This helped me by allowing

size_t length=0;
 NSData *bitmapARGB8Data = [
                            NSData dataWithBytes:[
                                                  ImageHelper convertUIImageToBitmapARGB8:compimage
                                                                                OutLength:&length
                                                  ]
                            length:length
                           ];

Otherwise I had no byte length and could not create the data object easily.

I am not sure this is the best approach, so putting it up here.

@trsa74
Copy link

trsa74 commented Dec 23, 2022

I am facing an issue of EXC_BAD_ACCESS (code=1, address=0x7fe836b00001), the program crashes
Error

@PaulSolt
Copy link
Author

@trsa74 What is your input buffer?

The function is based on the assumption that your source buffer is a RGBA format that is 32 bits per pixel, so 8 bits per color channel with alpha.

Your crash is on the input buffer on line 14 in your screenshot.

If your input format needs to be different, you'll need to extend the logic to not be hard coded to the RGBA 32-bit format.

+ (UIImage *) convertBitmapRGBA8ToUIImage:(unsigned char *) buffer 
		withWidth:(int) width
	   withHeight:(int) height {
	
	
	size_t bufferLength = width * height * 4;  /// Assumes 4 channels (RGBA)
	CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
	size_t bitsPerComponent = 8; 
	size_t bitsPerPixel = 32;
	size_t bytesPerRow = 4 * width; /// Assumes 4 channels

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment