1307 lines
39 KiB
C++
1307 lines
39 KiB
C++
/**********************************************************************
|
|
|
|
Filename : GImage.cpp
|
|
Content : Memory buffer Image class loading and manipulation
|
|
Created :
|
|
Authors :
|
|
|
|
Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved.
|
|
|
|
Notes :
|
|
|
|
Licensees may use this file in accordance with the valid Scaleform
|
|
Commercial License Agreement provided with the software.
|
|
|
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
|
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
|
|
|
|
**********************************************************************/
|
|
|
|
#include "GImage.h"
|
|
#include "GJPEGUtil.h"
|
|
#include "GSysFile.h"
|
|
#include "GStd.h"
|
|
#include "GHeapNew.h"
|
|
|
|
struct GImageColorMap
|
|
{
|
|
UInt StartIndex;
|
|
UInt NumEntries;
|
|
bool HasAlpha;
|
|
struct Entry
|
|
{
|
|
UInt32 R: 8;
|
|
UInt32 G: 8;
|
|
UInt32 B: 8;
|
|
UInt32 A: 8;
|
|
} Entries[256];
|
|
};
|
|
|
|
// ***** GImageBase
|
|
|
|
|
|
// Set pixel, sets only the appropriate channels
|
|
void GImageBase::SetPixelRGBA(SInt x, SInt y, UInt32 color)
|
|
{
|
|
// Bounds check
|
|
if ( (((UInt)x) >= Width) || (((UInt)y) >= Height))
|
|
return;
|
|
if (Format >= Image_DXT1)
|
|
{
|
|
GASSERT(0);
|
|
return;
|
|
}
|
|
|
|
UByte *pline = GetScanline(y);
|
|
|
|
switch(Format)
|
|
{
|
|
case Image_ARGB_8888:
|
|
*(((UInt32*)pline) + x) = GByteUtil::LEToSystem(color);
|
|
break;
|
|
case Image_RGB_888:
|
|
// Data order is packed 24-bit, RGBRGB..., regardless of the endian-ness of the CPU.
|
|
*(pline + x * 3) = (UByte) color & 0xFF;
|
|
*(pline + x * 3 + 1) = (UByte) (color>>8) & 0xFF;
|
|
*(pline + x * 3 + 2) = (UByte) (color>>16) & 0xFF;
|
|
break;
|
|
case Image_A_8:
|
|
*(pline + x) = (UByte) (color >> 24);
|
|
break;
|
|
case Image_L_8:
|
|
*(pline + x) = (UByte) color & 0xFF;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GImageBase::SetPixelAlpha(SInt x, SInt y, UByte alpha)
|
|
{
|
|
if ( (((UInt)x) >= Width) || (((UInt)y) >= Height))
|
|
return;
|
|
if (Format >= Image_DXT1)
|
|
{
|
|
GASSERT(0);
|
|
return;
|
|
}
|
|
|
|
UByte *pline = GetScanline(y);
|
|
|
|
switch(Format)
|
|
{
|
|
case Image_ARGB_8888:
|
|
// Target is always little-endian
|
|
*(pline + x * 4 + 3) = alpha;
|
|
break;
|
|
case Image_A_8:
|
|
*(pline + x) = alpha;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GImageBase::SetPixelLum(SInt x, SInt y, UByte lum)
|
|
{
|
|
if ( (((UInt)x) >= Width) || (((UInt)y) >= Height))
|
|
return;
|
|
if (Format >= Image_DXT1)
|
|
{
|
|
GASSERT(0);
|
|
return;
|
|
}
|
|
|
|
UByte *pline = GetScanline(y);
|
|
|
|
switch(Format)
|
|
{
|
|
case Image_ARGB_8888:
|
|
*(pline + x * 4) = lum;
|
|
*(pline + x * 4 + 1) = lum;
|
|
*(pline + x * 4 + 2) = lum;
|
|
break;
|
|
case Image_RGB_888:
|
|
*(pline + x * 3) = lum;
|
|
*(pline + x * 3 + 1) = lum;
|
|
*(pline + x * 3 + 2) = lum;
|
|
break;
|
|
case Image_L_8:
|
|
*(pline + x) = lum;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
UInt GImageBase::GetBytesPerPixel(GImageBase::ImageFormat fmt)
|
|
{
|
|
switch(fmt)
|
|
{
|
|
case Image_A_8:
|
|
case Image_L_8:
|
|
case Image_P_8:
|
|
return 1;
|
|
case Image_ARGB_8888:
|
|
return 4;
|
|
case Image_RGB_888:
|
|
return 3;
|
|
case Image_DXT1:
|
|
return 2;
|
|
case Image_DXT3:
|
|
case Image_DXT5:
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
UInt GImageBase::GetPitch(ImageFormat fmt, UInt width)
|
|
{
|
|
switch(fmt)
|
|
{
|
|
case Image_A_8:
|
|
case Image_L_8:
|
|
case Image_P_8:
|
|
return width;
|
|
case Image_ARGB_8888:
|
|
return width * 4;
|
|
case Image_RGB_888:
|
|
return (width * 3 + 3) & ~3;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Computes a hash of the given data buffer.
|
|
// Hash function suggested by http://www.cs.yorku.ca/~oz/hash.html
|
|
// Due to Dan Bernstein. Allegedly very good on strings.
|
|
//
|
|
// One problem with this hash function is that e.g. if you take a
|
|
// bunch of 32-bit ints and hash them, their hash values will be
|
|
// concentrated toward zero, instead of randomly distributed in
|
|
// [0,2^32-1], because of shifting up only 5 bits per byte.
|
|
GINLINE UPInt GImageBase_BernsteinHash(const void* pdataIn, UPInt size, UPInt seed = 5381)
|
|
{
|
|
const UByte* pdata = (const UByte*) pdataIn;
|
|
UPInt h = seed;
|
|
|
|
while (size > 0)
|
|
{
|
|
size--;
|
|
h = ((h << 5) + h) ^ (UInt) pdata[size];
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
// Compute a hash code based on image contents. Can be useful
|
|
// for comparing images. Will return 0 if pData is null.
|
|
UPInt GImageBase::ComputeHash() const
|
|
{
|
|
if (!pData || DataSize == 0) return 0;
|
|
|
|
UPInt h = GImageBase_BernsteinHash(&Width, sizeof(Width));
|
|
h = GImageBase_BernsteinHash(&Height, sizeof(Height), h);
|
|
h = GImageBase_BernsteinHash(&MipMapCount, sizeof(MipMapCount), h);
|
|
h = GImageBase_BernsteinHash(pData, DataSize, h);
|
|
return h;
|
|
}
|
|
|
|
UInt GImageBase::GetMipMapLevelSize(ImageFormat format, UInt w, UInt h)
|
|
{
|
|
UInt levelSize;
|
|
if (format == Image_DXT1)
|
|
levelSize = G_Max(1u, (w+3) / 4) * G_Max(1u, (h+3) / 4) * 8;
|
|
else if (format >= Image_DXT3 && format <= Image_DXT5)
|
|
levelSize = G_Max(1u, (w+3) / 4) * G_Max(1u, (h+3) / 4) * 16;
|
|
else
|
|
levelSize = GetPitch(format, w) * h;
|
|
return levelSize;
|
|
}
|
|
|
|
UByte* GImageBase::GetMipMapLevelData(UInt level, UInt* pwidth, UInt* pheight, UInt* ppitch)
|
|
{
|
|
if (level > MipMapCount)
|
|
return 0;
|
|
if (level == 0)
|
|
{
|
|
if (pwidth) *pwidth = UInt(Width);
|
|
if (pheight) *pheight = UInt(Height);
|
|
if (ppitch) *ppitch = UInt(Pitch);
|
|
return pData;
|
|
}
|
|
|
|
UInt32 w = Width;
|
|
UInt32 h = Height;
|
|
UByte* plevelData = pData;
|
|
UInt32 pitch = Pitch;
|
|
for(UInt i = 0; i < level; ++i)
|
|
{
|
|
plevelData += GetMipMapLevelSize(Format, w, h);
|
|
w = G_Max(UInt32(1), w/2);
|
|
h = G_Max(UInt32(1), h/2);
|
|
pitch/= 2;
|
|
}
|
|
if (pwidth) *pwidth = UInt(w);
|
|
if (pheight) *pheight = UInt(h);
|
|
if (ppitch) *ppitch = pitch; //UInt(GetPitch(Format, w));
|
|
GASSERT(plevelData < (pData + DataSize));
|
|
if (plevelData < (pData + DataSize))
|
|
return plevelData;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ***** GImage implementation
|
|
|
|
void GImage::CreateImageCopy(const GImageBase &src )
|
|
{
|
|
// Copy data
|
|
if (src.pData && (pData = (UByte*)GALLOC(src.DataSize, GStat_Image_Mem)) != 0)
|
|
{
|
|
DataSize= src.DataSize;
|
|
Format = src.Format;
|
|
Width = src.Width;
|
|
Height = src.Height;
|
|
Pitch = src.Pitch;
|
|
MipMapCount = src.MipMapCount;
|
|
ColorMap = src.ColorMap;
|
|
memcpy(pData, src.pData, src.DataSize);
|
|
}
|
|
else
|
|
{
|
|
Format = Image_None;
|
|
Width =
|
|
Height =
|
|
Pitch = 0;
|
|
pData = 0;
|
|
DataSize= 0;
|
|
MipMapCount = 1;
|
|
ColorMap.Clear();
|
|
}
|
|
}
|
|
|
|
GImage::GImage()
|
|
{
|
|
Format = Image_None;
|
|
Width =
|
|
Height =
|
|
Pitch = 0;
|
|
pData = 0;
|
|
DataSize= 0;
|
|
MipMapCount = 1;
|
|
}
|
|
|
|
GImage::GImage(ImageFormat format, UInt32 width, UInt32 height)
|
|
{
|
|
GFC_DEBUG_WARNING((width <= 0) || (height <=0), "GImage::GImage - creating image with zero size");
|
|
|
|
Pitch = GetPitch(format, width);
|
|
|
|
// This size calculation accommodates DXT formats as well.
|
|
DataSize = GetMipMapLevelSize(format, width, height);
|
|
if ((pData = (UByte*)GHEAP_AUTO_ALLOC(this, DataSize)) != 0) // Was: GStat_Image_Mem
|
|
{
|
|
Format = format;
|
|
Width = width;
|
|
Height = height;
|
|
memset(pData, 0, DataSize);
|
|
}
|
|
else
|
|
{
|
|
ClearImageBase();
|
|
}
|
|
MipMapCount = 1;
|
|
}
|
|
|
|
GImage::~GImage()
|
|
{
|
|
if (pData)
|
|
GFREE(pData);
|
|
}
|
|
|
|
void GImage::Clear()
|
|
{
|
|
if (pData)
|
|
GFREE(pData);
|
|
ClearImageBase();
|
|
}
|
|
|
|
|
|
// Create an image (return 0 if allocation failed)
|
|
GImage* GImage::CreateImage(ImageFormat format, UInt32 width, UInt32 height,
|
|
GMemoryHeap* pimageHeap)
|
|
{
|
|
GMemoryHeap* pheap = pimageHeap ? pimageHeap : GMemory::GetGlobalHeap();
|
|
GImage* pimage = GHEAP_NEW(pheap) GImage(format, width, height);
|
|
|
|
if (pimage->pData && (width !=0) && (height != 0))
|
|
return pimage;
|
|
pimage->Release();
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Raw comparison of data
|
|
bool GImage::operator == (const GImage &src) const
|
|
{
|
|
if ((Format != src.Format) ||
|
|
(Width != src.Width) ||
|
|
(Height != src.Height) ||
|
|
(Pitch != src.Pitch) ||
|
|
(MipMapCount != src.MipMapCount) ||
|
|
(DataSize != src.DataSize) ||
|
|
(ColorMap.GetSize() != src.ColorMap.GetSize()))
|
|
return 0;
|
|
|
|
if (!pData)
|
|
return (bool)(src.pData == 0);
|
|
|
|
if (ColorMap.GetSize() && memcmp(&ColorMap[0], &src.ColorMap[0], ColorMap.GetSize() * sizeof(GColor)))
|
|
return 0;
|
|
|
|
// Return 1 if two buffers are identical (i.e. memcmp == 0)
|
|
return memcmp(pData, src.pData, DataSize) == 0;
|
|
}
|
|
|
|
|
|
|
|
// ***** Image I/O utility functions
|
|
|
|
// Write the given image to the given out stream, in jpeg format.
|
|
bool GImage::WriteJpeg(GFile* pout, int quality, GJPEGSystem *psystem)
|
|
{
|
|
GJPEGOutput* pjout = psystem->CreateOutput(pout, Width, Height, quality);
|
|
|
|
for (UInt y = 0; y < Height; y++)
|
|
pjout->WriteScanline(GetScanline(y));
|
|
|
|
delete pjout;
|
|
|
|
return 1; // Error code
|
|
}
|
|
|
|
static bool ConvertScanline(const UByte* psrcScanline, UInt srcBitsPerPixel, GImage::ImageFormat srcFormat, UInt srcScanlineSize,
|
|
UByte* pdstScanline, UInt dstBitsPerPixel, GImage::ImageFormat dstFormat, UInt dstScanlineSize,
|
|
void* pextraInfo)
|
|
{
|
|
GUNUSED(pextraInfo);
|
|
|
|
UInt srcDelta = srcBitsPerPixel / 8;
|
|
UInt dstDelta = dstBitsPerPixel / 8;
|
|
if (srcFormat == GImage::Image_ARGB_8888)
|
|
{
|
|
if (dstFormat == GImage::Image_A_8)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
pdstScanline[j] = psrcScanline[i+3]; // take only alpha
|
|
}
|
|
return true;
|
|
}
|
|
else if (dstFormat == GImage::Image_RGB_888)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
pdstScanline[j] = psrcScanline[i];
|
|
pdstScanline[j+1] = psrcScanline[i+1];
|
|
pdstScanline[j+2] = psrcScanline[i+2];
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else if (srcFormat == GImage::Image_RGB_888)
|
|
{
|
|
if (dstFormat == GImage::Image_A_8)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
UByte alpha = UByte((UInt(psrcScanline[i]) + psrcScanline[i+1] + psrcScanline[i+2])/3);
|
|
pdstScanline[j] = alpha;
|
|
}
|
|
return true;
|
|
}
|
|
else if (dstFormat == GImage::Image_ARGB_8888)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
pdstScanline[j] = psrcScanline[i];
|
|
pdstScanline[j+1] = psrcScanline[i+1];
|
|
pdstScanline[j+2] = psrcScanline[i+2];
|
|
pdstScanline[j+3] = 0xFF;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else if (srcFormat == GImage::Image_P_8)
|
|
{
|
|
// pextraInfo is GImageColorMap
|
|
const GImageColorMap* pcolorMap = reinterpret_cast<GImageColorMap*>(pextraInfo);
|
|
if (dstFormat == GImage::Image_A_8)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
const GImageColorMap::Entry& cmEntry = pcolorMap->Entries[psrcScanline[i]];
|
|
UByte alpha;
|
|
if (pcolorMap->HasAlpha)
|
|
alpha = cmEntry.A;
|
|
else
|
|
alpha = UByte((UInt(cmEntry.R) + cmEntry.G + cmEntry.B)/3);
|
|
pdstScanline[j] = alpha;
|
|
}
|
|
return true;
|
|
}
|
|
else if (dstFormat == GImage::Image_RGB_888 || dstFormat == GImage::Image_ARGB_8888)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
const GImageColorMap::Entry& cmEntry = pcolorMap->Entries[psrcScanline[i]];
|
|
pdstScanline[j] = cmEntry.R;
|
|
pdstScanline[j+1] = cmEntry.G;
|
|
pdstScanline[j+2] = cmEntry.B;
|
|
if (dstFormat == GImage::Image_ARGB_8888)
|
|
pdstScanline[j+3] = cmEntry.A;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else if (srcFormat == GImage::Image_A_8)
|
|
{
|
|
if (dstFormat == GImage::Image_RGB_888)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
pdstScanline[j] = psrcScanline[i];
|
|
pdstScanline[j+1] = psrcScanline[i];
|
|
pdstScanline[j+2] = psrcScanline[i];
|
|
}
|
|
return true;
|
|
}
|
|
else if (dstFormat == GImage::Image_ARGB_8888)
|
|
{
|
|
for(UInt i = 0, j = 0; i < srcScanlineSize && j < dstScanlineSize; i += srcDelta, j += dstDelta)
|
|
{
|
|
pdstScanline[j] = 0xFF;
|
|
pdstScanline[j+1] = 0xFF;
|
|
pdstScanline[j+2] = 0xFF;
|
|
pdstScanline[j+3] = psrcScanline[i];
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GImage* GImage::ConvertImage(ImageFormat destFormat, GMemoryHeap *pimageHeap)
|
|
{
|
|
UInt w = Width;
|
|
UInt h = Height;
|
|
|
|
if (Format == destFormat)
|
|
{
|
|
AddRef();
|
|
return this; // no need conversion
|
|
}
|
|
|
|
GPtr<GImage> pdstImage = *GImage::CreateImage(destFormat, w, h, pimageHeap);
|
|
if (pdstImage)
|
|
{
|
|
for (UInt y = 0; y < h; y++)
|
|
{
|
|
const UByte* psrcScanline = GetScanline(y);
|
|
UByte* pdstScanline = pdstImage->GetScanline(y);
|
|
|
|
if (!ConvertScanline(psrcScanline, GetBytesPerPixel()*8, Format, GetPitch(),
|
|
pdstScanline, pdstImage->GetBytesPerPixel()*8, destFormat, pdstImage->GetPitch(),
|
|
NULL))
|
|
return 0; // unable to convert!
|
|
}
|
|
pdstImage->AddRef();
|
|
}
|
|
|
|
return pdstImage;
|
|
}
|
|
|
|
// Write a 32-bit Targa format bitmap. Dead simple, no compression.
|
|
bool GImage::WriteTga(GFile* pout)
|
|
{
|
|
if (!pout->IsWritable())
|
|
return false;
|
|
pout->WriteUByte(0); // ID length
|
|
|
|
// Color Map type
|
|
// 0 - indicates that no color-map data is included with this image.
|
|
// 1 - indicates that a color-map is included with this image.
|
|
if (Format == Image_A_8)
|
|
pout->WriteUByte(1);
|
|
else
|
|
pout->WriteUByte(0);
|
|
|
|
// Image Type
|
|
// 0 No Image Data Included
|
|
// 1 Uncompressed, Color-mapped Image
|
|
// 2 Uncompressed, True-color Image
|
|
// 3 Uncompressed, Black-and-white Image
|
|
// 9 Run-length encoded, Color-mapped Image
|
|
// 10 Run-length encoded, True-color Image
|
|
// 11 Run-length encoded, Black-and-white Image
|
|
if (Format == Image_A_8)
|
|
pout->WriteUByte(1);
|
|
else
|
|
pout->WriteUByte(2);
|
|
|
|
// Color Map Specification
|
|
if (Format == Image_A_8)
|
|
{
|
|
pout->WriteUInt16(0); // first entry index
|
|
pout->WriteUInt16(256); // color map length (in entries)
|
|
pout->WriteUByte(24); // color map entry size (in bits)
|
|
}
|
|
else
|
|
{
|
|
pout->WriteUInt16(0);
|
|
pout->WriteUInt16(0);
|
|
pout->WriteUByte(0);
|
|
}
|
|
pout->WriteUInt16(0); /* X origin */
|
|
pout->WriteUInt16(0); /* y origin */
|
|
pout->WriteUInt16((UInt16)Width);
|
|
pout->WriteUInt16((UInt16)Height);
|
|
|
|
if (Format == Image_A_8)
|
|
pout->WriteUByte(8); // 8 bit bitmap
|
|
else if (Format == Image_RGB_888)
|
|
pout->WriteUByte(24); // 24 bit bitmap
|
|
else
|
|
pout->WriteUByte(32); // 32 bit bitmap
|
|
UByte imageDescr = 0x20;
|
|
if (Format == Image_ARGB_8888)
|
|
imageDescr |= 8;
|
|
pout->WriteUByte(imageDescr);
|
|
// Image Descriptor:
|
|
// Bits: 7 6 5 4 3 2 1 0
|
|
// |0 0| |r r| |a a a a|
|
|
// where bits 3-0: These bits specify the number of attribute bits per
|
|
// pixel. In the case of the TrueVista, these bits indicate
|
|
// the number of bits per pixel which are designated as
|
|
// Alpha Channel bits.
|
|
// bits 5-4: These bits are used to indicate the order in which
|
|
// pixel data is transferred from the file to the screen.
|
|
// Bit 4 is for left-to-right ordering and bit 5 is for topto-
|
|
// bottom ordering as shown below:
|
|
// bits: 5 4
|
|
// 0 0 - bottom left
|
|
// 0 1 - bottom right
|
|
// 1 0 - top left
|
|
// 1 1 - top right
|
|
if (Format == Image_A_8)
|
|
{
|
|
// color map
|
|
for (UInt i = 0; i < 256; ++i)
|
|
{
|
|
pout->WriteUByte(UByte(i));
|
|
pout->WriteUByte(UByte(i));
|
|
pout->WriteUByte(UByte(i));
|
|
}
|
|
}
|
|
|
|
for (UInt y = 0; y < Height; y++)
|
|
{
|
|
UByte* p = GetScanline(y);
|
|
if (Format == Image_RGB_888)
|
|
{
|
|
// write 24-bit scanline
|
|
for (UInt x = 0, wx = Width*3; x < wx; x += 3)
|
|
{
|
|
pout->WriteUByte(p[x + 2]); // B
|
|
pout->WriteUByte(p[x + 1]); // G
|
|
pout->WriteUByte(p[x + 0]); // R
|
|
}
|
|
}
|
|
else if (Format == Image_ARGB_8888)
|
|
{
|
|
// write 32-bit scanline
|
|
for (UInt x = 0, wx = Width*4; x < wx; x += 4)
|
|
{
|
|
pout->WriteUByte(p[x + 2]); // B
|
|
pout->WriteUByte(p[x + 1]); // G
|
|
pout->WriteUByte(p[x + 0]); // R
|
|
pout->WriteUByte(p[x + 3]); // A
|
|
}
|
|
}
|
|
else if (Format == Image_A_8)
|
|
{
|
|
// write indices
|
|
for (UInt x = 0; x < Width; ++x)
|
|
{
|
|
pout->WriteUByte(p[x]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unsupported format of scanline
|
|
GASSERT(0);
|
|
}
|
|
}
|
|
|
|
if (!pout->IsWritable())
|
|
return false;
|
|
|
|
// error code ?
|
|
return true;
|
|
}
|
|
|
|
// Create and read a new image from the stream.
|
|
GImage* GImage::ReadTga(GFile* pin, ImageFormat destFormat, GMemoryHeap* pimageHeap)
|
|
{
|
|
if (!pin || !pin->IsValid()) return 0;
|
|
|
|
UByte idLen = pin->ReadUByte();
|
|
UByte colorMapType = pin->ReadUByte();
|
|
UByte imageType = pin->ReadUByte();
|
|
|
|
// Color Map Specification
|
|
GImageColorMap colorMap;
|
|
colorMap.StartIndex = pin->ReadUInt16(); // first entry index
|
|
colorMap.NumEntries = pin->ReadUInt16(); // color map length (in entries)
|
|
UInt cmEntrySize = pin->ReadUByte(); // color map entry size (in bits)
|
|
if (cmEntrySize > 0 && cmEntrySize != 24 && cmEntrySize != 32)
|
|
return 0; // only 24- or 32-bits color maps are supported
|
|
|
|
pin->ReadUInt16(); // X origin
|
|
pin->ReadUInt16(); // Y origin
|
|
|
|
UInt16 width = pin->ReadUInt16(); // width
|
|
UInt16 height = pin->ReadUInt16(); // height
|
|
|
|
UByte bitsPerPixel = pin->ReadUByte();
|
|
UByte imageDescr = pin->ReadUByte();
|
|
|
|
if (!((colorMapType == 0 && imageType == 2) || (colorMapType == 1 && imageType == 1)))
|
|
{
|
|
// only uncompressed RGB and color map formats are supported now
|
|
return 0;
|
|
}
|
|
if (idLen > 0)
|
|
pin->SkipBytes(idLen);
|
|
|
|
ImageFormat format;
|
|
SInt srcScanLineSize, dstScanLineSize;
|
|
switch(bitsPerPixel)
|
|
{
|
|
case 8: format = Image_P_8; srcScanLineSize = width; break;
|
|
case 24: format = Image_RGB_888; srcScanLineSize = width*3; break;
|
|
case 32: format = Image_ARGB_8888; srcScanLineSize = width*4; break;
|
|
default: return 0; // only 8/24/32-bits TrueType format supported
|
|
}
|
|
|
|
UByte destBitsPerPixel = bitsPerPixel;
|
|
if (destFormat == Image_None)
|
|
{
|
|
#if (!defined(GFC_OS_PSP) && !defined(GFC_OS_PS2))
|
|
if (format == Image_P_8)
|
|
{
|
|
// by default, convert 256-colors TGA to RGB_888 if no alpha in palette
|
|
if (cmEntrySize < 32)
|
|
{
|
|
destFormat = Image_RGB_888;
|
|
dstScanLineSize = width*3;
|
|
destBitsPerPixel = 24;
|
|
}
|
|
else
|
|
{ // or, to ARGB, if palette is 32 bit
|
|
destFormat = Image_ARGB_8888;
|
|
dstScanLineSize = width*4;
|
|
destBitsPerPixel = 32;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
destFormat = format;
|
|
dstScanLineSize = srcScanLineSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(destFormat)
|
|
{
|
|
case Image_A_8: destBitsPerPixel = 8; dstScanLineSize = width; break;
|
|
case Image_P_8: destBitsPerPixel = 8; dstScanLineSize = width; break;
|
|
case Image_RGB_888: destBitsPerPixel = 24; dstScanLineSize = width*3; break;
|
|
case Image_ARGB_8888: destBitsPerPixel = 32; dstScanLineSize = width*4; break;
|
|
default: // unsupported destination format
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (colorMapType == 1 && imageType == 1)
|
|
{
|
|
// load color map
|
|
UInt entrySizeInBytes = ((cmEntrySize+7)/8);
|
|
if (entrySizeInBytes*colorMap.NumEntries > sizeof(colorMap.Entries))
|
|
return 0; // too big color map, only 256*4 is supported
|
|
if (cmEntrySize == 32)
|
|
colorMap.HasAlpha = true;
|
|
else
|
|
colorMap.HasAlpha = false;
|
|
//Palette entries are BGR ordered
|
|
for (UInt i = 0; i < colorMap.NumEntries; ++i)
|
|
{
|
|
#if (defined(GFC_OS_PSP) || defined(GFC_OS_PS2))
|
|
colorMap.Entries[i].R = pin->ReadUByte();
|
|
colorMap.Entries[i].G = pin->ReadUByte();
|
|
colorMap.Entries[i].B = pin->ReadUByte();
|
|
#else
|
|
colorMap.Entries[i].B = pin->ReadUByte();
|
|
colorMap.Entries[i].G = pin->ReadUByte();
|
|
colorMap.Entries[i].R = pin->ReadUByte();
|
|
#endif
|
|
if (cmEntrySize == 32)
|
|
colorMap.Entries[i].A = pin->ReadUByte();
|
|
else
|
|
colorMap.Entries[i].A = 0xFF;
|
|
}
|
|
}
|
|
|
|
GPtr<GImage> pimage = *CreateImage(destFormat, width, height, pimageHeap);
|
|
if (pimage)
|
|
{
|
|
UByte* pscanline = 0;
|
|
UByte scanlineBuf[4096 * 4];
|
|
if (UInt(srcScanLineSize) > sizeof(scanlineBuf))
|
|
pscanline = (UByte*)GALLOC(srcScanLineSize, GStat_Image_Mem);
|
|
else
|
|
pscanline = scanlineBuf;
|
|
|
|
int ysl = (imageDescr & 0x20) ? 0 : height - 1;
|
|
UInt y;
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
// read scan-line
|
|
unsigned char* prgbData = pimage->GetScanline(G_Abs(ysl));
|
|
unsigned char* pcurScanline;
|
|
if (format == destFormat)
|
|
pcurScanline = prgbData;
|
|
else
|
|
pcurScanline = pscanline;
|
|
if (pin->Read(pcurScanline, srcScanLineSize) != srcScanLineSize)
|
|
break; // read error!
|
|
|
|
if (format == Image_ARGB_8888)
|
|
{
|
|
// convert BGRA->RGBA
|
|
for (UInt x = 0, wx = width*4; x < wx; x += 4)
|
|
{
|
|
UByte b = pcurScanline[x];
|
|
pcurScanline[x] = pcurScanline[x + 2];
|
|
pcurScanline[x + 2] = b;
|
|
}
|
|
}
|
|
else if (format == Image_RGB_888)
|
|
{
|
|
// convert BGR->RGB
|
|
for (UInt x = 0, wx = width*3; x < wx; x += 3)
|
|
{
|
|
UByte b = pcurScanline[x];
|
|
pcurScanline[x] = pcurScanline[x + 2];
|
|
pcurScanline[x + 2] = b;
|
|
}
|
|
}
|
|
else if (destFormat == Image_P_8)
|
|
{
|
|
pimage->ColorMap.Resize(colorMap.NumEntries);
|
|
for (UInt i = 0; i < colorMap.NumEntries; ++i)
|
|
pimage->ColorMap[i].SetRGBA(
|
|
colorMap.Entries[i].R, colorMap.Entries[i].G, colorMap.Entries[i].B,
|
|
colorMap.Entries[i].A);
|
|
}
|
|
if (format != destFormat)
|
|
{
|
|
if (!ConvertScanline(pscanline, bitsPerPixel, format, srcScanLineSize,
|
|
prgbData, destBitsPerPixel, destFormat, dstScanLineSize, &colorMap))
|
|
break;
|
|
}
|
|
--ysl;
|
|
}
|
|
if (pscanline && pscanline != scanlineBuf)
|
|
GFREE(pscanline);
|
|
|
|
if (y < height) // error occured
|
|
return 0;
|
|
|
|
pimage->AddRef();
|
|
}
|
|
|
|
return pimage;
|
|
}
|
|
|
|
|
|
// Create and read a new image from the given filename, if possible.
|
|
GImage* GImage::ReadJpeg(const char* filename, GJPEGSystem *psystem, GMemoryHeap* pimageHeap)
|
|
{
|
|
GSysFile in(filename);
|
|
if (in.IsValid())
|
|
return ReadJpeg(&in, psystem, pimageHeap);
|
|
return 0;
|
|
}
|
|
|
|
// Create and read a new image from the stream.
|
|
GImage* GImage::ReadJpeg(GFile* pin, GJPEGSystem *psystem, GMemoryHeap* pimageHeap)
|
|
{
|
|
GJPEGInput* pjin = psystem->CreateInput(pin);
|
|
if (!pjin) return 0;
|
|
|
|
GImage* pimage;
|
|
if (!pjin->IsErrorOccurred())
|
|
{
|
|
pimage = CreateImage(Image_RGB_888, pjin->GetWidth(), pjin->GetHeight(), pimageHeap);
|
|
if (pimage)
|
|
{
|
|
for (UInt y = 0; y < pimage->Height; y++)
|
|
{
|
|
if (!pjin->ReadScanline(pimage->GetScanline(y)))
|
|
{
|
|
pimage->Release();
|
|
pimage = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
pimage = NULL;
|
|
|
|
delete pjin;
|
|
return pimage;
|
|
}
|
|
|
|
|
|
// *** DDS Format loading
|
|
|
|
static const UByte* ParseUInt32(const UByte* buf, UInt32* pval)
|
|
{
|
|
*pval = GByteUtil::LEToSystem(*(UInt32*)buf);
|
|
return buf + 4;
|
|
}
|
|
|
|
struct GImage_DDSFormatDescr
|
|
{
|
|
UInt32 RGBBitCount;
|
|
UInt32 RBitMask;
|
|
UInt32 GBitMask;
|
|
UInt32 BBitMask;
|
|
UInt32 ABitMask;
|
|
bool HasAlpha;
|
|
|
|
inline GImage_DDSFormatDescr()
|
|
{
|
|
RGBBitCount = RBitMask = GBitMask = BBitMask = ABitMask = 0;
|
|
HasAlpha = false;
|
|
}
|
|
};
|
|
|
|
static bool GImage_ParseDDSHeader(GImageBase* pimage, const UByte* buf, const UByte** pdata, GImage_DDSFormatDescr* pDDSFmt)
|
|
{
|
|
enum {
|
|
GFx_DDSD_CAPS =0x00000001l,
|
|
GFx_DDSD_HEIGHT =0x00000002l,
|
|
GFx_DDSD_WIDTH =0x00000004l,
|
|
GFx_DDSD_PITCH =0x00000008l,
|
|
GFx_DDSD_BACKBUFFERCOUNT =0x00000020l,
|
|
GFx_DDSD_ZBUFFERBITDEPTH =0x00000040l,
|
|
GFx_DDSD_ALPHABITDEPTH =0x00000080l,
|
|
GFx_DDSD_LPSURFACE =0x00000800l,
|
|
GFx_DDSD_PIXELFORMAT =0x00001000l,
|
|
GFx_DDSD_CKDESTOVERLAY =0x00002000l,
|
|
GFx_DDSD_CKDESTBLT =0x00004000l,
|
|
GFx_DDSD_CKSRCOVERLAY =0x00008000l,
|
|
GFx_DDSD_CKSRCBLT =0x00010000l,
|
|
GFx_DDSD_MIPMAPCOUNT =0x00020000l,
|
|
GFx_DDSD_REFRESHRATE =0x00040000l,
|
|
GFx_DDSD_LINEARSIZE =0x00080000l,
|
|
GFx_DDSD_TEXTURESTAGE =0x00100000l,
|
|
GFx_DDSD_FVF =0x00200000l,
|
|
GFx_DDSD_SRCVBHANDLE =0x00400000l,
|
|
GFx_DDSD_DEPTH =0x00800000l
|
|
};
|
|
UInt32 flags;
|
|
UInt32 v;
|
|
buf = ParseUInt32(buf, &flags);
|
|
|
|
buf = ParseUInt32(buf, &v);
|
|
if (flags & GFx_DDSD_HEIGHT)
|
|
pimage->Height = v;
|
|
|
|
buf = ParseUInt32(buf, &v);
|
|
if (flags & GFx_DDSD_WIDTH)
|
|
pimage->Width = v;
|
|
buf = ParseUInt32(buf, &v);
|
|
if (flags & GFx_DDSD_PITCH)
|
|
pimage->Pitch = v;
|
|
else if (flags & GFx_DDSD_LINEARSIZE)
|
|
pimage->Pitch = v/pimage->Height*4; // Required by D3D10
|
|
|
|
buf = ParseUInt32(buf, &v);
|
|
//if (flags & GFx_DDSD_DEPTH)
|
|
// pimage->Depth = v;
|
|
|
|
buf = ParseUInt32(buf, &v);
|
|
if (flags & GFx_DDSD_MIPMAPCOUNT)
|
|
pimage->MipMapCount = v;
|
|
|
|
//buf = ParseUInt32(buf, &v); // alpha bit count
|
|
//if (flags & GFx_DDSD_ALPHABITDEPTH)
|
|
// pimage->AlphaBitDepth = v;
|
|
|
|
buf += 11 * 4;
|
|
|
|
if (flags & GFx_DDSD_PIXELFORMAT)
|
|
{
|
|
// pixel format (DDPIXELFORMAT)
|
|
buf = ParseUInt32(buf, &v); // dwSize
|
|
if (v != 32) // dwSize should be == 32
|
|
{
|
|
GASSERT(0);
|
|
return false;
|
|
}
|
|
enum GFx_DDPIXELFORMAT
|
|
{
|
|
GFx_DDPF_ALPHAPIXELS =0x00000001l,
|
|
GFx_DDPF_ALPHA =0x00000002l,
|
|
GFx_DDPF_FOURCC =0x00000004l,
|
|
GFx_DDPF_PALETTEINDEXED4 =0x00000008l,
|
|
GFx_DDPF_PALETTEINDEXEDTO8 =0x00000010l,
|
|
GFx_DDPF_PALETTEINDEXED8 =0x00000020l,
|
|
GFx_DDPF_RGB =0x00000040l,
|
|
GFx_DDPF_COMPRESSED =0x00000080l
|
|
};
|
|
UInt32 pfflags;
|
|
buf = ParseUInt32(buf, &pfflags); // dwFlags
|
|
buf = ParseUInt32(buf, &v); // dwFourCC
|
|
if (pfflags & GFx_DDPF_FOURCC)
|
|
{
|
|
if (v == 0x35545844) // DXT5
|
|
pimage->Format = GImage::Image_DXT5;
|
|
else if (v == 0x33545844) // DXT3
|
|
pimage->Format = GImage::Image_DXT3;
|
|
else if (v == 0x31545844) // DXT1
|
|
pimage->Format = GImage::Image_DXT1;
|
|
buf += 20; // skip remaining part of PixelFormat
|
|
}
|
|
else if ((pfflags & GFx_DDPF_RGB) || (pfflags & GFx_DDPF_ALPHA))
|
|
{
|
|
// uncompressed DDS. Only 32-bit/24-bit RGB formats and alpha only (A8) are supported
|
|
UInt32 bitCount;
|
|
buf = ParseUInt32(buf, &bitCount); // dwRGBBitCount
|
|
if (pDDSFmt) pDDSFmt->RGBBitCount = bitCount;
|
|
switch(bitCount)
|
|
{
|
|
case 32: pimage->Format = GImage::Image_ARGB_8888; break;
|
|
case 24: pimage->Format = GImage::Image_RGB_888; break;
|
|
case 8:
|
|
if (pfflags & GFx_DDPF_ALPHA)
|
|
{
|
|
pimage->Format = GImage::Image_A_8;
|
|
break;
|
|
}
|
|
default: GASSERT(0); // unsupported
|
|
}
|
|
if (!(flags & GFx_DDSD_PITCH))
|
|
pimage->Pitch = pimage->Width*(bitCount/8);
|
|
//AB: what is the Pitch in DDS for 24-bit RGB?
|
|
|
|
buf = ParseUInt32(buf, &v); // dwRBitMask
|
|
if (pDDSFmt) pDDSFmt->RBitMask = v;
|
|
buf = ParseUInt32(buf, &v); // dwGBitMask
|
|
if (pDDSFmt) pDDSFmt->GBitMask = v;
|
|
buf = ParseUInt32(buf, &v); // dwBBitMask
|
|
if (pDDSFmt) pDDSFmt->BBitMask = v;
|
|
buf = ParseUInt32(buf, &v); // dwRGBAlphaBitMask
|
|
if (pDDSFmt && (pfflags & GFx_DDPF_ALPHAPIXELS))
|
|
{
|
|
pDDSFmt->ABitMask = v;
|
|
pDDSFmt->HasAlpha = true;
|
|
}
|
|
|
|
// check for X8R8G8B8 - need to set alpha to 255
|
|
if (v == 0 && bitCount == 32)
|
|
{
|
|
GASSERT(0); // not supported for now.
|
|
//@TODO - need to have one more Image_<> format for X8R8G8B8
|
|
}
|
|
}
|
|
GASSERT(pimage->Format != GImage::Image_None); // Unsupported format
|
|
if (pimage->Format == GImage::Image_None)
|
|
return false;
|
|
}
|
|
else
|
|
buf += 32;
|
|
buf += 16; // skip ddsCaps
|
|
buf += 4; // skip reserved
|
|
if (pdata) *pdata = buf;
|
|
return true;
|
|
}
|
|
|
|
static UByte GFx_CalcShiftByMask(UInt32 mask)
|
|
{
|
|
UInt shifts = 0;
|
|
|
|
if (mask == 0) return 0;
|
|
|
|
if ((mask & 0xFFFFFFu) == 0)
|
|
{
|
|
mask >>= 24;
|
|
shifts += 24;
|
|
}
|
|
else if ((mask & 0xFFFFu) == 0)
|
|
{
|
|
mask >>= 16;
|
|
shifts += 16;
|
|
}
|
|
else if ((mask & 0xFFu) == 0)
|
|
{
|
|
mask >>= 8;
|
|
shifts += 8;
|
|
}
|
|
while((mask & 1) == 0)
|
|
{
|
|
mask >>= 1;
|
|
++shifts;
|
|
}
|
|
return UByte(shifts);
|
|
}
|
|
|
|
static bool PostProcessUDDSData(GImage* pimage, const GImage_DDSFormatDescr& ddsFmt)
|
|
{
|
|
if (!pimage->IsDataCompressed() &&
|
|
(pimage->Format == GImage::Image_ARGB_8888 || pimage->Format == GImage::Image_RGB_888))
|
|
{
|
|
UByte shiftR = UByte(GFx_CalcShiftByMask(ddsFmt.RBitMask));
|
|
UByte shiftG = UByte(GFx_CalcShiftByMask(ddsFmt.GBitMask));
|
|
UByte shiftB = UByte(GFx_CalcShiftByMask(ddsFmt.BBitMask));
|
|
UByte shiftA = UByte(GFx_CalcShiftByMask(ddsFmt.ABitMask));
|
|
// uncompressed DDS - reorganize RGBA in all mipmap levels
|
|
for (UInt curlevel = 0; curlevel < pimage->MipMapCount; ++curlevel)
|
|
{
|
|
UInt w, h;
|
|
UInt pitch;
|
|
UByte* pimgData = pimage->GetMipMapLevelData(curlevel, &w, &h, &pitch);
|
|
GASSERT(pimgData); // pimgData == NULL means DDS data is broken
|
|
if (!pimgData)
|
|
return false;
|
|
for (UInt y = 0; y < h; y++)
|
|
{
|
|
UByte* p = pimgData + pitch*y;
|
|
if (pimage->Format == GImage::Image_RGB_888)
|
|
{
|
|
for (UInt x = 0, wx = w*3; x < wx; x += 3)
|
|
{
|
|
UInt32 val = p[x + 0] | (UInt32(p[x + 1]) << 8) | (UInt32(p[x + 2]) << 16);
|
|
p[x + 2] = UByte((val >> shiftB) & 0xFF); // B
|
|
p[x + 1] = UByte((val >> shiftG) & 0xFF); // G
|
|
p[x + 0] = UByte((val >> shiftR) & 0xFF); // R
|
|
}
|
|
}
|
|
else if (pimage->Format == GImage::Image_ARGB_8888)
|
|
{
|
|
for (UInt x = 0, wx = w*4; x < wx; x += 4)
|
|
{
|
|
UInt32 val = p[x + 0] | (UInt32(p[x + 1]) << 8) | (UInt32(p[x + 2]) << 16) | (UInt32(p[x + 3]) << 24);
|
|
p[x + 2] = UByte((val >> shiftB) & 0xFF); // B
|
|
p[x + 1] = UByte((val >> shiftG) & 0xFF); // G
|
|
p[x + 0] = UByte((val >> shiftR) & 0xFF); // R
|
|
if (ddsFmt.HasAlpha)
|
|
p[x + 3] = UByte((val >> shiftA) & 0xFF); // A
|
|
else
|
|
p[x + 3] = 0xFF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Loads DDS from file creating GImage.
|
|
GImage* GImage::ReadDDS(GFile* pin, GMemoryHeap* pheap)
|
|
{
|
|
if (!pin || !pin->IsValid())
|
|
return NULL;
|
|
|
|
// First, read and verify the header.
|
|
GImageBase imageBase;
|
|
GImage * pimage = 0;
|
|
|
|
SInt fileSize = pin->GetLength();
|
|
UInt32 fourcc = pin->ReadUInt32();
|
|
if (fourcc != 0x20534444) // 'D','D','S',' '
|
|
return 0;
|
|
|
|
UInt32 sz = pin->ReadUInt32();
|
|
if (sz != 124)
|
|
return 0;
|
|
UByte buf[256];
|
|
if (pin->Read(buf, 120) != 120)
|
|
return 0;
|
|
|
|
imageBase.ClearImageBase();
|
|
GImage_DDSFormatDescr ddsFmt;
|
|
if (!GImage_ParseDDSHeader(&imageBase, buf, 0, &ddsFmt))
|
|
return 0;
|
|
|
|
// Allocate image and read-in data.
|
|
if ((pimage = new GImage())==0)
|
|
return 0;
|
|
pimage->Format = imageBase.Format;
|
|
pimage->Height = imageBase.Height;
|
|
pimage->Width = imageBase.Width;
|
|
pimage->Pitch = imageBase.Pitch;
|
|
pimage->MipMapCount = imageBase.MipMapCount;
|
|
|
|
if (!pheap)
|
|
pheap = GMemory::GetGlobalHeap();
|
|
|
|
SInt dataSize = fileSize - pin->Tell();
|
|
UByte* pdata = (UByte*)GHEAP_ALLOC(pheap, dataSize, GStat_Image_Mem);
|
|
if (!pdata)
|
|
{
|
|
pimage->Release();
|
|
return 0;
|
|
}
|
|
|
|
if (pin->Read(pdata, dataSize) != dataSize)
|
|
{
|
|
pimage->Release();
|
|
GFREE(pdata);
|
|
return 0;
|
|
}
|
|
|
|
// AB: do we need to do same for uncompressed DDS?
|
|
#ifdef GFC_OS_XBOX360
|
|
if (pimage->IsDataCompressed())
|
|
{
|
|
// We need to convert byte order for XBox360. This does not apply
|
|
// to other big-endian systems such as PS3.
|
|
UInt16 *pidata = (UInt16*)pdata;
|
|
SInt i;
|
|
for (i=0; i<dataSize/2; i++)
|
|
{
|
|
*pidata = GByteUtil::LEToSystem(*pidata);
|
|
pidata++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pimage->pData = pdata;
|
|
pimage->DataSize = dataSize;
|
|
|
|
if (!PostProcessUDDSData(pimage, ddsFmt))
|
|
{
|
|
pimage->Release();
|
|
return 0;
|
|
}
|
|
|
|
return pimage;
|
|
}
|
|
|
|
|
|
// Loads DDS from a chunk of memory, data is copied.
|
|
GImage* GImage::ReadDDSFromMemory(const UByte* ddsData, UPInt dataSize, GMemoryHeap* pheap)
|
|
{
|
|
GImage * pimage = 0;
|
|
const UByte* pdata;
|
|
|
|
UInt32 fourcc;
|
|
ddsData = ParseUInt32(ddsData, &fourcc);
|
|
if (fourcc != 0x20534444) // 'D','D','S',' '
|
|
return 0;
|
|
|
|
UInt32 sz;
|
|
ddsData = ParseUInt32(ddsData, &sz);
|
|
if (sz != 124)
|
|
return 0;
|
|
|
|
if ((pimage = new GImage()) == 0)
|
|
return 0;
|
|
|
|
GImage_DDSFormatDescr ddsFmt;
|
|
if (!GImage_ParseDDSHeader(pimage, ddsData, &pdata, &ddsFmt))
|
|
{
|
|
pimage->Release();
|
|
return 0;
|
|
}
|
|
|
|
// Alloc data and copy it.
|
|
if (!pheap)
|
|
pheap = GMemory::GetGlobalHeap();
|
|
|
|
pimage->DataSize = (UInt)(dataSize - (pdata - ddsData));
|
|
pimage->pData = (UByte*)GHEAP_ALLOC(pheap, pimage->DataSize, GStat_Image_Mem);
|
|
if (!pimage->pData)
|
|
{
|
|
pimage->Release();
|
|
return 0;
|
|
}
|
|
|
|
memcpy(pimage->pData, pdata, pimage->DataSize);
|
|
|
|
// AB: do we need to do same for uncompressed DDS? I guess - not.
|
|
#ifdef GFC_OS_XBOX360
|
|
if (pimage->IsDataCompressed())
|
|
{
|
|
// We need to convert byte order for XBox360. This does not apply
|
|
// to other big-endian systems such as PS3.
|
|
UInt16 *pidata = (UInt16*)pimage->pData;
|
|
UInt i;
|
|
for (i = 0; i < pimage->DataSize/2; i++)
|
|
{
|
|
*pidata = GByteUtil::LEToSystem(*pidata);
|
|
pidata++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!PostProcessUDDSData(pimage, ddsFmt))
|
|
{
|
|
pimage->Release();
|
|
return 0;
|
|
}
|
|
|
|
return pimage;
|
|
}
|
|
|
|
#ifndef GFC_USE_LIBPNG
|
|
// Create and read a new image from the given filename, if possible.
|
|
GImage* GImage::ReadPng(const char* filename, GMemoryHeap* pimageHeap)
|
|
{
|
|
GUNUSED2(filename, pimageHeap);
|
|
GFC_DEBUG_WARNING(1, "GImage::ReadPng failed - GFC_USE_LIBPNG not defined");
|
|
return NULL;
|
|
}
|
|
|
|
// Create and read a new image from the stream.
|
|
GImage* GImage::ReadPng(GFile* pin, GMemoryHeap* pimageHeap)
|
|
{
|
|
GUNUSED2(pin, pimageHeap);
|
|
GFC_DEBUG_WARNING(1, "GImage::ReadPng failed - GFC_USE_LIBPNG not defined");
|
|
return NULL;
|
|
}
|
|
|
|
bool WritePng(GFile* pout)
|
|
{
|
|
GUNUSED(pout);
|
|
GFC_DEBUG_WARNING(1, "GImage::WritePng failed - GFC_USE_LIBPNG not defined");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
|