Skip to content

Instantly share code, notes, and snippets.

@nielsbot
Created February 19, 2012 00:43
Show Gist options
  • Save nielsbot/1861465 to your computer and use it in GitHub Desktop.
Save nielsbot/1861465 to your computer and use it in GitHub Desktop.
Decoding JPEG 2000 files to UIImage
#import <Foundation/Foundation.h>
extern UIImage * UIImageWithJPEG2000Data( NSData * data ) ;
#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 ;
}
#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 ;
}
@samsonrck48
Copy link

Hi I am still waiting for your reply. When we added these files and tried to compile following errors occured:

ld: duplicate symbol _UIImageWithJPEG2000Data in /Users/user/Library/Developer/Xcode/DerivedData/ImageDecoder-cdbtscpixkufnxdtohvztbkldnzd/Build/Intermediates/ImageDecoder.build/Debug-iphonesimulator/ImageDecoder.build/Objects-normal/i386/UIImageJPEG2000.o and /Users/user/Library/Developer/Xcode/DerivedData/ImageDecoder-cdbtscpixkufnxdtohvztbkldnzd/Build/Intermediates/ImageDecoder.build/Debug-iphonesimulator/ImageDecoder.build/Objects-normal/i386/UIImage+JPEG2000.o for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I couldn't figure out where it went wrong. Hope you got a solution in handy and will show up with it. :)

@nielsbot
Copy link
Author

nielsbot commented May 30, 2012 via email

@srijithv
Copy link

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 :-)

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