public
Last active

Decoding JPEG 2000 files to UIImage

  • Download Gist
UIImage+JPEG2000.h
Objective-C
1 2 3
#import <Foundation/Foundation.h>
 
extern UIImage * UIImageWithJPEG2000Data( NSData * data ) ;
UIImage+JPEG2000.m
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
#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 ;
}
UIImageJPEG2000.m
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
#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 ;
}

Hi,

Thankyou so much for you information.

Can you please guide me from where i can get the "UIImage+JPEG2000.h".

Hope for your help.

Samson

I added it--but as you can see there is nothing there. :)

Thanks a lot for your quick turnaround.

We can't find a method like "UIImageWithJPEG2000File" OpenJpeg.h file we have.

So Can you please kindly guide us in how to call the method.

Is there any file like "UIImageWithJPEG2000File.h" so that we can call those methods.

Hoping for your help.

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

hi

there is an implementation some did based on this code which I expect to be posted on github soon--maybe it will have the fix

I guess the issue is the objects returned by obj_create_decompress and obj_decode. I would look in the openJPEG library for functions to dealloc 'decompressor' and 'image'.

As for CFDataSetLength, make sure CFRelease() is being called on bitmapCFData when you are done with it.

Make sense?

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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.