ddsFile.cpp

Engine/source/gfx/bitmap/ddsFile.cpp

More...

Public Functions

DefineEngineFunction(getActiveDDSFiles , S32 , () , "Returns the <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1ad43c3812e6d13e0518d9f8b8f463ffcf">count</a> of active DDSs files in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">memory.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rendering\n</a>" )

Detailed Description

Public Functions

DefineEngineFunction(getActiveDDSFiles , S32 , () , "Returns the <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1ad43c3812e6d13e0518d9f8b8f463ffcf">count</a> of active DDSs files in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">memory.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rendering\n</a>" )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "gfx/bitmap/ddsFile.h"
 26#include "gfx/bitmap/ddsData.h"
 27#include "gfx/bitmap/bitmapUtils.h"
 28#include "gfx/bitmap/imageUtils.h"
 29#include "gfx/gfxDevice.h"
 30#include "core/util/fourcc.h"
 31#include "console/console.h"
 32#include "core/resourceManager.h"
 33#include "core/stream/fileStream.h"
 34#include "gfx/bitmap/gBitmap.h"
 35#include "console/engineAPI.h"
 36
 37#include <squish.h>
 38
 39S32 DDSFile::smActiveCopies = 0;
 40U32 DDSFile::smDropMipCount = 0;
 41
 42DDSFile::DDSFile( const DDSFile &dds )
 43   :  mFlags( dds.mFlags ),
 44      mHeight( dds.mHeight ),
 45      mWidth( dds.mWidth ),
 46      mDepth( dds.mDepth ),
 47      mPitchOrLinearSize( dds.mPitchOrLinearSize ),
 48      mMipMapCount( dds.mMipMapCount ),
 49      mFormat( dds.mFormat ),
 50      mBytesPerPixel( dds.mBytesPerPixel ),
 51      mFourCC( dds.mFourCC ),
 52      mCacheString( dds.mCacheString ),
 53      mSourcePath( dds.mSourcePath ),
 54      mHasTransparency( dds.mHasTransparency )
 55{
 56   VECTOR_SET_ASSOCIATION( mSurfaces );
 57   smActiveCopies++;
 58   
 59   for ( U32 i=0; i < dds.mSurfaces.size(); i++ )
 60   {
 61      SurfaceData *surface = NULL;
 62      if ( dds.mSurfaces[i] )
 63         surface = new SurfaceData;
 64
 65      mSurfaces.push_back( surface );
 66
 67      if ( !surface )
 68         continue;
 69
 70      for ( U32 m=0; m < dds.mSurfaces[i]->mMips.size(); m++ )
 71      {
 72         U32 size = dds.getSurfaceSize( m );
 73         surface->mMips.push_back(new U8[size]);         
 74         dMemcpy( surface->mMips.last(), dds.mSurfaces[i]->mMips[m], size );
 75      }
 76   }
 77}
 78
 79void DDSFile::clear()
 80{
 81   mFlags = 0;
 82   mHeight = mWidth = mDepth = mPitchOrLinearSize = mMipMapCount = 0;
 83   mFormat = GFXFormatR8G8B8;
 84}
 85
 86U32 DDSFile::getSurfacePitch( U32 mipLevel ) const
 87{
 88   if(mFlags.test(CompressedData))
 89   {
 90      U32 sizeMultiple = 0;
 91
 92      switch(mFormat)
 93      {
 94      case GFXFormatBC1:
 95      case GFXFormatBC4:
 96         sizeMultiple = 8;
 97         break;
 98      case GFXFormatBC2:
 99      case GFXFormatBC3:      
100      case GFXFormatBC5:
101         sizeMultiple = 16;
102         break;
103      default:
104         AssertISV(false, "DDSFile::getPitch - invalid compressed texture format, we only support DXT1-5 right now.");
105         break;
106      }
107
108      // Maybe need to be DWORD aligned?
109      U32 align = getMax(U32(1), getWidth(mipLevel)/4) * sizeMultiple;
110      align += 3; align >>=2; align <<=2;
111      return align;
112
113   }
114   else
115      return getWidth(mipLevel) * mBytesPerPixel;
116}
117
118U32 DDSFile::getSurfaceSize( U32 height, U32 width, U32 mipLevel ) const 
119{
120   // Bump by the mip level.
121   height = getMax(U32(1), height >> mipLevel);
122   width  = getMax(U32(1), width >> mipLevel);
123
124   if(mFlags.test(CompressedData))
125   {
126      // From the directX docs:
127      // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
128
129      U32 sizeMultiple = 0;
130
131      switch(mFormat)
132      {
133      case GFXFormatBC1:
134      case GFXFormatBC4:
135         sizeMultiple = 8;
136         break;
137      case GFXFormatBC2:
138      case GFXFormatBC3:      
139      case GFXFormatBC5:
140         sizeMultiple = 16;
141         break;
142      default:
143         AssertISV(false, "DDSFile::getSurfaceSize - invalid compressed texture format, we only support DXT1-5 right now.");
144         break;
145      }
146
147      return getMax(U32(1), width/4) * getMax(U32(1), height/4) * sizeMultiple;
148   }
149   else
150   {
151      return height * width* mBytesPerPixel;
152   }
153}
154
155U32 DDSFile::getSizeInBytes() const
156{
157   // TODO: This doesn't take mDepth into account, so
158   // it doesn't work right for volume textures!
159
160   U32 bytes = 0;
161   if (mFlags.test(CubeMapFlag))
162   {
163      for(U32 cubeFace=0;cubeFace < Cubemap_Surface_Count;cubeFace++)
164         for (U32 i = 0; i < mMipMapCount; i++)
165            bytes += getSurfaceSize(mHeight, mWidth, i);
166   }
167   else
168   {
169      for (U32 i = 0; i < mMipMapCount; i++)
170         bytes += getSurfaceSize(mHeight, mWidth, i);
171   }
172
173   return bytes;
174}
175
176U32 DDSFile::getSizeInBytes( GFXFormat format, U32 height, U32 width, U32 mipLevels )
177{
178   AssertFatal( ImageUtil::isCompressedFormat(format), 
179      "DDSFile::getSizeInBytes - Must be a Block Compression format!" );
180
181   // From the directX docs:
182   // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
183
184   U32 sizeMultiple = 0;
185   if ( format == GFXFormatBC1 || format == GFXFormatBC1_SRGB || format == GFXFormatBC4)
186      sizeMultiple = 8;
187   else
188      sizeMultiple = 16;
189
190   U32 mipHeight, mipWidth; 
191   U32 bytes = 0;
192   for ( U32 m=0; m < mipLevels; m++ )
193   {
194      mipHeight = getMax( U32(1), height >> m );
195      mipWidth  = getMax( U32(1), width >> m );
196
197      bytes += getMax( U32(1), mipWidth / 4 ) * 
198               getMax( U32(1), mipHeight / 4 ) * sizeMultiple;
199   }
200
201   return bytes;
202}
203
204bool DDSFile::readHeader(Stream &s)
205{
206   U32 fourcc;
207   // Read the FOURCC
208   s.read(&fourcc);
209
210   if(fourcc != DDS_MAGIC)
211   {
212      Con::errorf("DDSFile::readHeader - unexpected magic number, wanted 'DDS '!");
213      return false;
214   }
215
216   //dds headers
217   dds::DDS_HEADER header = {};
218   dds::DDS_HEADER_DXT10 dx10header = {};
219   //todo DX10 formats
220   bool hasDx10Header = false;
221
222   //read in header
223   s.read(DDS_HEADER_SIZE, &header);
224   //check for dx10 header support
225   if ((header.ddspf.flags & DDS_FOURCC) && (header.ddspf.fourCC == dds::D3DFMT_DX10))
226   {
227      //read in dx10 header
228      s.read(DDS_HEADER_DX10_SIZE, &dx10header);
229      if (!dds::validateHeaderDx10(dx10header))
230         return false;
231
232      hasDx10Header = true;
233   }
234
235   //make sure our dds header is valid
236   if (!dds::validateHeader(header))
237      return false;
238
239   // store details
240   mPitchOrLinearSize = header.pitchOrLinearSize;
241   mMipMapCount = header.mipMapCount ? header.mipMapCount : 1;
242   mHeight = header.height;
243   mWidth = header.width;
244   mDepth = header.depth;
245   mFourCC = header.ddspf.fourCC;
246
247   //process dx10 header
248   if (hasDx10Header)
249   {
250      if (dx10header.arraySize > 1)
251      {
252         Con::errorf("DDSFile::readHeader - DX10 arrays not supported");
253         return false;
254      }
255
256      mFormat = dds::getGFXFormat(dx10header.dxgiFormat);
257      //make sure getGFXFormat gave us a valid format
258      if (mFormat == GFXFormat_FIRST)
259         return false;
260      //cubemap
261      if (dx10header.miscFlag & dds::D3D10_RESOURCE_MISC_TEXTURECUBE)
262      {
263         mFlags.set(CubeMap_All_Flags | ComplexFlag);
264      }
265
266      mHasTransparency = ImageUtil::isAlphaFormat(mFormat);
267
268      //mip map flag
269      if (mMipMapCount > 1)
270         mFlags.set(MipMapsFlag | ComplexFlag);
271
272      if (ImageUtil::isCompressedFormat(mFormat))
273         mFlags.set(CompressedData);
274      else
275      {
276         mBytesPerPixel = dds::getBitsPerPixel(dx10header.dxgiFormat) / 8;
277         mFlags.set(RGBData);
278      }
279
280      // we finished now
281      return true;
282   }
283
284   //process regular header
285
286   //D3DFMT_DX10 is caught above, no need to check now
287   if (header.ddspf.flags & DDS_FOURCC)
288   {
289      mFormat = dds::getGFXFormat(mFourCC);
290      //make sure getGFXFormat gave us a valid format
291      if (mFormat == GFXFormat_FIRST)
292         return false;
293
294      if (ImageUtil::isCompressedFormat(mFormat))
295         mFlags.set(CompressedData);
296      else
297      {
298         switch (header.ddspf.fourCC)
299         {
300         case 36: // D3DFMT_A16B16G16R16
301            mBytesPerPixel = 8;
302            break;
303         case 110: // D3DFMT_Q16W16V16U16
304            mBytesPerPixel = 8;
305            break;
306         case 111: // D3DFMT_R16F
307            mBytesPerPixel = 2;
308            break;
309         case 112: // D3DFMT_G16R16F
310            mBytesPerPixel = 4;
311            break;
312         case 113: // D3DFMT_A16B16G16R16F
313            mBytesPerPixel = 8;
314            break;
315         case 114: // D3DFMT_R32F
316            mBytesPerPixel = 4;
317            break;
318         case 115: // D3DFMT_G32R32F
319            mBytesPerPixel = 8;
320            break;
321         case 116: // D3DFMT_A32B32G32R32F
322            mBytesPerPixel = 16;
323            break;
324         }
325
326         mFlags.set(RGBData);
327      }
328   }
329   else
330   {
331      mFormat = dds::getGFXFormat(header.ddspf);
332      //make sure getGFXFormat gave us a valid format
333      if (mFormat == GFXFormat_FIRST)
334         return false;
335
336      mBytesPerPixel = header.ddspf.bpp / 8;
337      mFlags.set(RGBData);
338   }
339
340   //mip map flag
341   if (mMipMapCount > 1)
342      mFlags.set(MipMapsFlag | ComplexFlag);
343
344   //set transparency flag
345   mHasTransparency = (header.ddspf.flags & DDS_ALPHAPIXELS);
346
347   if (header.flags & DDS_HEADER_FLAGS_LINEARSIZE)
348      mFlags.set(LinearSizeFlag);
349   else if (header.flags & DDS_HEADER_FLAGS_PITCH)
350      mFlags.set(PitchSizeFlag);
351
352   //set cubemap flags
353   if (header.cubemapFlags & DDS_CUBEMAP)
354   {
355      mFlags.set(CubeMapFlag | ComplexFlag);
356      // Store the face flags too.
357      if (header.cubemapFlags & DDS_CUBEMAP_POSITIVEX) mFlags.set(CubeMap_PosX_Flag);
358      if (header.cubemapFlags & DDS_CUBEMAP_NEGATIVEX) mFlags.set(CubeMap_NegX_Flag);
359      if (header.cubemapFlags & DDS_CUBEMAP_POSITIVEY) mFlags.set(CubeMap_PosY_Flag);
360      if (header.cubemapFlags & DDS_CUBEMAP_NEGATIVEY) mFlags.set(CubeMap_NegY_Flag);
361      if (header.cubemapFlags & DDS_CUBEMAP_POSITIVEZ) mFlags.set(CubeMap_PosZ_Flag);
362      if (header.cubemapFlags & DDS_CUBEMAP_NEGATIVEZ) mFlags.set(CubeMap_NegZ_Flag);
363   }
364
365
366
367   return true;
368}
369
370bool DDSFile::read(Stream &s, U32 dropMipCount)
371{
372   if( !readHeader(s) )
373   {
374      Con::errorf("DDSFile::read - error reading header!");
375      return false;
376   }
377
378   // If we're droping mips then make sure we have enough.
379   dropMipCount = getMin( dropMipCount, mMipMapCount - 1 );
380
381   // At this point we know what sort of image we contain. So we should
382   // allocate some buffers, and read it in.
383
384   // How many surfaces are we talking about?
385   if(mFlags.test(CubeMapFlag))
386   {
387      mSurfaces.setSize( Cubemap_Surface_Count );
388
389      for ( U32 i=0; i < Cubemap_Surface_Count; i++ )
390      {
391         // Does the cubemap contain this surface?
392         if ( mFlags.test( CubeMap_PosX_Flag + ( i << 1 ) ) )
393            mSurfaces[i] = new SurfaceData();
394         else
395         {
396            mSurfaces[i] = NULL;
397            continue;
398         }
399
400         // Load all the mips.
401         for(S32 mip=0; mip<mMipMapCount; mip++)
402            mSurfaces[i]->readNextMip(this, s, mHeight, mWidth, mip, mip < dropMipCount );
403      }
404
405   }
406   else if (mFlags.test(VolumeFlag))
407   {
408      // Do something with volume
409   }
410   else
411   {
412      // It's a plain old texture.
413
414      // First allocate a SurfaceData to stick this in.
415      mSurfaces.push_back(new SurfaceData());
416
417      // Load however many mips there are.
418      for(S32 i=0; i<mMipMapCount; i++)
419         mSurfaces.last()->readNextMip(this, s, mHeight, mWidth, i, i < dropMipCount);
420
421      // Ok, we're done.
422   }
423
424   // If we're dropping mips then fix up the stats.
425   if ( dropMipCount > 0 )
426   {
427      // Fix up the pitch and/or linear size.
428      if( mFlags.test( LinearSizeFlag ) )
429         mPitchOrLinearSize = getSurfaceSize( dropMipCount );
430      else if ( mFlags.test( PitchSizeFlag ) )
431         mPitchOrLinearSize = getSurfacePitch( dropMipCount );
432
433      // Now fix up the rest of the 
434      mMipMapCount = getMax( (U32)1, mMipMapCount - dropMipCount );
435      mHeight = getHeight( dropMipCount );
436      mWidth = getWidth( dropMipCount );
437   }
438
439   return true;
440}
441
442bool DDSFile::writeHeader( Stream &s )
443{
444   // write DDS magic
445   U32 magic = DDS_MAGIC;
446   s.write(magic);
447
448   dds::DDS_HEADER header = {};
449   dds::DDS_HEADER_DXT10 dx10header = {};
450
451   bool hasDx10Header = false;
452   //flags
453   U32 surfaceFlags = DDS_SURFACE_FLAGS_TEXTURE;
454   U32 cubemapFlags = 0;
455   U32 headerFlags = DDS_HEADER_FLAGS_TEXTURE;
456
457   //pixel format
458   const dds::DDS_PIXELFORMAT &format = dds::getDDSFormat(mFormat);
459
460   // todo better dx10 support
461   if (format.fourCC == dds::D3DFMT_DX10)
462   {
463      dx10header.dxgiFormat = dds::getDXGIFormat(mFormat);
464      dx10header.arraySize = 1;
465      dx10header.resourceDimension = dds::D3D10_RESOURCE_DIMENSION_TEXTURE2D;
466      dx10header.miscFlag = 0;
467      dx10header.miscFlags2 = 0;
468      hasDx10Header = true;
469   }
470
471   if (mFlags.test(CompressedData))
472      headerFlags |= DDS_HEADER_FLAGS_LINEARSIZE;
473   else
474      headerFlags |= DDS_HEADER_FLAGS_PITCH;
475
476   if (mMipMapCount > 1)
477   {
478      surfaceFlags |= DDS_SURFACE_FLAGS_MIPMAP;
479      headerFlags |= DDS_HEADER_FLAGS_MIPMAP;
480   }   
481
482   //cubemap flags
483   if (mFlags.test(CubeMapFlag))
484   {
485      surfaceFlags |= DDS_SURFACE_FLAGS_CUBEMAP;
486      cubemapFlags |= DDS_CUBEMAP_ALLFACES;
487      if (hasDx10Header)
488         dx10header.miscFlag = dds::D3D10_RESOURCE_MISC_TEXTURECUBE;
489   }
490
491   //volume texture
492   if (mDepth > 0)
493   {
494      headerFlags |= DDS_HEADER_FLAGS_VOLUME;
495      dx10header.resourceDimension = dds::D3D10_RESOURCE_DIMENSION_TEXTURE3D;
496   }
497
498   //main dds header
499   header.size = sizeof(dds::DDS_HEADER);
500   header.flags = headerFlags;
501   header.height = mHeight;
502   header.width = mWidth;
503   header.pitchOrLinearSize = mPitchOrLinearSize;
504   header.depth = mDepth;
505   header.ddspf = format;
506   header.mipMapCount = mMipMapCount;
507   header.surfaceFlags = surfaceFlags;   
508   header.cubemapFlags = cubemapFlags;   
509   memset(header.reserved1, 0, sizeof(header.reserved1));
510   memset(header.reserved2, 0, sizeof(header.reserved2));
511
512   //check our header is ok
513   if (!dds::validateHeader(header))
514      return false;
515
516   //Write out the header
517   s.write(DDS_HEADER_SIZE, &header);
518   
519   //Write out dx10 header
520   if (hasDx10Header)
521      s.write(DDS_HEADER_DX10_SIZE, &dx10header);
522
523   return true;
524}
525
526bool DDSFile::write( Stream &s )
527{
528   if(!writeHeader(s))
529   {
530      Con::errorf("DDSFile::write - error writing header!");
531      return false;
532   }
533
534   // How many surfaces are we talking about?
535   if(mFlags.test(CubeMapFlag))
536   {
537      // Do something with cubemaps.
538      for (U32 cubeFace = 0; cubeFace < Cubemap_Surface_Count; cubeFace++)
539      {
540         // write the mips
541         for (S32 i = 0; i < mMipMapCount; i++)
542            mSurfaces[cubeFace]->writeNextMip(this, s, mHeight, mWidth, i);
543      }
544   }
545   else if (mFlags.test(VolumeFlag))
546   {
547      // Do something with volume
548   }
549   else
550   {
551      // It's a plain old texture.
552
553      // Load however many mips there are.
554      for ( S32 i = 0; i < mMipMapCount; i++ )
555         mSurfaces.last()->writeNextMip(this, s, mHeight, mWidth, i);
556
557      // Ok, we're done.
558   }
559
560   return true;
561}
562
563void DDSFile::SurfaceData::dumpImage(DDSFile *dds, U32 mip, const char *file)
564{
565   GBitmap *foo = new GBitmap(dds->mWidth >> mip, dds->mHeight >> mip, false, dds->mFormat);
566
567   // Copy our data in.
568   dMemcpy(foo->getWritableBits(), mMips[mip], dds->getSurfaceSize(dds->mHeight, dds->mWidth, mip) );
569   
570   FileStream  stream;
571
572   stream.open( file, Torque::FS::File::Write );
573
574   if ( stream.getStatus() == Stream::Ok )
575   {
576      // Write it out.
577      foo->writeBitmap("png", stream);
578   }
579
580   // Clean up.
581   delete foo;
582}
583
584void DDSFile::SurfaceData::readNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel, bool skip)
585{
586   U32 size = dds->getSurfaceSize(height, width, mipLevel);
587
588   // If we're skipping this mip then seek forward.
589   if ( skip )
590      s.setPosition( s.getPosition() + size );
591   else
592   {
593      mMips.push_back(new U8[size]);
594      if(!s.read(size, mMips.last()))
595         Con::errorf("DDSFile::SurfaceData::addNextMip - failed to read mip!");
596   }
597}
598
599void DDSFile::SurfaceData::writeNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel)
600{
601   U32 size = dds->getSurfaceSize(height, width, mipLevel);
602   if(!s.write(size, mMips[mipLevel]))
603      Con::errorf("DDSFile::SurfaceData::writeNextMip - failed to write mip!");
604}
605
606//------------------------------------------------------------------------------
607
608template<> void *Resource<DDSFile>::create( const Torque::Path &path )
609{
610#ifdef TORQUE_DEBUG_RES_MANAGER
611   Con::printf( "Resource<DDSFile>::create - [%s]", path.getFullPath().c_str() );
612#endif
613
614   FileStream stream;
615
616   stream.open( path.getFullPath(), Torque::FS::File::Read );
617
618   if ( stream.getStatus() != Stream::Ok )
619      return NULL;
620
621   DDSFile *retDDS = new DDSFile;
622
623   if( !retDDS->read( stream, DDSFile::smDropMipCount ) )
624   {
625      delete retDDS;
626      return NULL;
627   }
628   else
629   {
630      // Set source file name
631      retDDS->mSourcePath = path;
632      retDDS->mCacheString = Torque::Path::Join( path.getRoot(), ':', path.getPath() );
633      retDDS->mCacheString = Torque::Path::Join( retDDS->mCacheString, '/', path.getFileName() );
634   }
635
636   return retDDS;
637}
638
639template<> ResourceBase::Signature  Resource<DDSFile>::signature()
640{
641   return MakeFourCC('D','D','S',' '); // Direct Draw Surface
642}
643
644Resource<DDSFile> DDSFile::load( const Torque::Path &path, U32 dropMipCount )
645{
646   PROFILE_SCOPE( DDSFile_load );
647   
648   // HACK:  It sucks that we cannot pass parameters into 
649   // the resource manager loading system.
650   DDSFile::smDropMipCount = dropMipCount;
651   Resource<DDSFile> ret = ResourceManager::get().load( path );
652   DDSFile::smDropMipCount = 0;
653
654   // Any kind of error checking or path stepping can happen here
655
656   return ret;
657}
658
659//------------------------------------------------------------------------------
660
661DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
662{
663   if( gbmp == NULL )
664      return NULL;
665
666   DDSFile *ret = new DDSFile;
667
668   // Set up the DDSFile properties that matter. Since this is a GBitmap, there
669   // are assumptions that can be made
670   ret->mHeight = gbmp->getHeight();
671   ret->mWidth = gbmp->getWidth();
672   ret->mDepth = 0;
673   ret->mFormat = gbmp->getFormat();
674   ret->mFlags.set(RGBData);
675   ret->mBytesPerPixel = gbmp->getBytesPerPixel();
676   ret->mMipMapCount = gbmp->getNumMipLevels();
677   ret->mHasTransparency = gbmp->getHasTransparency();
678
679   // ASSUMPTION!!!
680   // This _most likely_ does not belong here, but it is safe to assume that if
681   // a GBitmap is 24-bit, and it's being converted to a DDS, it is most likely
682   // going to be either:
683   // a) Uploaded as a 32-bit texture, and just needs to be padded to RGBX
684   // b) Uploaded as a compressed format, and needs to be padded to 32-bits anyway
685   if( ret->mFormat == GFXFormatR8G8B8 )
686   {
687      ret->mFormat = GFXFormatR8G8B8X8;
688      ret->mBytesPerPixel = 4;
689   }
690
691   if( ret->mMipMapCount > 1 )
692      ret->mFlags.set(MipMapsFlag);
693
694   // One surface per GBitmap
695   ret->mSurfaces.push_back( new SurfaceData() );
696
697   // Load the mips
698   for( S32 i = 0; i < ret->mMipMapCount; i++ )
699   {
700      const U32 mipSz = ret->getSurfaceSize(i);
701      ret->mSurfaces.last()->mMips.push_back( new U8[mipSz] );
702
703      U8 *mipMem = ret->mSurfaces.last()->mMips.last();
704      
705      // If this is a straight copy, just do it, otherwise (ugh)
706      if( ret->mFormat == gbmp->getFormat() )
707         dMemcpy( mipMem, gbmp->getBits(i), mipSz );
708      else
709      {
710         // Assumption:
711         AssertFatal( gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert." );
712
713         for( S32 pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++ )
714         {
715            U8 *dst = &mipMem[pxl * ret->mBytesPerPixel];
716            const U8 *src = &gbmp->getBits(i)[pxl * gbmp->getBytesPerPixel()];
717            dMemcpy( dst, src, gbmp->getBytesPerPixel() * sizeof(U8) );
718            dst[ret->mBytesPerPixel - 1] = 255;
719         } 
720      }
721
722      // Uncomment to debug-dump each mip level
723      //ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
724   }
725
726   return ret;
727}
728
729DDSFile *DDSFile::createDDSCubemapFileFromGBitmaps(GBitmap **gbmps)
730{
731   if (gbmps == NULL)
732      return NULL;
733
734   AssertFatal(gbmps[0], "createDDSCubemapFileFromGBitmaps bitmap 0 is null");
735   AssertFatal(gbmps[1], "createDDSCubemapFileFromGBitmaps bitmap 1 is null");
736   AssertFatal(gbmps[2], "createDDSCubemapFileFromGBitmaps bitmap 2 is null");
737   AssertFatal(gbmps[3], "createDDSCubemapFileFromGBitmaps bitmap 3 is null");
738   AssertFatal(gbmps[4], "createDDSCubemapFileFromGBitmaps bitmap 4 is null");
739   AssertFatal(gbmps[5], "createDDSCubemapFileFromGBitmaps bitmap 5 is null");
740
741   DDSFile *ret = new DDSFile;
742   //all cubemaps have the same dimensions and formats
743   GBitmap *pBitmap = gbmps[0];
744
745   GFXFormat fmt = pBitmap->getFormat();
746   if (fmt != GFXFormatR8G8B8A8 && fmt != GFXFormatR16G16B16A16F)
747   {
748      Con::errorf("createDDSCubemapFileFromGBitmaps: unsupported format");
749      return NULL;
750   }
751
752   // Set up the DDSFile properties that matter. Since this is a GBitmap, there
753   // are assumptions that can be made
754   ret->mHeight = pBitmap->getHeight();
755   ret->mWidth = pBitmap->getWidth();
756   ret->mDepth = 0;
757   ret->mFormat = pBitmap->getFormat();
758   ret->mFlags.set( RGBData | CubeMapFlag | CubeMap_PosX_Flag | CubeMap_NegX_Flag | CubeMap_PosY_Flag |
759      CubeMap_NegY_Flag | CubeMap_PosZ_Flag | CubeMap_NegZ_Flag);
760   ret->mBytesPerPixel = pBitmap->getBytesPerPixel();
761   //todo implement mip mapping
762   ret->mMipMapCount = pBitmap->getNumMipLevels();
763   ret->mHasTransparency = pBitmap->getHasTransparency();
764
765   for (U32 cubeFace = 0; cubeFace < Cubemap_Surface_Count; cubeFace++)
766   {
767      ret->mSurfaces.push_back(new SurfaceData());
768      // Load the mips
769      for (S32 i = 0; i < ret->mMipMapCount; i++)
770      {
771         const U32 mipSz = ret->getSurfaceSize(i);
772         ret->mSurfaces.last()->mMips.push_back(new U8[mipSz]);
773
774         U8 *mipMem = ret->mSurfaces.last()->mMips.last();
775         //straight copy
776         dMemcpy(mipMem, gbmps[cubeFace]->getBits(i), mipSz);
777      }
778   }
779
780   return ret;
781}
782
783bool DDSFile::decompressToGBitmap(GBitmap *dest)
784{
785   // TBD: do we support other formats?
786   if (mFormat != GFXFormatBC1 && mFormat != GFXFormatBC2 && mFormat != GFXFormatBC3)
787      return false;
788
789   dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), GFXFormatR8G8B8A8);
790
791   // Decompress and copy mips...
792
793   U32 numMips = getMipLevels();
794
795   for (U32 i = 0; i < numMips; i++)
796   {
797      U8 *addr = dest->getAddress(0, 0, i);
798      const U8 *mipBuffer = mSurfaces[0]->mMips[i];
799      ImageUtil::decompress(mipBuffer, addr, getWidth(i), getHeight(i), mFormat);
800
801   }
802
803   return true;
804}
805
806DefineEngineFunction( getActiveDDSFiles, S32, (),,
807   "Returns the count of active DDSs files in memory.\n"
808   "@ingroup Rendering\n" )
809{
810   return DDSFile::smActiveCopies;
811}
812