Merge pull request #1148 from marauder2k9-torque/STBImageLoading-PR

Stb image loading
This commit is contained in:
Brian Roberts 2024-01-01 08:48:46 -06:00 committed by GitHub
commit 51426a3575
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 20385 additions and 1813 deletions

View file

@ -82,6 +82,9 @@ endif()
torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders"
"gfx/util" "gfx/video" "gfx/sim" )
# add the stb headers
set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "gfx/bitmap/loaders/stb")
if (TORQUE_OPENGL)
torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL")
endif (TORQUE_OPENGL)

View file

@ -565,15 +565,10 @@ void DDSFile::SurfaceData::dumpImage(DDSFile *dds, U32 mip, const char *file)
// Copy our data in.
dMemcpy(foo->getWritableBits(), mMips[mip], dds->getSurfaceSize(dds->mHeight, dds->mWidth, mip) );
FileStream stream;
stream.open( file, Torque::FS::File::Write );
if ( stream.getStatus() == Stream::Ok )
if(!foo->writeBitmap("png", file))
{
// Write it out.
foo->writeBitmap("png", stream);
Con::errorf("DDSFile::SurfaceData::dumpImage() - Error writing %s !", file);
}
// Clean up.

View file

@ -1192,7 +1192,7 @@ bool GBitmap::write(Stream& io_rStream) const
//-------------------------------------- Persistent I/O
//
bool GBitmap::readBitmap( const String &bmType, Stream &ioStream )
bool GBitmap::readBitmap(const String& bmType, const Torque::Path& path)
{
PROFILE_SCOPE(ResourceGBitmap_readBitmap);
const GBitmap::Registration *regInfo = GBitmap::sFindRegInfo( bmType );
@ -1203,11 +1203,22 @@ bool GBitmap::readBitmap( const String &bmType, Stream &ioStream )
return false;
}
return regInfo->readFunc( ioStream, this );
return regInfo->readFunc(path, this);
}
bool GBitmap::writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel )
bool GBitmap::writeBitmap( const String &bmType, const Torque::Path& path, U32 compressionLevel )
{
FileStream stream;
if (!stream.open(path, Torque::FS::File::Write))
{
Con::errorf("GBitmap::writeBitmap failed to open path %s", path.getFullFileName().c_str());
stream.close();
return false;
}
// free file for stb
stream.close();
const GBitmap::Registration *regInfo = GBitmap::sFindRegInfo( bmType );
if ( regInfo == NULL )
@ -1216,7 +1227,7 @@ bool GBitmap::writeBitmap( const String &bmType, Stream &ioStream, U32 compress
return false;
}
return regInfo->writeFunc( this, ioStream, (compressionLevel == U32_MAX) ? regInfo->defaultCompression : compressionLevel );
return regInfo->writeFunc(path, this, (compressionLevel == U32_MAX) ? regInfo->defaultCompression : compressionLevel );
}
template<> void *Resource<GBitmap>::create(const Torque::Path &path)
@ -1239,7 +1250,7 @@ template<> void *Resource<GBitmap>::create(const Torque::Path &path)
GBitmap *bmp = new GBitmap;
const String extension = path.getExtension();
if( !bmp->readBitmap( extension, stream ) )
if( !bmp->readBitmap( extension, path ) )
{
Con::errorf( "Resource<GBitmap>::create - error reading '%s'", path.getFullPath().c_str() );
delete bmp;
@ -1431,21 +1442,14 @@ DefineEngineFunction(saveScaledImage, bool, (const char* bitmapSource, const cha
Torque::Path destinationPath = Torque::Path(bitmapDest);
destinationPath.setExtension("png");
// Open up the file on disk.
FileStream fs;
if (!fs.open(destinationPath.getFullPath(), Torque::FS::File::Write))
if(!image->writeBitmap("png", destinationPath.getFullPath()))
{
Con::errorf("saveScaledImage() - Failed to open output file '%s'!", bitmapDest);
Con::errorf("saveScaledImage() - Error writing %s !", bitmapDest);
delete image;
return false;
}
else
{
image->writeBitmap("png", fs);
fs.close();
delete image;
}
delete image;
return true;
}

View file

@ -71,10 +71,10 @@ public:
struct Registration
{
/// The read function prototype.
typedef bool(*ReadFunc)(Stream &stream, GBitmap *bitmap);
typedef bool(*ReadFunc)(const Torque::Path& path, GBitmap* bitmap);
/// The write function prototype. Compression levels are image-specific - see their registration declaration for details.
typedef bool(*WriteFunc)(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
typedef bool(*WriteFunc)(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
/// Used to sort the registrations so that
/// lookups occur in a fixed order.
@ -241,13 +241,16 @@ public:
/// Read a bitmap from a stream
/// @param bmType This is a file extension to describe the type of the data [i.e. "png" for PNG file, etc]
/// @param ioStream The stream to read from
bool readBitmap( const String &bmType, Stream &ioStream );
bool readBitmap(const String& bmType, const Torque::Path& path);
/// Write a bitmap to a stream
/// @param bmType This is a file extension to describe the type of the data [i.e. "png" for PNG file, etc]
/// @param ioStream The stream to read from
/// @param compressionLevel Image format-specific compression level. If set to U32_MAX, we use the default compression defined when the format was registered.
bool writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel = U32_MAX );
/// @param compressionLevel Image format specific compression level. For JPEG sets the quality level percentage, range 0 to 100.
/// For PNG compression level is 0 - 10
/// Not used for other image formats.
bool writeBitmap( const String &bmType, const Torque::Path& path, U32 compressionLevel = U32_MAX );
bool readMNG(Stream& io_rStream); // located in bitmapMng.cc
bool writeMNG(Stream& io_rStream) const;

View file

@ -1,246 +0,0 @@
//-----------------------------------------------------------------------------
// 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 "core/stream/stream.h"
#include "gfx/bitmap/gBitmap.h"
static bool sReadBMP(Stream &stream, GBitmap *bitmap);
static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
static struct _privateRegisterBMP
{
_privateRegisterBMP()
{
GBitmap::Registration reg;
reg.extensions.push_back( "bmp" );
reg.readFunc = sReadBMP;
reg.writeFunc = sWriteBMP;
GBitmap::sRegisterFormat( reg );
}
} sStaticRegisterBMP;
// structures mirror those defined by the win32 API
struct RGBQUAD
{
U8 rgbBlue;
U8 rgbGreen;
U8 rgbRed;
U8 rgbReserved;
};
struct BITMAPFILEHEADER
{
U16 bfType;
U32 bfSize;
U16 bfReserved1;
U16 bfReserved2;
U32 bfOffBits;
};
struct BITMAPINFOHEADER
{
U32 biSize;
S32 biWidth;
S32 biHeight;
U16 biPlanes;
U16 biBitCount;
U32 biCompression;
U32 biSizeImage;
S32 biXPelsPerMeter;
S32 biYPelsPerMeter;
U32 biClrUsed;
U32 biClrImportant;
};
// constants for the biCompression field
#define BI_RGB 0L
#define BI_RLE8 1L
#define BI_RLE4 2L
#define BI_BITFIELDS 3L
//------------------------------------------------------------------------------
//-------------------------------------- Supplementary I/O (Partially located in
// bitmapPng.cc)
//
static bool sReadBMP(Stream &stream, GBitmap *bitmap)
{
PROFILE_SCOPE(sReadBMP);
BITMAPINFOHEADER bi;
BITMAPFILEHEADER bf;
RGBQUAD rgb[256];
stream.read(&bf.bfType);
stream.read(&bf.bfSize);
stream.read(&bf.bfReserved1);
stream.read(&bf.bfReserved2);
stream.read(&bf.bfOffBits);
stream.read(&bi.biSize);
stream.read(&bi.biWidth);
stream.read(&bi.biHeight);
stream.read(&bi.biPlanes);
stream.read(&bi.biBitCount);
stream.read(&bi.biCompression);
stream.read(&bi.biSizeImage);
stream.read(&bi.biXPelsPerMeter);
stream.read(&bi.biYPelsPerMeter);
stream.read(&bi.biClrUsed);
stream.read(&bi.biClrImportant);
GFXFormat fmt = GFXFormatR8G8B8;
if(bi.biBitCount == 8)
{
// read in texture palette
if(!bi.biClrUsed)
bi.biClrUsed = 256;
stream.read(sizeof(RGBQUAD) * bi.biClrUsed, rgb);
}
bitmap->allocateBitmap(bi.biWidth, bi.biHeight, false, fmt);
U32 width = bitmap->getWidth();
U32 height = bitmap->getHeight();
U32 bytesPerPixel = bitmap->getBytesPerPixel();
for(U32 i = 0; i < bi.biHeight; i++)
{
U8 *rowDest = bitmap->getAddress(0, height - i - 1);
if (bi.biBitCount == 8)
{
// use palette...don't worry about being slow
for (S32 j=0; j<width; j++)
{
U8 palIdx;
stream.read(&palIdx);
U8 * pixelLocation = &rowDest[j*bytesPerPixel];
pixelLocation[0] = rgb[palIdx].rgbRed;
pixelLocation[1] = rgb[palIdx].rgbGreen;
pixelLocation[2] = rgb[palIdx].rgbBlue;
if (bytesPerPixel==3)
pixelLocation[3] = 255;
}
}
else
stream.read(bytesPerPixel * width, rowDest);
}
if(bytesPerPixel == 3 && bi.biBitCount != 8) // do BGR swap
{
U8 *ptr = bitmap->getAddress(0,0);
for(S32 i = 0; i < width * height; i++)
{
U8 tmp = ptr[0];
ptr[0] = ptr[2];
ptr[2] = tmp;
ptr += 3;
}
}
// We know BMP's don't have any transparency
bitmap->setHasTransparency(false);
return true;
}
static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
{
TORQUE_UNUSED( compressionLevel ); // BMP does not use compression
BITMAPINFOHEADER bi;
BITMAPFILEHEADER bf;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bitmap->getWidth();
bi.biHeight = bitmap->getHeight(); //our data is top-down
bi.biPlanes = 1;
if(bitmap->getFormat() == GFXFormatR8G8B8)
{
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biClrUsed = 0;
}
else
{
bi.biBitCount = 0;
bi.biCompression = BI_RGB; // Removes warning C4701 on line
AssertISV(false, "GBitmap::writeMSBmp - only support R8G8B8 formats!");
}
U32 width = bitmap->getWidth();
U32 height = bitmap->getHeight();
U32 bytesPP = bi.biBitCount >> 3;
bi.biSizeImage = width * height * bytesPP;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
bf.bfType = makeFourCCTag('B','M',0,0); //Type of file 'BM'
bf.bfOffBits= sizeof(BITMAPINFOHEADER)
+ sizeof(BITMAPFILEHEADER)
+ (sizeof(RGBQUAD)*bi.biClrUsed);
bf.bfSize = bf.bfOffBits + bi.biSizeImage;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
stream.write(bf.bfType);
stream.write(bf.bfSize);
stream.write(bf.bfReserved1);
stream.write(bf.bfReserved2);
stream.write(bf.bfOffBits);
stream.write(bi.biSize);
stream.write(bi.biWidth);
stream.write(bi.biHeight);
stream.write(bi.biPlanes);
stream.write(bi.biBitCount);
stream.write(bi.biCompression);
stream.write(bi.biSizeImage);
stream.write(bi.biXPelsPerMeter);
stream.write(bi.biYPelsPerMeter);
stream.write(bi.biClrUsed);
stream.write(bi.biClrImportant);
//write the bitmap bits
U8* pMSUpsideDownBits = new U8[bi.biSizeImage];
for (U32 i = 0; i < height; i++)
{
const U8* pSrc = bitmap->getAddress(0, i);
U8* pDst = pMSUpsideDownBits + (height - i - 1) * width * bytesPP;
dMemcpy(pDst, pSrc, width * bytesPP);
}
stream.write(bi.biSizeImage, pMSUpsideDownBits);
delete [] pMSUpsideDownBits;
return stream.getStatus() == Stream::Ok;
}

View file

@ -1,251 +0,0 @@
//-----------------------------------------------------------------------------
// 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;
}

View file

@ -1,645 +0,0 @@
//-----------------------------------------------------------------------------
// 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((U32)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((U32)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((U32)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)
{
PROFILE_SCOPE(sReadPNG);
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)
{
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 = GFXFormatL16;
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_uint_32)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 == GFXFormatL16)
{
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;
}

View file

@ -0,0 +1,271 @@
//-----------------------------------------------------------------------------
// 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"
#ifdef __clang__
#define STBIWDEF static inline
#endif
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#include "stb_image_write.h"
static bool sReadSTB(const Torque::Path& path, GBitmap* bitmap);
static bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
static struct _privateRegisterSTB
{
_privateRegisterSTB()
{
GBitmap::Registration reg;
reg.priority = 100;
reg.extensions.push_back("png");
reg.extensions.push_back("bmp");
reg.extensions.push_back("jpg");
reg.extensions.push_back("jpeg");
reg.extensions.push_back("psd");
reg.extensions.push_back("hdr");
reg.extensions.push_back("tga");
reg.readFunc = sReadSTB;
reg.writeFunc = sWriteSTB;
// for png only.
reg.defaultCompression = 6;
GBitmap::sRegisterFormat(reg);
}
} sStaticRegisterSTB;
bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
{
PROFILE_SCOPE(sReadSTB);
S32 x, y, n, channels;
String ext = path.getExtension();
U32 prevWaterMark = FrameAllocator::getWaterMark();
if (!stbi_info(path.getFullPath().c_str(), &x, &y, &channels))
{
FrameAllocator::setWaterMark(prevWaterMark);
return false;
}
// do this to map 2 channels to 4, 2 channel not supported by gbitmap yet..
if (channels == 2)
channels = 4;
if (!ext.equal("png"))
{
if (stbi_is_16_bit(path.getFullPath().c_str()))
{
U16* data = stbi_load_16(path.getFullPath().c_str(), &x, &y, &n, channels);
// if succesful deal make the bitmap, else try other loaders.
if (data)
{
GFXFormat format;
if (n == 1)
format = GFXFormatL16;
else
format = GFXFormatR16G16B16A16; // not sure if this is correct.
bitmap->deleteImage();
// actually allocate the bitmap space...
bitmap->allocateBitmap(x, y,
false, // don't extrude miplevels...
format); // use determined format...
U16* pBase = (U16*)bitmap->getBits();
U32 rowBytes = bitmap->getByteSize();
dMemcpy(pBase, data, rowBytes);
stbi_image_free(data);
FrameAllocator::setWaterMark(prevWaterMark);
return true;
}
}
}
if (ext.equal("hdr"))
{
// force load to 4 channel.
float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 4);
unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, 4);
bitmap->deleteImage();
// actually allocate the bitmap space...
bitmap->allocateBitmap(x, y,
false,
GFXFormatR8G8B8A8);
U8* pBase = (U8*)bitmap->getBits();
U32 rowBytes = x * y * 4;
dMemcpy(pBase, dataChar, rowBytes);
stbi_image_free(dataChar);
FrameAllocator::setWaterMark(prevWaterMark);
return true;
}
unsigned char* data = stbi_load(path.getFullPath().c_str(), &x, &y, &n, channels);
bitmap->deleteImage();
GFXFormat format;
switch (channels) {
case 1:
format = GFXFormatA8;
break;
case 2:
format = GFXFormatA8L8;
break;
case 3:
format = GFXFormatR8G8B8;
break;
case 4:
format = GFXFormatR8G8B8A8;
break;
default:
FrameAllocator::setWaterMark(prevWaterMark);
return false;
}
// actually allocate the bitmap space...
bitmap->allocateBitmap(x, y,
false, // don't extrude miplevels...
format); // use determined format...
U8* pBase = (U8*)bitmap->getBits();
U32 rowBytes = bitmap->getByteSize();
dMemcpy(pBase, data, rowBytes);
stbi_image_free(data);
// Check this bitmap for transparency
if (channels == 4)
bitmap->checkForTransparency();
FrameAllocator::setWaterMark(prevWaterMark);
return true;
}
/**
* Write bitmap to an image file.
*
* @param[in] path Destination image file path. File name extension determines image format.
* ".bmp" for Microsoft Bitmap.
* ".hdr" for High Dynamic Range (HDR).
* ".jpg" or ".jpeg" for Joint Photographic Experts Group (JPEG).
* ".png" for Portable Network Graphics (PNG).
* ".tga" for Truevision TGA (TARGA).
*
*
* @param[in] bitmap Source bitmap to encode image from.
* @param compressionLevel Image format specific compression level.
* For JPEG sets the quality level percentage, range 0 to 100.
* Not used for other image formats.
*/
bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel)
{
PROFILE_SCOPE(sWriteSTB);
// get our data to be saved.
U32 width = bitmap->getWidth();
U32 height = bitmap->getHeight();
U32 bytes = bitmap->getBytesPerPixel();
GFXFormat format = bitmap->getFormat();
String ext = path.getExtension();
U32 stride = width * bytes;
// we always have at least 1
U32 comp = 1;
if (format == GFXFormatR8G8B8)
{
comp = 3;
}
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
{
comp = 4;
}
if (ext.equal("png"))
{
stbi_write_png_compression_level = compressionLevel;
if (stbi_write_png(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), 0))
return true;
}
if (ext.equal("tga"))
{
if (stbi_write_tga(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
return true;
}
if (ext.equal("bmp"))
{
if (stbi_write_bmp(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
return true;
}
if (ext.equal("jpg") || ext.equal("jpeg"))
{
if (stbi_write_jpg(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), compressionLevel))
return true;
}
if (ext.equal("hdr"))
{
if (stbi_write_hdr(path.getFullPath().c_str(), width, height, comp, (const F32*)bitmap->getWritableBits()))
return true;
}
return false;
}

View file

@ -1,491 +0,0 @@
//-----------------------------------------------------------------------------
// 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 "core/stream/stream.h"
#include "gfx/bitmap/gBitmap.h"
static bool sReadTGA(Stream &stream, GBitmap *bitmap);
static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
static struct _privateRegisterTGA
{
_privateRegisterTGA()
{
GBitmap::Registration reg;
reg.extensions.push_back( "tga" );
reg.readFunc = sReadTGA;
reg.writeFunc = sWriteTGA;
GBitmap::sRegisterFormat( reg );
}
} sStaticRegisterTGA;
//------------------------------------------------------------------------------
//-------------------------------------- Supplementary I/O
//
enum eImageType
{
TypeNoData = 0,
TypeUncPaletted = 1,
TypeUncTruecolor = 2,
TypeUncGrayscale = 3,
TypeRlePaletted = 9,
TypeRleTruecolor = 10,
TypeRleGrayscale = 11
};
enum ePixelMap
{
MapLowerLeft = 0,
MapLowerRight = 1,
MapUpperLeft = 2,
MapUpperRight = 3,
};
static void tga_write_pixel_to_mem( U8 * dat, U8 img_spec, U32 number,
U32 w, U32 h, U32 pixel, U32 bppOut )
{
// write the pixel to the data regarding how the
// header says the data is ordered.
U32 x, y;
switch( (img_spec & 0x30) >> 4 )
{
case MapLowerRight:
x = w - 1 - (number % w);
y = h - 1 - (number / w);
break;
case MapUpperLeft:
x = number % w;
y = number / w;
break;
case MapUpperRight:
x = w - 1 - (number % w);
y = number / w;
break;
case MapLowerLeft:
default:
x = number % w;
y = h - 1 - (number / w);
break;
}
U32 addy = (y * w + x) * bppOut;
for ( U32 j = 0; j < bppOut; j++ )
dat[addy + j] = (U8)((pixel >> (j * 8)) & 0xFF);
}
static U32 tga_get_pixel( Stream& stream, U8 bppIn,
U8 * colormap, U8 cmapBytesEntry )
{
/* get the image data value out */
U32 tmp_int32 = 0;
for ( U32 j = 0; j < bppIn; j++ )
{
U8 tmp_byte;
if ( !stream.read( &tmp_byte ) )
tmp_int32 = 0;
else
tmp_int32 += tmp_byte << (j * 8);
}
/* byte-order correct the thing */
switch( bppIn )
{
case 2:
tmp_int32 = convertLEndianToHost( (U16)tmp_int32 );
break;
case 3: /* intentional fall-thru */
case 4:
tmp_int32 = convertLEndianToHost( tmp_int32 );
break;
}
U32 tmp_col;
if ( colormap )
{
/* need to look up value to get real color */
tmp_col = 0;
for ( U32 j = 0; j < cmapBytesEntry; j++ )
tmp_col += colormap[cmapBytesEntry * tmp_int32 + j] << (8 * j);
}
else
{
tmp_col = tmp_int32;
}
return tmp_col;
}
static U32 tga_convert_color( U32 pixel, U32 bppIn, U8 alphabits, U32 bppOut )
{
// this is not only responsible for converting from different depths
// to other depths, it also switches BGR to RGB.
// this thing will also premultiply alpha, on a pixel by pixel basis.
U8 r, g, b, a;
switch( bppIn )
{
case 32:
if ( alphabits == 0 )
goto is_24_bit_in_disguise;
// 32-bit to 32-bit -- nop.
break;
case 24:
is_24_bit_in_disguise:
// 24-bit to 32-bit; (only force alpha to full)
pixel |= 0xFF000000;
break;
case 15:
is_15_bit_in_disguise:
r = (U8)(((F32)((pixel & 0x7C00) >> 10)) * 8.2258f);
g = (U8)(((F32)((pixel & 0x03E0) >> 5 )) * 8.2258f);
b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f);
// 15-bit to 32-bit; (force alpha to full)
pixel = 0xFF000000 + (r << 16) + (g << 8) + b;
break;
case 16:
if ( alphabits == 1 )
goto is_15_bit_in_disguise;
// 16-bit to 32-bit; (force alpha to full)
r = (U8)(((F32)((pixel & 0xF800) >> 11)) * 8.2258f);
g = (U8)(((F32)((pixel & 0x07E0) >> 5 )) * 4.0476f);
b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f);
pixel = 0xFF000000 + (r << 16) + (g << 8) + b;
break;
}
// convert the 32-bit pixel from BGR to RGB.
pixel = (pixel & 0xFF00FF00) + ((pixel & 0xFF) << 16) + ((pixel & 0xFF0000) >> 16);
r = pixel & 0x000000FF;
g = (pixel & 0x0000FF00) >> 8;
b = (pixel & 0x00FF0000) >> 16;
a = (pixel & 0xFF000000) >> 24;
// not premultiplied alpha -- multiply.
r = (U8)(((F32)r / 255.0f) * ((F32)a / 255.0f) * 255.0f);
g = (U8)(((F32)g / 255.0f) * ((F32)a / 255.0f) * 255.0f);
b = (U8)(((F32)b / 255.0f) * ((F32)a / 255.0f) * 255.0f);
pixel = r + (g << 8) + (b << 16) + (a << 24);
/* now convert from 32-bit to whatever they want. */
switch( bppOut )
{
case 4:
// 32 to 32 -- nop.
break;
case 3:
// 32 to 24 -- discard alpha.
pixel &= 0x00FFFFFF;
break;
}
return pixel;
}
static bool sReadTGA(Stream &stream, GBitmap *bitmap)
{
PROFILE_SCOPE(sReadTGA);
struct Header
{
U8 idLength; // length of the image_id string below.
U8 cmapType; // paletted image <=> cmapType
U8 imageType; // can be any of the IMG_TYPE constants above.
U16 cmapFirst; //
U16 cmapLength; // how long the colormap is
U8 cmapEntrySize; // how big a palette entry is.
U16 xOrigin; // the x origin of the image in the image data.
U16 yOrigin; // the y origin of the image in the image data.
U16 width; // the width of the image.
U16 height; // the height of the image.
U8 pixelDepth; // the depth of a pixel in the image.
U8 imageDesc; // the image descriptor.
};
// Read header
Header header;
stream.read( &header.idLength );
stream.read( &header.cmapType );
stream.read( &header.imageType );
stream.read( &header.cmapFirst );
stream.read( &header.cmapLength );
stream.read( &header.cmapEntrySize );
stream.read( &header.xOrigin );
stream.read( &header.yOrigin );
stream.read( &header.width );
stream.read( &header.height );
stream.read( &header.pixelDepth );
stream.read( &header.imageDesc );
U32 numPixels = header.width * header.height;
if ( numPixels == 0 )
{
//Con::errorf( "Texture has width and/or height set to 0" );
return false;
}
U8 alphabits = header.imageDesc & 0x0F;
/* seek past the image id, if there is one */
if ( header.idLength )
{
if ( !stream.setPosition( stream.getPosition() + header.idLength ) )
{
//Con::errorf( "Unexpected end of stream encountered" );
return false;
}
}
/* if this is a 'nodata' image, just jump out. */
if ( header.imageType == TypeNoData )
{
//Con::errorf( "Texture contains no data" );
return false;
}
/* deal with the colormap, if there is one. */
U8* colormap = NULL;
U32 cmapBytes = 0;
U8 cmapBytesEntry = 0;
if ( header.cmapType )
{
switch( header.imageType )
{
case TypeUncPaletted:
case TypeRlePaletted:
break;
case TypeUncTruecolor:
case TypeRleTruecolor:
// this should really be an error, but some really old
// crusty targas might actually be like this (created by TrueVision, no less!)
// so, we'll hack our way through it.
break;
case TypeUncGrayscale:
case TypeRleGrayscale:
//Con::errorf( "Found colormap for a grayscale image" );
return false;
}
/* ensure colormap entry size is something we support */
if ( !(header.cmapEntrySize == 15 ||
header.cmapEntrySize == 16 ||
header.cmapEntrySize == 24 ||
header.cmapEntrySize == 32) )
{
//Con::errorf( "Unsupported colormap entry size" );
return false;
}
/* allocate memory for a colormap */
if ( header.cmapEntrySize & 0x07 )
cmapBytesEntry = (((8 - (header.cmapEntrySize & 0x07)) + header.cmapEntrySize) >> 3);
else
cmapBytesEntry = (header.cmapEntrySize >> 3);
cmapBytes = cmapBytesEntry * header.cmapLength;
colormap = new U8[ cmapBytes ];
for ( U32 i = 0; i < header.cmapLength; i++ )
{
/* seek ahead to first entry used */
if ( header.cmapFirst != 0 )
stream.setPosition( stream.getPosition() + header.cmapFirst * cmapBytesEntry );
U32 tmp_int32 = 0;
for ( U32 j = 0; j < cmapBytesEntry; j++ )
{
U8 tmp_byte;
if ( !stream.read( &tmp_byte ) )
{
delete [] colormap;
//Con::errorf( "Bad colormap" );
return false;
}
tmp_int32 += tmp_byte << (j * 8);
}
// byte order correct.
tmp_int32 = convertLEndianToHost( tmp_int32 );
for ( U32 j = 0; j < cmapBytesEntry; j++ )
colormap[i * cmapBytesEntry + j] = (tmp_int32 >> (8 * j)) & 0xFF;
}
}
// compute number of bytes in an image data unit (either index or BGR triple)
U8 inBytesPerPixel = 0;
if ( header.pixelDepth & 0x07 )
inBytesPerPixel = (((8 - (header.pixelDepth & 0x07)) + header.pixelDepth) >> 3);
else
inBytesPerPixel = (header.pixelDepth >> 3);
/* assume that there's one byte per pixel */
if ( inBytesPerPixel == 0 )
inBytesPerPixel = 1;
GFXFormat gfxFmt;
U32 outBytesPerPixel;
switch ( header.pixelDepth )
{
case 32:
gfxFmt = GFXFormatR8G8B8A8;
outBytesPerPixel = 4;
break;
case 24:
default:
gfxFmt = GFXFormatR8G8B8;
outBytesPerPixel = 3;
break;
}
bitmap->allocateBitmap( header.width, header.height, false, gfxFmt );
// compute the true number of bits per pixel
U8 trueBitsPerPixel = header.cmapType ? header.cmapEntrySize : header.pixelDepth;
// Override the number of alpha bits if necessary
// Some apps generate transparent TGAs with alphabits set to 0 in the image descriptor
if ( ( trueBitsPerPixel == 32 ) && ( alphabits == 0 ) )
alphabits = 8;
switch( header.imageType )
{
case TypeUncTruecolor:
case TypeUncGrayscale:
case TypeUncPaletted:
/* FIXME: support grayscale */
for ( U32 i = 0; i < numPixels; i++ )
{
// get the color value.
U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry );
tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel );
// now write the data out.
tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc,
i, header.width, header.height, tmp_col, outBytesPerPixel );
}
break;
case TypeRleTruecolor:
case TypeRleGrayscale:
case TypeRlePaletted:
// FIXME: handle grayscale..
for ( U32 i = 0; i < numPixels; )
{
/* a bit of work to do to read the data.. */
U8 packet_header;
if ( !stream.read( 1, &packet_header ) )
{
// well, just let them fill the rest with null pixels then...
packet_header = 1;
}
if ( packet_header & 0x80 )
{
/* run length packet */
U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry );
tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel );
U8 repcount = (packet_header & 0x7F) + 1;
/* write all the data out */
for ( U32 j = 0; j < repcount; j++ )
{
tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc,
i + j, header.width, header.height, tmp_col, outBytesPerPixel );
}
i += repcount;
}
else
{
/* raw packet */
/* get pixel from file */
U8 repcount = (packet_header & 0x7F) + 1;
for ( U32 j = 0; j < repcount; j++ )
{
U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry );
tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel );
tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc,
i + j, header.width, header.height, tmp_col, outBytesPerPixel );
}
i += repcount;
}
}
break;
default:
//Con::errorf( "Unknown image type" );
delete[] colormap;
return false;
}
delete [] colormap;
// 32-bit tgas have an alpha channel
bitmap->setHasTransparency( header.pixelDepth == 32 );
return true;
}
static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
{
AssertISV(false, "GBitmap::writeTGA - doesn't support writing tga files!");
return false;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -699,7 +699,8 @@ bool GFont::read(Stream& io_rStream)
for(i = 0; i < numSheets; i++)
{
GBitmap *bmp = new GBitmap;
if(!bmp->readBitmap("png", io_rStream))
String path = String::ToString("%s/%s %d %d (%s).png", Con::getVariable("$GUI::fontCacheDirectory"), mFaceName.c_str(), mSize, i, getCharSetName(mCharSet));
if(!bmp->readBitmap("png", path))
{
delete bmp;
return false;
@ -775,8 +776,11 @@ bool GFont::write(Stream& stream)
}
stream.write(mTextureSheets.size());
for(i = 0; i < mTextureSheets.size(); i++)
mTextureSheets[i].getBitmap()->writeBitmap("png", stream);
for (i = 0; i < mTextureSheets.size(); i++)
{
String path = String::ToString("%s/%s %d %d (%s).png", Con::getVariable("$GUI::fontCacheDirectory"), mFaceName.c_str(), mSize, i, getCharSetName(mCharSet));
mTextureSheets[i].getBitmap()->writeBitmap("png", path);
}
stream.write(mCurX);
stream.write(mCurY);
@ -865,20 +869,9 @@ void GFont::exportStrip(const char *fileName, U32 padding, U32 kerning)
// Advance.
curWidth += mCharInfoList[i].width + kerning + 2*padding;
}
// Write the image!
FileStream fs;
fs.open( fileName, Torque::FS::File::Write );
if(fs.getStatus() != Stream::Ok)
{
Con::errorf("GFont::exportStrip - failed to open '%s' for writing.", fileName);
return;
}
// Done!
gb.writeBitmap("png", fs);
gb.writeBitmap("png", fileName);
}
void GFont::setPlatformFont(PlatformFont *inPlatformFont)

