X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=plugins%2Fimage%2Fjpeg.cpp;h=91fdf18e39cc54641170c71d9e8b7090b90555ef;hb=b85323ca88049904b00b284044ea1f048cc0d447;hp=66df32ffd0caf4c883871f14069811be85cae18f;hpb=5265d3cc1517566910718738ee6fa48e2466d3ea;p=xonotic%2Fnetradiant.git diff --git a/plugins/image/jpeg.cpp b/plugins/image/jpeg.cpp index 66df32ff..91fdf18e 100644 --- a/plugins/image/jpeg.cpp +++ b/plugins/image/jpeg.cpp @@ -1,32 +1,32 @@ /* -Copyright (c) 2001, Loki software, inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -Neither the name of Loki software nor the names of its contributors may be used -to endorse or promote products derived from this software without specific prior -written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + Copyright (c) 2001, Loki software, inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Loki software nor the names of its contributors may be used + to endorse or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ // // Functions to load JPEG files from a buffer, based on jdatasrc.c @@ -34,34 +34,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Leonardo Zide (leo@lokigames.com) // -#include +#include "jpeg.h" + #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; -#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ +const int INPUT_BUF_SIZE = 4096; /* choose an efficiently fread'able size */ /* @@ -69,15 +74,14 @@ 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; } @@ -114,35 +118,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; } @@ -158,25 +164,24 @@ 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; + } } @@ -198,9 +203,8 @@ 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 */ } @@ -210,37 +214,36 @@ 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 */ } // ============================================================================= @@ -249,160 +252,149 @@ 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 + 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 ); + + 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; +} - vfsFreeFile( fbuffer ); +Image* LoadJPG( ArchiveFile& file ){ + ScopedArchiveBuffer buffer( file ); + return LoadJPGBuff_( buffer.buffer, static_cast( buffer.length ) ); }