2019-04-15 20:32:12 +00:00
|
|
|
#import <Onyx2D/O2ImageDecoder_JPEG_libjpeg.h>
|
2012-11-28 17:34:54 +00:00
|
|
|
|
2019-04-15 20:32:12 +00:00
|
|
|
#import <Onyx2D/O2Defines_libjpeg.h>
|
2012-05-14 15:14:09 +00:00
|
|
|
|
|
|
|
#ifdef LIBJPEG_PRESENT
|
|
|
|
|
2017-04-25 09:13:21 +00:00
|
|
|
#import <jpeglib.h>
|
2012-05-14 15:14:09 +00:00
|
|
|
|
|
|
|
@implementation O2ImageDecoder_JPEG_libjpeg
|
|
|
|
|
|
|
|
typedef struct o2jpg_error_mgr {
|
2020-05-11 15:52:05 +00:00
|
|
|
struct jpeg_error_mgr err;
|
|
|
|
jmp_buf jmp; // Additional info about where to go on error
|
2012-05-14 15:14:09 +00:00
|
|
|
} o2jpg_error_mgr;
|
|
|
|
|
|
|
|
// Use libjpeg to do the decoding
|
2020-05-11 15:52:05 +00:00
|
|
|
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);
|
2012-05-14 15:14:09 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
static unsigned char *stbi_jpeg_load_from_memory(const uint8_t const *buffer,
|
2020-05-12 00:04:26 +00:00
|
|
|
int len, int *x, int *y)
|
|
|
|
{
|
2020-05-11 15:52:05 +00:00
|
|
|
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, (unsigned 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 {
|
2012-05-14 15:14:09 +00:00
|
|
|
#ifdef JCS_ALPHA_EXTENSIONS
|
2020-05-11 15:52:05 +00:00
|
|
|
// libjpeg turbo can convert the output data directly to the RGBA buffer
|
|
|
|
// format we want
|
|
|
|
cinfo.out_color_space = JCS_EXT_RGBA;
|
2012-05-14 15:14:09 +00:00
|
|
|
#else
|
2020-05-11 15:52:05 +00:00
|
|
|
cinfo.out_color_space = JCS_RGB;
|
2012-05-14 15:14:09 +00:00
|
|
|
#endif
|
2020-05-11 15:52:05 +00:00
|
|
|
}
|
|
|
|
int wantedPixelSize = cinfo.output_components = cinfo.out_color_components =
|
2020-05-12 18:51:39 +00:00
|
|
|
4;
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
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 =
|
2020-05-12 18:51:39 +00:00
|
|
|
(unsigned char *) malloc(bytesPerRow * cinfo.output_height);
|
2020-05-11 15:52:05 +00:00
|
|
|
if (outputImage) {
|
2012-05-14 15:14:09 +00:00
|
|
|
#ifdef JCS_ALPHA_EXTENSIONS
|
2020-05-11 15:52:05 +00:00
|
|
|
// Scanline buffers pointers - they'll point directly to the final image
|
|
|
|
// buffer
|
2013-12-09 16:59:44 +00:00
|
|
|
JSAMPROW scanlineBuffer[cinfo.rec_outbuf_height];
|
|
|
|
#else
|
|
|
|
// Scanline buffers
|
2020-05-12 18:51:39 +00:00
|
|
|
JSAMPARRAY scanlineBuffer = (*cinfo.mem->alloc_sarray)(
|
|
|
|
(j_common_ptr) &cinfo, JPOOL_IMAGE, bytesPerRow,
|
|
|
|
cinfo.rec_outbuf_height);
|
2012-05-14 15:14:09 +00:00
|
|
|
#endif
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
while (cinfo.output_scanline < cinfo.image_height) {
|
2013-12-09 16:59:44 +00:00
|
|
|
int currentLine = cinfo.output_scanline;
|
2012-05-14 15:14:09 +00:00
|
|
|
#ifdef JCS_ALPHA_EXTENSIONS
|
2013-12-09 16:59:44 +00:00
|
|
|
// We'll decode directly into the final buffer
|
|
|
|
for (int i = 0; i < cinfo.rec_outbuf_height; ++i) {
|
2020-05-11 15:52:05 +00:00
|
|
|
scanlineBuffer[i] =
|
2020-05-12 18:51:39 +00:00
|
|
|
outputImage + (currentLine + i) * bytesPerRow;
|
2013-12-09 16:59:44 +00:00
|
|
|
}
|
2020-05-11 15:52:05 +00:00
|
|
|
jpeg_read_scanlines(&cinfo, scanlineBuffer,
|
|
|
|
cinfo.rec_outbuf_height);
|
2013-12-09 16:59:44 +00:00
|
|
|
if (cinfo.out_color_space == JCS_CMYK) {
|
|
|
|
// Convert from CMYK to RGBA
|
|
|
|
for (int i = 0; i < cinfo.rec_outbuf_height; ++i) {
|
2020-05-11 15:52:05 +00:00
|
|
|
unsigned char *out =
|
2020-05-12 18:51:39 +00:00
|
|
|
outputImage + (currentLine++) * bytesPerRow;
|
2013-12-09 16:59:44 +00:00
|
|
|
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];
|
2020-05-11 15:52:05 +00:00
|
|
|
int r = (c * k) / 255;
|
|
|
|
int g = (m * k) / 255;
|
|
|
|
int b = (y * k) / 255;
|
|
|
|
|
2013-12-09 16:59:44 +00:00
|
|
|
*out++ = r;
|
|
|
|
*out++ = g;
|
|
|
|
*out++ = b;
|
|
|
|
*out++ = 0xff; // add the alpha component
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-14 15:14:09 +00:00
|
|
|
#else
|
2020-05-11 15:52:05 +00:00
|
|
|
int count = jpeg_read_scanlines(&cinfo, scanlineBuffer,
|
|
|
|
cinfo.rec_outbuf_height);
|
2013-12-09 16:59:44 +00:00
|
|
|
for (int i = 0; i < count; ++i) {
|
2020-05-11 15:52:05 +00:00
|
|
|
char *out = outputImage + (currentLine++) * bytesPerRow;
|
2013-12-09 16:59:44 +00:00
|
|
|
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++;
|
2020-05-11 15:52:05 +00:00
|
|
|
int r = (c * k) / 255;
|
|
|
|
int g = (m * k) / 255;
|
|
|
|
int b = (y * k) / 255;
|
|
|
|
|
2013-12-09 16:59:44 +00:00
|
|
|
*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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-14 15:14:09 +00:00
|
|
|
#endif
|
2013-12-09 16:59:44 +00:00
|
|
|
}
|
2020-05-11 15:52:05 +00:00
|
|
|
}
|
|
|
|
// We're done - do some cleanup
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
|
|
|
|
return outputImage;
|
2012-05-14 15:14:09 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- initWithDataProvider: (O2DataProviderRef) dataProvider {
|
|
|
|
|
|
|
|
_compressionType = O2ImageCompressionJPEG;
|
|
|
|
_dataProvider = [dataProvider retain];
|
|
|
|
|
|
|
|
CFDataRef encodedData = O2DataProviderCopyData(dataProvider);
|
|
|
|
CFIndex encodedLength = CFDataGetLength(encodedData);
|
|
|
|
const uint8_t *encodedBytes = CFDataGetBytePtr(encodedData);
|
|
|
|
|
|
|
|
int comp;
|
2012-05-14 15:14:09 +00:00
|
|
|
uint8_t *bitmap;
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
int width, height;
|
|
|
|
|
|
|
|
bitmap = stbi_jpeg_load_from_memory(encodedBytes, encodedLength, &width,
|
|
|
|
&height);
|
|
|
|
|
2013-05-22 18:12:15 +00:00
|
|
|
CFRelease(encodedData);
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
if (bitmap == NULL) {
|
2012-05-14 15:14:09 +00:00
|
|
|
[self dealloc];
|
|
|
|
return nil;
|
|
|
|
}
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
_width = width;
|
|
|
|
_height = height;
|
|
|
|
_bitsPerComponent = 8;
|
|
|
|
_bitsPerPixel = 32;
|
|
|
|
_bytesPerRow = (_bitsPerPixel / (sizeof(char) * 8)) * _width;
|
|
|
|
_colorSpace = O2ColorSpaceCreateDeviceRGB();
|
|
|
|
_bitmapInfo = kO2BitmapByteOrder32Big | kO2ImageAlphaPremultipliedLast;
|
|
|
|
|
2020-05-12 18:51:39 +00:00
|
|
|
_pixelData = (CFDataRef)
|
|
|
|
[[NSData alloc] initWithBytesNoCopy: bitmap
|
|
|
|
length: _bytesPerRow * _height
|
|
|
|
freeWhenDone: YES];
|
2020-05-11 15:52:05 +00:00
|
|
|
|
2012-05-14 15:14:09 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (void) dealloc {
|
2012-05-14 15:14:09 +00:00
|
|
|
[_dataProvider release];
|
|
|
|
[_colorSpace release];
|
|
|
|
CFRelease(_pixelData);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (CFDataRef) createPixelData {
|
2012-05-14 15:14:09 +00:00
|
|
|
return CFRetain(_pixelData);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
#endif
|