123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- /**
- * @file llimagejpeg.cpp
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include <csetjmp>
- #include "llimagejpeg.h"
- static thread_local jmp_buf tSetjmpBuffer;
- 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() || !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
- // Error exit handler: does not return to caller:
- jerr.error_exit = &LLImageJPEG::errorExit;
- // Conditionally emit a trace or warning message:
- jerr.emit_message = &LLImageJPEG::errorEmitMessage;
- // Routine that actually outputs a trace or error message!
- jerr.output_message = &LLImageJPEG::errorOutputMessage;
- // 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(tSetjmpBuffer))
- {
- 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;
- // For now, use default method, but we should be able to do better.
- cinfo.src->resync_to_restart = jpeg_resync_to_restart;
- 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. Force to 3 components (RGB)
- setSize(cinfo.image_width, cinfo.image_height, 3);
- /*
- // 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 (...)
- {
- 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)
- {
- llassert_always(raw_image);
- resetLastError();
- if (!raw_image)
- {
- llwarns << "Attempted to decode a NULL raw image buffer address"
- << llendl;
- llassert(false);
- return false;
- }
- // Check to make sure that this instance has been initialized with data
- if (!getData() || getDataSize() == 0)
- {
- 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
- // Error exit handler: does not return to caller:
- jerr.error_exit = &LLImageJPEG::errorExit;
- // Conditionally emit a trace or warning message:
- jerr.emit_message = &LLImageJPEG::errorEmitMessage;
- // Routine that actually outputs a trace or error message:
- jerr.output_message = &LLImageJPEG::errorOutputMessage;
- // 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(tSetjmpBuffer))
- {
- 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;
- // For now, use default method, but we should be able to do better:
- cinfo.src->resync_to_restart = jpeg_resync_to_restart;
- 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.
- // Force to 3 components (RGB)
- setSize(cinfo.image_width, cinfo.image_height, 3);
- if (!raw_image->resize(getWidth(), getHeight(), getComponents()))
- {
- setLastError("LLImageJPEG failed to resize image");
- jpeg_destroy_decompress(&cinfo);
- return true; // Done
- }
- 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 is 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 (...)
- {
- 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)
- {
- LLMemory::allocationFailed(new_buffer_size);
- return false;
- }
- memcpy(new_buffer, self->mOutputBuffer, self->mOutputBufferSize);
- 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);
- if (self->allocateData(file_bytes))
- {
- memcpy(self->getData(), self->mOutputBuffer, file_bytes);
- }
- }
- // 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(tSetjmpBuffer, 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];
- (*cinfo->err->format_message) (cinfo, buffer);
- std::string error = buffer ;
- LLImage::setLastError(error);
- bool is_decode = (cinfo->is_decompressor != 0);
- llwarns << "JPEG " << (is_decode ? "decode" : "encode") << " failed: "
- << buffer << llendl;
- }
- bool LLImageJPEG::encode(const LLImageRaw* raw_image)
- {
- llassert_always(raw_image);
- resetLastError();
- switch (raw_image->getComponents())
- {
- case 1:
- case 3:
- break;
- default:
- setLastError("Unable to encode a JPEG image that does not 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)
- if (mOutputBuffer)
- {
- delete[] mOutputBuffer;
- }
- mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024;
- mOutputBuffer = new (std::nothrow) U8[mOutputBufferSize];
- if (!mOutputBuffer)
- {
- LLMemory::allocationFailed(mOutputBufferSize);
- setLastError("Unable to encode a JPEG image: out of memory.");
- mOutputBufferSize = 0;
- 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
- // Error exit handler: does not return to caller
- jerr.error_exit = &LLImageJPEG::errorExit;
- // Conditionally emit a trace or warning message
- jerr.emit_message = &LLImageJPEG::errorEmitMessage;
- // Routine that actually outputs a trace or error message
- jerr.output_message = &LLImageJPEG::errorOutputMessage;
- // try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws
- // an error; instead, we use setjmp/longjmp to avoid this crash, which is
- // the best we can get. --bao
- if (setjmp(tSetjmpBuffer))
- {
- // 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));
- }
- // => next byte to write in buffer
- cinfo.dest->next_output_byte = mOutputBuffer;
- // # of byte spaces remaining in buffer
- cinfo.dest->free_in_buffer = mOutputBufferSize;
- 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:
- // Image width and height, in pixels
- cinfo.image_width = getWidth();
- cinfo.image_height = getHeight();
- switch (getComponents())
- {
- case 1:
- // # of color components per pixel
- cinfo.input_components = 1;
- // Colorspace of input image
- cinfo.in_color_space = JCS_GRAYSCALE;
- break;
- case 3:
- // # of color components per pixel
- cinfo.input_components = 3;
- // Colorspace of input image
- cinfo.in_color_space = JCS_RGB;
- break;
- default:
- setLastError("Unable to encode a JPEG image that does not 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.
- // Limit to baseline-JPEG values
- jpeg_set_quality(&cinfo, mEncodeQuality, true);
- // 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.
- // JSAMPLEs per row in image_buffer
- row_stride = getWidth() * getComponents();
- // 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 is more convenient.
- // Ugly const uncast here (jpeg_write_scanlines should take a
- // const* but does not)
- //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 (...)
- {
- jpeg_destroy_compress(&cinfo);
- delete[] mOutputBuffer;
- mOutputBuffer = NULL;
- mOutputBufferSize = 0;
- return false;
- }
- return true;
- }
|