View file

@ -246,14 +246,10 @@ U32 GFXTextureObject::getEstimatedSizeInBytes() const
bool GFXTextureObject::dumpToDisk( const String &bmType, const String &path )
{
FileStream stream;
if ( !stream.open( path, Torque::FS::File::Write ) )
return false;
if ( mBitmap )
return mBitmap->writeBitmap( bmType, stream );
return mBitmap->writeBitmap( bmType, path);
GBitmap bitmap( getWidth(), getHeight(), false, getFormat() );
copyToBmp( &bitmap );
return bitmap.writeBitmap( bmType, stream );
return bitmap.writeBitmap( bmType, path);
}

View file

@ -126,89 +126,75 @@ void ScreenShot::capture( GuiCanvas *canvas )
// Open up the file on disk.
dSprintf( filename, 256, "%s.%s", mFilename, "png" );
FileStream fs;
if ( !fs.open( filename, Torque::FS::File::Write ) )
Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename );
// Open a PNG stream for the final image
DeferredPNGWriter pngWriter;
pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0);
// Render each tile to generate a huge screenshot.
for( U32 ty=0; ty < mTiles; ty++ )
//// Render each tile to generate a huge screenshot.
for (U32 ty = 0; ty < mTiles; ty++)
{
for( S32 tx=0; tx < mTiles; tx++ )
for (S32 tx = 0; tx < mTiles; tx++)
{
// Set the current tile offset for tileFrustum().
mCurrTile.set( tx, mTiles - ty - 1 );
mCurrTile.set(tx, mTiles - ty - 1);
// Let the canvas render the scene.
canvas->renderFrame( false );
canvas->renderFrame(false);
// Now grab the current back buffer.
GBitmap *gb = _captureBackBuffer();
GBitmap* gb = _captureBackBuffer();
// The current GFX device couldn't capture the backbuffer or it's unable of doing so.
if (gb == NULL)
return;
// The current GFX device couldn't capture the backbuffer or it's unable of doing so.
if (gb == NULL)
return;
// Copy the captured bitmap into its tile
// within the output bitmap.
const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();
const U8 *inColor = gb->getBits() + inStride * overlapPixels.y;
const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();
const U8* inColor = gb->getBits() + inStride * overlapPixels.y;
const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel();
const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();
const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2;
const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel();
U8 *outColor = outBuffer->getWritableBits() + outOffset;
for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ )
const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();
const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel() * 2;
const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x * 2)) * gb->getBytesPerPixel();
U8 * outColor = outBuffer->getWritableBits() + outOffset;
for (U32 row = 0; row < gb->getHeight() - overlapPixels.y; row++)
{
dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride );
//Grandient blend the left overlap area of this tile over the previous tile left border
dMemcpy(outColor, inColor + inOverlapOffset, inStride - inOverlapStride);
//Grandient blend the left overlap area of this tile over the previous tile left borde
if (tx && !(ty && row < overlapPixels.y))
{
U8 *blendOverlapSrc = (U8*)inColor;
U8 *blendOverlapDst = outColor - inOverlapOffset;
for ( U32 px=0; px < overlapPixels.x; px++)
U8* blendOverlapSrc = (U8*)inColor;
U8* blendOverlapDst = outColor - inOverlapOffset;
for (U32 px = 0; px < overlapPixels.x; px++)
{
F32 blendFactor = (F32)px / (F32)overlapPixels.x;
sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);
sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);
blendOverlapSrc += gb->getBytesPerPixel();
blendOverlapDst += outBuffer->getBytesPerPixel();
}
blendOverlapDst += outBuffer->getBytesPerPixel();
}
}
//Gradient blend against the rows the excess overlap rows already in the buffer
//Gradient blend against the rows the excess overlap rows already in the buffer
if (ty && row < overlapPixels.y)
{
F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y;
U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
U8 *blendDst = outColor;
for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++)
{
sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor);
U8* blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
U8* blendDst = outColor;
for (U32 px = 0; px < gb->getWidth() - overlapPixels.x * 2; px++)
{
sBlendPixelRGB888(blendSrc, blendDst, 1.0 - rowBlendFactor);
blendSrc += gb->getBytesPerPixel();
blendDst += outBuffer->getBytesPerPixel();
}
blendDst += outBuffer->getBytesPerPixel();
}
}
inColor += inStride;
outColor += outStride;
}
delete gb;
}
// Write the captured tile row into the PNG stream
pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y);
}
//Close the PNG stream
pngWriter.end();
outBuffer->writeBitmap("png", filename);
// We captured... clear the flag.
mPending = false;
@ -234,20 +220,11 @@ void ScreenShot::_singleCapture( GuiCanvas *canvas )
char filename[256];
dSprintf( filename, 256, "%s.%s", mFilename, mWriteJPG ? "jpg" : "png" );
// Open up the file on disk.
FileStream fs;
if ( !fs.open( filename, Torque::FS::File::Write ) )
Con::errorf( "ScreenShot::_singleCapture() - Failed to open output file '%s'!", filename );
// Write it and close.
if ( mWriteJPG )
bitmap->writeBitmap( "jpg", filename);
else
{
// Write it and close.
if ( mWriteJPG )
bitmap->writeBitmap( "jpg", fs );
else
bitmap->writeBitmap( "png", fs );
fs.close();
}
bitmap->writeBitmap( "png", filename);
// Cleanup.
delete bitmap;

