From 74c8b028d42a8c5b080bb861e427f38cedd4ad7c Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 15 Dec 2023 18:26:14 +0100 Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety --- indra/llimage/llimagejpeg.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'indra/llimage/llimagejpeg.cpp') diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index 32a5472ec8..a35171601a 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -50,6 +50,8 @@ bool LLImageJPEG::updateData() { resetLastError(); + LLImageDataLock lock(this); + // Check to make sure that this instance has been initialized with data if (!getData() || (0 == getDataSize())) { @@ -188,7 +190,10 @@ bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) llassert_always(raw_image); resetLastError(); - + + LLImageDataLock lockIn(this); + LLImageDataLock lockOut(raw_image); + // Check to make sure that this instance has been initialized with data if (!getData() || (0 == getDataSize())) { @@ -408,6 +413,8 @@ void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo ) { LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + LLImageDataLock lock(self); + S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer); self->allocateData(file_bytes); @@ -484,6 +491,9 @@ bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) resetLastError(); + LLImageDataSharedLock lockIn(raw_image); + LLImageDataLock lockOut(this); + switch( raw_image->getComponents() ) { case 1: -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/llimage/llimagejpeg.cpp | 1348 ++++++++++++++++++++--------------------- 1 file changed, 674 insertions(+), 674 deletions(-) (limited to 'indra/llimage/llimagejpeg.cpp') diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index e3271d8a83..0e7ec365d4 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -1,674 +1,674 @@ -/** - * @file llimagejpeg.cpp - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "stdtypes.h" - -#include "llimagejpeg.h" - -#include "llerror.h" -#include "llexception.h" - -jmp_buf LLImageJPEG::sSetjmpBuffer ; -LLImageJPEG::LLImageJPEG(S32 quality) -: LLImageFormatted(IMG_CODEC_JPEG), - mOutputBuffer( NULL ), - mOutputBufferSize( 0 ), - mEncodeQuality( quality ) // on a scale from 1 to 100 -{ -} - -LLImageJPEG::~LLImageJPEG() -{ - llassert( !mOutputBuffer ); // Should already be deleted at end of encode. - delete[] mOutputBuffer; -} - -bool LLImageJPEG::updateData() -{ - resetLastError(); - - LLImageDataLock lock(this); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("Uninitialized instance of LLImageJPEG"); - return false; - } - - //////////////////////////////////////// - // Step 1: allocate and initialize JPEG decompression object - - // This struct contains the JPEG decompression parameters and pointers to - // working space (which is allocated as needed by the JPEG library). - struct jpeg_decompress_struct cinfo; - cinfo.client_data = this; - - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - - // Customize with our own callbacks - jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller - jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message - jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message - - // - //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error - //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao - // - if(setjmp(sSetjmpBuffer)) - { - jpeg_destroy_decompress(&cinfo); - return false; - } - try - { - // Now we can initialize the JPEG decompression object. - jpeg_create_decompress(&cinfo); - - //////////////////////////////////////// - // Step 2: specify data source - // (Code is modified version of jpeg_stdio_src(); - if (cinfo.src == NULL) - { - cinfo.src = (struct jpeg_source_mgr *) - (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_source_mgr)); - } - cinfo.src->init_source = &LLImageJPEG::decodeInitSource; - cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; - cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; - cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. - cinfo.src->term_source = &LLImageJPEG::decodeTermSource; - - cinfo.src->bytes_in_buffer = getDataSize(); - cinfo.src->next_input_byte = getData(); - - //////////////////////////////////////// - // Step 3: read file parameters with jpeg_read_header() - jpeg_read_header( &cinfo, true ); - - // Data set by jpeg_read_header - setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) - - /* - // More data set by jpeg_read_header - cinfo.num_components; - cinfo.jpeg_color_space; // Colorspace of image - cinfo.saw_JFIF_marker; // true if a JFIF APP0 marker was seen - cinfo.JFIF_major_version; // Version information from JFIF marker - cinfo.JFIF_minor_version; // - cinfo.density_unit; // Resolution data from JFIF marker - cinfo.X_density; - cinfo.Y_density; - cinfo.saw_Adobe_marker; // true if an Adobe APP14 marker was seen - cinfo.Adobe_transform; // Color transform code from Adobe marker - */ - } - catch (int) - { - jpeg_destroy_decompress(&cinfo); - - return false; - } - //////////////////////////////////////// - // Step 4: Release JPEG decompression object - jpeg_destroy_decompress(&cinfo); - - return true; -} - -// Initialize source --- called by jpeg_read_header -// before any data is actually read. -void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo ) -{ - // no work necessary here -} - -// Fill the input buffer --- called whenever buffer is emptied. -boolean LLImageJPEG::decodeFillInputBuffer( j_decompress_ptr cinfo ) -{ -// jpeg_source_mgr* src = cinfo->src; -// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - - // Should never get here, since we provide the entire buffer up front. - ERREXIT(cinfo, JERR_INPUT_EMPTY); - - return true; -} - -// Skip data --- used to skip over a potentially large amount of -// uninteresting data (such as an APPn marker). -// -// Writers of suspendable-input applications must note that skip_input_data -// is not granted the right to give a suspension return. If the skip extends -// beyond the data currently in the buffer, the buffer can be marked empty so -// that the next read will cause a fill_input_buffer call that can suspend. -// Arranging for additional bytes to be discarded before reloading the input -// buffer is the application writer's problem. -void LLImageJPEG::decodeSkipInputData (j_decompress_ptr cinfo, long num_bytes) -{ - jpeg_source_mgr* src = cinfo->src; -// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - - src->next_input_byte += (size_t) num_bytes; - src->bytes_in_buffer -= (size_t) num_bytes; -} - -void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo) -{ - // no work necessary here -} - - -// Returns true when done, whether or not decode was successful. -bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) -{ - llassert_always(raw_image); - - resetLastError(); - - LLImageDataLock lockIn(this); - LLImageDataLock lockOut(raw_image); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("LLImageJPEG trying to decode an image with no data!"); - return true; // done - } - - S32 row_stride = 0; - U8* raw_image_data = NULL; - - //////////////////////////////////////// - // Step 1: allocate and initialize JPEG decompression object - - // This struct contains the JPEG decompression parameters and pointers to - // working space (which is allocated as needed by the JPEG library). - struct jpeg_decompress_struct cinfo; - - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - - // Customize with our own callbacks - jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller - jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message - jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message - - // - //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error - //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao - // - if(setjmp(sSetjmpBuffer)) - { - jpeg_destroy_decompress(&cinfo); - return true; // done - } - try - { - // Now we can initialize the JPEG decompression object. - jpeg_create_decompress(&cinfo); - - //////////////////////////////////////// - // Step 2: specify data source - // (Code is modified version of jpeg_stdio_src(); - if (cinfo.src == NULL) - { - cinfo.src = (struct jpeg_source_mgr *) - (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_source_mgr)); - } - cinfo.src->init_source = &LLImageJPEG::decodeInitSource; - cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; - cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; - cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. - cinfo.src->term_source = &LLImageJPEG::decodeTermSource; - cinfo.src->bytes_in_buffer = getDataSize(); - cinfo.src->next_input_byte = getData(); - - //////////////////////////////////////// - // Step 3: read file parameters with jpeg_read_header() - - jpeg_read_header(&cinfo, true); - - // We can ignore the return value from jpeg_read_header since - // (a) suspension is not possible with our data source, and - // (b) we passed true to reject a tables-only JPEG file as an error. - // See libjpeg.doc for more info. - - setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) - - if (!raw_image->resize(getWidth(), getHeight(), getComponents())) - { - throw std::bad_alloc(); - } - raw_image_data = raw_image->getData(); - - - //////////////////////////////////////// - // Step 4: set parameters for decompression - cinfo.out_color_components = 3; - cinfo.out_color_space = JCS_RGB; - - - //////////////////////////////////////// - // Step 5: Start decompressor - - jpeg_start_decompress(&cinfo); - // We can ignore the return value since suspension is not possible - // with our data source. - - // We may need to do some setup of our own at this point before reading - // the data. After jpeg_start_decompress() we have the correct scaled - // output image dimensions available, as well as the output colormap - // if we asked for color quantization. - // In this example, we need to make an output work buffer of the right size. - - // JSAMPLEs per row in output buffer - row_stride = cinfo.output_width * cinfo.output_components; - - //////////////////////////////////////// - // Step 6: while (scan lines remain to be read) - // jpeg_read_scanlines(...); - - // Here we use the library's state variable cinfo.output_scanline as the - // loop counter, so that we don't have to keep track ourselves. - - // Move pointer to last line - raw_image_data += row_stride * (cinfo.output_height - 1); - - while (cinfo.output_scanline < cinfo.output_height) - { - // jpeg_read_scanlines expects an array of pointers to scanlines. - // Here the array is only one element long, but you could ask for - // more than one scanline at a time if that's more convenient. - - jpeg_read_scanlines(&cinfo, &raw_image_data, 1); - raw_image_data -= row_stride; // move pointer up a line - } - - //////////////////////////////////////// - // Step 7: Finish decompression - jpeg_finish_decompress(&cinfo); - - //////////////////////////////////////// - // Step 8: Release JPEG decompression object - jpeg_destroy_decompress(&cinfo); - } - - catch (std::bad_alloc&) - { - setLastError( "Out of memory"); - jpeg_destroy_decompress(&cinfo); - return true; // done - } - - catch (int) - { - jpeg_destroy_decompress(&cinfo); - return true; // done - } - - // Check to see whether any corrupt-data warnings occurred - if( jerr.num_warnings != 0 ) - { - // TODO: extract the warning to find out what went wrong. - setLastError( "Unable to decode JPEG image."); - return true; // done - } - - return true; -} - - -// Initialize destination --- called by jpeg_start_compress before any data is actually written. -// static -void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo ) -{ - LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - - cinfo->dest->next_output_byte = self->mOutputBuffer; - cinfo->dest->free_in_buffer = self->mOutputBufferSize; -} - - -// Empty the output buffer --- called whenever buffer fills up. -// -// In typical applications, this should write the entire output buffer -// (ignoring the current state of next_output_byte & free_in_buffer), -// reset the pointer & count to the start of the buffer, and return true -// indicating that the buffer has been dumped. -// -// In applications that need to be able to suspend compression due to output -// overrun, a false return indicates that the buffer cannot be emptied now. -// In this situation, the compressor will return to its caller (possibly with -// an indication that it has not accepted all the supplied scanlines). The -// application should resume compression after it has made more room in the -// output buffer. Note that there are substantial restrictions on the use of -// suspension --- see the documentation. -// -// When suspending, the compressor will back up to a convenient restart point -// (typically the start of the current MCU). next_output_byte & free_in_buffer -// indicate where the restart point will be if the current call returns false. -// Data beyond this point will be regenerated after resumption, so do not -// write it out when emptying the buffer externally. - -boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) -{ - LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - - // Should very rarely happen, since our output buffer is - // as large as the input to start out with. - - // Double the buffer size; - S32 new_buffer_size = self->mOutputBufferSize * 2; - U8* new_buffer = new(std::nothrow) U8[ new_buffer_size ]; - if (!new_buffer) - { - self->setLastError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )"); - LLTHROW(LLContinueError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )")); - } - memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ - delete[] self->mOutputBuffer; - self->mOutputBuffer = new_buffer; - - cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize; - cinfo->dest->free_in_buffer = self->mOutputBufferSize; - self->mOutputBufferSize = new_buffer_size; - - return true; -} - -// Terminate destination --- called by jpeg_finish_compress -// after all data has been written. Usually needs to flush buffer. -// -// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding -// application must deal with any cleanup that should happen even -// for error exit. -void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo ) -{ - LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - - LLImageDataLock lock(self); - - S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer); - self->allocateData(file_bytes); - - memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */ -} - -// static -void LLImageJPEG::errorExit( j_common_ptr cinfo ) -{ - //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - - // Always display the message - (*cinfo->err->output_message)(cinfo); - - // Let the memory manager delete any temp files - jpeg_destroy(cinfo); - - // Return control to the setjmp point - longjmp(sSetjmpBuffer, 1) ; -} - -// Decide whether to emit a trace or warning message. -// msg_level is one of: -// -1: recoverable corrupt-data warning, may want to abort. -// 0: important advisory messages (always display to user). -// 1: first level of tracing detail. -// 2,3,...: successively more detailed tracing messages. -// An application might override this method if it wanted to abort on warnings -// or change the policy about which messages to display. -// static -void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level ) -{ - struct jpeg_error_mgr * err = cinfo->err; - - if (msg_level < 0) - { - // It's a warning message. Since corrupt files may generate many warnings, - // the policy implemented here is to show only the first warning, - // unless trace_level >= 3. - if (err->num_warnings == 0 || err->trace_level >= 3) - { - (*err->output_message) (cinfo); - } - // Always count warnings in num_warnings. - err->num_warnings++; - } - else - { - // It's a trace message. Show it if trace_level >= msg_level. - if (err->trace_level >= msg_level) - { - (*err->output_message) (cinfo); - } - } -} - -// static -void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo ) -{ - // Create the message - char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */ - (*cinfo->err->format_message) (cinfo, buffer); - - std::string error = buffer ; - LLImage::setLastError(error); - - bool is_decode = (cinfo->is_decompressor != 0); - LL_WARNS() << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << LL_ENDL; -} - -bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) -{ - llassert_always(raw_image); - - resetLastError(); - - LLImageDataSharedLock lockIn(raw_image); - LLImageDataLock lockOut(this); - - switch( raw_image->getComponents() ) - { - case 1: - case 3: - break; - default: - setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); - return false; - } - - setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); - - // Allocate a temporary buffer big enough to hold the entire compressed image (and then some) - // (Note: we make it bigger in emptyOutputBuffer() if we need to) - delete[] mOutputBuffer; - mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024; - mOutputBuffer = new(std::nothrow) U8[ mOutputBufferSize ]; - if (mOutputBuffer == NULL) - { - mOutputBufferSize = 0; - setLastError("Failed to allocate output buffer"); - return false; - } - - const U8* raw_image_data = NULL; - S32 row_stride = 0; - - //////////////////////////////////////// - // Step 1: allocate and initialize JPEG compression object - - // This struct contains the JPEG compression parameters and pointers to - // working space (which is allocated as needed by the JPEG library). - struct jpeg_compress_struct cinfo; - cinfo.client_data = this; - - // We have to set up the error handler first, in case the initialization - // step fails. (Unlikely, but it could happen if you are out of memory.) - // This routine fills in the contents of struct jerr, and returns jerr's - // address which we place into the link field in cinfo. - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - - // Customize with our own callbacks - jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller - jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message - jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message - - // - //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error - //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao - // - if( setjmp(sSetjmpBuffer) ) - { - // If we get here, the JPEG code has signaled an error. - // We need to clean up the JPEG object, close the input file, and return. - jpeg_destroy_compress(&cinfo); - delete[] mOutputBuffer; - mOutputBuffer = NULL; - mOutputBufferSize = 0; - return false; - } - - try - { - - // Now we can initialize the JPEG compression object. - jpeg_create_compress(&cinfo); - - //////////////////////////////////////// - // Step 2: specify data destination - // (code is a modified form of jpeg_stdio_dest() ) - if( cinfo.dest == NULL) - { - cinfo.dest = (struct jpeg_destination_mgr *) - (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_destination_mgr)); - } - cinfo.dest->next_output_byte = mOutputBuffer; // => next byte to write in buffer - cinfo.dest->free_in_buffer = mOutputBufferSize; // # of byte spaces remaining in buffer - cinfo.dest->init_destination = &LLImageJPEG::encodeInitDestination; - cinfo.dest->empty_output_buffer = &LLImageJPEG::encodeEmptyOutputBuffer; - cinfo.dest->term_destination = &LLImageJPEG::encodeTermDestination; - - //////////////////////////////////////// - // Step 3: set parameters for compression - // - // First we supply a description of the input image. - // Four fields of the cinfo struct must be filled in: - - cinfo.image_width = getWidth(); // image width and height, in pixels - cinfo.image_height = getHeight(); - - switch( getComponents() ) - { - case 1: - cinfo.input_components = 1; // # of color components per pixel - cinfo.in_color_space = JCS_GRAYSCALE; // colorspace of input image - break; - case 3: - cinfo.input_components = 3; // # of color components per pixel - cinfo.in_color_space = JCS_RGB; // colorspace of input image - break; - default: - setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); - return false; - } - - // Now use the library's routine to set default compression parameters. - // (You must set at least cinfo.in_color_space before calling this, - // since the defaults depend on the source color space.) - jpeg_set_defaults(&cinfo); - - // Now you can set any non-default parameters you wish to. - jpeg_set_quality(&cinfo, mEncodeQuality, true ); // limit to baseline-JPEG values - - //////////////////////////////////////// - // Step 4: Start compressor - // - // true ensures that we will write a complete interchange-JPEG file. - // Pass true unless you are very sure of what you're doing. - - jpeg_start_compress(&cinfo, true); - - //////////////////////////////////////// - // Step 5: while (scan lines remain to be written) - // jpeg_write_scanlines(...); - - // Here we use the library's state variable cinfo.next_scanline as the - // loop counter, so that we don't have to keep track ourselves. - // To keep things simple, we pass one scanline per call; you can pass - // more if you wish, though. - - row_stride = getWidth() * getComponents(); // JSAMPLEs per row in image_buffer - - // NOTE: For compatibility with LLImage, we need to invert the rows. - raw_image_data = raw_image->getData(); - - const U8* last_row_data = raw_image_data + (getHeight()-1) * row_stride; - - JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] - while (cinfo.next_scanline < cinfo.image_height) - { - // jpeg_write_scanlines expects an array of pointers to scanlines. - // Here the array is only one element long, but you could pass - // more than one scanline at a time if that's more convenient. - - //Ugly const uncast here (jpeg_write_scanlines should take a const* but doesn't) - //row_pointer[0] = (JSAMPROW)(raw_image_data + (cinfo.next_scanline * row_stride)); - row_pointer[0] = (JSAMPROW)(last_row_data - (cinfo.next_scanline * row_stride)); - - jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - //////////////////////////////////////// - // Step 6: Finish compression - jpeg_finish_compress(&cinfo); - - // After finish_compress, we can release the temp output buffer. - delete[] mOutputBuffer; - mOutputBuffer = NULL; - mOutputBufferSize = 0; - - //////////////////////////////////////// - // Step 7: release JPEG compression object - jpeg_destroy_compress(&cinfo); - } - - catch(int) - { - jpeg_destroy_compress(&cinfo); - delete[] mOutputBuffer; - mOutputBuffer = NULL; - mOutputBufferSize = 0; - return false; - } - - return true; -} +/** + * @file llimagejpeg.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "stdtypes.h" + +#include "llimagejpeg.h" + +#include "llerror.h" +#include "llexception.h" + +jmp_buf LLImageJPEG::sSetjmpBuffer ; +LLImageJPEG::LLImageJPEG(S32 quality) +: LLImageFormatted(IMG_CODEC_JPEG), + mOutputBuffer( NULL ), + mOutputBufferSize( 0 ), + mEncodeQuality( quality ) // on a scale from 1 to 100 +{ +} + +LLImageJPEG::~LLImageJPEG() +{ + llassert( !mOutputBuffer ); // Should already be deleted at end of encode. + delete[] mOutputBuffer; +} + +bool LLImageJPEG::updateData() +{ + resetLastError(); + + LLImageDataLock lock(this); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("Uninitialized instance of LLImageJPEG"); + return false; + } + + //////////////////////////////////////// + // Step 1: allocate and initialize JPEG decompression object + + // This struct contains the JPEG decompression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + struct jpeg_decompress_struct cinfo; + cinfo.client_data = this; + + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + // Customize with our own callbacks + jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller + jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message + jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message + + // + //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error + //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao + // + if(setjmp(sSetjmpBuffer)) + { + jpeg_destroy_decompress(&cinfo); + return false; + } + try + { + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + //////////////////////////////////////// + // Step 2: specify data source + // (Code is modified version of jpeg_stdio_src(); + if (cinfo.src == NULL) + { + cinfo.src = (struct jpeg_source_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_source_mgr)); + } + cinfo.src->init_source = &LLImageJPEG::decodeInitSource; + cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; + cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; + cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. + cinfo.src->term_source = &LLImageJPEG::decodeTermSource; + + cinfo.src->bytes_in_buffer = getDataSize(); + cinfo.src->next_input_byte = getData(); + + //////////////////////////////////////// + // Step 3: read file parameters with jpeg_read_header() + jpeg_read_header( &cinfo, true ); + + // Data set by jpeg_read_header + setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) + + /* + // More data set by jpeg_read_header + cinfo.num_components; + cinfo.jpeg_color_space; // Colorspace of image + cinfo.saw_JFIF_marker; // true if a JFIF APP0 marker was seen + cinfo.JFIF_major_version; // Version information from JFIF marker + cinfo.JFIF_minor_version; // + cinfo.density_unit; // Resolution data from JFIF marker + cinfo.X_density; + cinfo.Y_density; + cinfo.saw_Adobe_marker; // true if an Adobe APP14 marker was seen + cinfo.Adobe_transform; // Color transform code from Adobe marker + */ + } + catch (int) + { + jpeg_destroy_decompress(&cinfo); + + return false; + } + //////////////////////////////////////// + // Step 4: Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); + + return true; +} + +// Initialize source --- called by jpeg_read_header +// before any data is actually read. +void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo ) +{ + // no work necessary here +} + +// Fill the input buffer --- called whenever buffer is emptied. +boolean LLImageJPEG::decodeFillInputBuffer( j_decompress_ptr cinfo ) +{ +// jpeg_source_mgr* src = cinfo->src; +// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + + // Should never get here, since we provide the entire buffer up front. + ERREXIT(cinfo, JERR_INPUT_EMPTY); + + return true; +} + +// Skip data --- used to skip over a potentially large amount of +// uninteresting data (such as an APPn marker). +// +// Writers of suspendable-input applications must note that skip_input_data +// is not granted the right to give a suspension return. If the skip extends +// beyond the data currently in the buffer, the buffer can be marked empty so +// that the next read will cause a fill_input_buffer call that can suspend. +// Arranging for additional bytes to be discarded before reloading the input +// buffer is the application writer's problem. +void LLImageJPEG::decodeSkipInputData (j_decompress_ptr cinfo, long num_bytes) +{ + jpeg_source_mgr* src = cinfo->src; +// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + + src->next_input_byte += (size_t) num_bytes; + src->bytes_in_buffer -= (size_t) num_bytes; +} + +void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo) +{ + // no work necessary here +} + + +// Returns true when done, whether or not decode was successful. +bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) +{ + llassert_always(raw_image); + + resetLastError(); + + LLImageDataLock lockIn(this); + LLImageDataLock lockOut(raw_image); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImageJPEG trying to decode an image with no data!"); + return true; // done + } + + S32 row_stride = 0; + U8* raw_image_data = NULL; + + //////////////////////////////////////// + // Step 1: allocate and initialize JPEG decompression object + + // This struct contains the JPEG decompression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + struct jpeg_decompress_struct cinfo; + + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + // Customize with our own callbacks + jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller + jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message + jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message + + // + //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error + //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao + // + if(setjmp(sSetjmpBuffer)) + { + jpeg_destroy_decompress(&cinfo); + return true; // done + } + try + { + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + //////////////////////////////////////// + // Step 2: specify data source + // (Code is modified version of jpeg_stdio_src(); + if (cinfo.src == NULL) + { + cinfo.src = (struct jpeg_source_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_source_mgr)); + } + cinfo.src->init_source = &LLImageJPEG::decodeInitSource; + cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; + cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; + cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. + cinfo.src->term_source = &LLImageJPEG::decodeTermSource; + cinfo.src->bytes_in_buffer = getDataSize(); + cinfo.src->next_input_byte = getData(); + + //////////////////////////////////////// + // Step 3: read file parameters with jpeg_read_header() + + jpeg_read_header(&cinfo, true); + + // We can ignore the return value from jpeg_read_header since + // (a) suspension is not possible with our data source, and + // (b) we passed true to reject a tables-only JPEG file as an error. + // See libjpeg.doc for more info. + + setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) + + if (!raw_image->resize(getWidth(), getHeight(), getComponents())) + { + throw std::bad_alloc(); + } + raw_image_data = raw_image->getData(); + + + //////////////////////////////////////// + // Step 4: set parameters for decompression + cinfo.out_color_components = 3; + cinfo.out_color_space = JCS_RGB; + + + //////////////////////////////////////// + // Step 5: Start decompressor + + jpeg_start_decompress(&cinfo); + // We can ignore the return value since suspension is not possible + // with our data source. + + // We may need to do some setup of our own at this point before reading + // the data. After jpeg_start_decompress() we have the correct scaled + // output image dimensions available, as well as the output colormap + // if we asked for color quantization. + // In this example, we need to make an output work buffer of the right size. + + // JSAMPLEs per row in output buffer + row_stride = cinfo.output_width * cinfo.output_components; + + //////////////////////////////////////// + // Step 6: while (scan lines remain to be read) + // jpeg_read_scanlines(...); + + // Here we use the library's state variable cinfo.output_scanline as the + // loop counter, so that we don't have to keep track ourselves. + + // Move pointer to last line + raw_image_data += row_stride * (cinfo.output_height - 1); + + while (cinfo.output_scanline < cinfo.output_height) + { + // jpeg_read_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could ask for + // more than one scanline at a time if that's more convenient. + + jpeg_read_scanlines(&cinfo, &raw_image_data, 1); + raw_image_data -= row_stride; // move pointer up a line + } + + //////////////////////////////////////// + // Step 7: Finish decompression + jpeg_finish_decompress(&cinfo); + + //////////////////////////////////////// + // Step 8: Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); + } + + catch (std::bad_alloc&) + { + setLastError( "Out of memory"); + jpeg_destroy_decompress(&cinfo); + return true; // done + } + + catch (int) + { + jpeg_destroy_decompress(&cinfo); + return true; // done + } + + // Check to see whether any corrupt-data warnings occurred + if( jerr.num_warnings != 0 ) + { + // TODO: extract the warning to find out what went wrong. + setLastError( "Unable to decode JPEG image."); + return true; // done + } + + return true; +} + + +// Initialize destination --- called by jpeg_start_compress before any data is actually written. +// static +void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo ) +{ + LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + + cinfo->dest->next_output_byte = self->mOutputBuffer; + cinfo->dest->free_in_buffer = self->mOutputBufferSize; +} + + +// Empty the output buffer --- called whenever buffer fills up. +// +// In typical applications, this should write the entire output buffer +// (ignoring the current state of next_output_byte & free_in_buffer), +// reset the pointer & count to the start of the buffer, and return true +// indicating that the buffer has been dumped. +// +// In applications that need to be able to suspend compression due to output +// overrun, a false return indicates that the buffer cannot be emptied now. +// In this situation, the compressor will return to its caller (possibly with +// an indication that it has not accepted all the supplied scanlines). The +// application should resume compression after it has made more room in the +// output buffer. Note that there are substantial restrictions on the use of +// suspension --- see the documentation. +// +// When suspending, the compressor will back up to a convenient restart point +// (typically the start of the current MCU). next_output_byte & free_in_buffer +// indicate where the restart point will be if the current call returns false. +// Data beyond this point will be regenerated after resumption, so do not +// write it out when emptying the buffer externally. + +boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) +{ + LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + + // Should very rarely happen, since our output buffer is + // as large as the input to start out with. + + // Double the buffer size; + S32 new_buffer_size = self->mOutputBufferSize * 2; + U8* new_buffer = new(std::nothrow) U8[ new_buffer_size ]; + if (!new_buffer) + { + self->setLastError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )"); + LLTHROW(LLContinueError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )")); + } + memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ + delete[] self->mOutputBuffer; + self->mOutputBuffer = new_buffer; + + cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize; + cinfo->dest->free_in_buffer = self->mOutputBufferSize; + self->mOutputBufferSize = new_buffer_size; + + return true; +} + +// Terminate destination --- called by jpeg_finish_compress +// after all data has been written. Usually needs to flush buffer. +// +// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding +// application must deal with any cleanup that should happen even +// for error exit. +void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo ) +{ + LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + + LLImageDataLock lock(self); + + S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer); + self->allocateData(file_bytes); + + memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */ +} + +// static +void LLImageJPEG::errorExit( j_common_ptr cinfo ) +{ + //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + + // Always display the message + (*cinfo->err->output_message)(cinfo); + + // Let the memory manager delete any temp files + jpeg_destroy(cinfo); + + // Return control to the setjmp point + longjmp(sSetjmpBuffer, 1) ; +} + +// Decide whether to emit a trace or warning message. +// msg_level is one of: +// -1: recoverable corrupt-data warning, may want to abort. +// 0: important advisory messages (always display to user). +// 1: first level of tracing detail. +// 2,3,...: successively more detailed tracing messages. +// An application might override this method if it wanted to abort on warnings +// or change the policy about which messages to display. +// static +void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level ) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) + { + // It's a warning message. Since corrupt files may generate many warnings, + // the policy implemented here is to show only the first warning, + // unless trace_level >= 3. + if (err->num_warnings == 0 || err->trace_level >= 3) + { + (*err->output_message) (cinfo); + } + // Always count warnings in num_warnings. + err->num_warnings++; + } + else + { + // It's a trace message. Show it if trace_level >= msg_level. + if (err->trace_level >= msg_level) + { + (*err->output_message) (cinfo); + } + } +} + +// static +void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo ) +{ + // Create the message + char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */ + (*cinfo->err->format_message) (cinfo, buffer); + + std::string error = buffer ; + LLImage::setLastError(error); + + bool is_decode = (cinfo->is_decompressor != 0); + LL_WARNS() << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << LL_ENDL; +} + +bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) +{ + llassert_always(raw_image); + + resetLastError(); + + LLImageDataSharedLock lockIn(raw_image); + LLImageDataLock lockOut(this); + + switch( raw_image->getComponents() ) + { + case 1: + case 3: + break; + default: + setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); + return false; + } + + setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); + + // Allocate a temporary buffer big enough to hold the entire compressed image (and then some) + // (Note: we make it bigger in emptyOutputBuffer() if we need to) + delete[] mOutputBuffer; + mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024; + mOutputBuffer = new(std::nothrow) U8[ mOutputBufferSize ]; + if (mOutputBuffer == NULL) + { + mOutputBufferSize = 0; + setLastError("Failed to allocate output buffer"); + return false; + } + + const U8* raw_image_data = NULL; + S32 row_stride = 0; + + //////////////////////////////////////// + // Step 1: allocate and initialize JPEG compression object + + // This struct contains the JPEG compression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + struct jpeg_compress_struct cinfo; + cinfo.client_data = this; + + // We have to set up the error handler first, in case the initialization + // step fails. (Unlikely, but it could happen if you are out of memory.) + // This routine fills in the contents of struct jerr, and returns jerr's + // address which we place into the link field in cinfo. + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + // Customize with our own callbacks + jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller + jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message + jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message + + // + //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error + //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao + // + if( setjmp(sSetjmpBuffer) ) + { + // If we get here, the JPEG code has signaled an error. + // We need to clean up the JPEG object, close the input file, and return. + jpeg_destroy_compress(&cinfo); + delete[] mOutputBuffer; + mOutputBuffer = NULL; + mOutputBufferSize = 0; + return false; + } + + try + { + + // Now we can initialize the JPEG compression object. + jpeg_create_compress(&cinfo); + + //////////////////////////////////////// + // Step 2: specify data destination + // (code is a modified form of jpeg_stdio_dest() ) + if( cinfo.dest == NULL) + { + cinfo.dest = (struct jpeg_destination_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_destination_mgr)); + } + cinfo.dest->next_output_byte = mOutputBuffer; // => next byte to write in buffer + cinfo.dest->free_in_buffer = mOutputBufferSize; // # of byte spaces remaining in buffer + cinfo.dest->init_destination = &LLImageJPEG::encodeInitDestination; + cinfo.dest->empty_output_buffer = &LLImageJPEG::encodeEmptyOutputBuffer; + cinfo.dest->term_destination = &LLImageJPEG::encodeTermDestination; + + //////////////////////////////////////// + // Step 3: set parameters for compression + // + // First we supply a description of the input image. + // Four fields of the cinfo struct must be filled in: + + cinfo.image_width = getWidth(); // image width and height, in pixels + cinfo.image_height = getHeight(); + + switch( getComponents() ) + { + case 1: + cinfo.input_components = 1; // # of color components per pixel + cinfo.in_color_space = JCS_GRAYSCALE; // colorspace of input image + break; + case 3: + cinfo.input_components = 3; // # of color components per pixel + cinfo.in_color_space = JCS_RGB; // colorspace of input image + break; + default: + setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); + return false; + } + + // Now use the library's routine to set default compression parameters. + // (You must set at least cinfo.in_color_space before calling this, + // since the defaults depend on the source color space.) + jpeg_set_defaults(&cinfo); + + // Now you can set any non-default parameters you wish to. + jpeg_set_quality(&cinfo, mEncodeQuality, true ); // limit to baseline-JPEG values + + //////////////////////////////////////// + // Step 4: Start compressor + // + // true ensures that we will write a complete interchange-JPEG file. + // Pass true unless you are very sure of what you're doing. + + jpeg_start_compress(&cinfo, true); + + //////////////////////////////////////// + // Step 5: while (scan lines remain to be written) + // jpeg_write_scanlines(...); + + // Here we use the library's state variable cinfo.next_scanline as the + // loop counter, so that we don't have to keep track ourselves. + // To keep things simple, we pass one scanline per call; you can pass + // more if you wish, though. + + row_stride = getWidth() * getComponents(); // JSAMPLEs per row in image_buffer + + // NOTE: For compatibility with LLImage, we need to invert the rows. + raw_image_data = raw_image->getData(); + + const U8* last_row_data = raw_image_data + (getHeight()-1) * row_stride; + + JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] + while (cinfo.next_scanline < cinfo.image_height) + { + // jpeg_write_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could pass + // more than one scanline at a time if that's more convenient. + + //Ugly const uncast here (jpeg_write_scanlines should take a const* but doesn't) + //row_pointer[0] = (JSAMPROW)(raw_image_data + (cinfo.next_scanline * row_stride)); + row_pointer[0] = (JSAMPROW)(last_row_data - (cinfo.next_scanline * row_stride)); + + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + //////////////////////////////////////// + // Step 6: Finish compression + jpeg_finish_compress(&cinfo); + + // After finish_compress, we can release the temp output buffer. + delete[] mOutputBuffer; + mOutputBuffer = NULL; + mOutputBufferSize = 0; + + //////////////////////////////////////// + // Step 7: release JPEG compression object + jpeg_destroy_compress(&cinfo); + } + + catch(int) + { + jpeg_destroy_compress(&cinfo); + delete[] mOutputBuffer; + mOutputBuffer = NULL; + mOutputBufferSize = 0; + return false; + } + + return true; +} -- cgit v1.2.3