123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "ljpeg/jpeglib.h"
- #include "core/stream/stream.h"
- #include "gfx/bitmap/gBitmap.h"
- static bool sReadJPG(Stream &stream, GBitmap *bitmap);
- static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
- static struct _privateRegisterJPG
- {
- _privateRegisterJPG()
- {
- GBitmap::Registration reg;
- reg.priority = 50;
- reg.extensions.push_back( "jpeg" );
- reg.extensions.push_back( "jpg" );
- reg.readFunc = sReadJPG;
- reg.writeFunc = sWriteJPG;
- GBitmap::sRegisterFormat( reg );
- }
- } sStaticRegisterJPG;
- //-------------------------------------- Replacement I/O for standard LIBjpeg
- // functions. we don't wanna use
- // FILE*'s...
- static S32 jpegReadDataFn(void *client_data, U8 *data, S32 length)
- {
- Stream *stream = (Stream*)client_data;
- AssertFatal(stream != NULL, "jpegReadDataFn::No stream.");
- S32 pos = stream->getPosition();
- if (stream->read(length, data))
- return length;
- if (stream->getStatus() == Stream::EOS)
- return (stream->getPosition()-pos);
- else
- return 0;
- }
- //--------------------------------------
- static S32 jpegWriteDataFn(void *client_data, U8 *data, S32 length)
- {
- Stream *stream = (Stream*)client_data;
- AssertFatal(stream != NULL, "jpegWriteDataFn::No stream.");
- if (stream->write(length, data))
- return length;
- else
- return 0;
- }
- //--------------------------------------
- static S32 jpegFlushDataFn(void *)
- {
- // do nothing since we can't flush the stream object
- return 0;
- }
- //--------------------------------------
- static S32 jpegErrorFn(void *client_data)
- {
- Stream *stream = (Stream*)client_data;
- AssertFatal(stream != NULL, "jpegErrorFn::No stream.");
- return (stream->getStatus() != Stream::Ok);
- }
- //--------------------------------------
- static bool sReadJPG(Stream &stream, GBitmap *bitmap)
- {
- PROFILE_SCOPE(sReadJPG);
- JFREAD = jpegReadDataFn;
- JFERROR = jpegErrorFn;
- jpeg_decompress_struct cinfo;
- jpeg_error_mgr jerr;
- // We set up the normal JPEG error routines, then override error_exit.
- //cinfo.err = jpeg_std_error(&jerr.pub);
- //jerr.pub.error_exit = my_error_exit;
- // if (setjmp(jerr.setjmp_buffer))
- // {
- // // 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_decompress(&cinfo);
- // return false;
- // }
- cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines.
- cinfo.client_data = (void*)&stream; // set the stream into the client_data
- // Now we can initialize the JPEG decompression object.
- jpeg_create_decompress(&cinfo);
- jpeg_stdio_src(&cinfo);
- // Read file header, set default decompression parameters
- jpeg_read_header(&cinfo, true);
- GFXFormat format;
- switch (cinfo.out_color_space)
- {
- case JCS_GRAYSCALE: format = GFXFormatA8; break;
- case JCS_RGB: format = GFXFormatR8G8B8; break;
- default:
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
- // Start decompressor
- jpeg_start_decompress(&cinfo);
- // allocate the bitmap space and init internal variables...
- bitmap->allocateBitmap(cinfo.output_width, cinfo.output_height, false, format);
- // Set up the row pointers...
- U32 rowBytes = cinfo.output_width * cinfo.output_components;
- U8* pBase = (U8*)bitmap->getBits();
- for (U32 i = 0; i < bitmap->getHeight(); i++)
- {
- JSAMPROW rowPointer = pBase + (i * rowBytes);
- jpeg_read_scanlines(&cinfo, &rowPointer, 1);
- }
- // Finish decompression
- jpeg_finish_decompress(&cinfo);
- // Release JPEG decompression object
- // This is an important step since it will release a good deal of memory.
- jpeg_destroy_decompress(&cinfo);
- // We know JPEG's don't have any transparency
- bitmap->setHasTransparency(false);
- return true;
- }
- //--------------------------------------------------------------------------
- static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
- {
- TORQUE_UNUSED(compressionLevel); // compression level not currently hooked up
- GFXFormat format = bitmap->getFormat();
- // JPEG format does not support transparency so any image
- // in Alpha format should be saved as a grayscale which coincides
- // with how the readJPEG function will read-in a JPEG. So the
- // only formats supported are RGB and Alpha, not RGBA.
- AssertFatal(format == GFXFormatR8G8B8 || format == GFXFormatA8,
- "GBitmap::writeJPEG: ONLY RGB bitmap writing supported at this time.");
- if (format != GFXFormatR8G8B8 && format != GFXFormatA8)
- return false;
- // maximum image size allowed
- #define MAX_HEIGHT 4096
- if (bitmap->getHeight() > MAX_HEIGHT)
- return false;
- // Bind our own stream writing, error, and memory flush functions
- // to the jpeg library interface
- JFWRITE = jpegWriteDataFn;
- JFFLUSH = jpegFlushDataFn;
- JFERROR = jpegErrorFn;
- // Allocate and initialize our jpeg compression structure and error manager
- jpeg_compress_struct cinfo;
- jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines.
- cinfo.client_data = (void*)&stream; // set the stream into the client_data
- jpeg_create_compress(&cinfo); // allocates a small amount of memory
- // specify the destination for the compressed data(our stream)
- jpeg_stdio_dest(&cinfo);
- // set the image properties
- cinfo.image_width = bitmap->getWidth(); // image width
- cinfo.image_height = bitmap->getHeight(); // image height
- cinfo.input_components = bitmap->getBytesPerPixel(); // samples per pixel(RGB:3, Alpha:1)
-
- switch (format)
- {
- case GFXFormatA8: // no alpha support in JPEG format, so turn it into a grayscale
- cinfo.in_color_space = JCS_GRAYSCALE;
- break;
- case GFXFormatR8G8B8: // otherwise we are writing in RGB format
- cinfo.in_color_space = JCS_RGB;
- break;
- default:
- AssertFatal( false, "Format not handled in GBitmap::writeJPEG() switch" );
- break;
- }
- // use default compression params(75% compression)
- jpeg_set_defaults(&cinfo);
- // begin JPEG compression cycle
- jpeg_start_compress(&cinfo, true);
- // Set up the row pointers...
- U32 rowBytes = cinfo.image_width * cinfo.input_components;
- U8* pBase = (U8*)bitmap->getBits();
- for (U32 i = 0; i < bitmap->getHeight(); i++)
- {
- // write the image data
- JSAMPROW rowPointer = pBase + (i * rowBytes);
- jpeg_write_scanlines(&cinfo, &rowPointer, 1);
- }
- // complete the compression cycle
- jpeg_finish_compress(&cinfo);
- // release the JPEG compression object
- jpeg_destroy_compress(&cinfo);
- // return success
- return true;
- }
|