View file

@ -44,18 +44,12 @@ public:
/// Pushes a new frame into the video stream
bool pushFrame( GBitmap * bitmap )
{
FileStream fs;
String framePath = mPath + String::ToString("%.6u.png", mCurrentFrame);
if ( !fs.open( framePath, Torque::FS::File::Write ) )
{
Con::errorf( "VideoEncoderPNG::pushFrame() - Failed to open output file '%s'!", framePath.c_str() );
return false;
}
//Increment
mCurrentFrame++;
bool result = bitmap->writeBitmap("png", fs, 0);
bool result = bitmap->writeBitmap("png", framePath, 0);
pushProcessedBitmap(bitmap);
return result;
@ -73,4 +67,4 @@ public:
}
};
REGISTER_VIDEO_ENCODER(VideoEncoderPNG, PNG)
REGISTER_VIDEO_ENCODER(VideoEncoderPNG, PNG)

View file

@ -71,7 +71,7 @@ bool blTerrainChunk::read(Stream & stream)
return(false);
mLightmap = new GBitmap();
return mLightmap->readBitmap("png",stream);
return false;//mLightmap->readBitmap("png",stream);
}
bool blTerrainChunk::write(Stream & stream)
@ -82,8 +82,8 @@ bool blTerrainChunk::write(Stream & stream)
if(!mLightmap)
return(false);
if(!mLightmap->writeBitmap("png",stream))
return(false);
//if(!mLightmap->writeBitmap("png",stream))
//return(false);
return(true);
}

View file

@ -67,14 +67,7 @@ bool TerrainBlock::exportHeightMap( const UTF8 *filePath, const String &format )
}
}
FileStream stream;
if ( !stream.open( filePath, Torque::FS::File::Write ) )
{
Con::errorf( "TerrainBlock::exportHeightMap() - Error opening file for writing: %s !", filePath );
return false;
}
if ( !output.writeBitmap( format, stream ) )
if ( !output.writeBitmap( format, filePath) )
{
Con::errorf( "TerrainBlock::exportHeightMap() - Error writing %s: %s !", format.c_str(), filePath );
return false;
@ -120,14 +113,7 @@ bool TerrainBlock::exportLayerMaps( const UTF8 *filePrefix, const String &format
UTF8 filePath[1024];
dSprintf( filePath, 1024, "%s_%d_%s.%s", filePrefix, i, mFile->mMaterials[i]->getInternalName(), format.c_str() );
FileStream stream;
if ( !stream.open( filePath, Torque::FS::File::Write ) )
{
Con::errorf( "TerrainBlock::exportLayerMaps() - Error opening file for writing: %s !", filePath );
return false;
}
if ( !output.writeBitmap( format, stream ) )
if ( !output.writeBitmap( format, filePath) )
{
Con::errorf( "TerrainBlock::exportLayerMaps() - Error writing %s: %s !", format.c_str(), filePath );
return false;

View file

@ -477,16 +477,9 @@ void TerrainBlock::_updateBaseTexture(bool writeToCache)
}
else
{
FileStream stream;
if (!stream.open(_getBaseTexCacheFileName(), Torque::FS::File::Write))
{
mBaseTex = blendTex;
return;
}
GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8A8);
blendTex->copyToBmp(&bitmap);
bitmap.writeBitmap(formatToExtension(mBaseTexFormat), stream);
bitmap.writeBitmap(formatToExtension(mBaseTexFormat), _getBaseTexCacheFileName());
}
}

