mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
252 lines
8 KiB
C++
252 lines
8 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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 + (U64)(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;
|
|
}
|