Created
February 19, 2012 00:43
-
-
Save nielsbot/1861465 to your computer and use it in GitHub Desktop.
Decoding JPEG 2000 files to UIImage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
extern UIImage * UIImageWithJPEG2000Data( NSData * data ) ; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "UIImage+JPEG2000.h" | |
#import <objc/runtime.h> | |
#import "openjpeg.h" | |
static UIImage * UIImageWithJPEG2000File( id self, SEL selector, NSString * name ) ; | |
@implementation NSObject (UIImage_JPEG2000) | |
static UIImage * (*old_UIImage_imageNamed)(id target, SEL selector, NSString * name ) = NULL ; | |
+(void)load | |
{ | |
NSAutoreleasePool * pool = [[ NSAutoreleasePool alloc ] init ] ; | |
Method m = class_getClassMethod( [ UIImage class ], @selector( imageNamed: )) ; | |
old_UIImage_imageNamed = (UIImage *(*)(id, SEL, NSString*))method_setImplementation( m, (IMP)UIImageWithJPEG2000File ) ; | |
DebugAssert( old_UIImage_imageNamed ) ; | |
[ pool release ] ; | |
} | |
@end | |
static CGImageRef CGImageCreateWithJPEG2000Image( opj_image_t * image ) | |
{ | |
long w = image->comps[0].w ; | |
long h = image->comps[0].h ; | |
DebugAssert( w > 0 && h > 0 ) ; | |
CGColorSpaceRef cs = NULL ; | |
BOOL hasAlpha = NO ; | |
if ( image->numcomps == 1 ) | |
{ | |
cs = CGColorSpaceCreateDeviceGray() ; | |
} | |
else | |
{ | |
// only support 3 (RGB) or 4 (RGBA) component images | |
DebugAssert( image->numcomps == 3 || image->numcomps == 4 ) ; | |
hasAlpha = image->numcomps == 4 ; | |
cs = CGColorSpaceCreateDeviceRGB() ; | |
} | |
DebugAssert( cs ) ; | |
for( int index=0; index < image->numcomps; ++index ) | |
{ | |
DebugAssert( image->comps[ index ].prec == 8 && | |
w == image->comps[index].w | |
&& h == image->comps[index].h ) ; | |
} | |
size_t bitmapNumBytes = w * h * 4;//image->numcomps ; | |
CFMutableDataRef bitmapCFData = CFDataCreateMutable( kCFAllocatorDefault, 0 ) ; | |
CFDataSetLength( bitmapCFData, bitmapNumBytes ) ; | |
DebugAssert( bitmapCFData ) ; | |
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Big ; | |
if ( image->numcomps == 1 ) | |
{ | |
uint8_t * p = (uint8_t*)CFDataGetMutableBytePtr( bitmapCFData ) ; | |
uint32_t * s = (uint32_t*)image->comps[0].data ; | |
for( int index=0, count = w * h; index < count; ++index ) | |
{ | |
*p = *s ; | |
++p ; | |
++s ; | |
} | |
} | |
else | |
{ | |
uint8_t * p = (uint8_t*)CFDataGetMutableBytePtr( bitmapCFData ) ; | |
uint32_t * r = (uint32_t *)image->comps[0].data ; | |
uint32_t * g = (uint32_t *)image->comps[1].data ; | |
uint32_t * b = (uint32_t *)image->comps[2].data ; | |
if ( hasAlpha ) | |
{ | |
bitmapInfo |= kCGImageAlphaPremultipliedLast ; | |
uint32_t * a = (uint32_t *)image->comps[3].data ; | |
for( int index=0, count = w * h; index < count; ++index ) | |
{ | |
*p++ = *r++ ; | |
*p++ = *g++ ; | |
*p++ = *b++ ; | |
*p++ = *a++ ; | |
} | |
} | |
else | |
{ | |
bitmapInfo |= kCGImageAlphaNoneSkipLast ; | |
for( int index=0, count = w * h; index < count; ++index ) | |
{ | |
*p++ = *r++ ; | |
*p++ = *g++ ; | |
*p++ = *b++ ; | |
*p++ = 0xFF ; | |
} | |
} | |
} | |
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData( bitmapCFData ) ; | |
if ( bitmapCFData ) { CFRelease( bitmapCFData ) ; } | |
DebugAssert( dataProvider ) ; | |
int bpc = image->comps[0].prec ; // bits per component | |
int bpp = image->numcomps == 1 ? bpc : (4 * bpc) ; // bits per pixel | |
int bpr = bpp * w / 8 ; // bytes per row | |
CGImageRef cgImage = CGImageCreate(w, h, bpc, bpp, bpr, cs, bitmapInfo, dataProvider, NULL, false, kCGRenderingIntentDefault ) ; | |
CGDataProviderRelease( dataProvider ) ; | |
CGColorSpaceRelease( cs ) ; | |
DebugAssert( cgImage ) ; | |
return cgImage ; | |
} | |
UIImage * UIImageWithJPEG2000Data( NSData * data ) | |
{ | |
if ( data.length == 0 ) | |
{ | |
DebugLog(@"%s data is empty\n", __PRETTY_FUNCTION__ ) ; | |
return nil ; | |
} | |
opj_dinfo_t * decompressor = opj_create_decompress(CODEC_JP2) ; // CODEC_JP2? Just a guess. | |
DebugAssert( decompressor ) ; | |
opj_dparameters_t params ; | |
opj_set_default_decoder_parameters( & params ); | |
opj_setup_decoder( decompressor, & params ) ; | |
opj_cio_t * cio = opj_cio_open( (opj_common_struct_t*)decompressor, (unsigned char *)data.bytes, data.length ) ; | |
DebugAssert( cio ) ; | |
// opj_set_event_mgr( (opj_common_ptr)decompressor, & (opj_event_mgr_t){ error_handler, warning_handler, info_handler }, NULL ) ; | |
opj_image_t * image = opj_decode( decompressor, cio ) ; | |
CGImageRef cgImage = nil ; | |
if ( image ) | |
{ | |
cgImage = CGImageCreateWithJPEG2000Image( image ) ; | |
opj_image_destroy( image ) ; | |
} | |
opj_cio_close( cio ) ; | |
opj_destroy_decompress( decompressor ) ; | |
if ( !cgImage ) | |
{ | |
return nil ; | |
} | |
UIImage * result = [ UIImage imageWithCGImage:cgImage ] ; | |
CGImageRelease( cgImage ) ; | |
return result ; | |
} | |
static UIImage * UIImageWithJPEG2000File( id self, SEL selector, NSString * name ) | |
{ | |
UIImage * result = (*old_UIImage_imageNamed)( self, selector, name ) ; | |
if ( !result ) | |
{ | |
NSError * error = nil ; | |
NSString * baseName = [ name stringByDeletingPathExtension ] ; | |
NSString * extension = [ name pathExtension ] ; | |
NSString * path = [ [ NSBundle mainBundle ] pathForResource:baseName ofType:extension ] ; | |
if ( path ) | |
{ | |
NSData * data = [ NSData dataWithContentsOfFile:path options:NSDataReadingMapped error:&error ] ; | |
DebugAssert( data ) ; | |
result = UIImageWithJPEG2000Data( data ) ; | |
} | |
} | |
return result ; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "UIImage+JPEG2000.h" | |
#import <objc/runtime.h> | |
#import "openjpeg.h" | |
static UIImage * UIImageWithJPEG2000File( id self, SEL selector, NSString * name ) ; | |
@implementation NSObject (UIImage_JPEG2000) | |
static UIImage * (*old_UIImage_imageNamed)(id target, SEL selector, NSString * name ) = NULL ; | |
+(void)load | |
{ | |
NSAutoreleasePool * pool = [[ NSAutoreleasePool alloc ] init ] ; | |
Method m = class_getClassMethod( [ UIImage class ], @selector( imageNamed: )) ; | |
old_UIImage_imageNamed = (UIImage *(*)(id, SEL, NSString*))method_setImplementation( m, (IMP)UIImageWithJPEG2000File ) ; | |
DebugAssert( old_UIImage_imageNamed ) ; | |
[ pool release ] ; | |
} | |
@end | |
static CGImageRef CGImageCreateWithJPEG2000Image( opj_image_t * image ) | |
{ | |
long w = image->comps[0].w ; | |
long h = image->comps[0].h ; | |
DebugAssert( w > 0 && h > 0 ) ; | |
CGColorSpaceRef cs = NULL ; | |
BOOL hasAlpha = NO ; | |
if ( image->numcomps == 1 ) | |
{ | |
cs = CGColorSpaceCreateDeviceGray() ; | |
} | |
else | |
{ | |
// only support 3 (RGB) or 4 (RGBA) component images | |
DebugAssert( image->numcomps == 3 || image->numcomps == 4 ) ; | |
hasAlpha = image->numcomps == 4 ; | |
cs = CGColorSpaceCreateDeviceRGB() ; | |
} | |
DebugAssert( cs ) ; | |
for( int index=0; index < image->numcomps; ++index ) | |
{ | |
DebugAssert( image->comps[ index ].prec == 8 && | |
w == image->comps[index].w | |
&& h == image->comps[index].h ) ; | |
} | |
size_t bitmapNumBytes = w * h * 4;//image->numcomps ; | |
CFMutableDataRef bitmapCFData = CFDataCreateMutable( kCFAllocatorDefault, 0 ) ; | |
CFDataSetLength( bitmapCFData, bitmapNumBytes ) ; | |
DebugAssert( bitmapCFData ) ; | |
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Big ; | |
if ( image->numcomps == 1 ) | |
{ | |
uint8_t * p = (uint8_t*)CFDataGetMutableBytePtr( bitmapCFData ) ; | |
uint32_t * s = (uint32_t*)image->comps[0].data ; | |
for( int index=0, count = w * h; index < count; ++index ) | |
{ | |
*p = *s ; | |
++p ; | |
++s ; | |
} | |
} | |
else | |
{ | |
uint8_t * p = (uint8_t*)CFDataGetMutableBytePtr( bitmapCFData ) ; | |
uint32_t * r = (uint32_t *)image->comps[0].data ; | |
uint32_t * g = (uint32_t *)image->comps[1].data ; | |
uint32_t * b = (uint32_t *)image->comps[2].data ; | |
if ( hasAlpha ) | |
{ | |
bitmapInfo |= kCGImageAlphaPremultipliedLast ; | |
uint32_t * a = (uint32_t *)image->comps[3].data ; | |
for( int index=0, count = w * h; index < count; ++index ) | |
{ | |
*p++ = *r++ ; | |
*p++ = *g++ ; | |
*p++ = *b++ ; | |
*p++ = *a++ ; | |
} | |
} | |
else | |
{ | |
bitmapInfo |= kCGImageAlphaNoneSkipLast ; | |
for( int index=0, count = w * h; index < count; ++index ) | |
{ | |
*p++ = *r++ ; | |
*p++ = *g++ ; | |
*p++ = *b++ ; | |
*p++ = 0xFF ; | |
} | |
} | |
} | |
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData( bitmapCFData ) ; | |
if ( bitmapCFData ) { CFRelease( bitmapCFData ) ; } | |
DebugAssert( dataProvider ) ; | |
int bpc = image->comps[0].prec ; // bits per component | |
int bpp = image->numcomps == 1 ? bpc : (4 * bpc) ; // bits per pixel | |
int bpr = bpp * w / 8 ; // bytes per row | |
CGImageRef cgImage = CGImageCreate(w, h, bpc, bpp, bpr, cs, bitmapInfo, dataProvider, NULL, false, kCGRenderingIntentDefault ) ; | |
CGDataProviderRelease( dataProvider ) ; | |
CGColorSpaceRelease( cs ) ; | |
DebugAssert( cgImage ) ; | |
return cgImage ; | |
} | |
UIImage * UIImageWithJPEG2000Data( NSData * data ) | |
{ | |
if ( data.length == 0 ) | |
{ | |
DebugLog(@"%s data is empty\n", __PRETTY_FUNCTION__ ) ; | |
return nil ; | |
} | |
opj_dinfo_t * decompressor = opj_create_decompress(CODEC_JP2) ; // CODEC_JP2? Just a guess. | |
DebugAssert( decompressor ) ; | |
opj_dparameters_t params ; | |
opj_set_default_decoder_parameters( & params ); | |
opj_setup_decoder( decompressor, & params ) ; | |
opj_cio_t * cio = opj_cio_open( (opj_common_struct_t*)decompressor, (unsigned char *)data.bytes, data.length ) ; | |
DebugAssert( cio ) ; | |
// opj_set_event_mgr( (opj_common_ptr)decompressor, & (opj_event_mgr_t){ error_handler, warning_handler, info_handler }, NULL ) ; | |
opj_image_t * image = opj_decode( decompressor, cio ) ; | |
CGImageRef cgImage = nil ; | |
if ( image ) | |
{ | |
cgImage = CGImageCreateWithJPEG2000Image( image ) ; | |
opj_image_destroy( image ) ; | |
} | |
opj_cio_close( cio ) ; | |
opj_destroy_decompress( decompressor ) ; | |
if ( !cgImage ) | |
{ | |
return nil ; | |
} | |
UIImage * result = [ UIImage imageWithCGImage:cgImage ] ; | |
CGImageRelease( cgImage ) ; | |
return result ; | |
} | |
static UIImage * UIImageWithJPEG2000File( id self, SEL selector, NSString * name ) | |
{ | |
UIImage * result = (*old_UIImage_imageNamed)( self, selector, name ) ; | |
if ( !result ) | |
{ | |
NSError * error = nil ; | |
NSString * baseName = [ name stringByDeletingPathExtension ] ; | |
NSString * extension = [ name pathExtension ] ; | |
NSString * path = [ [ NSBundle mainBundle ] pathForResource:baseName ofType:extension ] ; | |
if ( path ) | |
{ | |
NSData * data = [ NSData dataWithContentsOfFile:path options:NSDataReadingMapped error:&error ] ; | |
DebugAssert( data ) ; | |
result = UIImageWithJPEG2000Data( data ) ; | |
} | |
} | |
return result ; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey,
Sorry for that comment. Removed it now. That was completely my mistake! It works like a gem!! Apologies! Memory was loosing on my end, and instrument pointed it to your code :-)