mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 22:54:34 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
284
Engine/source/gfx/bitmap/bitmapUtils.cpp
Normal file
284
Engine/source/gfx/bitmap/bitmapUtils.cpp
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "gfx/bitmap/bitmapUtils.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
|
||||
void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
{
|
||||
const U16 *src = (const U16 *) srcMip;
|
||||
U16 *dst = (U16 *) mip;
|
||||
U32 stride = srcHeight != 1 ? srcWidth : 0;
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
|
||||
if (srcWidth != 1)
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
for(U32 x = 0; x < width; x++)
|
||||
{
|
||||
U32 a = src[0];
|
||||
U32 b = src[1];
|
||||
U32 c = src[stride];
|
||||
U32 d = src[stride+1];
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
dst[x] = ((( (a >> 10) + (b >> 10) + (c >> 10) + (d >> 10)) >> 2) << 10) |
|
||||
((( ((a >> 5) & 0x1F) + ((b >> 5) & 0x1F) + ((c >> 5) & 0x1F) + ((d >> 5) & 0x1F)) >> 2) << 5) |
|
||||
((( ((a >> 0) & 0x1F) + ((b >> 0) & 0x1F) + ((c >> 0) & 0x1F) + ((d >> 0) & 0x1F)) >> 2) << 0);
|
||||
#else
|
||||
dst[x] = ((( (a >> 11) + (b >> 11) + (c >> 11) + (d >> 11)) >> 2) << 11) |
|
||||
((( ((a >> 6) & 0x1F) + ((b >> 6) & 0x1F) + ((c >> 6) & 0x1F) + ((d >> 6) & 0x1F)) >> 2) << 6) |
|
||||
((( ((a >> 1) & 0x1F) + ((b >> 1) & 0x1F) + ((c >> 1) & 0x1F) + ((d >> 1) & 0x1F)) >> 2) << 1);
|
||||
#endif
|
||||
src += 2;
|
||||
}
|
||||
src += stride;
|
||||
dst += width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
U32 a = src[0];
|
||||
U32 c = src[stride];
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
dst[y] = ((( (a >> 10) + (c >> 10)) >> 1) << 10) |
|
||||
((( ((a >> 5) & 0x1F) + ((c >> 5) & 0x1f)) >> 1) << 5) |
|
||||
((( ((a >> 0) & 0x1F) + ((c >> 0) & 0x1f)) >> 1) << 0);
|
||||
#else
|
||||
dst[y] = ((( (a >> 11) + (c >> 11)) >> 1) << 11) |
|
||||
((( ((a >> 6) & 0x1f) + ((c >> 6) & 0x1f)) >> 1) << 6) |
|
||||
((( ((a >> 1) & 0x1F) + ((c >> 1) & 0x1f)) >> 1) << 1);
|
||||
#endif
|
||||
src += 1 + stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
{
|
||||
const U8 *src = (const U8 *) srcMip;
|
||||
U8 *dst = (U8 *) mip;
|
||||
U32 stride = srcHeight != 1 ? (srcWidth) * 3 : 0;
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
|
||||
if (srcWidth != 1)
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
for(U32 x = 0; x < width; x++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
|
||||
src += 4;
|
||||
}
|
||||
src += stride; // skip
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src += 4;
|
||||
|
||||
src += stride; // skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapExtrudeRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
{
|
||||
const U8 *src = (const U8 *) srcMip;
|
||||
U8 *dst = (U8 *) mip;
|
||||
U32 stride = srcHeight != 1 ? (srcWidth) * 4 : 0;
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
|
||||
if (srcWidth != 1)
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
for(U32 x = 0; x < width; x++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src += 5;
|
||||
}
|
||||
src += stride; // skip
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src += 5;
|
||||
|
||||
src += stride; // skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width) = bitmapExtrude5551_c;
|
||||
void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGB_c;
|
||||
void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGBA_c;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void bitmapConvertRGB_to_1555_c(U8 *src, U32 pixels)
|
||||
{
|
||||
U16 *dst = (U16 *)src;
|
||||
for(U32 j = 0; j < pixels; j++)
|
||||
{
|
||||
U32 r = src[0] >> 3;
|
||||
U32 g = src[1] >> 3;
|
||||
U32 b = src[2] >> 3;
|
||||
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
*dst++ = 0x8000 | (b << 10) | (g << 5) | (r << 0);
|
||||
#else
|
||||
*dst++ = b | (g << 5) | (r << 10) | 0x8000;
|
||||
#endif
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels) = bitmapConvertRGB_to_1555_c;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void bitmapConvertRGB_to_5551_c(U8 *src, U32 pixels)
|
||||
{
|
||||
U16 *dst = (U16 *)src;
|
||||
for(U32 j = 0; j < pixels; j++)
|
||||
{
|
||||
U32 r = src[0] >> 3;
|
||||
U32 g = src[1] >> 3;
|
||||
U32 b = src[2] >> 3;
|
||||
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
*dst++ = (1 << 15) | (b << 10) | (g << 5) | (r << 0);
|
||||
#else
|
||||
*dst++ = (b << 1) | (g << 6) | (r << 11) | 1;
|
||||
#endif
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels) = bitmapConvertRGB_to_5551_c;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void bitmapConvertRGB_to_RGBX_c( U8 **src, U32 pixels )
|
||||
{
|
||||
const U8 *oldBits = *src;
|
||||
U8 *newBits = new U8[pixels * 4];
|
||||
dMemset( newBits, 0xFF, pixels * 4 ); // This is done to set alpha values -patw
|
||||
|
||||
// Copy the bits over to the new memory
|
||||
for( U32 i = 0; i < pixels; i++ )
|
||||
dMemcpy( &newBits[i * 4], &oldBits[i * 3], sizeof(U8) * 3 );
|
||||
|
||||
// Now hose the old bits
|
||||
delete [] *src;
|
||||
*src = newBits;
|
||||
}
|
||||
|
||||
void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels ) = bitmapConvertRGB_to_RGBX_c;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void bitmapConvertRGBX_to_RGB_c( U8 **src, U32 pixels )
|
||||
{
|
||||
const U8 *oldBits = *src;
|
||||
U8 *newBits = new U8[pixels * 3];
|
||||
|
||||
// Copy the bits over to the new memory
|
||||
for( U32 i = 0; i < pixels; i++ )
|
||||
dMemcpy( &newBits[i * 3], &oldBits[i * 4], sizeof(U8) * 3 );
|
||||
|
||||
// Now hose the old bits
|
||||
delete [] *src;
|
||||
*src = newBits;
|
||||
}
|
||||
|
||||
void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels ) = bitmapConvertRGBX_to_RGB_c;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void bitmapConvertA8_to_RGBA_c( U8 **src, U32 pixels )
|
||||
{
|
||||
const U8 *oldBits = *src;
|
||||
U8 *newBits = new U8[pixels * 4];
|
||||
|
||||
// Zero new bits
|
||||
dMemset( newBits, 0, pixels * 4 );
|
||||
|
||||
// Copy Alpha values
|
||||
for( U32 i = 0; i < pixels; i++ )
|
||||
newBits[i * 4 + 3] = oldBits[i];
|
||||
|
||||
// Now hose the old bits
|
||||
delete [] *src;
|
||||
*src = newBits;
|
||||
}
|
||||
|
||||
void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels ) = bitmapConvertA8_to_RGBA_c;
|
||||
41
Engine/source/gfx/bitmap/bitmapUtils.h
Normal file
41
Engine/source/gfx/bitmap/bitmapUtils.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITMAPUTILS_H_
|
||||
#define _BITMAPUTILS_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
extern void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels);
|
||||
extern void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels);
|
||||
extern void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels );
|
||||
extern void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels );
|
||||
extern void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels );
|
||||
|
||||
void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
|
||||
#endif //_BITMAPUTILS_H_
|
||||
205
Engine/source/gfx/bitmap/ddsFile.h
Normal file
205
Engine/source/gfx/bitmap/ddsFile.h
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DDSFILE_H_
|
||||
#define _DDSFILE_H_
|
||||
|
||||
#ifndef _GFXSTRUCTS_H_
|
||||
#include "gfx/gfxStructs.h"
|
||||
#endif
|
||||
#ifndef _BITSET_H_
|
||||
#include "core/bitSet.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
|
||||
class Stream;
|
||||
class GBitmap;
|
||||
|
||||
|
||||
struct DDSFile
|
||||
{
|
||||
enum DDSFlags
|
||||
{
|
||||
ComplexFlag = BIT(0), ///< Indicates this includes a mipchain, cubemap, or
|
||||
/// volume texture, ie, isn't a plain old bitmap.
|
||||
MipMapsFlag = BIT(1), ///< Indicates we have a mipmap chain in the file.
|
||||
CubeMapFlag = BIT(2), ///< Indicates we are a cubemap. Requires all six faces.
|
||||
VolumeFlag = BIT(3), ///< Indicates we are a volume texture.
|
||||
|
||||
PitchSizeFlag = BIT(4), ///< Cue as to how to interpret our pitchlinear value.
|
||||
LinearSizeFlag = BIT(5), ///< Cue as to how to interpret our pitchlinear value.
|
||||
|
||||
RGBData = BIT(6), ///< Indicates that this is straight out RGBA data.
|
||||
CompressedData = BIT(7), ///< Indicates that this is compressed or otherwise
|
||||
/// exotic data.
|
||||
|
||||
/// These are the flags for which cubemap
|
||||
/// surfaces are included in the file.
|
||||
CubeMap_PosX_Flag = BIT(8),
|
||||
CubeMap_NegX_Flag = BIT(9),
|
||||
CubeMap_PosY_Flag = BIT(10),
|
||||
CubeMap_NegY_Flag = BIT(11),
|
||||
CubeMap_PosZ_Flag = BIT(12),
|
||||
CubeMap_NegZ_Flag = BIT(13),
|
||||
};
|
||||
|
||||
/// The index into mSurfaces for each
|
||||
/// cubemap face.
|
||||
enum
|
||||
{
|
||||
Cubemap_Surface_PosX,
|
||||
Cubemap_Surface_NegX,
|
||||
Cubemap_Surface_PosY,
|
||||
Cubemap_Surface_NegY,
|
||||
Cubemap_Surface_PosZ,
|
||||
Cubemap_Surface_NegZ,
|
||||
Cubemap_Surface_Count,
|
||||
};
|
||||
|
||||
BitSet32 mFlags;
|
||||
U32 mHeight;
|
||||
U32 mWidth;
|
||||
U32 mDepth;
|
||||
U32 mPitchOrLinearSize;
|
||||
U32 mMipMapCount;
|
||||
|
||||
GFXFormat mFormat;
|
||||
U32 mBytesPerPixel; ///< Ignored if we're a compressed texture.
|
||||
U32 mFourCC;
|
||||
String mCacheString;
|
||||
Torque::Path mSourcePath;
|
||||
|
||||
bool mHasTransparency;
|
||||
|
||||
// This is ugly... but it allows us to pass the number of
|
||||
// mips to drop into the ResourceManager loading process.
|
||||
static U32 smDropMipCount;
|
||||
|
||||
struct SurfaceData
|
||||
{
|
||||
SurfaceData()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mMips );
|
||||
}
|
||||
|
||||
~SurfaceData()
|
||||
{
|
||||
// Free our mips!
|
||||
for(S32 i=0; i<mMips.size(); i++)
|
||||
delete[] mMips[i];
|
||||
}
|
||||
|
||||
Vector<U8*> mMips;
|
||||
|
||||
// Helper function to read in a mipchain.
|
||||
bool readMipChain();
|
||||
|
||||
void dumpImage(DDSFile *dds, U32 mip, const char *file);
|
||||
|
||||
/// Helper for reading a mip level.
|
||||
void readNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel, bool skip);
|
||||
|
||||
/// Helper for writing a mip level.
|
||||
void writeNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel);
|
||||
};
|
||||
|
||||
Vector<SurfaceData*> mSurfaces;
|
||||
|
||||
/// Clear all our information; used before reading.
|
||||
void clear();
|
||||
|
||||
/// Reads a DDS file from the stream.
|
||||
bool read(Stream &s, U32 dropMipCount);
|
||||
|
||||
/// Called from read() to read in the DDS header.
|
||||
bool readHeader(Stream &s);
|
||||
|
||||
/// Writes this DDS file to the stream.
|
||||
bool write(Stream &s);
|
||||
|
||||
/// Called from write() to write the DDS header.
|
||||
bool writeHeader(Stream &s);
|
||||
|
||||
/// For our current format etc., what is the size of a surface with the
|
||||
/// given dimensions?
|
||||
U32 getSurfaceSize( U32 mipLevel = 0 ) const { return getSurfaceSize( mHeight, mWidth, mipLevel ); }
|
||||
U32 getSurfaceSize( U32 height, U32 width, U32 mipLevel = 0 ) const;
|
||||
|
||||
// Helper for getting the size in bytes of a compressed DDS texture.
|
||||
static U32 getSizeInBytes( GFXFormat format, U32 height, U32 width, U32 mipLevels );
|
||||
|
||||
/// Returns the total video memory size of the texture
|
||||
/// including all mipmaps and compression settings.
|
||||
U32 getSizeInBytes() const;
|
||||
|
||||
U32 getWidth( U32 mipLevel = 0 ) const { return getMax( U32(1), mWidth >> mipLevel ); }
|
||||
U32 getHeight( U32 mipLevel = 0 ) const { return getMax(U32(1), mHeight >> mipLevel); }
|
||||
U32 getDepth( U32 mipLevel = 0 ) const { return getMax(U32(1), mDepth >> mipLevel); }
|
||||
|
||||
U32 getMipLevels() const { return mMipMapCount; }
|
||||
|
||||
bool getHasTransparency() const { return mHasTransparency; }
|
||||
|
||||
bool isCubemap() const { return mFlags.test( CubeMapFlag ); }
|
||||
|
||||
GFXFormat getFormat() const { return mFormat; }
|
||||
|
||||
U32 getSurfacePitch( U32 mipLevel = 0 ) const;
|
||||
|
||||
const Torque::Path &getSourcePath() const { return mSourcePath; }
|
||||
const String &getTextureCacheString() const { return mCacheString; }
|
||||
|
||||
static Resource<DDSFile> load( const Torque::Path &path, U32 dropMipCount );
|
||||
|
||||
// For debugging fun!
|
||||
static S32 smActiveCopies;
|
||||
|
||||
DDSFile()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mSurfaces );
|
||||
smActiveCopies++;
|
||||
|
||||
mHasTransparency = false;
|
||||
}
|
||||
|
||||
DDSFile( const DDSFile &dds );
|
||||
|
||||
~DDSFile()
|
||||
{
|
||||
smActiveCopies--;
|
||||
|
||||
// Free our surfaces!
|
||||
for(S32 i=0; i<mSurfaces.size(); i++)
|
||||
delete mSurfaces[i];
|
||||
|
||||
mSurfaces.clear();
|
||||
}
|
||||
|
||||
static DDSFile *createDDSFileFromGBitmap( const GBitmap *gbmp );
|
||||
};
|
||||
|
||||
#endif // _DDSFILE_H_
|
||||
922
Engine/source/gfx/bitmap/ddsLoader.cpp
Normal file
922
Engine/source/gfx/bitmap/ddsLoader.cpp
Normal file
|
|
@ -0,0 +1,922 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "gfx/bitmap/ddsFile.h"
|
||||
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "core/util/fourcc.h"
|
||||
#include "console/console.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
|
||||
|
||||
S32 DDSFile::smActiveCopies = 0;
|
||||
U32 DDSFile::smDropMipCount = 0;
|
||||
|
||||
// These were copied from the DX9 docs. The names are changed
|
||||
// from the "real" defines since not all platforms have them.
|
||||
enum DDSSurfaceDescFlags
|
||||
{
|
||||
DDSDCaps = 0x00000001l,
|
||||
DDSDHeight = 0x00000002l,
|
||||
DDSDWidth = 0x00000004l,
|
||||
DDSDPitch = 0x00000008l,
|
||||
DDSDPixelFormat = 0x00001000l,
|
||||
DDSDMipMapCount = 0x00020000l,
|
||||
DDSDLinearSize = 0x00080000l,
|
||||
DDSDDepth = 0x00800000l,
|
||||
};
|
||||
|
||||
enum DDSPixelFormatFlags
|
||||
{
|
||||
DDPFAlphaPixels = 0x00000001,
|
||||
DDPFFourCC = 0x00000004,
|
||||
DDPFRGB = 0x00000040,
|
||||
DDPFLUMINANCE = 0x00020000
|
||||
};
|
||||
|
||||
|
||||
enum DDSCapFlags
|
||||
{
|
||||
DDSCAPSComplex = 0x00000008,
|
||||
DDSCAPSTexture = 0x00001000,
|
||||
DDSCAPSMipMap = 0x00400000,
|
||||
|
||||
DDSCAPS2Cubemap = 0x00000200,
|
||||
DDSCAPS2Cubemap_POSITIVEX = 0x00000400,
|
||||
DDSCAPS2Cubemap_NEGATIVEX = 0x00000800,
|
||||
DDSCAPS2Cubemap_POSITIVEY = 0x00001000,
|
||||
DDSCAPS2Cubemap_NEGATIVEY = 0x00002000,
|
||||
DDSCAPS2Cubemap_POSITIVEZ = 0x00004000,
|
||||
DDSCAPS2Cubemap_NEGATIVEZ = 0x00008000,
|
||||
DDSCAPS2Volume = 0x00200000,
|
||||
};
|
||||
|
||||
#define FOURCC_DXT1 (MakeFourCC('D','X','T','1'))
|
||||
#define FOURCC_DXT2 (MakeFourCC('D','X','T','2'))
|
||||
#define FOURCC_DXT3 (MakeFourCC('D','X','T','3'))
|
||||
#define FOURCC_DXT4 (MakeFourCC('D','X','T','4'))
|
||||
#define FOURCC_DXT5 (MakeFourCC('D','X','T','5'))
|
||||
|
||||
DDSFile::DDSFile( const DDSFile &dds )
|
||||
: mFlags( dds.mFlags ),
|
||||
mHeight( dds.mHeight ),
|
||||
mWidth( dds.mWidth ),
|
||||
mDepth( dds.mDepth ),
|
||||
mPitchOrLinearSize( dds.mPitchOrLinearSize ),
|
||||
mMipMapCount( dds.mMipMapCount ),
|
||||
mFormat( dds.mFormat ),
|
||||
mBytesPerPixel( dds.mBytesPerPixel ),
|
||||
mFourCC( dds.mFourCC ),
|
||||
mCacheString( dds.mCacheString ),
|
||||
mSourcePath( dds.mSourcePath ),
|
||||
mHasTransparency( dds.mHasTransparency )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mSurfaces );
|
||||
smActiveCopies++;
|
||||
|
||||
for ( U32 i=0; i < dds.mSurfaces.size(); i++ )
|
||||
{
|
||||
SurfaceData *surface = NULL;
|
||||
if ( dds.mSurfaces[i] )
|
||||
surface = new SurfaceData;
|
||||
|
||||
mSurfaces.push_back( surface );
|
||||
|
||||
if ( !surface )
|
||||
continue;
|
||||
|
||||
for ( U32 m=0; m < dds.mSurfaces[i]->mMips.size(); m++ )
|
||||
{
|
||||
U32 size = dds.getSurfaceSize( m );
|
||||
surface->mMips.push_back(new U8[size]);
|
||||
dMemcpy( surface->mMips.last(), dds.mSurfaces[i]->mMips[m], size );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DDSFile::clear()
|
||||
{
|
||||
mFlags = 0;
|
||||
mHeight = mWidth = mDepth = mPitchOrLinearSize = mMipMapCount = 0;
|
||||
mFormat = GFXFormatR8G8B8;
|
||||
}
|
||||
|
||||
U32 DDSFile::getSurfacePitch( U32 mipLevel ) const
|
||||
{
|
||||
if(mFlags.test(CompressedData))
|
||||
{
|
||||
U32 sizeMultiple = 0;
|
||||
|
||||
switch(mFormat)
|
||||
{
|
||||
case GFXFormatDXT1:
|
||||
sizeMultiple = 8;
|
||||
break;
|
||||
case GFXFormatDXT2:
|
||||
case GFXFormatDXT3:
|
||||
case GFXFormatDXT4:
|
||||
case GFXFormatDXT5:
|
||||
sizeMultiple = 16;
|
||||
break;
|
||||
default:
|
||||
AssertISV(false, "DDSFile::getPitch - invalid compressed texture format, we only support DXT1-5 right now.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Maybe need to be DWORD aligned?
|
||||
U32 align = getMax(U32(1), getWidth(mipLevel)/4) * sizeMultiple;
|
||||
align += 3; align >>=2; align <<=2;
|
||||
return align;
|
||||
|
||||
}
|
||||
else
|
||||
return getWidth(mipLevel) * mBytesPerPixel;
|
||||
}
|
||||
|
||||
U32 DDSFile::getSurfaceSize( U32 height, U32 width, U32 mipLevel ) const
|
||||
{
|
||||
// Bump by the mip level.
|
||||
height = getMax(U32(1), height >> mipLevel);
|
||||
width = getMax(U32(1), width >> mipLevel);
|
||||
|
||||
if(mFlags.test(CompressedData))
|
||||
{
|
||||
// From the directX docs:
|
||||
// max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
|
||||
|
||||
U32 sizeMultiple = 0;
|
||||
|
||||
switch(mFormat)
|
||||
{
|
||||
case GFXFormatDXT1:
|
||||
sizeMultiple = 8;
|
||||
break;
|
||||
case GFXFormatDXT2:
|
||||
case GFXFormatDXT3:
|
||||
case GFXFormatDXT4:
|
||||
case GFXFormatDXT5:
|
||||
sizeMultiple = 16;
|
||||
break;
|
||||
default:
|
||||
AssertISV(false, "DDSFile::getSurfaceSize - invalid compressed texture format, we only support DXT1-5 right now.");
|
||||
break;
|
||||
}
|
||||
|
||||
return getMax(U32(1), width/4) * getMax(U32(1), height/4) * sizeMultiple;
|
||||
}
|
||||
else
|
||||
{
|
||||
return height * width* mBytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
U32 DDSFile::getSizeInBytes() const
|
||||
{
|
||||
// TODO: This doesn't take mDepth into account, so
|
||||
// it doesn't work right for volume or cubemap textures!
|
||||
|
||||
U32 bytes = 0;
|
||||
for ( U32 i=0; i < mMipMapCount; i++ )
|
||||
bytes += getSurfaceSize( mHeight, mWidth, i );
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
U32 DDSFile::getSizeInBytes( GFXFormat format, U32 height, U32 width, U32 mipLevels )
|
||||
{
|
||||
AssertFatal( format >= GFXFormatDXT1 && format <= GFXFormatDXT5,
|
||||
"DDSFile::getSizeInBytes - Must be a DXT format!" );
|
||||
|
||||
// From the directX docs:
|
||||
// max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
|
||||
|
||||
U32 sizeMultiple = 0;
|
||||
if ( format == GFXFormatDXT1 )
|
||||
sizeMultiple = 8;
|
||||
else
|
||||
sizeMultiple = 16;
|
||||
|
||||
U32 mipHeight, mipWidth;
|
||||
U32 bytes = 0;
|
||||
for ( U32 m=0; m < mipLevels; m++ )
|
||||
{
|
||||
mipHeight = getMax( U32(1), height >> m );
|
||||
mipWidth = getMax( U32(1), width >> m );
|
||||
|
||||
bytes += getMax( U32(1), mipWidth / 4 ) *
|
||||
getMax( U32(1), mipHeight / 4 ) * sizeMultiple;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool DDSFile::readHeader(Stream &s)
|
||||
{
|
||||
U32 tmp;
|
||||
|
||||
// Read the FOURCC
|
||||
s.read(&tmp);
|
||||
|
||||
if(tmp != MakeFourCC('D', 'D', 'S', ' '))
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - unexpected magic number, wanted 'DDS '!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the size of the header.
|
||||
s.read(&tmp);
|
||||
|
||||
if(tmp != 124)
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - incorrect header size. Expected 124 bytes.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read some flags...
|
||||
U32 ddsdFlags;
|
||||
s.read(&ddsdFlags);
|
||||
|
||||
// "Always include DDSD_CAPS, DDSD_PIXELFORMAT, DDSD_WIDTH, DDSD_HEIGHT."
|
||||
if(!(ddsdFlags & (DDSDCaps | DDSDPixelFormat | DDSDWidth | DDSDHeight)))
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - incorrect surface description flags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read height and width (always present)
|
||||
s.read(&mHeight);
|
||||
s.read(&mWidth);
|
||||
|
||||
// Read pitch or linear size.
|
||||
|
||||
// First make sure we have valid flags (either linear size or pitch).
|
||||
if((ddsdFlags & (DDSDLinearSize | DDSDPitch)) == (DDSDLinearSize | DDSDPitch))
|
||||
{
|
||||
// Both are invalid!
|
||||
Con::errorf("DDSFile::readHeader - encountered both DDSD_LINEARSIZE and DDSD_PITCH!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ok, some flags are set, so let's do some reading.
|
||||
s.read(&mPitchOrLinearSize);
|
||||
|
||||
if(ddsdFlags & DDSDLinearSize)
|
||||
{
|
||||
mFlags.set(LinearSizeFlag); // ( mHeight / 4 ) * ( mWidth / 4 ) * DDSSIZE
|
||||
}
|
||||
else if (ddsdFlags & DDSDPitch)
|
||||
{
|
||||
mFlags.set(PitchSizeFlag); // ( mWidth / 4 ) * DDSSIZE ???
|
||||
}
|
||||
else
|
||||
{
|
||||
// Neither set! This appears to be depressingly common.
|
||||
// Con::warnf("DDSFile::readHeader - encountered neither DDSD_LINEARSIZE nor DDSD_PITCH!");
|
||||
}
|
||||
|
||||
// Do we need to read depth? If so, we are a volume texture!
|
||||
s.read(&mDepth);
|
||||
|
||||
if(ddsdFlags & DDSDDepth)
|
||||
{
|
||||
mFlags.set(VolumeFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wipe it if the flag wasn't set!
|
||||
mDepth = 0;
|
||||
}
|
||||
|
||||
// Deal with mips!
|
||||
s.read(&mMipMapCount);
|
||||
|
||||
if(ddsdFlags & DDSDMipMapCount)
|
||||
{
|
||||
mFlags.set(MipMapsFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wipe it if the flag wasn't set!
|
||||
mMipMapCount = 1;
|
||||
}
|
||||
|
||||
// Deal with 11 DWORDS of reserved space (this reserved space brought to
|
||||
// you by DirectDraw and the letters F and U).
|
||||
for(U32 i=0; i<11; i++)
|
||||
s.read(&tmp);
|
||||
|
||||
// Now we're onto the pixel format!
|
||||
s.read(&tmp);
|
||||
|
||||
if(tmp != 32)
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - pixel format chunk has unexpected size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 ddpfFlags;
|
||||
|
||||
s.read(&ddpfFlags);
|
||||
|
||||
// Read the next few values so we can deal with them all in one go.
|
||||
U32 pfFourCC, pfBitCount, pfRMask, pfGMask, pfBMask, pfAlphaMask;
|
||||
|
||||
s.read(&pfFourCC);
|
||||
s.read(&pfBitCount);
|
||||
s.read(&pfRMask);
|
||||
s.read(&pfGMask);
|
||||
s.read(&pfBMask);
|
||||
s.read(&pfAlphaMask);
|
||||
|
||||
// Sanity check flags...
|
||||
if(!(ddpfFlags & (DDPFRGB | DDPFFourCC | DDPFLUMINANCE)))
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - incoherent pixel flags, neither RGB, FourCC, or Luminance!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now let's just dump the header info.
|
||||
if(ddpfFlags & DDPFLUMINANCE)
|
||||
{
|
||||
mFlags.set(RGBData);
|
||||
|
||||
mBytesPerPixel = pfBitCount / 8;
|
||||
|
||||
bool hasAlpha = ddpfFlags & DDPFAlphaPixels;
|
||||
|
||||
mHasTransparency = hasAlpha;
|
||||
|
||||
// Try to match a format.
|
||||
if(hasAlpha)
|
||||
{
|
||||
// If it has alpha it is one of...
|
||||
// GFXFormatA8L8
|
||||
// GFXFormatA4L4
|
||||
|
||||
if(pfBitCount == 16)
|
||||
mFormat = GFXFormatA8L8;
|
||||
else if(pfBitCount == 8)
|
||||
mFormat = GFXFormatA4L4;
|
||||
else
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - unable to match alpha Luminance format!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise it is one of...
|
||||
// GFXFormatL16
|
||||
// GFXFormatL8
|
||||
|
||||
if(pfBitCount == 16)
|
||||
mFormat = GFXFormatL16;
|
||||
else if(pfBitCount == 8)
|
||||
mFormat = GFXFormatL8;
|
||||
else
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - unable to match non-alpha Luminance format!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ddpfFlags & DDPFRGB)
|
||||
{
|
||||
mFlags.set(RGBData);
|
||||
|
||||
//Con::printf("RGB Pixel format of DDS:");
|
||||
//Con::printf(" bitcount = %d (16, 24, 32)", pfBitCount);
|
||||
mBytesPerPixel = pfBitCount / 8;
|
||||
//Con::printf(" red mask = %x", pfRMask);
|
||||
//Con::printf(" green mask = %x", pfGMask);
|
||||
//Con::printf(" blue mask = %x", pfBMask);
|
||||
|
||||
bool hasAlpha = false;
|
||||
|
||||
if(ddpfFlags & DDPFAlphaPixels)
|
||||
{
|
||||
hasAlpha = true;
|
||||
//Con::printf(" alpha mask = %x", pfAlphaMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Con::printf(" no alpha.");
|
||||
}
|
||||
|
||||
mHasTransparency = hasAlpha;
|
||||
|
||||
// Try to match a format.
|
||||
if(hasAlpha)
|
||||
{
|
||||
// If it has alpha it is one of...
|
||||
// GFXFormatR8G8B8A8
|
||||
// GFXFormatR5G5B5A1
|
||||
// GFXFormatA8
|
||||
|
||||
if(pfBitCount == 32)
|
||||
mFormat = GFXFormatR8G8B8A8;
|
||||
else if(pfBitCount == 16)
|
||||
mFormat = GFXFormatR5G5B5A1;
|
||||
else if(pfBitCount == 8)
|
||||
mFormat = GFXFormatA8;
|
||||
else
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - unable to match alpha RGB format!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise it is one of...
|
||||
// GFXFormatR8G8B8
|
||||
// GFXFormatR8G8B8X8
|
||||
// GFXFormatR5G6B5
|
||||
// GFXFormatL8
|
||||
|
||||
if(pfBitCount == 24)
|
||||
mFormat = GFXFormatR8G8B8;
|
||||
else if(pfBitCount == 32)
|
||||
mFormat = GFXFormatR8G8B8X8;
|
||||
else if(pfBitCount == 16)
|
||||
mFormat = GFXFormatR5G6B5;
|
||||
else if(pfBitCount == 8)
|
||||
{
|
||||
// luminance
|
||||
mFormat = GFXFormatL8;
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::errorf("DDSFile::readHeader - unable to match non-alpha RGB format!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sweet, all done.
|
||||
}
|
||||
else if (ddpfFlags & DDPFFourCC)
|
||||
{
|
||||
mHasTransparency = (ddpfFlags & DDPFAlphaPixels);
|
||||
mFlags.set(CompressedData);
|
||||
|
||||
/* Con::printf("FourCC Pixel format of DDS:");
|
||||
Con::printf(" fourcc = '%c%c%c%c'", ((U8*)&pfFourCC)[0], ((U8*)&pfFourCC)[1], ((U8*)&pfFourCC)[2], ((U8*)&pfFourCC)[3]); */
|
||||
|
||||
// Ok, make a format determination.
|
||||
switch(pfFourCC)
|
||||
{
|
||||
case FOURCC_DXT1:
|
||||
mFormat = GFXFormatDXT1;
|
||||
break;
|
||||
case FOURCC_DXT2:
|
||||
mFormat = GFXFormatDXT2;
|
||||
break;
|
||||
case FOURCC_DXT3:
|
||||
mFormat = GFXFormatDXT3;
|
||||
break;
|
||||
case FOURCC_DXT4:
|
||||
mFormat = GFXFormatDXT4;
|
||||
break;
|
||||
case FOURCC_DXT5:
|
||||
mFormat = GFXFormatDXT5;
|
||||
break;
|
||||
default:
|
||||
Con::errorf("DDSFile::readHeader - unknown fourcc = '%c%c%c%c'", ((U8*)&pfFourCC)[0], ((U8*)&pfFourCC)[1], ((U8*)&pfFourCC)[2], ((U8*)&pfFourCC)[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Deal with final caps bits... Is this really necessary?
|
||||
|
||||
U32 caps1, caps2;
|
||||
s.read(&caps1);
|
||||
s.read(&caps2);
|
||||
s.read(&tmp);
|
||||
s.read(&tmp); // More icky reserved space.
|
||||
|
||||
// Screw caps1.
|
||||
// if(!(caps1 & DDSCAPS_TEXTURE)))
|
||||
// {
|
||||
// }
|
||||
|
||||
// Caps2 has cubemap/volume info. Care about that.
|
||||
if(caps2 & DDSCAPS2Cubemap)
|
||||
{
|
||||
mFlags.set(CubeMapFlag);
|
||||
|
||||
// Store the face flags too.
|
||||
if ( caps2 & DDSCAPS2Cubemap_POSITIVEX ) mFlags.set( CubeMap_PosX_Flag );
|
||||
if ( caps2 & DDSCAPS2Cubemap_NEGATIVEX ) mFlags.set( CubeMap_NegX_Flag );
|
||||
if ( caps2 & DDSCAPS2Cubemap_POSITIVEY ) mFlags.set( CubeMap_PosY_Flag );
|
||||
if ( caps2 & DDSCAPS2Cubemap_NEGATIVEY ) mFlags.set( CubeMap_NegY_Flag );
|
||||
if ( caps2 & DDSCAPS2Cubemap_POSITIVEZ ) mFlags.set( CubeMap_PosZ_Flag );
|
||||
if ( caps2 & DDSCAPS2Cubemap_NEGATIVEZ ) mFlags.set( CubeMap_NegZ_Flag );
|
||||
}
|
||||
|
||||
// MS has ANOTHER reserved word here. This one particularly sucks.
|
||||
s.read(&tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DDSFile::read(Stream &s, U32 dropMipCount)
|
||||
{
|
||||
if( !readHeader(s) || mMipMapCount == 0 )
|
||||
{
|
||||
Con::errorf("DDSFile::read - error reading header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're droping mips then make sure we have enough.
|
||||
dropMipCount = getMin( dropMipCount, mMipMapCount - 1 );
|
||||
|
||||
// At this point we know what sort of image we contain. So we should
|
||||
// allocate some buffers, and read it in.
|
||||
|
||||
// How many surfaces are we talking about?
|
||||
if(mFlags.test(CubeMapFlag))
|
||||
{
|
||||
mSurfaces.setSize( Cubemap_Surface_Count );
|
||||
|
||||
for ( U32 i=0; i < Cubemap_Surface_Count; i++ )
|
||||
{
|
||||
// Does the cubemap contain this surface?
|
||||
if ( mFlags.test( CubeMap_PosX_Flag + ( i << 1 ) ) )
|
||||
mSurfaces[i] = new SurfaceData();
|
||||
else
|
||||
{
|
||||
mSurfaces[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load all the mips.
|
||||
for(S32 l=0; l<mMipMapCount; l++)
|
||||
mSurfaces[i]->readNextMip(this, s, mHeight, mWidth, l, l < dropMipCount );
|
||||
}
|
||||
|
||||
}
|
||||
else if (mFlags.test(VolumeFlag))
|
||||
{
|
||||
// Do something with volume
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a plain old texture.
|
||||
|
||||
// First allocate a SurfaceData to stick this in.
|
||||
mSurfaces.push_back(new SurfaceData());
|
||||
|
||||
// Load however many mips there are.
|
||||
for(S32 i=0; i<mMipMapCount; i++)
|
||||
mSurfaces.last()->readNextMip(this, s, mHeight, mWidth, i, i < dropMipCount);
|
||||
|
||||
// Ok, we're done.
|
||||
}
|
||||
|
||||
// If we're dropping mips then fix up the stats.
|
||||
if ( dropMipCount > 0 )
|
||||
{
|
||||
// Fix up the pitch and/or linear size.
|
||||
if( mFlags.test( LinearSizeFlag ) )
|
||||
mPitchOrLinearSize = getSurfaceSize( dropMipCount );
|
||||
else if ( mFlags.test( PitchSizeFlag ) )
|
||||
mPitchOrLinearSize = getSurfacePitch( dropMipCount );
|
||||
else
|
||||
mPitchOrLinearSize = mPitchOrLinearSize; // Do nothing?
|
||||
|
||||
// Now fix up the rest of the
|
||||
mMipMapCount = getMax( (U32)1, mMipMapCount - dropMipCount );
|
||||
mHeight = getHeight( dropMipCount );
|
||||
mWidth = getWidth( dropMipCount );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DDSFile::writeHeader( Stream &s )
|
||||
{
|
||||
// Read the FOURCC
|
||||
s.write( 4, "DDS " );
|
||||
|
||||
U32 tmp = 0;
|
||||
|
||||
// Read the size of the header.
|
||||
s.write( 124 );
|
||||
|
||||
// Read some flags...
|
||||
U32 ddsdFlags = DDSDCaps | DDSDPixelFormat | DDSDWidth | DDSDHeight;
|
||||
|
||||
if ( mFlags.test( CompressedData ) )
|
||||
ddsdFlags |= DDSDLinearSize;
|
||||
else
|
||||
ddsdFlags |= DDSDPitch;
|
||||
|
||||
if ( mMipMapCount > 0 )
|
||||
ddsdFlags |= DDSDMipMapCount;
|
||||
|
||||
s.write( ddsdFlags );
|
||||
|
||||
// Read height and width (always present)
|
||||
s.write( mHeight );
|
||||
s.write( mWidth );
|
||||
|
||||
// Ok, some flags are set, so let's do some reading.
|
||||
s.write( mPitchOrLinearSize );
|
||||
|
||||
// Do we need to read depth? If so, we are a volume texture!
|
||||
s.write( mDepth );
|
||||
|
||||
// Deal with mips!
|
||||
s.write( mMipMapCount );
|
||||
|
||||
// Deal with 11 DWORDS of reserved space (this reserved space brought to
|
||||
// you by DirectDraw and the letters F and U).
|
||||
for(U32 i=0; i<11; i++)
|
||||
s.write( tmp ); // is this right?
|
||||
|
||||
// Now we're onto the pixel format!
|
||||
|
||||
// This is the size, in bits,
|
||||
// of the pixel format data.
|
||||
tmp = 32;
|
||||
s.write( tmp );
|
||||
|
||||
U32 ddpfFlags;
|
||||
|
||||
U32 fourCC = 0;
|
||||
|
||||
if ( mFlags.test( CompressedData ) )
|
||||
{
|
||||
ddpfFlags = DDPFFourCC;
|
||||
if (mFormat == GFXFormatDXT1)
|
||||
fourCC = FOURCC_DXT1;
|
||||
if (mFormat == GFXFormatDXT3)
|
||||
fourCC = FOURCC_DXT3;
|
||||
if (mFormat == GFXFormatDXT5)
|
||||
fourCC = FOURCC_DXT5;
|
||||
}
|
||||
else
|
||||
ddpfFlags = mBytesPerPixel == 4 ? DDPFRGB | DDPFAlphaPixels : DDPFRGB;
|
||||
|
||||
s.write( ddpfFlags );
|
||||
|
||||
// Read the next few values so we can deal with them all in one go.
|
||||
//U32 pfFourCC, pfBitCount, pfRMask, pfGMask, pfBMask, pfAlphaMask;
|
||||
|
||||
s.write( fourCC );
|
||||
s.write( mBytesPerPixel * 8 );
|
||||
s.write( 0x000000FF );
|
||||
s.write( 0x00FF0000 );
|
||||
s.write( 0x0000FF00 );
|
||||
s.write( 0xFF000000 );
|
||||
|
||||
// Deal with final caps bits... Is this really necessary?
|
||||
|
||||
U32 caps1 = DDSCAPSTexture;
|
||||
if ( mMipMapCount > 0 )
|
||||
caps1 |= DDSCAPSComplex | DDSCAPSMipMap;
|
||||
|
||||
tmp = 0;
|
||||
|
||||
s.write( caps1 );
|
||||
s.write( tmp );
|
||||
s.write( tmp );
|
||||
s.write( tmp );// More icky reserved space.
|
||||
|
||||
// MS has ANOTHER reserved word here. This one particularly sucks.
|
||||
s.write( tmp );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DDSFile::write( Stream &s )
|
||||
{
|
||||
if(!writeHeader(s))
|
||||
{
|
||||
Con::errorf("DDSFile::write - error writing header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point we know what sort of image we contain. So we should
|
||||
// allocate some buffers, and read it in.
|
||||
|
||||
// How many surfaces are we talking about?
|
||||
if(mFlags.test(CubeMapFlag))
|
||||
{
|
||||
// Do something with cubemaps.
|
||||
}
|
||||
else if (mFlags.test(VolumeFlag))
|
||||
{
|
||||
// Do something with volume
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a plain old texture.
|
||||
|
||||
// Load however many mips there are.
|
||||
for ( S32 i = 0; i < mMipMapCount; i++ )
|
||||
mSurfaces.last()->writeNextMip(this, s, mHeight, mWidth, i);
|
||||
|
||||
// Ok, we're done.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DDSFile::SurfaceData::dumpImage(DDSFile *dds, U32 mip, const char *file)
|
||||
{
|
||||
GBitmap *foo = new GBitmap(dds->mWidth >> mip, dds->mHeight >> mip, false, dds->mFormat);
|
||||
|
||||
// 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 )
|
||||
{
|
||||
// Write it out.
|
||||
foo->writeBitmap("png", stream);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
delete foo;
|
||||
}
|
||||
|
||||
void DDSFile::SurfaceData::readNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel, bool skip)
|
||||
{
|
||||
U32 size = dds->getSurfaceSize(height, width, mipLevel);
|
||||
|
||||
// If we're skipping this mip then seek forward.
|
||||
if ( skip )
|
||||
s.setPosition( s.getPosition() + size );
|
||||
else
|
||||
{
|
||||
mMips.push_back(new U8[size]);
|
||||
if(!s.read(size, mMips.last()))
|
||||
Con::errorf("DDSFile::SurfaceData::addNextMip - failed to read mip!");
|
||||
}
|
||||
}
|
||||
|
||||
void DDSFile::SurfaceData::writeNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel)
|
||||
{
|
||||
U32 size = dds->getSurfaceSize(height, width, mipLevel);
|
||||
if(!s.write(size, mMips[mipLevel]))
|
||||
Con::errorf("DDSFile::SurfaceData::writeNextMip - failed to write mip!");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<> void *Resource<DDSFile>::create( const Torque::Path &path )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_RES_MANAGER
|
||||
Con::printf( "Resource<DDSFile>::create - [%s]", path.getFullPath().c_str() );
|
||||
#endif
|
||||
|
||||
FileStream stream;
|
||||
|
||||
stream.open( path.getFullPath(), Torque::FS::File::Read );
|
||||
|
||||
if ( stream.getStatus() != Stream::Ok )
|
||||
return NULL;
|
||||
|
||||
DDSFile *retDDS = new DDSFile;
|
||||
|
||||
if( !retDDS->read( stream, DDSFile::smDropMipCount ) )
|
||||
{
|
||||
delete retDDS;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set source file name
|
||||
retDDS->mSourcePath = path;
|
||||
retDDS->mCacheString = Torque::Path::Join( path.getRoot(), ':', path.getPath() );
|
||||
retDDS->mCacheString = Torque::Path::Join( retDDS->mCacheString, '/', path.getFileName() );
|
||||
}
|
||||
|
||||
return retDDS;
|
||||
}
|
||||
|
||||
template<> ResourceBase::Signature Resource<DDSFile>::signature()
|
||||
{
|
||||
return MakeFourCC('D','D','S',' '); // Direct Draw Surface
|
||||
}
|
||||
|
||||
Resource<DDSFile> DDSFile::load( const Torque::Path &path, U32 dropMipCount )
|
||||
{
|
||||
PROFILE_SCOPE( DDSFile_load );
|
||||
|
||||
// HACK: It sucks that we cannot pass parameters into
|
||||
// the resource manager loading system.
|
||||
DDSFile::smDropMipCount = dropMipCount;
|
||||
Resource<DDSFile> ret = ResourceManager::get().load( path );
|
||||
DDSFile::smDropMipCount = 0;
|
||||
|
||||
// Any kind of error checking or path stepping can happen here
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
|
||||
{
|
||||
if( gbmp == NULL )
|
||||
return NULL;
|
||||
|
||||
DDSFile *ret = new DDSFile;
|
||||
|
||||
// Set up the DDSFile properties that matter. Since this is a GBitmap, there
|
||||
// are assumptions that can be made
|
||||
ret->mHeight = gbmp->getHeight();
|
||||
ret->mWidth = gbmp->getWidth();
|
||||
ret->mDepth = 0;
|
||||
ret->mFormat = gbmp->getFormat();
|
||||
ret->mFlags.set(RGBData);
|
||||
ret->mBytesPerPixel = gbmp->getBytesPerPixel();
|
||||
ret->mMipMapCount = gbmp->getNumMipLevels();
|
||||
ret->mHasTransparency = gbmp->getHasTransparency();
|
||||
|
||||
// ASSUMPTION!!!
|
||||
// This _most likely_ does not belong here, but it is safe to assume that if
|
||||
// a GBitmap is 24-bit, and it's being converted to a DDS, it is most likely
|
||||
// going to be either:
|
||||
// a) Uploaded as a 32-bit texture, and just needs to be padded to RGBX
|
||||
// b) Uploaded as a compressed format, and needs to be padded to 32-bits anyway
|
||||
if( ret->mFormat == GFXFormatR8G8B8 )
|
||||
{
|
||||
ret->mFormat = GFXFormatR8G8B8X8;
|
||||
ret->mBytesPerPixel = 4;
|
||||
}
|
||||
|
||||
if( ret->mMipMapCount > 1 )
|
||||
ret->mFlags.set(MipMapsFlag);
|
||||
|
||||
// One surface per GBitmap
|
||||
ret->mSurfaces.push_back( new SurfaceData() );
|
||||
|
||||
// Load the mips
|
||||
for( int i = 0; i < ret->mMipMapCount; i++ )
|
||||
{
|
||||
const U32 mipSz = ret->getSurfaceSize(i);
|
||||
ret->mSurfaces.last()->mMips.push_back( new U8[mipSz] );
|
||||
|
||||
U8 *mipMem = ret->mSurfaces.last()->mMips.last();
|
||||
|
||||
// If this is a straight copy, just do it, otherwise (ugh)
|
||||
if( ret->mFormat == gbmp->getFormat() )
|
||||
dMemcpy( mipMem, gbmp->getBits(i), mipSz );
|
||||
else
|
||||
{
|
||||
// Assumption:
|
||||
AssertFatal( gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert." );
|
||||
|
||||
for( int pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++ )
|
||||
{
|
||||
U8 *dst = &mipMem[pxl * ret->mBytesPerPixel];
|
||||
const U8 *src = &gbmp->getBits(i)[pxl * gbmp->getBytesPerPixel()];
|
||||
dMemcpy( dst, src, gbmp->getBytesPerPixel() * sizeof(U8) );
|
||||
dst[ret->mBytesPerPixel - 1] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment to debug-dump each mip level
|
||||
//ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DefineEngineFunction( getActiveDDSFiles, S32, (),,
|
||||
"Returns the count of active DDSs files in memory.\n"
|
||||
"@ingroup Rendering\n" )
|
||||
{
|
||||
return DDSFile::smActiveCopies;
|
||||
}
|
||||
116
Engine/source/gfx/bitmap/ddsUtils.cpp
Normal file
116
Engine/source/gfx/bitmap/ddsUtils.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "squish/squish.h"
|
||||
#include "gfx/bitmap/ddsFile.h"
|
||||
#include "gfx/bitmap/ddsUtils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// If false is returned, from this method, the source DDS is not modified
|
||||
bool DDSUtil::squishDDS( DDSFile *srcDDS, const GFXFormat dxtFormat )
|
||||
{
|
||||
// Sanity check
|
||||
if( srcDDS->mBytesPerPixel != 4 )
|
||||
{
|
||||
AssertFatal( false, "Squish wants 32-bit source data" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build flags, start with fast compress
|
||||
U32 squishFlags = squish::kColourRangeFit;
|
||||
|
||||
// Flag which format we are using
|
||||
switch( dxtFormat )
|
||||
{
|
||||
case GFXFormatDXT1:
|
||||
squishFlags |= squish::kDxt1;
|
||||
break;
|
||||
|
||||
case GFXFormatDXT2:
|
||||
case GFXFormatDXT3:
|
||||
squishFlags |= squish::kDxt3;
|
||||
break;
|
||||
|
||||
case GFXFormatDXT4:
|
||||
case GFXFormatDXT5:
|
||||
squishFlags |= squish::kDxt5;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal( false, "Assumption failed" );
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
// We got this far, so assume we can finish (gosh I hope so)
|
||||
srcDDS->mFormat = dxtFormat;
|
||||
srcDDS->mFlags.set( DDSFile::CompressedData );
|
||||
|
||||
// If this has alpha, set the flag
|
||||
if( srcDDS->mFormat == GFXFormatR8G8B8A8 )
|
||||
squishFlags |= squish::kWeightColourByAlpha;
|
||||
|
||||
// The source surface is the original surface of the file
|
||||
DDSFile::SurfaceData *srcSurface = srcDDS->mSurfaces.last();
|
||||
|
||||
// Create a new surface, this will be the DXT compressed surface. Once we
|
||||
// are done, we can discard the old surface, and replace it with this one.
|
||||
DDSFile::SurfaceData *newSurface = new DDSFile::SurfaceData();
|
||||
|
||||
for( int i = 0; i < srcDDS->mMipMapCount; i++ )
|
||||
{
|
||||
const U8 *srcBits = srcSurface->mMips[i];
|
||||
|
||||
const U32 mipSz = srcDDS->getSurfaceSize(i);
|
||||
U8 *dstBits = new U8[mipSz];
|
||||
newSurface->mMips.push_back( dstBits );
|
||||
|
||||
PROFILE_START(SQUISH_DXT_COMPRESS);
|
||||
|
||||
// Compress with Squish
|
||||
//
|
||||
// squish::CompressImageOMP will call squish::CompressImage if OpenMP is
|
||||
// not enabled.
|
||||
squish::CompressImageOMP( srcBits, srcDDS->getWidth(i), srcDDS->getHeight(i),
|
||||
dstBits, squishFlags );
|
||||
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
// Now delete the source surface, and return.
|
||||
srcDDS->mSurfaces.pop_back();
|
||||
delete srcSurface;
|
||||
srcDDS->mSurfaces.push_back( newSurface );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DDSUtil::swizzleDDS( DDSFile *srcDDS, const Swizzle<U8, 4> &swizzle )
|
||||
{
|
||||
for( int i = 0; i < srcDDS->mMipMapCount; i++ )
|
||||
{
|
||||
swizzle.InPlace( srcDDS->mSurfaces.last()->mMips[i], srcDDS->getSurfaceSize( i ) );
|
||||
}
|
||||
}
|
||||
34
Engine/source/gfx/bitmap/ddsUtils.h
Normal file
34
Engine/source/gfx/bitmap/ddsUtils.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DDS_UTILS_H_
|
||||
#define _DDS_UTILS_H_
|
||||
|
||||
struct DDSFile;
|
||||
|
||||
namespace DDSUtil
|
||||
{
|
||||
bool squishDDS( DDSFile *srcDDS, const GFXFormat dxtFormat );
|
||||
void swizzleDDS( DDSFile *srcDDS, const Swizzle<U8, 4> &swizzle );
|
||||
};
|
||||
|
||||
#endif
|
||||
1184
Engine/source/gfx/bitmap/gBitmap.cpp
Normal file
1184
Engine/source/gfx/bitmap/gBitmap.cpp
Normal file
File diff suppressed because it is too large
Load diff
327
Engine/source/gfx/bitmap/gBitmap.h
Normal file
327
Engine/source/gfx/bitmap/gBitmap.h
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GBITMAP_H_
|
||||
#define _GBITMAP_H_
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SWIZZLE_H_
|
||||
#include "core/util/swizzle.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GFXENUMS_H_
|
||||
#include "gfx/gfxEnums.h" // For the format
|
||||
#endif
|
||||
|
||||
//-------------------------------------- Forward decls.
|
||||
class Stream;
|
||||
class RectI;
|
||||
class Point2I;
|
||||
class ColorI;
|
||||
class ColorF;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- GBitmap
|
||||
|
||||
class GBitmap
|
||||
{
|
||||
public:
|
||||
enum Constants
|
||||
{
|
||||
/// The maximum mipmap levels we support. The current
|
||||
/// value lets us support up to 4096 x 4096 images.
|
||||
c_maxMipLevels = 13
|
||||
};
|
||||
|
||||
struct Registration
|
||||
{
|
||||
/// The read function prototype.
|
||||
typedef bool(*ReadFunc)(Stream &stream, 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);
|
||||
|
||||
/// Used to sort the registrations so that
|
||||
/// lookups occur in a fixed order.
|
||||
U32 priority;
|
||||
|
||||
Vector<String> extensions; ///< the list of file extensions for this bitmap type [these should be lower case]
|
||||
|
||||
ReadFunc readFunc; ///< the read function to call for this bitmap type
|
||||
WriteFunc writeFunc; ///< the write function to call for this bitmap type
|
||||
U32 defaultCompression; ///< the default compression level [levels are image-specific - see their registration declaration for details]
|
||||
|
||||
Registration()
|
||||
{
|
||||
priority = 0;
|
||||
VECTOR_SET_ASSOCIATION( extensions );
|
||||
}
|
||||
};
|
||||
|
||||
/// Load the given bitmap file. It will try known file
|
||||
/// extensions if one is not specified. If all else fails
|
||||
/// it will look up the folder hierarchy for a match.
|
||||
///
|
||||
/// Important: Don't do something like this...
|
||||
///
|
||||
/// @code
|
||||
/// GBitmap* bitmap; // WRONG TYPE!
|
||||
/// bitmap = GBitmap::load( filename );
|
||||
/// @endcode
|
||||
///
|
||||
/// Resources are reference-counted and the smart pointer conversion will
|
||||
/// release the bitmap and thus render the resulting bitmap pointer invalid!
|
||||
/// The right way is like this:
|
||||
///
|
||||
/// @code
|
||||
/// Resource<GBitmap> bitmap; // Correct!
|
||||
/// bitmap = GBitmap::load( filename );
|
||||
/// @endcode
|
||||
///
|
||||
static Resource<GBitmap> load(const Torque::Path &path);
|
||||
|
||||
protected:
|
||||
|
||||
static Resource<GBitmap> _load(const Torque::Path &path);
|
||||
static Resource<GBitmap> _search(const Torque::Path &path);
|
||||
|
||||
public:
|
||||
GBitmap();
|
||||
GBitmap(const GBitmap&);
|
||||
|
||||
GBitmap(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const bool in_extrudeMipLevels = false,
|
||||
const GFXFormat in_format = GFXFormatR8G8B8 );
|
||||
|
||||
// This builds a GBitmap with the R8G8B8A8 format using the passed in
|
||||
// data (assumes that there is width * height * 4 U8's in data)
|
||||
GBitmap(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const U8* data );
|
||||
|
||||
virtual ~GBitmap();
|
||||
|
||||
|
||||
static void sRegisterFormat( const Registration ® );
|
||||
static const Registration* sFindRegInfo( const String &extension );
|
||||
|
||||
/// Find the first file matching the registered extensions
|
||||
/// skipping the original.
|
||||
static bool sFindFile( const Torque::Path &path, Torque::Path *outPath );
|
||||
|
||||
/// Given a path to a file, try all known extensions. If the file exists on disk, fill in path
|
||||
/// with the correct extension and return true. Otherwise, return false.
|
||||
static bool sFindFiles( const Torque::Path &path, Vector<Torque::Path> *outFoundPaths );
|
||||
|
||||
/// Returns a space separated string of all registered extensions.
|
||||
static String sGetExtensionList();
|
||||
|
||||
void allocateBitmap(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const bool in_extrudeMipLevels = false,
|
||||
const GFXFormat in_format = GFXFormatR8G8B8 );
|
||||
|
||||
void extrudeMipLevels(bool clearBorders = false);
|
||||
void extrudeMipLevelsDetail();
|
||||
|
||||
U32 getNumMipLevels() const { return mNumMipLevels; }
|
||||
|
||||
GBitmap *createPaddedBitmap() const;
|
||||
GBitmap *createPow2Bitmap() const;
|
||||
|
||||
/// Copies a color channel by index into the first channel
|
||||
/// of the output bitmap. The output bitmap must be the same
|
||||
/// dimensions as the source.
|
||||
void copyChannel( U32 index, GBitmap *outBitmap ) const;
|
||||
|
||||
void copyRect(const GBitmap *in, const RectI &srcRect, const Point2I &dstPoint, const U32 srcMipLevel = 0, const U32 dstMipLevel = 0);
|
||||
|
||||
GFXFormat getFormat() const { return mInternalFormat; }
|
||||
bool setFormat(GFXFormat fmt);
|
||||
|
||||
U32 getWidth(const U32 in_mipLevel = 0) const;
|
||||
U32 getHeight(const U32 in_mipLevel = 0) const;
|
||||
U32 getDepth(const U32 in_mipLevel = 0) const;
|
||||
|
||||
U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0);
|
||||
const U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0) const;
|
||||
|
||||
const U8* getBits(const U32 in_mipLevel = 0) const;
|
||||
U8* getWritableBits(const U32 in_mipLevel = 0);
|
||||
|
||||
U32 getByteSize() const { return mByteSize; }
|
||||
U32 getBytesPerPixel() const { return mBytesPerPixel; }
|
||||
|
||||
/// Use these functions to set and get the mHasTransparency value
|
||||
/// This is used to indicate that this bitmap has pixels that have
|
||||
/// an alpha value less than 255 (used by the auto-Material mapper)
|
||||
bool getHasTransparency() const { return mHasTransparency; }
|
||||
void setHasTransparency(bool hasTransparency) { mHasTransparency = hasTransparency; }
|
||||
|
||||
/// In general you will want to use this function if there is not a
|
||||
/// good spot in the bitmap loader(s) to check the alpha value of
|
||||
/// the pixels. This function uses the texture format to loop over
|
||||
/// the bitmap bits and to check for alpha values less than 255
|
||||
bool checkForTransparency();
|
||||
|
||||
ColorF sampleTexel(F32 u, F32 v) const;
|
||||
bool getColor(const U32 x, const U32 y, ColorI& rColor) const;
|
||||
bool setColor(const U32 x, const U32 y, const ColorI& rColor);
|
||||
|
||||
/// This method will combine bitmapA and bitmapB using the operation specified
|
||||
/// by combineOp. The result will be stored in the bitmap that this method is
|
||||
/// called on. The size of the resulting bitmap will be the larger of A and B.
|
||||
/// The format of the resulting bitmap will be the format of A or B, whichever
|
||||
/// has a larger byte size.
|
||||
///
|
||||
/// @note There are some restrictions on ops and formats that will probably change
|
||||
/// based on how we use this function.
|
||||
bool combine( const GBitmap *bitmapA, const GBitmap *bitmapB, const GFXTextureOp combineOp );
|
||||
|
||||
/// Fills the first mip level of the bitmap with the specified color.
|
||||
void fill( const ColorI &rColor );
|
||||
|
||||
/// An optimized version of fill().
|
||||
void fillWhite();
|
||||
|
||||
//-------------------------------------- Internal data/operators
|
||||
|
||||
void deleteImage();
|
||||
|
||||
//-------------------------------------- Input/Output interface
|
||||
|
||||
/// 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 );
|
||||
|
||||
/// 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 );
|
||||
|
||||
bool readMNG(Stream& io_rStream); // located in bitmapMng.cc
|
||||
bool writeMNG(Stream& io_rStream) const;
|
||||
|
||||
bool read(Stream& io_rStream);
|
||||
bool write(Stream& io_rStream) const;
|
||||
|
||||
template<class T, dsize_t mapLength>
|
||||
void swizzle(const Swizzle<T,mapLength> *s);
|
||||
|
||||
static Vector<Registration> sRegistrations;
|
||||
|
||||
private:
|
||||
GFXFormat mInternalFormat;
|
||||
|
||||
U8* mBits; // Master bytes
|
||||
U32 mByteSize;
|
||||
U32 mWidth;
|
||||
U32 mHeight;
|
||||
U32 mBytesPerPixel;
|
||||
|
||||
U32 mNumMipLevels;
|
||||
U32 mMipLevelOffsets[c_maxMipLevels];
|
||||
|
||||
bool mHasTransparency;
|
||||
|
||||
static const U32 csFileVersion;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Inlines
|
||||
//
|
||||
|
||||
inline U32 GBitmap::getWidth(const U32 in_mipLevel) const
|
||||
{
|
||||
AssertFatal(in_mipLevel < mNumMipLevels,
|
||||
avar("GBitmap::getWidth: mip level out of range: (%d, %d)",
|
||||
in_mipLevel, mNumMipLevels));
|
||||
|
||||
U32 retVal = mWidth >> in_mipLevel;
|
||||
|
||||
return (retVal != 0) ? retVal : 1;
|
||||
}
|
||||
|
||||
inline U32 GBitmap::getHeight(const U32 in_mipLevel) const
|
||||
{
|
||||
AssertFatal(in_mipLevel < mNumMipLevels,
|
||||
avar("Bitmap::getHeight: mip level out of range: (%d, %d)",
|
||||
in_mipLevel, mNumMipLevels));
|
||||
|
||||
U32 retVal = mHeight >> in_mipLevel;
|
||||
|
||||
return (retVal != 0) ? retVal : 1;
|
||||
}
|
||||
|
||||
inline const U8* GBitmap::getBits(const U32 in_mipLevel) const
|
||||
{
|
||||
AssertFatal(in_mipLevel < mNumMipLevels,
|
||||
avar("GBitmap::getBits: mip level out of range: (%d, %d)",
|
||||
in_mipLevel, mNumMipLevels));
|
||||
|
||||
return &mBits[mMipLevelOffsets[in_mipLevel]];
|
||||
}
|
||||
|
||||
inline U8* GBitmap::getWritableBits(const U32 in_mipLevel)
|
||||
{
|
||||
AssertFatal(in_mipLevel < mNumMipLevels,
|
||||
avar("GBitmap::getWritableBits: mip level out of range: (%d, %d)",
|
||||
in_mipLevel, mNumMipLevels));
|
||||
|
||||
return &mBits[mMipLevelOffsets[in_mipLevel]];
|
||||
}
|
||||
|
||||
inline U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel)
|
||||
{
|
||||
return (getWritableBits(mipLevel) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel);
|
||||
}
|
||||
|
||||
inline const U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel) const
|
||||
{
|
||||
return (getBits(mipLevel) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel);
|
||||
}
|
||||
|
||||
template<class T, dsize_t mapLength>
|
||||
void GBitmap::swizzle(const Swizzle<T,mapLength> *s )
|
||||
{
|
||||
const U32 memSize = getWidth() * getHeight() * mBytesPerPixel;
|
||||
|
||||
void *b = dMalloc(memSize);
|
||||
|
||||
s->ToBuffer(b, getWritableBits(), memSize);
|
||||
|
||||
dMemcpy(getWritableBits(), b, memSize);
|
||||
|
||||
dFree(b);
|
||||
}
|
||||
|
||||
#endif //_GBITMAP_H_
|
||||
245
Engine/source/gfx/bitmap/loaders/bitmapBmp.cpp
Normal file
245
Engine/source/gfx/bitmap/loaders/bitmapBmp.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
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(int 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;
|
||||
}
|
||||
231
Engine/source/gfx/bitmap/loaders/bitmapGif.cpp
Normal file
231
Engine/source/gfx/bitmap/loaders/bitmapGif.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/util/path.h"
|
||||
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
// This must come after our headers due to a conflicting definition of VoidPtr
|
||||
#include "lungif/gif_lib.h"
|
||||
|
||||
|
||||
using namespace Torque;
|
||||
|
||||
static bool sReadGIF(Stream &stream, GBitmap *bitmap);
|
||||
static bool sWriteGIF(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
|
||||
static struct _privateRegisterGIF
|
||||
{
|
||||
_privateRegisterGIF()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
|
||||
reg.extensions.push_back( "gif" );
|
||||
|
||||
reg.readFunc = sReadGIF;
|
||||
reg.writeFunc = sWriteGIF;
|
||||
|
||||
GBitmap::sRegisterFormat( reg );
|
||||
}
|
||||
} sStaticRegisterGIF;
|
||||
|
||||
//-------------------------------------- Replacement I/O for standard LIBjpeg
|
||||
// functions. we don't wanna use
|
||||
// FILE*'s...
|
||||
static int gifReadDataFn(GifFileType *gifinfo, GifByteType *data, int length)
|
||||
{
|
||||
Stream *stream = (Stream*)gifinfo->UserData;
|
||||
AssertFatal(stream != NULL, "gifReadDataFn::No stream.");
|
||||
int pos = stream->getPosition();
|
||||
if (stream->read(length, data))
|
||||
return length;
|
||||
|
||||
if (stream->getStatus() == Stream::EOS)
|
||||
return (stream->getPosition()-pos);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
#if 0
|
||||
// CodeReview - until we can write these, get rid of warning by disabling method.
|
||||
static int gifWriteDataFn(GifFileType *gifinfo, GifByteType *data, int length)
|
||||
{
|
||||
Stream *stream = (Stream*)gifinfo->UserData;
|
||||
AssertFatal(stream != NULL, "gifWriteDataFn::No stream.");
|
||||
if (stream->write(length, data))
|
||||
return length;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------
|
||||
static bool sReadGIF( Stream &stream, GBitmap *bitmap )
|
||||
{
|
||||
GifFileType *gifinfo = DGifOpen( (void*)&stream, gifReadDataFn);
|
||||
if (!gifinfo)
|
||||
return false;
|
||||
|
||||
GifRecordType recordType;
|
||||
do
|
||||
{
|
||||
if (DGifGetRecordType(gifinfo, &recordType) == GIF_ERROR)
|
||||
break;
|
||||
|
||||
if (recordType == IMAGE_DESC_RECORD_TYPE)
|
||||
{
|
||||
if (DGifGetImageDesc(gifinfo) == GIF_ERROR)
|
||||
break;
|
||||
|
||||
GFXFormat format = (gifinfo->SBackGroundColor == 0 ) ? GFXFormatR8G8B8 : GFXFormatR8G8B8A8;
|
||||
bitmap->allocateBitmap(gifinfo->SWidth, gifinfo->SHeight, false, format);
|
||||
|
||||
// Assume no transparency until proven otherwise
|
||||
bitmap->setHasTransparency(false);
|
||||
|
||||
U32 gwidth = gifinfo->Image.Width ? gifinfo->Image.Width : bitmap->getWidth();
|
||||
U32 gheight= gifinfo->Image.Height ? gifinfo->Image.Height : bitmap->getHeight();
|
||||
U32 gifSize = gwidth * gheight;
|
||||
U8 *data = new U8[gifSize];
|
||||
|
||||
if (DGifGetLine(gifinfo, data, gifSize) != GIF_ERROR)
|
||||
{
|
||||
// use the global or local color table ?
|
||||
GifColorType *color = NULL;
|
||||
if (gifinfo->Image.ColorMap)
|
||||
color = gifinfo->Image.ColorMap->Colors;
|
||||
else if (gifinfo->SColorMap)
|
||||
color = gifinfo->SColorMap->Colors;
|
||||
|
||||
if (color)
|
||||
{
|
||||
U8 *dst = bitmap->getAddress(gifinfo->Image.Left, gifinfo->Image.Top);
|
||||
U8 *src = data;
|
||||
U32 right = gifinfo->Image.Left + gwidth;
|
||||
U32 bottom = gifinfo->Image.Top + gheight;
|
||||
U32 next = (bitmap->getWidth() - gwidth) * bitmap->getBytesPerPixel();
|
||||
|
||||
if (format == GFXFormatR8G8B8A8)
|
||||
{
|
||||
for (U32 y=gifinfo->Image.Top; y<bottom; y++)
|
||||
{
|
||||
for (U32 x=gifinfo->Image.Left; x<right; x++, src++)
|
||||
{
|
||||
if (*src == gifinfo->SBackGroundColor)
|
||||
{
|
||||
// this is a transparent pixel
|
||||
dst[0] = 0; // red
|
||||
dst[1] = 0; // green
|
||||
dst[2] = 0; // blue
|
||||
dst[3] = 0; // alpha
|
||||
|
||||
bitmap->setHasTransparency(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = color[*src].Red;
|
||||
dst[1] = color[*src].Green;
|
||||
dst[2] = color[*src].Blue;
|
||||
dst[3] = 0; // alpha
|
||||
}
|
||||
dst += bitmap->getBytesPerPixel();
|
||||
}
|
||||
dst += next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (U32 y=gifinfo->Image.Top; y<bottom; y++)
|
||||
{
|
||||
for (U32 x=gifinfo->Image.Left; x<right; x++, src++)
|
||||
{
|
||||
dst[0] = color[*src].Red;
|
||||
dst[1] = color[*src].Green;
|
||||
dst[2] = color[*src].Blue;
|
||||
dst += bitmap->getBytesPerPixel();
|
||||
}
|
||||
dst += next;
|
||||
}
|
||||
}
|
||||
delete [] data;
|
||||
DGifCloseFile(gifinfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// failure
|
||||
delete [] data;
|
||||
break;
|
||||
}
|
||||
else if (recordType == EXTENSION_RECORD_TYPE)
|
||||
{
|
||||
GifByteType *extension;
|
||||
S32 extCode;
|
||||
|
||||
// Skip any extension blocks in file
|
||||
if (DGifGetExtension(gifinfo, &extCode, &extension) != GIF_ERROR)
|
||||
{
|
||||
while (extension != NULL)
|
||||
{
|
||||
if (DGifGetExtensionNext(gifinfo, &extension) == GIF_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// There used to be a break right here. This caused the while condition to
|
||||
// never get processed, and so it never looped through all the records in
|
||||
// the GIF. I took a quick peek back at TGB and TGE histories and I am not
|
||||
// sure where this change got made, but I can't figure out why the loading
|
||||
// worked at all, ever, with that break in there. The only case I can think
|
||||
// of is if the first record in the GIF was the bitmap data.
|
||||
// [6/6/2007 Pat]
|
||||
|
||||
}while (recordType != TERMINATE_RECORD_TYPE);
|
||||
|
||||
|
||||
DGifCloseFile(gifinfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool sWriteGIF(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
TORQUE_UNUSED( bitmap );
|
||||
TORQUE_UNUSED( stream );
|
||||
TORQUE_UNUSED( compressionLevel );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
250
Engine/source/gfx/bitmap/loaders/bitmapJpeg.cpp
Normal file
250
Engine/source/gfx/bitmap/loaders/bitmapJpeg.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 int jpegReadDataFn(void *client_data, unsigned char *data, int length)
|
||||
{
|
||||
Stream *stream = (Stream*)client_data;
|
||||
AssertFatal(stream != NULL, "jpegReadDataFn::No stream.");
|
||||
int pos = stream->getPosition();
|
||||
if (stream->read(length, data))
|
||||
return length;
|
||||
|
||||
if (stream->getStatus() == Stream::EOS)
|
||||
return (stream->getPosition()-pos);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static int jpegWriteDataFn(void *client_data, unsigned char *data, int length)
|
||||
{
|
||||
Stream *stream = (Stream*)client_data;
|
||||
AssertFatal(stream != NULL, "jpegWriteDataFn::No stream.");
|
||||
if (stream->write(length, data))
|
||||
return length;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static int jpegFlushDataFn(void *)
|
||||
{
|
||||
// do nothing since we can't flush the stream object
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static int 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)
|
||||
{
|
||||
JFREAD = jpegReadDataFn;
|
||||
JFERROR = jpegErrorFn;
|
||||
|
||||
jpeg_decompress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
|
||||
// We set up the normal JPEG error routines, then override error_exit.
|
||||
//cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
//jerr.pub.error_exit = my_error_exit;
|
||||
|
||||
// if (setjmp(jerr.setjmp_buffer))
|
||||
// {
|
||||
// // If we get here, the JPEG code has signaled an error.
|
||||
// // We need to clean up the JPEG object, close the input file, and return.
|
||||
// jpeg_destroy_decompress(&cinfo);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines.
|
||||
cinfo.client_data = (void*)&stream; // set the stream into the client_data
|
||||
|
||||
// Now we can initialize the JPEG decompression object.
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
jpeg_stdio_src(&cinfo);
|
||||
|
||||
// Read file header, set default decompression parameters
|
||||
jpeg_read_header(&cinfo, true);
|
||||
|
||||
GFXFormat format;
|
||||
switch (cinfo.out_color_space)
|
||||
{
|
||||
case JCS_GRAYSCALE: format = GFXFormatA8; break;
|
||||
case JCS_RGB: format = GFXFormatR8G8B8; break;
|
||||
default:
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start decompressor
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
// allocate the bitmap space and init internal variables...
|
||||
bitmap->allocateBitmap(cinfo.output_width, cinfo.output_height, false, format);
|
||||
|
||||
// Set up the row pointers...
|
||||
U32 rowBytes = cinfo.output_width * cinfo.output_components;
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
for (U32 i = 0; i < bitmap->getHeight(); i++)
|
||||
{
|
||||
JSAMPROW rowPointer = pBase + (i * rowBytes);
|
||||
jpeg_read_scanlines(&cinfo, &rowPointer, 1);
|
||||
}
|
||||
|
||||
// Finish decompression
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
// Release JPEG decompression object
|
||||
// This is an important step since it will release a good deal of memory.
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
// We know JPEG's don't have any transparency
|
||||
bitmap->setHasTransparency(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
TORQUE_UNUSED(compressionLevel); // compression level not currently hooked up
|
||||
|
||||
GFXFormat format = bitmap->getFormat();
|
||||
|
||||
// JPEG format does not support transparency so any image
|
||||
// in Alpha format should be saved as a grayscale which coincides
|
||||
// with how the readJPEG function will read-in a JPEG. So the
|
||||
// only formats supported are RGB and Alpha, not RGBA.
|
||||
AssertFatal(format == GFXFormatR8G8B8 || format == GFXFormatA8,
|
||||
"GBitmap::writeJPEG: ONLY RGB bitmap writing supported at this time.");
|
||||
if (format != GFXFormatR8G8B8 && format != GFXFormatA8)
|
||||
return false;
|
||||
|
||||
// maximum image size allowed
|
||||
#define MAX_HEIGHT 4096
|
||||
if (bitmap->getHeight() > MAX_HEIGHT)
|
||||
return false;
|
||||
|
||||
// Bind our own stream writing, error, and memory flush functions
|
||||
// to the jpeg library interface
|
||||
JFWRITE = jpegWriteDataFn;
|
||||
JFFLUSH = jpegFlushDataFn;
|
||||
JFERROR = jpegErrorFn;
|
||||
|
||||
// Allocate and initialize our jpeg compression structure and error manager
|
||||
jpeg_compress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines.
|
||||
cinfo.client_data = (void*)&stream; // set the stream into the client_data
|
||||
jpeg_create_compress(&cinfo); // allocates a small amount of memory
|
||||
|
||||
// specify the destination for the compressed data(our stream)
|
||||
jpeg_stdio_dest(&cinfo);
|
||||
|
||||
// set the image properties
|
||||
cinfo.image_width = bitmap->getWidth(); // image width
|
||||
cinfo.image_height = bitmap->getHeight(); // image height
|
||||
cinfo.input_components = bitmap->getBytesPerPixel(); // samples per pixel(RGB:3, Alpha:1)
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case GFXFormatA8: // no alpha support in JPEG format, so turn it into a grayscale
|
||||
cinfo.in_color_space = JCS_GRAYSCALE;
|
||||
break;
|
||||
case GFXFormatR8G8B8: // otherwise we are writing in RGB format
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
break;
|
||||
default:
|
||||
AssertFatal( false, "Format not handled in GBitmap::writeJPEG() switch" );
|
||||
break;
|
||||
}
|
||||
// use default compression params(75% compression)
|
||||
jpeg_set_defaults(&cinfo);
|
||||
|
||||
// begin JPEG compression cycle
|
||||
jpeg_start_compress(&cinfo, true);
|
||||
|
||||
// Set up the row pointers...
|
||||
U32 rowBytes = cinfo.image_width * cinfo.input_components;
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
for (U32 i = 0; i < bitmap->getHeight(); i++)
|
||||
{
|
||||
// write the image data
|
||||
JSAMPROW rowPointer = pBase + (i * rowBytes);
|
||||
jpeg_write_scanlines(&cinfo, &rowPointer, 1);
|
||||
}
|
||||
|
||||
// complete the compression cycle
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
// release the JPEG compression object
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
421
Engine/source/gfx/bitmap/loaders/bitmapMng.cpp
Normal file
421
Engine/source/gfx/bitmap/loaders/bitmapMng.cpp
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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"
|
||||
|
||||
#include "core/color.h"
|
||||
|
||||
#define MNG_NO_CMS
|
||||
#define MNG_SUPPORT_READ
|
||||
#define MNG_SUPPORT_WRITE
|
||||
#define MNG_SUPPORT_DISPLAY
|
||||
#define MNG_STORE_CHUNKS
|
||||
#define MNG_ACCESS_CHUNKS
|
||||
|
||||
#include "lmng/libmng.h"
|
||||
|
||||
|
||||
|
||||
static bool sReadMNG(Stream &stream, GBitmap *bitmap);
|
||||
static bool sWriteMNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
|
||||
static struct _privateRegisterMNG
|
||||
{
|
||||
_privateRegisterMNG()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
|
||||
reg.extensions.push_back( "jng" );
|
||||
reg.extensions.push_back( "mng" );
|
||||
|
||||
reg.readFunc = sReadMNG;
|
||||
reg.writeFunc = sWriteMNG;
|
||||
|
||||
GBitmap::sRegisterFormat( reg );
|
||||
}
|
||||
} sStaticRegisterMNG;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GBitmap* image;
|
||||
Stream* stream;
|
||||
} mngstuff;
|
||||
|
||||
static mng_ptr mngMallocFn(mng_size_t size)
|
||||
{
|
||||
mng_ptr data = dMalloc(size);
|
||||
return dMemset(data, 0, size);
|
||||
}
|
||||
|
||||
static void mngFreeFn(mng_ptr p, mng_size_t size)
|
||||
{
|
||||
dFree(p);
|
||||
}
|
||||
|
||||
static mng_bool mngOpenDataFn(mng_handle mng)
|
||||
{
|
||||
return MNG_TRUE;
|
||||
}
|
||||
|
||||
static mng_bool mngCloseDataFn(mng_handle mng)
|
||||
{
|
||||
return MNG_TRUE;
|
||||
}
|
||||
|
||||
static mng_bool mngReadDataFn(mng_handle mng, mng_ptr data, mng_uint32 length, mng_uint32 *bytesread)
|
||||
{
|
||||
mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
|
||||
AssertFatal(mymng->stream != NULL, "No stream?");
|
||||
|
||||
bool success = mymng->stream->read(length, data);
|
||||
*bytesread = length; // stupid hack
|
||||
|
||||
AssertFatal(success, "MNG read catastrophic error!");
|
||||
if(success)
|
||||
return MNG_TRUE;
|
||||
else
|
||||
return MNG_FALSE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// CodeReview - until we can write these, get rid of warning by disabling method.
|
||||
static mng_bool mngWriteDataFn(mng_handle mng, mng_ptr data, mng_uint32 length, mng_uint32 *iWritten)
|
||||
{
|
||||
mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
|
||||
AssertFatal(mymng->stream != NULL, "No stream?");
|
||||
|
||||
bool success = mymng->stream->write(length, data);
|
||||
*iWritten = length; // stupid hack
|
||||
|
||||
AssertFatal(success, "MNG write catastrophic error!");
|
||||
if(success)
|
||||
return MNG_TRUE;
|
||||
else
|
||||
return MNG_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static mng_bool mngProcessHeaderFn(mng_handle mng, mng_uint32 width, mng_uint32 height)
|
||||
{
|
||||
mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
|
||||
|
||||
GFXFormat format;
|
||||
mng_uint8 colorType = mng_get_colortype(mng);
|
||||
mng_uint8 alphaDepth = mng_get_alphadepth(mng);
|
||||
switch(colorType)
|
||||
{
|
||||
case MNG_COLORTYPE_GRAY:
|
||||
case MNG_COLORTYPE_JPEGGRAY:
|
||||
format = GFXFormatR8G8B8;
|
||||
mng_set_canvasstyle(mng, MNG_CANVAS_RGB8);
|
||||
break;
|
||||
|
||||
case MNG_COLORTYPE_INDEXED:
|
||||
if(alphaDepth >= 1)
|
||||
{
|
||||
format = GFXFormatR8G8B8A8;
|
||||
mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
|
||||
}
|
||||
else
|
||||
{
|
||||
format = GFXFormatR8G8B8;
|
||||
mng_set_canvasstyle(mng, MNG_CANVAS_RGB8);
|
||||
}
|
||||
|
||||
case MNG_COLORTYPE_RGB:
|
||||
case MNG_COLORTYPE_JPEGCOLOR:
|
||||
if(alphaDepth >= 1)
|
||||
{
|
||||
format = GFXFormatR8G8B8A8;
|
||||
mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
|
||||
}
|
||||
else
|
||||
{
|
||||
format = GFXFormatR8G8B8;
|
||||
mng_set_canvasstyle(mng, MNG_CANVAS_RGB8);
|
||||
}
|
||||
break;
|
||||
|
||||
case MNG_COLORTYPE_RGBA:
|
||||
case MNG_COLORTYPE_JPEGCOLORA:
|
||||
format = GFXFormatR8G8B8A8;
|
||||
mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
|
||||
break;
|
||||
|
||||
default:
|
||||
// This case should never get hit, however it resolves a compiler
|
||||
// warning
|
||||
format = GFXFormat_FIRST;
|
||||
AssertISV( false, "Unknown color format in bitmap MNG Loading" );
|
||||
}
|
||||
|
||||
mymng->image->allocateBitmap(width, height, false, format);
|
||||
return MNG_TRUE;
|
||||
}
|
||||
|
||||
static mng_ptr mngCanvasLineFn(mng_handle mng, mng_uint32 line)
|
||||
{
|
||||
mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
|
||||
return (mng_ptr) mymng->image->getAddress(0, line);
|
||||
}
|
||||
|
||||
static mng_bool mngRefreshFn(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)
|
||||
{
|
||||
return MNG_TRUE;
|
||||
}
|
||||
|
||||
static mng_uint32 mngGetTicksFn(mng_handle mng)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static mng_bool mngSetTimerFn(mng_handle mng, mng_uint32 msecs)
|
||||
{
|
||||
return MNG_TRUE;
|
||||
}
|
||||
|
||||
static mng_bool mngFatalErrorFn(mng_handle mng, mng_int32 code, mng_int8 severity, mng_chunkid chunktype, mng_uint32 chunkseq, mng_int32 extra1, mng_int32 extra2, mng_pchar text)
|
||||
{
|
||||
mng_cleanup(&mng);
|
||||
|
||||
AssertISV(false, avar("Error reading MNG file:\n %s", (const char*)text));
|
||||
return MNG_FALSE;
|
||||
}
|
||||
|
||||
static bool sReadMNG(Stream &stream, GBitmap *bitmap)
|
||||
{
|
||||
mngstuff mnginfo;
|
||||
dMemset(&mnginfo, 0, sizeof(mngstuff));
|
||||
|
||||
mng_handle mng = mng_initialize(&mnginfo, mngMallocFn, mngFreeFn, MNG_NULL);
|
||||
if(mng == NULL)
|
||||
return false;
|
||||
|
||||
// setup the callbacks
|
||||
mng_setcb_errorproc(mng, mngFatalErrorFn);
|
||||
mng_setcb_openstream(mng, mngOpenDataFn);
|
||||
mng_setcb_closestream(mng, mngCloseDataFn);
|
||||
mng_setcb_readdata(mng, mngReadDataFn);
|
||||
mng_setcb_processheader(mng, mngProcessHeaderFn);
|
||||
mng_setcb_getcanvasline(mng, mngCanvasLineFn);
|
||||
mng_setcb_refresh(mng, mngRefreshFn);
|
||||
mng_setcb_gettickcount(mng, mngGetTicksFn);
|
||||
mng_setcb_settimer(mng, mngSetTimerFn);
|
||||
|
||||
mnginfo.image = bitmap;
|
||||
mnginfo.stream = &stream;
|
||||
|
||||
mng_read(mng);
|
||||
mng_display(mng);
|
||||
|
||||
// hacks :(
|
||||
// libmng doesn't support returning data in gray/gray alpha format,
|
||||
// so we grab as RGB/RGBA and just cut off the g and b
|
||||
mng_uint8 colorType = mng_get_colortype(mng);
|
||||
switch(colorType)
|
||||
{
|
||||
case MNG_COLORTYPE_GRAY:
|
||||
case MNG_COLORTYPE_JPEGGRAY:
|
||||
{
|
||||
GBitmap temp(*bitmap);
|
||||
bitmap->deleteImage();
|
||||
bitmap->allocateBitmap(temp.getWidth(), temp.getHeight(), false, GFXFormatA8);
|
||||
|
||||
// force getColor to read in in the same color value for each channel
|
||||
// since the gray colortype has the real alpha in the first channel
|
||||
temp.setFormat( GFXFormatA8 );
|
||||
|
||||
ColorI color;
|
||||
for(U32 row = 0; row < bitmap->getHeight(); row++)
|
||||
{
|
||||
for(U32 col = 0; col < bitmap->getWidth(); col++)
|
||||
{
|
||||
temp.getColor(col, row, color);
|
||||
bitmap->setColor(col, row, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
mng_cleanup(&mng);
|
||||
|
||||
// Check this bitmap for transparency
|
||||
bitmap->checkForTransparency();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sWriteMNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
TORQUE_UNUSED( bitmap );
|
||||
TORQUE_UNUSED( stream );
|
||||
TORQUE_UNUSED( compressionLevel );
|
||||
|
||||
return false;
|
||||
#if 0
|
||||
// ONLY RGB bitmap writing supported at this time!
|
||||
AssertFatal(getFormat() == GFXFormatR8G8B8 || getFormat() == GFXFormatR8G8B8A8 || getFormat() == GFXFormatA8, "GBitmap::writeMNG: ONLY RGB bitmap writing supported at this time.");
|
||||
if(getFormat() != GFXFormatR8G8B8 && getFormat() != GFXFormatR8G8B8A8 && getFormat() != GFXFormatA8)
|
||||
return (false);
|
||||
|
||||
// maximum image size allowed
|
||||
#define MAX_HEIGHT 4096
|
||||
if(getHeight() >= MAX_HEIGHT)
|
||||
return false;
|
||||
|
||||
mngstuff mnginfo;
|
||||
dMemset(&mnginfo, 0, sizeof(mngstuff));
|
||||
mng_handle mng = mng_initialize(&mnginfo, mngMallocFn, mngFreeFn, MNG_NULL);
|
||||
if(mng == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup the callbacks
|
||||
mng_setcb_openstream(mng, mngOpenDataFn);
|
||||
mng_setcb_closestream(mng, mngCloseDataFn);
|
||||
mng_setcb_writedata(mng, mngWriteDataFn);
|
||||
|
||||
// create the file in memory
|
||||
mng_create(mng);
|
||||
|
||||
mng_putchunk_defi(mng, 0, 0, 0, MNG_FALSE, 0, 0, MNG_FALSE, 0, getWidth(), 0, getHeight());
|
||||
|
||||
mnginfo.image = (GBitmap*)this;
|
||||
mnginfo.stream = &stream;
|
||||
|
||||
switch(getFormat()) {
|
||||
case GFXFormatA8:
|
||||
mng_putchunk_ihdr(mng, getWidth(), getHeight(),
|
||||
MNG_BITDEPTH_8,
|
||||
MNG_COLORTYPE_GRAY,
|
||||
MNG_COMPRESSION_DEFLATE,
|
||||
MNG_FILTER_ADAPTIVE,
|
||||
MNG_INTERLACE_NONE);
|
||||
|
||||
// not implemented in lib yet
|
||||
//mng_putimgdata_ihdr(mng, getWidth(), getHeight(),
|
||||
// MNG_COLORTYPE_GRAY,
|
||||
// MNG_BITDEPTH_8,
|
||||
// MNG_COMPRESSION_DEFLATE,
|
||||
// MNG_FILTER_ADAPTIVE,
|
||||
// MNG_INTERLACE_NONE,
|
||||
// MNG_CANVAS_GRAY8, mngCanvasLineFn);
|
||||
break;
|
||||
case GFXFormatR8G8B8:
|
||||
mng_putchunk_ihdr(mng, getWidth(), getHeight(),
|
||||
MNG_BITDEPTH_8,
|
||||
MNG_COLORTYPE_RGB,
|
||||
MNG_COMPRESSION_DEFLATE,
|
||||
MNG_FILTER_ADAPTIVE,
|
||||
MNG_INTERLACE_NONE);
|
||||
|
||||
// not implemented in lib yet
|
||||
//mng_putimgdata_ihdr(mng, getWidth(), getHeight(),
|
||||
// MNG_COLORTYPE_RGB,
|
||||
// MNG_BITDEPTH_8,
|
||||
// MNG_COMPRESSION_DEFLATE,
|
||||
// MNG_FILTER_ADAPTIVE,
|
||||
// MNG_INTERLACE_NONE,
|
||||
// MNG_CANVAS_RGB8, mngCanvasLineFn);
|
||||
break;
|
||||
case GFXFormatR8G8B8A8:
|
||||
mng_putchunk_ihdr(mng, getWidth(), getHeight(),
|
||||
MNG_BITDEPTH_8,
|
||||
MNG_COLORTYPE_RGBA,
|
||||
MNG_COMPRESSION_DEFLATE,
|
||||
MNG_FILTER_ADAPTIVE,
|
||||
MNG_INTERLACE_NONE);
|
||||
|
||||
// not implemented in lib yet
|
||||
//mng_putimgdata_ihdr(mng, getWidth(), getHeight(),
|
||||
// MNG_COLORTYPE_RGBA,
|
||||
// MNG_BITDEPTH_8,
|
||||
// MNG_COMPRESSION_DEFLATE,
|
||||
// MNG_FILTER_ADAPTIVE,
|
||||
// MNG_INTERLACE_NONE,
|
||||
// MNG_CANVAS_RGBA8, mngCanvasLineFn);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// below is a hack until libmng is mature enough to handle this itself
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
U8 *tmpbuffer = new U8[this->byteSize + getHeight()];
|
||||
if(tmpbuffer == 0)
|
||||
{
|
||||
mng_cleanup(&mng);
|
||||
return false;
|
||||
}
|
||||
|
||||
// transfer data, add filterbyte
|
||||
U32 effwdt = getWidth() * this->bytesPerPixel;
|
||||
for(U32 Row = 0; Row < getHeight(); Row++)
|
||||
{
|
||||
// first Byte in each scanline is filterbyte: currently 0 -> no filter
|
||||
tmpbuffer[Row * (effwdt + 1)] = 0;
|
||||
|
||||
// copy the scanline
|
||||
dMemcpy(tmpbuffer + Row * (effwdt + 1) + 1, getAddress(0, Row), effwdt);
|
||||
}
|
||||
|
||||
// compress data with zlib
|
||||
U8 *dstbuffer = new U8[this->byteSize + getHeight()];
|
||||
if(dstbuffer == 0)
|
||||
{
|
||||
delete [] tmpbuffer;
|
||||
mng_cleanup(&mng);
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 dstbufferSize = this->byteSize + getHeight();
|
||||
if(Z_OK != compress2((Bytef*)dstbuffer,(uLongf*)&dstbufferSize, (const Bytef*)tmpbuffer, dstbufferSize, 9))
|
||||
{
|
||||
delete [] tmpbuffer;
|
||||
delete [] dstbuffer;
|
||||
mng_cleanup(&mng);
|
||||
return false;
|
||||
}
|
||||
|
||||
mng_putchunk_idat(mng, dstbufferSize, (mng_ptr*)dstbuffer);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
mng_putchunk_iend(mng);
|
||||
|
||||
delete [] tmpbuffer;
|
||||
delete [] dstbuffer;
|
||||
|
||||
mng_write(mng);
|
||||
mng_cleanup(&mng);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
647
Engine/source/gfx/bitmap/loaders/bitmapPng.cpp
Normal file
647
Engine/source/gfx/bitmap/loaders/bitmapPng.cpp
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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_ptr->io_ptr != NULL, "No stream?");
|
||||
|
||||
Stream *strm = (Stream*)png_ptr->io_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_ptr->io_ptr != NULL, "No stream?");
|
||||
|
||||
Stream *strm = (Stream*)png_ptr->io_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, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
||||
|
||||
if ( format != GFXFormatR8G8B8 &&
|
||||
format != GFXFormatR8G8B8A8 &&
|
||||
format != GFXFormatR8G8B8X8 &&
|
||||
format != GFXFormatA8 &&
|
||||
format != GFXFormatR5G6B5 )
|
||||
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, image filters, and compression strategy...
|
||||
png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
|
||||
png_ptr->zlib_strategy = strategy;
|
||||
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)
|
||||
{
|
||||
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, image filters, and compression strategy...
|
||||
mData->png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
|
||||
mData->png_ptr->zlib_strategy = 0;
|
||||
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;
|
||||
}
|
||||
489
Engine/source/gfx/bitmap/loaders/bitmapTga.cpp
Normal file
489
Engine/source/gfx/bitmap/loaders/bitmapTga.cpp
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
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" );
|
||||
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;
|
||||
}
|
||||
49
Engine/source/gfx/bitmap/pngUtils.h
Normal file
49
Engine/source/gfx/bitmap/pngUtils.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PNG_UTILS_H_
|
||||
#define _PNG_UTILS_H_
|
||||
|
||||
#ifndef _GFXENUMS_H_
|
||||
#include "gfx/gfxEnums.h"
|
||||
#endif
|
||||
|
||||
struct DeferredPNGWriterData; // This is used to avoid including png.h in this header
|
||||
class GBitmap;
|
||||
class Stream;
|
||||
|
||||
/// This class is used to write PNGs in row batches
|
||||
class DeferredPNGWriter {
|
||||
protected:
|
||||
DeferredPNGWriterData *mData;
|
||||
bool mActive;
|
||||
|
||||
public:
|
||||
DeferredPNGWriter();
|
||||
~DeferredPNGWriter();
|
||||
|
||||
bool begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel );
|
||||
void append( GBitmap* bitmap, U32 rows );
|
||||
void end();
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue