X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=plugins%2Fimage%2Fjpeg.cpp;h=fdc1ef0418acf5f1e15eac863249233c14cf93ea;hb=9dfae1c9b270ee369c6362903a9205b30751b95f;hp=9386664a29f1d371484e5b1db4bf5e38e2133ce1;hpb=830125fad042fad35dc029b6eb57c8156ad7e176;p=xonotic%2Fnetradiant.git diff --git a/plugins/image/jpeg.cpp b/plugins/image/jpeg.cpp index 9386664a..fdc1ef04 100644 --- a/plugins/image/jpeg.cpp +++ b/plugins/image/jpeg.cpp @@ -34,39 +34,39 @@ // Leonardo Zide (leo@lokigames.com) // -#ifdef _WIN32 -// Ugly hack so that INT32 is defined before jmorecfg.h is parsed. -#include -#endif +#include "jpeg.h" -#include #include #include +#include #include -#include extern "C" { #include #include } -#include "image.h" +#include "ifilesystem.h" + +#include "imagelib.h" + +typedef unsigned char byte; /* Expanded data source object for stdio input */ typedef struct { - struct jpeg_source_mgr pub; /* public fields */ + struct jpeg_source_mgr pub; /* public fields */ - int src_size; - JOCTET * src_buffer; + int src_size; + JOCTET *src_buffer; - JOCTET * buffer; /* start of buffer */ - boolean start_of_file; /* have we gotten any data yet? */ + JOCTET *buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ } my_source_mgr; -typedef my_source_mgr * my_src_ptr; +typedef my_source_mgr *my_src_ptr; -#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ +const int INPUT_BUF_SIZE = 4096; /* choose an efficiently fread'able size */ /* @@ -74,14 +74,15 @@ typedef my_source_mgr * my_src_ptr; * before any data is actually read. */ -static void my_init_source( j_decompress_ptr cinfo ){ - my_src_ptr src = (my_src_ptr) cinfo->src; +static void my_init_source(j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - src->start_of_file = TRUE; + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; } @@ -118,37 +119,37 @@ static void my_init_source( j_decompress_ptr cinfo ){ * the front of the buffer rather than discarding it. */ -static boolean my_fill_input_buffer( j_decompress_ptr cinfo ){ - my_src_ptr src = (my_src_ptr) cinfo->src; - size_t nbytes; - - if ( src->src_size > INPUT_BUF_SIZE ) { - nbytes = INPUT_BUF_SIZE; - } - else{ - nbytes = src->src_size; - } - - memcpy( src->buffer, src->src_buffer, nbytes ); - src->src_buffer += nbytes; - src->src_size -= nbytes; - - if ( nbytes <= 0 ) { - if ( src->start_of_file ) { /* Treat empty input file as fatal error */ - ERREXIT( cinfo, JERR_INPUT_EMPTY ); - } - WARNMS( cinfo, JWRN_JPEG_EOF ); - /* Insert a fake EOI marker */ - src->buffer[0] = (JOCTET) 0xFF; - src->buffer[1] = (JOCTET) JPEG_EOI; - nbytes = 2; - } - - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = nbytes; - src->start_of_file = FALSE; - - return TRUE; +static boolean my_fill_input_buffer(j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + + if (src->src_size > INPUT_BUF_SIZE) { + nbytes = INPUT_BUF_SIZE; + } else { + nbytes = src->src_size; + } + + memcpy(src->buffer, src->src_buffer, nbytes); + src->src_buffer += nbytes; + src->src_size -= nbytes; + + if (nbytes <= 0) { + if (src->start_of_file) { /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + } + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; } @@ -164,24 +165,25 @@ static boolean my_fill_input_buffer( j_decompress_ptr cinfo ){ * buffer is the application writer's problem. */ -static void my_skip_input_data( j_decompress_ptr cinfo, long num_bytes ){ - my_src_ptr src = (my_src_ptr) cinfo->src; - - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if ( num_bytes > 0 ) { - while ( num_bytes > (long) src->pub.bytes_in_buffer ) { - num_bytes -= (long) src->pub.bytes_in_buffer; - (void) my_fill_input_buffer( cinfo ); - /* note we assume that fill_input_buffer will never return FALSE, - * so suspension need not be handled. - */ - } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; - } +static void my_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) my_fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } } @@ -203,8 +205,9 @@ static void my_skip_input_data( j_decompress_ptr cinfo, long num_bytes ){ * for error exit. */ -static void my_term_source( j_decompress_ptr cinfo ){ - /* no work necessary here */ +static void my_term_source(j_decompress_ptr cinfo) +{ + /* no work necessary here */ } @@ -214,197 +217,187 @@ static void my_term_source( j_decompress_ptr cinfo ){ * for closing it after finishing decompression. */ -static void jpeg_buffer_src( j_decompress_ptr cinfo, void* buffer, int bufsize ){ - my_src_ptr src; - - /* The source object and input buffer are made permanent so that a series - * of JPEG images can be read from the same file by calling jpeg_stdio_src - * only before the first one. (If we discarded the buffer at the end of - * one image, we'd likely lose the start of the next one.) - * This makes it unsafe to use this manager and a different source - * manager serially with the same JPEG object. Caveat programmer. - */ - if ( cinfo->src == NULL ) { /* first time for this JPEG object? */ - cinfo->src = (struct jpeg_source_mgr *) - ( *cinfo->mem->alloc_small )( (j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof( my_source_mgr ) ); - src = (my_src_ptr) cinfo->src; - src->buffer = (JOCTET *) - ( *cinfo->mem->alloc_small )( (j_common_ptr) cinfo, JPOOL_PERMANENT, - INPUT_BUF_SIZE * sizeof( JOCTET ) ); - } - - src = (my_src_ptr) cinfo->src; - src->pub.init_source = my_init_source; - src->pub.fill_input_buffer = my_fill_input_buffer; - src->pub.skip_input_data = my_skip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ - src->pub.term_source = my_term_source; - src->src_buffer = (JOCTET *)buffer; - src->src_size = bufsize; - src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ - src->pub.next_input_byte = NULL; /* until buffer loaded */ +static void jpeg_buffer_src(j_decompress_ptr cinfo, void *buffer, int bufsize) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = my_init_source; + src->pub.fill_input_buffer = my_fill_input_buffer; + src->pub.skip_input_data = my_skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = my_term_source; + src->src_buffer = (JOCTET *) buffer; + src->src_size = bufsize; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ } // ============================================================================= static char errormsg[JMSG_LENGTH_MAX]; -typedef struct my_jpeg_error_mgr -{ - struct jpeg_error_mgr pub; // "public" fields - jmp_buf setjmp_buffer; // for return to caller +typedef struct my_jpeg_error_mgr { + struct jpeg_error_mgr pub; // "public" fields + jmp_buf setjmp_buffer; // for return to caller } bt_jpeg_error_mgr; -static void my_jpeg_error_exit( j_common_ptr cinfo ){ - my_jpeg_error_mgr* myerr = (bt_jpeg_error_mgr*) cinfo->err; +static void my_jpeg_error_exit(j_common_ptr cinfo) +{ + my_jpeg_error_mgr *myerr = (bt_jpeg_error_mgr *) cinfo->err; - ( *cinfo->err->format_message )( cinfo, errormsg ); + (*cinfo->err->format_message)(cinfo, errormsg); - longjmp( myerr->setjmp_buffer, 1 ); + longjmp(myerr->setjmp_buffer, 1); } // stash a scanline -static void j_putRGBScanline( unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row ){ - int offset = row * widthPix * 4; - int count; - - for ( count = 0; count < widthPix; count++ ) - { - unsigned char iRed, iBlu, iGrn; - unsigned char *oRed, *oBlu, *oGrn, *oAlp; - - iRed = *( jpegline + count * 3 + 0 ); - iGrn = *( jpegline + count * 3 + 1 ); - iBlu = *( jpegline + count * 3 + 2 ); - - oRed = outBuf + offset + count * 4 + 0; - oGrn = outBuf + offset + count * 4 + 1; - oBlu = outBuf + offset + count * 4 + 2; - oAlp = outBuf + offset + count * 4 + 3; - - *oRed = iRed; - *oGrn = iGrn; - *oBlu = iBlu; - *oAlp = 255; - } +static void j_putRGBScanline(unsigned char *jpegline, int widthPix, unsigned char *outBuf, int row) +{ + int offset = row * widthPix * 4; + int count; + + for (count = 0; count < widthPix; count++) { + unsigned char iRed, iBlu, iGrn; + unsigned char *oRed, *oBlu, *oGrn, *oAlp; + + iRed = *(jpegline + count * 3 + 0); + iGrn = *(jpegline + count * 3 + 1); + iBlu = *(jpegline + count * 3 + 2); + + oRed = outBuf + offset + count * 4 + 0; + oGrn = outBuf + offset + count * 4 + 1; + oBlu = outBuf + offset + count * 4 + 2; + oAlp = outBuf + offset + count * 4 + 3; + + *oRed = iRed; + *oGrn = iGrn; + *oBlu = iBlu; + *oAlp = 255; + } } // stash a scanline -static void j_putRGBAScanline( unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row ){ - int offset = row * widthPix * 4; - int count; - - for ( count = 0; count < widthPix; count++ ) - { - unsigned char iRed, iBlu, iGrn, iAlp; - unsigned char *oRed, *oBlu, *oGrn, *oAlp; - - iRed = *( jpegline + count * 4 + 0 ); - iGrn = *( jpegline + count * 4 + 1 ); - iBlu = *( jpegline + count * 4 + 2 ); - iAlp = *( jpegline + count * 4 + 3 ); - - oRed = outBuf + offset + count * 4 + 0; - oGrn = outBuf + offset + count * 4 + 1; - oBlu = outBuf + offset + count * 4 + 2; - oAlp = outBuf + offset + count * 4 + 3; - - *oRed = iRed; - *oGrn = iGrn; - *oBlu = iBlu; - // ydnar: see bug 900 - *oAlp = 255; //% iAlp; - } +static void j_putRGBAScanline(unsigned char *jpegline, int widthPix, unsigned char *outBuf, int row) +{ + int offset = row * widthPix * 4; + int count; + + for (count = 0; count < widthPix; count++) { + unsigned char iRed, iBlu, iGrn, iAlp; + unsigned char *oRed, *oBlu, *oGrn, *oAlp; + + iRed = *(jpegline + count * 4 + 0); + iGrn = *(jpegline + count * 4 + 1); + iBlu = *(jpegline + count * 4 + 2); + iAlp = *(jpegline + count * 4 + 3); + + oRed = outBuf + offset + count * 4 + 0; + oGrn = outBuf + offset + count * 4 + 1; + oBlu = outBuf + offset + count * 4 + 2; + oAlp = outBuf + offset + count * 4 + 3; + + *oRed = iRed; + *oGrn = iGrn; + *oBlu = iBlu; + + //!\todo fix jpeglib, it leaves alpha channel uninitialised +#if 1 + (void) iAlp; + *oAlp = 255; +#else + *oAlp = iAlp; +#endif + } } // stash a gray scanline -static void j_putGrayScanlineToRGB( unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row ){ - int offset = row * widthPix * 4; - int count; - - for ( count = 0; count < widthPix; count++ ) - { - unsigned char iGray; - unsigned char *oRed, *oBlu, *oGrn, *oAlp; - - // get our grayscale value - iGray = *( jpegline + count ); - - oRed = outBuf + offset + count * 4; - oGrn = outBuf + offset + count * 4 + 1; - oBlu = outBuf + offset + count * 4 + 2; - oAlp = outBuf + offset + count * 4 + 3; - - *oRed = iGray; - *oGrn = iGray; - *oBlu = iGray; - *oAlp = 255; - } +static void j_putGrayScanlineToRGB(unsigned char *jpegline, int widthPix, unsigned char *outBuf, int row) +{ + int offset = row * widthPix * 4; + int count; + + for (count = 0; count < widthPix; count++) { + unsigned char iGray; + unsigned char *oRed, *oBlu, *oGrn, *oAlp; + + // get our grayscale value + iGray = *(jpegline + count); + + oRed = outBuf + offset + count * 4; + oGrn = outBuf + offset + count * 4 + 1; + oBlu = outBuf + offset + count * 4 + 2; + oAlp = outBuf + offset + count * 4 + 3; + + *oRed = iGray; + *oGrn = iGray; + *oBlu = iGray; + *oAlp = 255; + } } -static int LoadJPGBuff( void *src_buffer, int src_size, unsigned char **pic, int *width, int *height ) { - struct jpeg_decompress_struct cinfo; - struct my_jpeg_error_mgr jerr; - JSAMPARRAY buffer; - int row_stride, size; - - cinfo.err = jpeg_std_error( &jerr.pub ); - jerr.pub.error_exit = my_jpeg_error_exit; - - if ( setjmp( jerr.setjmp_buffer ) ) { - *pic = (unsigned char*)errormsg; - jpeg_destroy_decompress( &cinfo ); - return -1; - } - - jpeg_create_decompress( &cinfo ); - jpeg_buffer_src( &cinfo, src_buffer, src_size ); - jpeg_read_header( &cinfo, TRUE ); - jpeg_start_decompress( &cinfo ); - - row_stride = cinfo.output_width * cinfo.output_components; - - size = cinfo.output_width * cinfo.output_height * 4; - *width = cinfo.output_width; - *height = cinfo.output_height; - *pic = (unsigned char*) ( g_malloc( size + 1 ) ); - memset( *pic, 0, size + 1 ); - - buffer = ( *cinfo.mem->alloc_sarray )( ( j_common_ptr ) & cinfo, JPOOL_IMAGE, row_stride, 1 ); - - while ( cinfo.output_scanline < cinfo.output_height ) - { - jpeg_read_scanlines( &cinfo, buffer, 1 ); - - if ( cinfo.out_color_components == 4 ) { - j_putRGBAScanline( buffer[0], cinfo.output_width, *pic, cinfo.output_scanline - 1 ); - } - else if ( cinfo.out_color_components == 3 ) { - j_putRGBScanline( buffer[0], cinfo.output_width, *pic, cinfo.output_scanline - 1 ); - } - else if ( cinfo.out_color_components == 1 ) { - j_putGrayScanlineToRGB( buffer[0], cinfo.output_width, *pic, cinfo.output_scanline - 1 ); - } - } - - jpeg_finish_decompress( &cinfo ); - jpeg_destroy_decompress( &cinfo ); - - return 0; -} +static Image *LoadJPGBuff_(const void *src_buffer, int src_size) +{ + struct jpeg_decompress_struct cinfo; + struct my_jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_jpeg_error_exit; + + if (setjmp(jerr.setjmp_buffer)) { //< TODO: use c++ exceptions instead of setjmp/longjmp to handle errors + globalErrorStream() << "WARNING: JPEG library error: " << errormsg << "\n"; + jpeg_destroy_decompress(&cinfo); + return 0; + } + + jpeg_create_decompress(&cinfo); + jpeg_buffer_src(&cinfo, const_cast( src_buffer ), src_size); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); -void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height ) { - unsigned char *fbuffer = NULL; - int nLen = vfsLoadFile( (char *)filename, (void **)&fbuffer, 0 ); - if ( nLen == -1 ) { - return; - } + int row_stride = cinfo.output_width * cinfo.output_components; - if ( LoadJPGBuff( fbuffer, nLen, pic, width, height ) != 0 ) { - g_FuncTable.m_pfnSysPrintf( "WARNING: JPEG library failed to load %s because %s\n", filename, *pic ); - *pic = NULL; - } + RGBAImage *image = new RGBAImage(cinfo.output_width, cinfo.output_height); - vfsFreeFile( fbuffer ); + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + + if (cinfo.out_color_components == 4) { + j_putRGBAScanline(buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1); + } else if (cinfo.out_color_components == 3) { + j_putRGBScanline(buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1); + } else if (cinfo.out_color_components == 1) { + j_putGrayScanlineToRGB(buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1); + } + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return image; +} + +Image *LoadJPG(ArchiveFile &file) +{ + ScopedArchiveBuffer buffer(file); + return LoadJPGBuff_(buffer.buffer, static_cast( buffer.length )); }