/** * @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(); // 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 !(LL_DARWIN && defined(__arm64__)) if(setjmp(sSetjmpBuffer)) { jpeg_destroy_decompress(&cinfo); return false; } #endif 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(); // 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 !(LL_DARWIN && defined(__arm64__)) if(setjmp(sSetjmpBuffer)) { jpeg_destroy_decompress(&cinfo); return true; // done } #endif 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; 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 #if !(LL_DARWIN && defined(__arm64__)) longjmp(sSetjmpBuffer, 1) ; #endif } // 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(); 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 !(LL_DARWIN && defined(__arm64__)) 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; } #endif 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; }