2012-05-14 15:14:09 +00:00
|
|
|
#import "O2ImageDecoder_JPEG_libjpeg.h"
|
|
|
|
#import "O2Defines_libjpeg.h"
|
|
|
|
|
|
|
|
#ifdef LIBJPEG_PRESENT
|
|
|
|
|
|
|
|
#import <libjpeg/include/jpeglib.h>
|
|
|
|
|
|
|
|
@implementation O2ImageDecoder_JPEG_libjpeg
|
|
|
|
|
|
|
|
typedef struct o2jpg_error_mgr {
|
|
|
|
struct jpeg_error_mgr err;
|
|
|
|
jmp_buf jmp; // Additional info about where to go on error
|
|
|
|
} o2jpg_error_mgr;
|
|
|
|
|
|
|
|
// Use libjpeg to do the decoding
|
|
|
|
static void o2error_exit(j_common_ptr cinfo)
|
|
|
|
{
|
|
|
|
o2jpg_error_mgr *o2err = (o2jpg_error_mgr *)cinfo->err;
|
|
|
|
|
|
|
|
// Doesn't hurt to display what went wrong
|
|
|
|
(*cinfo->err->output_message)(cinfo);
|
|
|
|
|
|
|
|
// Jump to our error handling code
|
|
|
|
longjmp(o2err->jmp, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned char *stbi_jpeg_load_from_memory(char const *buffer, int len, int *x, int *y)
|
|
|
|
{
|
|
|
|
struct jpeg_decompress_struct cinfo;
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
|
|
|
|
o2jpg_error_mgr jerr;
|
|
|
|
|
|
|
|
cinfo.err = jpeg_std_error(&jerr.err);
|
|
|
|
// Use our own exit routine - we don't want to exit the app when something goes wrong
|
|
|
|
jerr.err.error_exit = o2error_exit;
|
|
|
|
// We need to use setjmp since the error exit method must no return
|
|
|
|
if (setjmp(jerr.jmp)) {
|
|
|
|
// Do some cleaning and return an empty image
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Read the data from our memory buffer
|
|
|
|
jpeg_mem_src( &cinfo, (char *)buffer, len );
|
|
|
|
|
|
|
|
// Setup the jpeg header and set the decompress options
|
|
|
|
jpeg_read_header(&cinfo, TRUE);
|
|
|
|
|
|
|
|
// We only support RGBA format for the output format
|
|
|
|
if (cinfo.jpeg_color_space == JCS_CMYK || cinfo.jpeg_color_space == JCS_YCCK) {
|
|
|
|
// libjpeg doesn't know how to do the CMYK->RGBx conversion - we'll have to do it
|
|
|
|
// ourself
|
|
|
|
cinfo.out_color_space = JCS_CMYK;
|
|
|
|
} else {
|
|
|
|
#ifdef JCS_ALPHA_EXTENSIONS
|
|
|
|
// libjpeg turbo can convert the output data directly to the RGBA buffer format we want
|
|
|
|
cinfo.out_color_space = JCS_EXT_RGBA;
|
|
|
|
#else
|
|
|
|
cinfo.out_color_space = JCS_RGB;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
int wantedPixelSize = cinfo.output_components = cinfo.out_color_components = 4;
|
|
|
|
|
|
|
|
|
|
|
|
jpeg_start_decompress( &cinfo );
|
|
|
|
*x = cinfo.output_width;
|
|
|
|
*y = cinfo.output_height;
|
|
|
|
|
|
|
|
// Number of bytes in a decompressed row
|
|
|
|
int bytesPerRow = cinfo.output_width*wantedPixelSize;
|
|
|
|
|
|
|
|
// Buffer for the final decompressed data
|
|
|
|
unsigned char *outputImage = (unsigned char*)malloc(bytesPerRow*cinfo.output_height);
|
|
|
|
|
|
|
|
#ifdef JCS_ALPHA_EXTENSIONS
|
|
|
|
// Scanline buffers pointers - they'll point directly to the final image buffer
|
|
|
|
JSAMPROW scanlineBuffer[cinfo.rec_outbuf_height];
|
|
|
|
#else
|
|
|
|
// Scanline buffers
|
|
|
|
JSAMPARRAY scanlineBuffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, bytesPerRow, cinfo.rec_outbuf_height);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while(cinfo.output_scanline < cinfo.image_height) {
|
|
|
|
int currentLine = cinfo.output_scanline;
|
|
|
|
#ifdef JCS_ALPHA_EXTENSIONS
|
|
|
|
// We'll decode directly into the final buffer
|
|
|
|
for (int i = 0; i < cinfo.rec_outbuf_height; ++i) {
|
|
|
|
scanlineBuffer[i] = outputImage + (currentLine + i)*bytesPerRow;
|
|
|
|
}
|
|
|
|
jpeg_read_scanlines(&cinfo, scanlineBuffer, cinfo.rec_outbuf_height);
|
|
|
|
if (cinfo.out_color_space == JCS_CMYK) {
|
|
|
|
// Convert from CMYK to RGBA
|
|
|
|
for (int i = 0; i < cinfo.rec_outbuf_height; ++i) {
|
|
|
|
char *out = outputImage + (currentLine++)*bytesPerRow;
|
|
|
|
for (int j = 0; j < cinfo.output_width; ++j) {
|
|
|
|
unsigned char c = out[0];
|
|
|
|
unsigned char m = out[1];
|
|
|
|
unsigned char y = out[2];
|
|
|
|
unsigned char k = out[3];
|
|
|
|
int r = (c*k)/255;
|
|
|
|
int g = (m*k)/255;
|
|
|
|
int b = (y*k)/255;
|
|
|
|
|
|
|
|
*out++ = r;
|
|
|
|
*out++ = g;
|
|
|
|
*out++ = b;
|
|
|
|
*out++ = 0xff; // add the alpha component
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int count = jpeg_read_scanlines(&cinfo, scanlineBuffer, cinfo.rec_outbuf_height);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
char *out = outputImage + (currentLine++)*bytesPerRow;
|
|
|
|
const char *scanline = scanlineBuffer[i];
|
|
|
|
for (int j = 0; j < cinfo.output_width; ++j) {
|
|
|
|
if (cinfo.out_color_space == JCS_CMYK) {
|
|
|
|
// Convert from CMYK to RGBA
|
|
|
|
unsigned char c = *scanline++;
|
|
|
|
unsigned char m = *scanline++;
|
|
|
|
unsigned char y = *scanline++;
|
|
|
|
unsigned char k = *scanline++;
|
|
|
|
int r = (c*k)/255;
|
|
|
|
int g = (m*k)/255;
|
|
|
|
int b = (y*k)/255;
|
|
|
|
|
|
|
|
*out++ = r;
|
|
|
|
*out++ = g;
|
|
|
|
*out++ = b;
|
|
|
|
*out++ = 0xff; // add the alpha component
|
|
|
|
} else {
|
|
|
|
// Convert from RGB to RGBA
|
|
|
|
*out++ = *scanline++;
|
|
|
|
*out++ = *scanline++;
|
|
|
|
*out++ = *scanline++;
|
|
|
|
*out++ = 0xff; // add the alpha component
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// We're done - do some cleanup
|
|
|
|
jpeg_finish_decompress( &cinfo );
|
|
|
|
jpeg_destroy_decompress( &cinfo );
|
|
|
|
|
|
|
|
return outputImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
-initWithDataProvider:(O2DataProviderRef)dataProvider {
|
|
|
|
|
2012-05-15 02:18:17 +00:00
|
|
|
_compressionType=O2ImageCompressionJPEG;
|
2012-05-14 15:14:09 +00:00
|
|
|
_dataProvider=[dataProvider retain];
|
|
|
|
|
|
|
|
CFDataRef encodedData=O2DataProviderCopyData(dataProvider);
|
|
|
|
CFIndex encodedLength=CFDataGetLength(encodedData);
|
|
|
|
const uint8_t *encodedBytes=CFDataGetBytePtr(encodedData);
|
|
|
|
|
|
|
|
int comp;
|
|
|
|
uint8_t *bitmap;
|
|
|
|
|
|
|
|
int width,height;
|
|
|
|
|
|
|
|
bitmap=stbi_jpeg_load_from_memory(encodedBytes,encodedLength,&width,&height);
|
|
|
|
|
|
|
|
if(bitmap==NULL){
|
|
|
|
[self dealloc];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
_width=width;
|
|
|
|
_height=height;
|
|
|
|
_bitsPerComponent=8;
|
|
|
|
_bitsPerPixel=32;
|
|
|
|
_bytesPerRow=(_bitsPerPixel/(sizeof(char)*8))*_width;
|
|
|
|
_colorSpace=O2ColorSpaceCreateDeviceRGB();
|
|
|
|
_bitmapInfo=kO2BitmapByteOrder32Big|kO2ImageAlphaPremultipliedLast;
|
|
|
|
|
|
|
|
_pixelData=CFDataCreateWithBytesNoCopy(NULL, bitmap, _bytesPerRow*_height, NULL);
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)dealloc {
|
|
|
|
[_dataProvider release];
|
|
|
|
[_colorSpace release];
|
|
|
|
CFRelease(_pixelData);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(CFDataRef)createPixelData {
|
|
|
|
return CFRetain(_pixelData);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|