View file

@ -504,14 +504,10 @@ void TSLastDetail::_update()
String imposterPath = _getDiffuseMapPath();
String normalsPath = _getNormalMapPath();
FileStream stream;
if ( stream.open( imposterPath, Torque::FS::File::Write ) )
destBmp.writeBitmap( "png", stream );
stream.close();
if ( stream.open( normalsPath, Torque::FS::File::Write ) )
destNormal.writeBitmap( "png", stream );
stream.close();
if (!destBmp.writeBitmap("png", imposterPath))
Con::errorf("TSLastDetail::_update() - failed to write imposter %s", imposterPath.c_str());
if (!destNormal.writeBitmap("png", normalsPath))
Con::errorf("TSLastDetail::_update() - failed to write normal %s", normalsPath.c_str());
}
// DEBUG: Some code to force usage of a test image.

View file

@ -316,16 +316,9 @@ void ImposterCapture::_separateAlpha( GBitmap *imposterOut )
if ( 0 )
{
FileStream fs;
if ( fs.open( "./imposterout.png", Torque::FS::File::Write ) )
imposterOut->writeBitmap( "png", fs );
imposterOut->writeBitmap("png", "./imposterout.png");
fs.close();
if ( fs.open( "./temp.png", Torque::FS::File::Write ) )
bmp->writeBitmap( "png", fs );
fs.close();
bmp->writeBitmap("png", "./temp.png");
}
@ -482,26 +475,13 @@ void ImposterCapture::capture( const MatrixF &rotMatrix,
if ( 0 )
{
// Render out the bitmaps for debug purposes.
FileStream fs;
if ( fs.open( "./blackbmp.png", Torque::FS::File::Write ) )
mBlackBmp->writeBitmap( "png", fs );
mBlackBmp->writeBitmap( "png", "./blackbmp.png" );
fs.close();
mWhiteBmp->writeBitmap( "png", "./whitebmp.png" );
if ( fs.open( "./whitebmp.png", Torque::FS::File::Write ) )
mWhiteBmp->writeBitmap( "png", fs );
(*normalMapOut)->writeBitmap( "png", "./normalbmp.png" );
fs.close();
if ( fs.open( "./normalbmp.png", Torque::FS::File::Write ) )
(*normalMapOut)->writeBitmap( "png", fs );
fs.close();
if ( fs.open( "./finalimposter.png", Torque::FS::File::Write ) )
(*imposterOut)->writeBitmap( "png", fs );
fs.close();
(*imposterOut)->writeBitmap( "png", "./finalimposter.png" );
}
}

View file

@ -94,7 +94,7 @@ function ImportAssetWindow::onWake(%this)
//
function isImageFormat(%fileExt)
{
if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tif"))
if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tif") || (%fileExt $= ".psd") || (%fileExt $= ".gif") || (%fileExt $= ".hdr"))
return true;
return false;