Commit e2aee66c authored by nadro's avatar nadro

- Removed IImageCompressed and derived.

- Extended IImage class for support compressed textures.
- Improved DDS loader for support mip map loading.
- Added DXT1-5 compressed textures support for OpenGL.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@4449 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 6f278dd4
......@@ -98,6 +98,12 @@ public:
//! fills the surface with given color
virtual void fill(const SColor &color) =0;
//! Inform whether the image is compressed
virtual bool isCompressed() const = 0;
//! Inform wheather the image has mipmaps
virtual bool hasMipMap() const = 0;
//! get the amount of Bits per Pixel of the given color format
static u32 getBitsPerPixelFromFormat(const ECOLOR_FORMAT format)
{
......@@ -111,6 +117,13 @@ public:
return 24;
case ECF_A8R8G8B8:
return 32;
case ECF_DXT1:
return 16;
case ECF_DXT2:
case ECF_DXT3:
case ECF_DXT4:
case ECF_DXT5:
return 32;
case ECF_R16F:
return 16;
case ECF_G16R16F:
......@@ -128,6 +141,22 @@ public:
}
}
//! test if this is compressed color format
static bool isCompressedFormat(const ECOLOR_FORMAT format)
{
switch(format)
{
case ECF_DXT1:
case ECF_DXT2:
case ECF_DXT3:
case ECF_DXT4:
case ECF_DXT5:
return true;
default:
return false;
}
}
//! test if the color format is only viable for RenderTarget textures
/** Since we don't have support for e.g. floating point IImage formats
one should test if the color format can be used for arbitrary usage, or
......@@ -148,56 +177,6 @@ public:
};
//! Interface for software compressed image data.
/** Image loaders create these images from files. IVideoDrivers convert
these images into their (hardware) textures.
*/
class IImageCompressed : public virtual IReferenceCounted
{
public:
//! Use this to get a pointer to the image data.
virtual const void* getData() const = 0;
//! Returns width and height of image data.
virtual const core::dimension2d<u32>& getDimension() const = 0;
//! Returns bits per pixel.
virtual u32 getBitsPerPixel() const = 0;
//! Returns bytes per pixel
virtual u32 getBytesPerPixel() const = 0;
//! Returns image data size in bytes
virtual u32 getImageDataSizeInBytes() const = 0;
//! Returns image data size in pixels
virtual u32 getImageDataSizeInPixels() const = 0;
//! Returns the color format
virtual ECOLOR_FORMAT getColorFormat() const = 0;
//! Returns pitch of image
virtual u32 getPitch() const = 0;
//! get the amount of Bits per Pixel of the given color format
static u32 getBitsPerPixelFromFormat(const ECOLOR_FORMAT format)
{
switch(format)
{
case ECF_DXT1:
return 16;
case ECF_DXT2:
case ECF_DXT3:
case ECF_DXT4:
case ECF_DXT5:
return 32;
default:
return 0;
}
}
};
} // end namespace video
} // end namespace irr
......
......@@ -43,15 +43,6 @@ public:
/** \param file File handle to check.
\return Pointer to newly created image, or 0 upon error. */
virtual IImage* loadImage(io::IReadFile* file) const = 0;
//! Creates a compressed surface from the file
/** \param file File handle to check.
\return Pointer to newly created image, or 0 upon error. */
virtual IImageCompressed* loadImageCompressed(io::IReadFile* file) const
{
// The most of supported file formats are uncompressed, so we define it here.
return 0;
}
};
......
......@@ -1218,18 +1218,6 @@ namespace video
bool ownForeignMemory=false,
bool deleteMemory = true) =0;
//! Creates a compressed software image from a file.
virtual IImageCompressed* createImageCompressedFromFile(const io::path& filename) = 0;
//! Creates a compressed software image from a file.
virtual IImageCompressed* createImageCompressedFromFile(io::IReadFile* file) = 0;
//! Creates a software compressed image from a byte array.
virtual IImageCompressed* createImageCompressedFromData(ECOLOR_FORMAT format,
const core::dimension2d<u32>& size, void *data,
bool ownForeignMemory=false,
bool deleteMemory = true) = 0;
//! Creates an empty software image.
/**
\param format Desired color format of the image.
......
......@@ -6,6 +6,7 @@
#include "irrString.h"
#include "CColorConverter.h"
#include "CBlit.h"
#include "os.h"
namespace irr
{
......@@ -14,7 +15,7 @@ namespace video
//! Constructor of empty image
CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size)
:Data(0), Size(size), Format(format), DeleteMemory(true)
:Data(0), Size(size), Format(format), DeleteMemory(true), Compressed(false)
{
initData();
}
......@@ -22,8 +23,8 @@ CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size)
//! Constructor from raw data
CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size, void* data,
bool ownForeignMemory, bool deleteForeignMemory)
: Data(0), Size(size), Format(format), DeleteMemory(deleteForeignMemory)
bool ownForeignMemory, bool deleteForeignMemory, bool compressed, bool hasMipMapData)
: Data(0), Size(size), Format(format), DeleteMemory(deleteForeignMemory), Compressed(compressed), HasMipMap(hasMipMapData)
{
if (ownForeignMemory)
{
......@@ -181,6 +182,12 @@ u32 CImage::getAlphaMask() const
//! sets a pixel
void CImage::setPixel(u32 x, u32 y, const SColor &color, bool blend)
{
if (Compressed)
{
os::Printer::log("IImage::setPixel method doesn't work with compressed images.", ELL_WARNING);
return;
}
if (x >= Size.Width || y >= Size.Height)
return;
......@@ -222,6 +229,12 @@ void CImage::setPixel(u32 x, u32 y, const SColor &color, bool blend)
//! returns a pixel
SColor CImage::getPixel(u32 x, u32 y) const
{
if (Compressed)
{
os::Printer::log("IImage::getPixel method doesn't work with compressed images.", ELL_WARNING);
return SColor(0);
}
if (x >= Size.Width || y >= Size.Height)
return SColor(0);
......@@ -258,6 +271,12 @@ ECOLOR_FORMAT CImage::getColorFormat() const
//! copies this surface into another at given position
void CImage::copyTo(IImage* target, const core::position2d<s32>& pos)
{
if (Compressed)
{
os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);
return;
}
Blit(BLITTER_TEXTURE, target, 0, &pos, this, 0, 0);
}
......@@ -265,6 +284,12 @@ void CImage::copyTo(IImage* target, const core::position2d<s32>& pos)
//! copies this surface partially into another at given position
void CImage::copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect)
{
if (Compressed)
{
os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);
return;
}
Blit(BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0);
}
......@@ -272,6 +297,12 @@ void CImage::copyTo(IImage* target, const core::position2d<s32>& pos, const core
//! copies this surface into another, using the alpha mask, a cliprect and a color to add with
void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const SColor &color, const core::rect<s32>* clipRect)
{
if (Compressed)
{
os::Printer::log("IImage::copyToWithAlpha method doesn't work with compressed images.", ELL_WARNING);
return;
}
// color blend only necessary on not full spectrum aka. color.color != 0xFFFFFFFF
Blit(color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND: BLITTER_TEXTURE_ALPHA_COLOR_BLEND,
target, clipRect, &pos, this, &sourceRect, color.color);
......@@ -282,6 +313,12 @@ void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, c
// note: this is very very slow.
void CImage::copyToScaling(void* target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch)
{
if (Compressed)
{
os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);
return;
}
if (!target || !width || !height)
return;
......@@ -338,6 +375,12 @@ void CImage::copyToScaling(void* target, u32 width, u32 height, ECOLOR_FORMAT fo
// note: this is very very slow.
void CImage::copyToScaling(IImage* target)
{
if (Compressed)
{
os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);
return;
}
if (!target)
return;
......@@ -357,6 +400,12 @@ void CImage::copyToScaling(IImage* target)
//! copies this surface into another, scaling it to fit it.
void CImage::copyToScalingBoxFilter(IImage* target, s32 bias, bool blend)
{
if (Compressed)
{
os::Printer::log("IImage::copyToScalingBoxFilter method doesn't work with compressed images.", ELL_WARNING);
return;
}
const core::dimension2d<u32> destSize = target->getDimension();
const f32 sourceXStep = (f32) Size.Width / (f32) destSize.Width;
......@@ -389,6 +438,12 @@ void CImage::copyToScalingBoxFilter(IImage* target, s32 bias, bool blend)
//! fills the surface with given color
void CImage::fill(const SColor &color)
{
if (Compressed)
{
os::Printer::log("IImage::fill method doesn't work with compressed images.", ELL_WARNING);
return;
}
u32 c;
switch ( Format )
......@@ -424,9 +479,28 @@ void CImage::fill(const SColor &color)
}
//! Inform whether the image is compressed
bool CImage::isCompressed() const
{
return Compressed;
}
//! Inform wheather the image store mipmaps
bool CImage::hasMipMap() const
{
return HasMipMap;
}
//! get a filtered pixel
inline SColor CImage::getPixelBox( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) const
{
if (Compressed)
{
os::Printer::log("IImage::getPixelBox method doesn't work with compressed images.", ELL_WARNING);
return SColor(0);
}
SColor c;
s32 a = 0, r = 0, g = 0, b = 0;
......@@ -458,111 +532,5 @@ inline SColor CImage::getPixelBox( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) cons
}
/** Compressed image **/
//! Constructor
CImageCompressed::CImageCompressed(ECOLOR_FORMAT format, const core::dimension2d<u32>& size, void* data,
bool ownForeignMemory, bool deleteForeignMemory)
: Data(0), Size(size), Format(format), DeleteMemory(deleteForeignMemory)
{
if (ownForeignMemory)
{
Data = (u8*)0xbadf00d;
initData();
Data = (u8*)data;
}
else
{
Data = 0;
initData();
memcpy(Data, data, Size.Height * Pitch);
}
}
//! Destructor
CImageCompressed::~CImageCompressed()
{
if (DeleteMemory)
delete [] Data;
}
//! assumes format and size has been set and creates the rest
void CImageCompressed::initData()
{
#ifdef _DEBUG
setDebugName("CImageCompressed");
#endif
BytesPerPixel = getBitsPerPixelFromFormat(Format) / 8;
// Pitch should be aligned...
Pitch = BytesPerPixel * Size.Width;
if (!Data)
{
DeleteMemory=true;
Data = new u8[Size.Height * Pitch];
}
}
//! Use this to get a pointer to the image data.
const void* CImageCompressed::getData() const
{
return Data;
}
//! Returns width and height of image data.
const core::dimension2d<u32>& CImageCompressed::getDimension() const
{
return Size;
}
//! Returns bits per pixel.
u32 CImageCompressed::getBitsPerPixel() const
{
return getBitsPerPixelFromFormat(Format);
}
//! Returns bytes per pixel
u32 CImageCompressed::getBytesPerPixel() const
{
return BytesPerPixel;
}
//! Returns image data size in bytes
u32 CImageCompressed::getImageDataSizeInBytes() const
{
return Pitch * Size.Height;
}
//! Returns image data size in pixels
u32 CImageCompressed::getImageDataSizeInPixels() const
{
return Size.Width * Size.Height;
}
//! returns the color format
ECOLOR_FORMAT CImageCompressed::getColorFormat() const
{
return Format;
}
//! returns pitch of image
u32 CImageCompressed::getPitch() const
{
return Pitch;
}
} // end namespace video
} // end namespace irr
......@@ -24,7 +24,7 @@ public:
directly and own it from now on, which means it will also try to delete [] the
data when the image will be destructed. If false, the memory will by copied. */
CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size,
void* data, bool ownForeignMemory=true, bool deleteMemory = true);
void* data, bool ownForeignMemory=true, bool deleteMemory = true, bool compressed = false, bool hasMipMapData = false);
//! constructor for empty image
CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size);
......@@ -103,6 +103,12 @@ public:
//! fills the surface with given color
virtual void fill(const SColor &color);
//! Inform whether the image is compressed
virtual bool isCompressed() const;
//! Inform wheather the image has mipmaps
virtual bool hasMipMap() const;
private:
//! assumes format and size has been set and creates the rest
......@@ -117,59 +123,9 @@ private:
ECOLOR_FORMAT Format;
bool DeleteMemory;
};
//! Internal interface for software compressed image data.
class CImageCompressed : public IImageCompressed
{
public:
//! Constructor
/** \param useForeignMemory: If true, the image will use the data pointer
directly and own it from now on, which means it will also try to delete [] the
data when the image will be destructed. If false, the memory will by copied. */
CImageCompressed(ECOLOR_FORMAT format, const core::dimension2d<u32>& size,
void* data, bool ownForeignMemory=true, bool deleteMemory = true);
//! Destructor
virtual ~CImageCompressed();
//! Use this to get a pointer to the image data.
virtual const void* getData() const;
//! Returns width and height of image data.
virtual const core::dimension2d<u32>& getDimension() const;
//! Returns bits per pixel.
virtual u32 getBitsPerPixel() const;
//! Returns bytes per pixel
virtual u32 getBytesPerPixel() const;
//! Returns image data size in bytes
virtual u32 getImageDataSizeInBytes() const;
//! Returns image data size in pixels
virtual u32 getImageDataSizeInPixels() const;
//! returns the color format
virtual ECOLOR_FORMAT getColorFormat() const;
//! returns pitch of image
virtual u32 getPitch() const;
private:
//! assumes format and size has been set and creates the rest
void initData();
u8* Data;
core::dimension2d<u32> Size;
u32 BytesPerPixel;
u32 Pitch;
ECOLOR_FORMAT Format;
bool DeleteMemory;
bool Compressed;
bool HasMipMap;
};
} // end namespace video
......
......@@ -113,6 +113,8 @@ s32 DDSGetInfo(ddsHeader* dds, s32* width, s32* height, eDDSPixelFormat* pf)
}
#ifdef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
/*
DDSDecompressARGB8888()
decompresses an argb 8888 format texture
......@@ -141,8 +143,6 @@ s32 DDSDecompressARGB8888(ddsHeader* dds, u8* data, s32 width, s32 height, u8* p
}
#ifdef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
/*!
DDSGetColorBlockColors()
extracts colors from a dds color block
......@@ -692,92 +692,188 @@ IImage* CImageLoaderDDS::loadImage(io::IReadFile* file) const
IImage* image = 0;
s32 width, height;
eDDSPixelFormat pixelFormat;
ECOLOR_FORMAT format = ECF_UNKNOWN;
u32 dataSize = 0;
bool is3D = false;
bool useAlpha = false;
u32 mipMapCount = 0;
file->seek(0);
file->read(&header, sizeof(ddsHeader));
if (0 == DDSGetInfo(&header, &width, &height, &pixelFormat))
{
#ifndef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
if(pixelFormat == DDS_PF_ARGB8888)
#endif
{
u32 newSize = file->getSize() - sizeof(ddsHeader);
u8* memFile = new u8[newSize];
file->read(memFile, newSize);
is3D = header.Depth > 0 && (header.Flags & DDSD_DEPTH);
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
if (!is3D)
header.Depth = 1;
#ifndef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
DDSDecompressARGB8888(&header, memFile, width, height, (u8*)image->lock());
#else
if (DDSDecompress(&header, memFile, (u8*)image->lock()) == -1)
{
image->unlock();
image->drop();
image = 0;
}
#endif
useAlpha = header.PixelFormat.Flags & DDPF_ALPHAPIXELS;
delete[] memFile;
if (header.MipMapCount > 0 && (header.Flags & DDSD_MIPMAPCOUNT))
mipMapCount = header.MipMapCount;
if (image)
image->unlock();
}
}
#ifdef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
u32 newSize = file->getSize() - sizeof(ddsHeader);
u8* memFile = new u8[newSize];
file->read(memFile, newSize);
return image;
}
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
if (DDSDecompress(&header, memFile, (u8*)image->lock()) == -1)
{
image->unlock();
image->drop();
image = 0;
}
//! creates a compressed surface from the file
IImageCompressed* CImageLoaderDDS::loadImageCompressed(io::IReadFile* file) const
{
#ifndef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
ddsHeader header;
IImageCompressed* image = 0;
s32 width, height;
eDDSPixelFormat pixelFormat;
delete[] memFile;
#else
if (header.PixelFormat.Flags & DDPF_RGB) // Uncompressed formats
{
u32 byteCount = header.PixelFormat.RGBBitCount / 8;
if( header.Flags & DDSD_PITCH )
dataSize = header.PitchOrLinearSize * header.Height * header.Depth * (header.PixelFormat.RGBBitCount / 8);
else
dataSize = header.Width * header.Height * header.Depth * (header.PixelFormat.RGBBitCount / 8);
file->seek(0);
file->read(&header, sizeof(ddsHeader));
u8* data = new u8[dataSize];
file->read(data, dataSize);
ECOLOR_FORMAT format = ECF_UNKNOWN;
u32 dataSize = 0;
bool is3D = false;
switch (header.PixelFormat.RGBBitCount) // Bytes per pixel
{
case 16:
{
if (useAlpha)
{
if (header.PixelFormat.ABitMask == 0x8000)
format = ECF_A1R5G5B5;
}
else
{
if (header.PixelFormat.RBitMask == 0xf800)
format = ECF_R5G6B5;
}
if (0 == DDSGetInfo(&header, &width, &height, &pixelFormat))
{
is3D = header.Depth > 0 && (header.Flags & DDSD_DEPTH);
break;
}
case 24:
{
if (!useAlpha)
{
if (header.PixelFormat.RBitMask == 0xff0000)
format = ECF_R8G8B8;
}
if (!is3D)
header.Depth = 1;
break;
}
case 32:
{
if (useAlpha)
{
if (header.PixelFormat.RBitMask & 0xff0000)
format = ECF_A8R8G8B8;
else if (header.PixelFormat.RBitMask & 0xff)
{
// convert from A8B8G8R8 to A8R8G8B8
u8 tmp = 0;
for (u32 i = 0; i < dataSize; i += 4)
{
tmp = data[i];
data[i] = data[i+2];
data[i+2] = tmp;
}
}
}
break;
}
}
if (header.PixelFormat.Flags & DDPF_FOURCC) // Compressed formats
if (format != ECF_UNKNOWN)
{
if (!is3D) // Currently 3D textures are unsupported.
{
image = new CImage(format, core::dimension2d<u32>(header.Width, header.Height ), data, true, true, false);
}
}
else
{
delete[] data;
}
}
else if (header.PixelFormat.Flags & DDPF_FOURCC) // Compressed formats
{
switch(pixelFormat)
{
case DDS_PF_DXT1:
{
dataSize = (header.Width / 4 ) * (header.Height / 4) * 8;
u32 curWidth = header.Width;
u32 curHeight = header.Height;
dataSize = ((curWidth + 3) / 4) * ((curHeight + 3) / 4) * 8;
do
{
if (curWidth > 1)
curWidth >>= 1;
if (curHeight > 1)
curHeight >>= 1;
dataSize += ((curWidth + 3) / 4) * ((curHeight + 3) / 4) * 8;
}
while (curWidth != 1 || curWidth != 1);
format = ECF_DXT1;
os::Printer::log("Detected ECF_DXT1 format", ELL_DEBUG);
break;
}
case DDS_PF_DXT2:
case DDS_PF_DXT3:
{
dataSize = (header.Width / 4 ) * (header.Height / 4) * 16;
u32 curWidth = header.Width;
u32 curHeight = header.Height;
dataSize = ((curWidth + 3) / 4) * ((curHeight + 3) / 4) * 16;
do
{
if (curWidth > 1)
curWidth >>= 1;
if (curHeight > 1)
curHeight >>= 1;
dataSize += ((curWidth + 3) / 4) * ((curHeight + 3) / 4) * 16;
}
while (curWidth != 1 || curWidth != 1);
format = ECF_DXT3;
os::Printer::log("Detected ECF_DXT3 format", ELL_DEBUG);
break;
}
case DDS_PF_DXT4:
case DDS_PF_DXT5:
{
dataSize = (header.Width / 4 ) * (header.Height / 4) * 16;
u32 curWidth = header.Width;
u32 curHeight = header.Height;
dataSize = ((curWidth + 3) / 4) * ((curHeight + 3) / 4) * 16;
do
{
if (curWidth > 1)
curWidth >>= 1;
if (curHeight > 1)
curHeight >>= 1;
dataSize += ((curWidth + 3) / 4) * ((curHeight + 3) / 4) * 16;
}
while (curWidth != 1 || curWidth != 1);
format = ECF_DXT5;
os::Printer::log("Detected ECF_DXT5 format", ELL_DEBUG);
break;
}
}
......@@ -789,16 +885,16 @@ IImageCompressed* CImageLoaderDDS::loadImageCompressed(io::IReadFile* file) cons
u8* data = new u8[dataSize];
file->read(data, dataSize);
image = new CImageCompressed(format, core::dimension2d<u32>(header.Width, header.Height ), data);
bool hasMipMap = (mipMapCount > 0) ? true : false;
image = new CImage(format, core::dimension2d<u32>(header.Width, header.Height), data, true, true, true, hasMipMap);
}
}
}
#endif
}
return image;
#else
return 0;
#endif
}
......
......@@ -204,9 +204,6 @@ public:
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file) const;
//! creates a compressed surface from the file
virtual IImageCompressed* loadImageCompressed(io::IReadFile* file) const;
};
......
......@@ -1402,77 +1402,6 @@ IImage* CNullDriver::createImageFromData(ECOLOR_FORMAT format,
}
//! Creates a compressed software image from a file.
IImageCompressed* CNullDriver::createImageCompressedFromFile(const io::path& filename)
{
if (!filename.size())
return 0;
IImageCompressed* image = 0;
io::IReadFile* file = FileSystem->createAndOpenFile(filename);
if (file)
{
image = createImageCompressedFromFile(file);
file->drop();
}
else
os::Printer::log("Could not open file of image", filename, ELL_WARNING);
return image;
}
//! Creates a compressed software image from a file.
IImageCompressed* CNullDriver::createImageCompressedFromFile(io::IReadFile* file)
{
if (!file)
return 0;
IImageCompressed* image = 0;
s32 i;
// try to load file based on file extension
for (i=SurfaceLoader.size()-1; i>=0; --i)
{
if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName()))
{
// reset file position which might have changed due to previous loadImage calls
file->seek(0);
image = SurfaceLoader[i]->loadImageCompressed(file);
if (image)
return image;
}
}
// try to load file based on what is in it
for (i=SurfaceLoader.size()-1; i>=0; --i)
{
// dito
file->seek(0);
if (SurfaceLoader[i]->isALoadableFileFormat(file))
{
file->seek(0);
image = SurfaceLoader[i]->loadImageCompressed(file);
if (image)
return image;
}
}
return 0; // failed to load
}
//! Creates a software compressed image from a byte array.
IImageCompressed* CNullDriver::createImageCompressedFromData(ECOLOR_FORMAT format,
const core::dimension2d<u32>& size, void *data,
bool ownForeignMemory, bool deleteMemory)
{
return new CImageCompressed(format, size, data, ownForeignMemory, deleteMemory);
}
//! Creates an empty software image.
IImage* CNullDriver::createImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size)
{
......
......@@ -352,17 +352,6 @@ namespace video
const core::dimension2d<u32>& size, void *data,
bool ownForeignMemory=true, bool deleteForeignMemory = true);
//! Creates a compressed software image from a file.
virtual IImageCompressed* createImageCompressedFromFile(const io::path& filename);
//! Creates a compressed software image from a file.
virtual IImageCompressed* createImageCompressedFromFile(io::IReadFile* file);
//! Creates a software compressed image from a byte array.
virtual IImageCompressed* createImageCompressedFromData(ECOLOR_FORMAT format,
const core::dimension2d<u32>& size, void *data,
bool ownForeignMemory=false, bool deleteMemory = true);
//! Creates an empty software image.
virtual IImage* createImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size);
......
......@@ -50,7 +50,7 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() :
pGlPointParameterfARB(0), pGlPointParameterfvARB(0),
pGlStencilFuncSeparate(0), pGlStencilOpSeparate(0),
pGlStencilFuncSeparateATI(0), pGlStencilOpSeparateATI(0),
pGlCompressedTexImage2D(0),
pGlCompressedTexImage2D(0), pGlCompressedTexSubImage2D(0),
// ARB framebuffer object
pGlBindFramebuffer(0), pGlDeleteFramebuffers(0), pGlGenFramebuffers(0),
pGlCheckFramebufferStatus(0), pGlFramebufferTexture2D(0),
......@@ -473,6 +473,7 @@ void COpenGLExtensionHandler::initExtensions(bool stencilBuffer)
// compressed textures
pGlCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) IRR_OGL_LOAD_EXTENSION("glCompressedTexImage2D");
pGlCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) IRR_OGL_LOAD_EXTENSION("glCompressedTexSubImage2D");
// ARB FrameBufferObjects
pGlBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) IRR_OGL_LOAD_EXTENSION("glBindFramebuffer");
......
......@@ -992,6 +992,9 @@ class COpenGLExtensionHandler
void extGlCompressedTexImage2D(GLenum target, GLint level,
GLenum internalformat, GLsizei width, GLsizei height,
GLint border, GLsizei imageSize, const void* data);
void extGlCompressedTexSubImage2D(GLenum target, GLint level,
GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
GLenum format, GLsizei imageSize, const void* data);
// shader programming
void extGlGenPrograms(GLsizei n, GLuint *programs);
......@@ -1168,6 +1171,7 @@ class COpenGLExtensionHandler
PFNGLSTENCILFUNCSEPARATEATIPROC pGlStencilFuncSeparateATI;
PFNGLSTENCILOPSEPARATEATIPROC pGlStencilOpSeparateATI;
PFNGLCOMPRESSEDTEXIMAGE2DPROC pGlCompressedTexImage2D;
PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC pGlCompressedTexSubImage2D;
// ARB framebuffer object
PFNGLBINDFRAMEBUFFERPROC pGlBindFramebuffer;
PFNGLDELETEFRAMEBUFFERSPROC pGlDeleteFramebuffers;
......@@ -1934,6 +1938,19 @@ inline void COpenGLExtensionHandler::extGlCompressedTexImage2D (GLenum target, G
#endif
}
inline void COpenGLExtensionHandler::extGlCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
{
#ifdef _IRR_OPENGL_USE_EXTPOINTER_
if (pGlCompressedTexSubImage2D)
pGlCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
#elif defined(GL_ARB_texture_compression)
glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
#else
os::Printer::log("glCompressedTexSubImage2D not supported", ELL_ERROR);
#endif
}
inline void COpenGLExtensionHandler::extGlBindFramebuffer(GLenum target, GLuint framebuffer)
{
#ifdef _IRR_OPENGL_USE_EXTPOINTER_
......
......@@ -25,7 +25,7 @@ COpenGLTexture::COpenGLTexture(IImage* origImage, const io::path& name, void* mi
TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT),
PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0), MipmapLegacyMode(true),
IsRenderTarget(false), AutomaticMipmapUpdate(false),
ReadOnlyLock(false), KeepImage(true)
ReadOnlyLock(false), KeepImage(true), Compressed(false)
{
#ifdef _DEBUG
setDebugName("COpenGLTexture");
......@@ -36,7 +36,21 @@ COpenGLTexture::COpenGLTexture(IImage* origImage, const io::path& name, void* mi
glGenTextures(1, &TextureName);
if (ImageSize==TextureSize)
if (ColorFormat == ECF_DXT1 || ColorFormat == ECF_DXT2 || ColorFormat == ECF_DXT3 || ColorFormat == ECF_DXT4 || ColorFormat == ECF_DXT5)
{
if(ImageSize != TextureSize)
{
os::Printer::log("Invalid size of image for compressed texture, size of image must be POT.", ELL_ERROR);
return;
}
else
{
Compressed = true;
Image = origImage;
Image->grab();
}
}
else if (ImageSize==TextureSize)
{
Image = Driver->createImage(ColorFormat, ImageSize);
origImage->copyTo(Image);
......@@ -62,7 +76,7 @@ COpenGLTexture::COpenGLTexture(const io::path& name, COpenGLDriver* driver)
TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT),
PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0), HasMipMaps(true),
MipmapLegacyMode(true), IsRenderTarget(false), AutomaticMipmapUpdate(false),
ReadOnlyLock(false), KeepImage(true)
ReadOnlyLock(false), KeepImage(true), Compressed(false)
{
#ifdef _DEBUG
setDebugName("COpenGLTexture");
......@@ -159,7 +173,23 @@ GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT
type=GL_UNSIGNED_INT_8_8_8_8_REV;
internalformat = GL_RGBA;
break;
// Floating Point texture formats. Thanks to Patryk "Nadro" Nadrowski.
case ECF_DXT1:
colorformat = GL_BGRA_EXT;
type = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
internalformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case ECF_DXT2:
case ECF_DXT3:
colorformat = GL_BGRA_EXT;
type = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
internalformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case ECF_DXT4:
case ECF_DXT5:
colorformat = GL_BGRA_EXT;
type = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
internalformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
case ECF_R16F:
{
#ifdef GL_ARB_texture_rg
......@@ -293,7 +323,10 @@ void COpenGLTexture::getImageValues(IImage* image)
}
TextureSize=ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT));
ColorFormat = getBestColorFormat(image->getColorFormat());
if(image->getColorFormat() == ECF_DXT1 || image->getColorFormat() == ECF_DXT2 || image->getColorFormat() == ECF_DXT3 || image->getColorFormat() == ECF_DXT4 || image->getColorFormat() == ECF_DXT5)
ColorFormat = image->getColorFormat();
else
ColorFormat = getBestColorFormat(image->getColorFormat());
}
......@@ -325,36 +358,110 @@ void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level)
// mipmap handling for main texture
if (!level && newTexture)
{
#ifndef DISABLE_MIPMAPPING
#ifdef GL_SGIS_generate_mipmap
// auto generate if possible and no mipmap data is given
if (HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
if (!Compressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
{
if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST);
else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
else
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_DONT_CARE);
AutomaticMipmapUpdate=true;
if (!Driver->queryFeature(EVDF_FRAMEBUFFER_OBJECT))
{
#ifdef GL_SGIS_generate_mipmap
if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST);
else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
else
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_DONT_CARE);
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
MipmapLegacyMode=true;
AutomaticMipmapUpdate=true;
#endif
}
else
{
if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
else
glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);
MipmapLegacyMode=false;
AutomaticMipmapUpdate=true;
}
}
// enable bilinear filter without mipmaps
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
StatesCache.BilinearFilter = true;
StatesCache.TrilinearFilter = false;
StatesCache.MipMapStatus = false;
}
// now get image data and upload to GPU
u32 compressedDataSize = 0;
void* source = image->lock();
if (newTexture)
{
if(Compressed)
{
if(ColorFormat == ECF_DXT1)
compressedDataSize = ((image->getDimension().Width + 3) / 4) * ((image->getDimension().Height + 3) / 4) * 8;
else if (ColorFormat == ECF_DXT2 || ColorFormat == ECF_DXT3 || ColorFormat == ECF_DXT4 || ColorFormat == ECF_DXT5)
compressedDataSize = ((image->getDimension().Width + 3) / 4) * ((image->getDimension().Height + 3) / 4) * 16;
Driver->extGlCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width,
image->getDimension().Height, 0, compressedDataSize, source);
}
else
#endif
glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,
image->getDimension().Height, 0, PixelFormat, PixelType, source);
}
else
{
if(Compressed)
{
if(ColorFormat == ECF_DXT1)
compressedDataSize = ((image->getDimension().Width + 3) / 4) * ((image->getDimension().Height + 3) / 4) * 8;
else if (ColorFormat == ECF_DXT2 || ColorFormat == ECF_DXT3 || ColorFormat == ECF_DXT4 || ColorFormat == ECF_DXT5)
compressedDataSize = ((image->getDimension().Width + 3) / 4) * ((image->getDimension().Height + 3) / 4) * 16;
Driver->extGlCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
image->getDimension().Height, PixelFormat, compressedDataSize, source);
}
else
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
image->getDimension().Height, PixelFormat, PixelType, source);
}
image->unlock();
if (!level && newTexture)
{
if (!Compressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
{
if (!MipmapLegacyMode && AutomaticMipmapUpdate)
{
glEnable(GL_TEXTURE_2D);
Driver->extGlGenerateMipmap(GL_TEXTURE_2D);
}
}
else if(HasMipMaps)
{
// Either generate manually due to missing capability
// or use predefined mipmap data
// or use predefined mipmap data eg. for compressed textures
AutomaticMipmapUpdate=false;
if (Compressed)
if (image->hasMipMap())
mipmapData = static_cast<u8*>(image->lock())+compressedDataSize;
else
HasMipMaps = false;
regenerateMipMapLevels(mipmapData);
}
if (HasMipMaps) // might have changed in regenerateMipMapLevels
{
// enable bilinear mipmap filter
......@@ -365,37 +472,7 @@ void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level)
StatesCache.TrilinearFilter = false;
StatesCache.MipMapStatus = true;
}
else
#else
HasMipMaps=false;
os::Printer::log("Did not create OpenGL texture mip maps.", ELL_INFORMATION);
#endif
{
// enable bilinear filter without mipmaps
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
StatesCache.BilinearFilter = true;
StatesCache.TrilinearFilter = false;
StatesCache.MipMapStatus = false;
}
}
// now get image data and upload to GPU
void* source = image->lock();
if (newTexture)
glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,
image->getDimension().Height, 0, PixelFormat, PixelType, source);
else
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
image->getDimension().Height, PixelFormat, PixelType, source);
image->unlock();
if (!MipmapLegacyMode && AutomaticMipmapUpdate)
{
glEnable(GL_TEXTURE_2D);
Driver->extGlGenerateMipmap(GL_TEXTURE_2D);
}
}
if (Driver->testGLError())
os::Printer::log("Could not glTexImage2D", ELL_ERROR);
......@@ -405,6 +482,9 @@ void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level)
//! lock function
void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel)
{
if (Compressed) // TO-DO
return 0;
// store info about which image is locked
IImage* image = (mipmapLevel==0)?Image:MipImage;
ReadOnlyLock |= (mode==ETLM_READ_ONLY);
......@@ -510,6 +590,9 @@ void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel)
//! unlock function
void COpenGLTexture::unlock()
{
if (Compressed) // TO-DO
return;
// test if miplevel or main texture was locked
IImage* image = MipImage?MipImage:Image;
if (!image)
......@@ -595,12 +678,16 @@ bool COpenGLTexture::hasMipMaps() const
//! modifying the texture
void COpenGLTexture::regenerateMipMapLevels(void* mipmapData)
{
if(Compressed && !mipmapData)
return;
if (AutomaticMipmapUpdate || !HasMipMaps || !Image)
return;
if ((Image->getDimension().Width==1) && (Image->getDimension().Height==1))
return;
// Manually create mipmaps or use prepared version
u32 compressedDataSize = 0;
u32 width=Image->getDimension().Width;
u32 height=Image->getDimension().Height;
u32 i=0;
......@@ -611,18 +698,38 @@ void COpenGLTexture::regenerateMipMapLevels(void* mipmapData)
width>>=1;
if (height>1)
height>>=1;
++i;
if (!target)
target = new u8[width*height*Image->getBytesPerPixel()];
// create scaled version if no mipdata available
if (!mipmapData)
Image->copyToScaling(target, width, height, Image->getColorFormat());
glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height,
0, PixelFormat, PixelType, target);
if (Compressed)
{
if(ColorFormat == ECF_DXT1)
compressedDataSize = ((width + 3) / 4) * ((height + 3) / 4) * 8;
else if (ColorFormat == ECF_DXT2 || ColorFormat == ECF_DXT3 || ColorFormat == ECF_DXT4 || ColorFormat == ECF_DXT5)
compressedDataSize = ((width + 3) / 4) * ((height + 3) / 4) * 16;
Driver->extGlCompressedTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width,
height, 0, compressedDataSize, target);
}
else
glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height,
0, PixelFormat, PixelType, target);
// get next prepared mipmap data if available
if (mipmapData)
{
mipmapData = static_cast<u8*>(mipmapData)+width*height*Image->getBytesPerPixel();
if (Compressed)
mipmapData = static_cast<u8*>(mipmapData)+compressedDataSize;
else
mipmapData = static_cast<u8*>(mipmapData)+width*height*Image->getBytesPerPixel();
target = static_cast<u8*>(mipmapData);
}
}
......
......@@ -169,6 +169,8 @@ protected:
bool ReadOnlyLock;
bool KeepImage;
bool Compressed;
mutable SStatesCache StatesCache;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment