mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
- Overlays are implemented (sans input for the moment) - Fixed a problem where the movemanager was using the wrong values for hmd rotation & position
644 lines
20 KiB
C++
644 lines
20 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 "platform/platform.h"
|
|
|
|
#include "core/stream/fileStream.h"
|
|
#include "core/stream/memStream.h"
|
|
#include "core/strings/stringFunctions.h"
|
|
|
|
#include "gfx/bitmap/gBitmap.h"
|
|
#include "gfx/bitmap/pngUtils.h"
|
|
|
|
#define PNG_INTERNAL 1
|
|
#include <time.h>
|
|
#include "lpng/png.h"
|
|
#include "zlib/zlib.h"
|
|
|
|
#ifdef NULL
|
|
#undef NULL
|
|
#define NULL 0
|
|
#endif
|
|
|
|
|
|
static bool sReadPNG(Stream &stream, GBitmap *bitmap);
|
|
|
|
/// Compression levels for PNGs range from 0-9.
|
|
/// A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow.
|
|
static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
|
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter);
|
|
|
|
static struct _privateRegisterPNG
|
|
{
|
|
_privateRegisterPNG()
|
|
{
|
|
GBitmap::Registration reg;
|
|
|
|
reg.priority = 100;
|
|
reg.extensions.push_back( "png" );
|
|
|
|
reg.readFunc = sReadPNG;
|
|
reg.writeFunc = sWritePNG;
|
|
reg.defaultCompression = 6;
|
|
|
|
GBitmap::sRegisterFormat( reg );
|
|
}
|
|
} sStaticRegisterPNG;
|
|
|
|
|
|
//-------------------------------------- Replacement I/O for standard LIBPng
|
|
// functions. we don't wanna use
|
|
// FILE*'s...
|
|
static void pngReadDataFn(png_structp png_ptr,
|
|
png_bytep data,
|
|
png_size_t length)
|
|
{
|
|
AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?");
|
|
|
|
Stream *strm = (Stream*)png_get_io_ptr(png_ptr);
|
|
bool success = strm->read(length, data);
|
|
AssertFatal(success, "pngReadDataFn - failed to read from stream!");
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static void pngWriteDataFn(png_structp png_ptr,
|
|
png_bytep data,
|
|
png_size_t length)
|
|
{
|
|
AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?");
|
|
|
|
Stream *strm = (Stream*)png_get_io_ptr(png_ptr);
|
|
bool success = strm->write(length, data);
|
|
AssertFatal(success, "pngWriteDataFn - failed to write to stream!");
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static void pngFlushDataFn(png_structp /*png_ptr*/)
|
|
{
|
|
//
|
|
}
|
|
|
|
static png_voidp pngMallocFn(png_structp /*png_ptr*/, png_size_t size)
|
|
{
|
|
return FrameAllocator::alloc(size);
|
|
}
|
|
|
|
static void pngFreeFn(png_structp /*png_ptr*/, png_voidp /*mem*/)
|
|
{
|
|
}
|
|
|
|
static png_voidp pngRealMallocFn(png_structp /*png_ptr*/, png_size_t size)
|
|
{
|
|
return (png_voidp)dMalloc(size);
|
|
}
|
|
|
|
static void pngRealFreeFn(png_structp /*png_ptr*/, png_voidp mem)
|
|
{
|
|
dFree(mem);
|
|
}
|
|
|
|
//--------------------------------------
|
|
static void pngFatalErrorFn(png_structp /*png_ptr*/,
|
|
png_const_charp pMessage)
|
|
{
|
|
AssertISV(false, avar("Error reading PNG file:\n %s", pMessage));
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static void pngWarningFn(png_structp, png_const_charp /*pMessage*/)
|
|
{
|
|
// AssertWarn(false, avar("Warning reading PNG file:\n %s", pMessage));
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static bool sReadPNG(Stream &stream, GBitmap *bitmap)
|
|
{
|
|
static const U32 cs_headerBytesChecked = 8;
|
|
|
|
U8 header[cs_headerBytesChecked];
|
|
stream.read(cs_headerBytesChecked, header);
|
|
|
|
bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0;
|
|
if (isPng == false)
|
|
{
|
|
AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG");
|
|
return false;
|
|
}
|
|
|
|
U32 prevWaterMark = FrameAllocator::getWaterMark();
|
|
png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
|
|
NULL,
|
|
pngFatalErrorFn,
|
|
pngWarningFn,
|
|
NULL,
|
|
pngRealMallocFn,
|
|
pngRealFreeFn);
|
|
|
|
if (png_ptr == NULL)
|
|
{
|
|
FrameAllocator::setWaterMark(prevWaterMark);
|
|
return false;
|
|
}
|
|
|
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
if (info_ptr == NULL)
|
|
{
|
|
png_destroy_read_struct(&png_ptr,
|
|
(png_infopp)NULL,
|
|
(png_infopp)NULL);
|
|
|
|
FrameAllocator::setWaterMark(prevWaterMark);
|
|
return false;
|
|
}
|
|
|
|
png_infop end_info = png_create_info_struct(png_ptr);
|
|
if (end_info == NULL)
|
|
{
|
|
png_destroy_read_struct(&png_ptr,
|
|
&info_ptr,
|
|
(png_infopp)NULL);
|
|
|
|
FrameAllocator::setWaterMark(prevWaterMark);
|
|
return false;
|
|
}
|
|
|
|
png_set_read_fn(png_ptr, &stream, pngReadDataFn);
|
|
|
|
// Read off the info on the image.
|
|
png_set_sig_bytes(png_ptr, cs_headerBytesChecked);
|
|
png_read_info(png_ptr, info_ptr);
|
|
|
|
// OK, at this point, if we have reached it ok, then we can reset the
|
|
// image to accept the new data...
|
|
//
|
|
bitmap->deleteImage();
|
|
|
|
png_uint_32 width;
|
|
png_uint_32 height;
|
|
S32 bit_depth;
|
|
S32 color_type;
|
|
|
|
png_get_IHDR(png_ptr, info_ptr,
|
|
&width, &height, // obv.
|
|
&bit_depth, &color_type, // obv.
|
|
NULL, // interlace
|
|
NULL, // compression_type
|
|
NULL); // filter_type
|
|
|
|
// First, handle the color transformations. We need this to read in the
|
|
// data as RGB or RGBA, _always_, with a maximal channel width of 8 bits.
|
|
//
|
|
bool transAlpha = false;
|
|
GFXFormat format = GFXFormatR8G8B8;
|
|
|
|
// Strip off any 16 bit info
|
|
//
|
|
if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY)
|
|
{
|
|
png_set_strip_16(png_ptr);
|
|
}
|
|
|
|
// Expand a transparency channel into a full alpha channel...
|
|
//
|
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
|
{
|
|
png_set_expand(png_ptr);
|
|
transAlpha = true;
|
|
}
|
|
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
png_set_expand(png_ptr);
|
|
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
|
}
|
|
else if (color_type == PNG_COLOR_TYPE_GRAY)
|
|
{
|
|
png_set_expand(png_ptr);
|
|
|
|
if (bit_depth == 16)
|
|
format = GFXFormatR5G6B5;
|
|
else
|
|
format = GFXFormatA8;
|
|
}
|
|
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
{
|
|
png_set_expand(png_ptr);
|
|
png_set_gray_to_rgb(png_ptr);
|
|
format = GFXFormatR8G8B8A8;
|
|
}
|
|
else if (color_type == PNG_COLOR_TYPE_RGB)
|
|
{
|
|
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
|
png_set_expand(png_ptr);
|
|
}
|
|
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
|
{
|
|
png_set_expand(png_ptr);
|
|
format = GFXFormatR8G8B8A8;
|
|
}
|
|
|
|
// Update the info pointer with the result of the transformations
|
|
// above...
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
|
|
png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr);
|
|
if (format == GFXFormatR8G8B8)
|
|
{
|
|
AssertFatal(rowBytes == width * 3,
|
|
"Error, our rowbytes are incorrect for this transform... (3)");
|
|
}
|
|
else if (format == GFXFormatR8G8B8A8)
|
|
{
|
|
AssertFatal(rowBytes == width * 4,
|
|
"Error, our rowbytes are incorrect for this transform... (4)");
|
|
}
|
|
else if (format == GFXFormatR5G6B5)
|
|
{
|
|
AssertFatal(rowBytes == width * 2,
|
|
"Error, our rowbytes are incorrect for this transform... (2)");
|
|
}
|
|
|
|
// actually allocate the bitmap space...
|
|
bitmap->allocateBitmap(width, height,
|
|
false, // don't extrude miplevels...
|
|
format); // use determined format...
|
|
|
|
// Set up the row pointers...
|
|
png_bytep* rowPointers = new png_bytep[ height ];
|
|
U8* pBase = (U8*)bitmap->getBits();
|
|
|
|
for (U32 i = 0; i < height; i++)
|
|
rowPointers[i] = pBase + (i * rowBytes);
|
|
|
|
// And actually read the image!
|
|
png_read_image(png_ptr, rowPointers);
|
|
|
|
// We're outta here, destroy the png structs, and release the lock
|
|
// as quickly as possible...
|
|
//png_read_end(png_ptr, end_info);
|
|
delete [] rowPointers;
|
|
png_read_end(png_ptr, NULL);
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
|
|
|
// Ok, the image is read in, now we need to finish up the initialization,
|
|
// which means: setting up the detailing members, init'ing the palette
|
|
// key, etc...
|
|
//
|
|
// actually, all of that was handled by allocateBitmap, so we're outta here
|
|
//
|
|
|
|
// Check this bitmap for transparency
|
|
bitmap->checkForTransparency();
|
|
|
|
FrameAllocator::setWaterMark(prevWaterMark);
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter)
|
|
{
|
|
GFXFormat format = bitmap->getFormat();
|
|
|
|
// ONLY RGB bitmap writing supported at this time!
|
|
AssertFatal( format == GFXFormatR8G8B8 ||
|
|
format == GFXFormatR8G8B8A8 ||
|
|
format == GFXFormatR8G8B8X8 ||
|
|
format == GFXFormatA8 ||
|
|
format == GFXFormatR5G6B5 ||
|
|
format == GFXFormatR8G8B8A8_LINEAR_FORCE, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
|
|
|
if ( format != GFXFormatR8G8B8 &&
|
|
format != GFXFormatR8G8B8A8 &&
|
|
format != GFXFormatR8G8B8X8 &&
|
|
format != GFXFormatA8 &&
|
|
format != GFXFormatR5G6B5 && format != GFXFormatR8G8B8A8_LINEAR_FORCE)
|
|
return false;
|
|
|
|
png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
|
NULL,
|
|
pngFatalErrorFn,
|
|
pngWarningFn,
|
|
NULL,
|
|
pngMallocFn,
|
|
pngFreeFn);
|
|
if (png_ptr == NULL)
|
|
return (false);
|
|
|
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
if (info_ptr == NULL)
|
|
{
|
|
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
|
return false;
|
|
}
|
|
|
|
png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
|
|
|
// Set the compression level and image filters
|
|
png_set_compression_window_bits(png_ptr, 15);
|
|
png_set_compression_level(png_ptr, compressionLevel);
|
|
png_set_filter(png_ptr, 0, filter);
|
|
|
|
// Set the image information here. Width and height are up to 2^31,
|
|
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
|
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
|
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
|
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
|
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
|
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
|
|
|
U32 width = bitmap->getWidth();
|
|
U32 height = bitmap->getHeight();
|
|
|
|
if (format == GFXFormatR8G8B8)
|
|
{
|
|
png_set_IHDR(png_ptr, info_ptr,
|
|
width, height, // the width & height
|
|
8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type,
|
|
NULL, // no interlace
|
|
NULL, // compression type
|
|
NULL); // filter type
|
|
}
|
|
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
|
|
{
|
|
png_set_IHDR(png_ptr, info_ptr,
|
|
width, height, // the width & height
|
|
8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type,
|
|
NULL, // no interlace
|
|
NULL, // compression type
|
|
NULL); // filter type
|
|
}
|
|
else if (format == GFXFormatA8)
|
|
{
|
|
png_set_IHDR(png_ptr, info_ptr,
|
|
width, height, // the width & height
|
|
8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
NULL, // no interlace
|
|
NULL, // compression type
|
|
NULL); // filter type
|
|
}
|
|
else if (format == GFXFormatR5G6B5)
|
|
{
|
|
png_set_IHDR(png_ptr, info_ptr,
|
|
width, height, // the width & height
|
|
16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
PNG_INTERLACE_NONE, // no interlace
|
|
PNG_COMPRESSION_TYPE_DEFAULT, // compression type
|
|
PNG_FILTER_TYPE_DEFAULT); // filter type
|
|
|
|
png_color_8_struct sigBit = { 0 };
|
|
sigBit.gray = 16;
|
|
png_set_sBIT(png_ptr, info_ptr, &sigBit );
|
|
|
|
png_set_swap( png_ptr );
|
|
}
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
FrameAllocatorMarker marker;
|
|
png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) );
|
|
for (U32 i=0; i<height; i++)
|
|
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
|
|
|
png_write_image(png_ptr, row_pointers);
|
|
|
|
// Write S3TC data if present...
|
|
// Write FXT1 data if present...
|
|
|
|
png_write_end(png_ptr, info_ptr);
|
|
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
|
{
|
|
U32 waterMark = FrameAllocator::getWaterMark();
|
|
|
|
if ( compressionLevel < 10 )
|
|
{
|
|
bool retVal = _writePNG(bitmap, stream, compressionLevel, 0, PNG_ALL_FILTERS);
|
|
FrameAllocator::setWaterMark(waterMark);
|
|
return retVal;
|
|
}
|
|
|
|
// check all our methods of compression to find the best one and use it
|
|
U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough...
|
|
MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true);
|
|
|
|
const U32 zStrategies[] = { Z_DEFAULT_STRATEGY,
|
|
Z_FILTERED };
|
|
const U32 pngFilters[] = { PNG_FILTER_NONE,
|
|
PNG_FILTER_SUB,
|
|
PNG_FILTER_UP,
|
|
PNG_FILTER_AVG,
|
|
PNG_FILTER_PAETH,
|
|
PNG_ALL_FILTERS };
|
|
|
|
U32 minSize = 0xFFFFFFFF;
|
|
U32 bestStrategy = 0xFFFFFFFF;
|
|
U32 bestFilter = 0xFFFFFFFF;
|
|
U32 bestCLevel = 0xFFFFFFFF;
|
|
|
|
for (U32 cl = 0; cl <=9; cl++)
|
|
{
|
|
for (U32 zs = 0; zs < 2; zs++)
|
|
{
|
|
for (U32 pf = 0; pf < 6; pf++)
|
|
{
|
|
pMemStream->setPosition(0);
|
|
|
|
U32 waterMarkInner = FrameAllocator::getWaterMark();
|
|
|
|
if (_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]) == false)
|
|
AssertFatal(false, "Handle this error!");
|
|
|
|
FrameAllocator::setWaterMark(waterMarkInner);
|
|
|
|
if (pMemStream->getPosition() < minSize)
|
|
{
|
|
minSize = pMemStream->getPosition();
|
|
bestStrategy = zs;
|
|
bestFilter = pf;
|
|
bestCLevel = cl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
AssertFatal(minSize != 0xFFFFFFFF, "Error, no best found?");
|
|
|
|
delete pMemStream;
|
|
delete [] buffer;
|
|
|
|
|
|
bool retVal = _writePNG(bitmap, stream,
|
|
bestCLevel,
|
|
zStrategies[bestStrategy],
|
|
pngFilters[bestFilter]);
|
|
FrameAllocator::setWaterMark(waterMark);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Stores PNG stream data
|
|
struct DeferredPNGWriterData {
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
U32 width;
|
|
U32 height;
|
|
};
|
|
DeferredPNGWriter::DeferredPNGWriter() :
|
|
mData( NULL ),
|
|
mActive(false)
|
|
{
|
|
mData = new DeferredPNGWriterData();
|
|
}
|
|
DeferredPNGWriter::~DeferredPNGWriter()
|
|
{
|
|
delete mData;
|
|
}
|
|
|
|
bool DeferredPNGWriter::begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel )
|
|
{
|
|
// ONLY RGB bitmap writing supported at this time!
|
|
AssertFatal( format == GFXFormatR8G8B8 ||
|
|
format == GFXFormatR8G8B8A8 ||
|
|
format == GFXFormatR8G8B8X8 ||
|
|
format == GFXFormatA8 ||
|
|
format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
|
|
|
if ( format != GFXFormatR8G8B8 &&
|
|
format != GFXFormatR8G8B8A8 &&
|
|
format != GFXFormatR8G8B8X8 &&
|
|
format != GFXFormatA8 &&
|
|
format != GFXFormatR5G6B5 )
|
|
return false;
|
|
|
|
mData->png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
|
NULL,
|
|
pngFatalErrorFn,
|
|
pngWarningFn,
|
|
NULL,
|
|
pngRealMallocFn,
|
|
pngRealFreeFn);
|
|
if (mData->png_ptr == NULL)
|
|
return (false);
|
|
|
|
mData->info_ptr = png_create_info_struct(mData->png_ptr);
|
|
if (mData->info_ptr == NULL)
|
|
{
|
|
png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL);
|
|
return false;
|
|
}
|
|
|
|
png_set_write_fn(mData->png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
|
|
|
// Set the compression level and image filters
|
|
png_set_compression_window_bits(mData->png_ptr, 15);
|
|
png_set_compression_level(mData->png_ptr, compressionLevel);
|
|
png_set_filter(mData->png_ptr, 0, PNG_ALL_FILTERS);
|
|
|
|
// Set the image information here. Width and height are up to 2^31,
|
|
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
|
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
|
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
|
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
|
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
|
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
|
|
|
if (format == GFXFormatR8G8B8)
|
|
{
|
|
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
width, height, // the width & height
|
|
8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type,
|
|
NULL, // no interlace
|
|
NULL, // compression type
|
|
NULL); // filter type
|
|
}
|
|
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8)
|
|
{
|
|
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
width, height, // the width & height
|
|
8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type,
|
|
NULL, // no interlace
|
|
NULL, // compression type
|
|
NULL); // filter type
|
|
}
|
|
else if (format == GFXFormatA8)
|
|
{
|
|
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
width, height, // the width & height
|
|
8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
NULL, // no interlace
|
|
NULL, // compression type
|
|
NULL); // filter type
|
|
}
|
|
else if (format == GFXFormatR5G6B5)
|
|
{
|
|
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
width, height, // the width & height
|
|
16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
PNG_INTERLACE_NONE, // no interlace
|
|
PNG_COMPRESSION_TYPE_DEFAULT, // compression type
|
|
PNG_FILTER_TYPE_DEFAULT); // filter type
|
|
|
|
png_color_8_struct sigBit = { 0 };
|
|
sigBit.gray = 16;
|
|
png_set_sBIT(mData->png_ptr, mData->info_ptr, &sigBit );
|
|
|
|
png_set_swap( mData->png_ptr );
|
|
}
|
|
|
|
png_write_info(mData->png_ptr, mData->info_ptr);
|
|
|
|
mActive = true;
|
|
|
|
return true;
|
|
}
|
|
void DeferredPNGWriter::append( GBitmap* bitmap, U32 rows)
|
|
{
|
|
AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!");
|
|
|
|
U32 height = getMin( bitmap->getHeight(), rows);
|
|
|
|
FrameAllocatorMarker marker;
|
|
png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) );
|
|
for (U32 i=0; i<height; i++)
|
|
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
|
|
|
png_write_rows(mData->png_ptr, row_pointers, height);
|
|
}
|
|
void DeferredPNGWriter::end()
|
|
{
|
|
AssertFatal(mActive, "Cannot end an inactive DeferredPNGWriter!");
|
|
|
|
png_write_end(mData->png_ptr, mData->info_ptr);
|
|
png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL);
|
|
|
|
mActive = false;
|
|
} |