gBitmap.cpp

Engine/source/gfx/bitmap/gBitmap.cpp

More...

Public Functions

DefineEngineFunction(getBitmapInfo , String , (const char *filename) , "Returns image info in the following format: width TAB height TAB bytesPerPixel TAB format. " "It will return an empty string <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> is not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">found.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rendering\n</a>" )

Detailed Description

Public Functions

DefineEngineFunction(getBitmapInfo , String , (const char *filename) , "Returns image info in the following format: width TAB height TAB bytesPerPixel TAB format. " "It will return an empty string <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> is not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">found.\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/gBitmap.h"
  26
  27#include "core/resourceManager.h"
  28#include "core/stream/fileStream.h"
  29#include "core/strings/stringFunctions.h"
  30#include "core/color.h"
  31#include "gfx/bitmap/bitmapUtils.h"
  32#include "math/mRect.h"
  33#include "console/console.h"
  34#include "platform/profiler.h"
  35#include "console/engineAPI.h"
  36
  37using namespace Torque;
  38
  39const U32 GBitmap::csFileVersion   = 3;
  40
  41Vector<GBitmap::Registration>   GBitmap::sRegistrations( __FILE__, __LINE__ );
  42
  43
  44GBitmap::GBitmap()
  45 : mInternalFormat(GFXFormatR8G8B8),
  46   mBits(NULL),
  47   mByteSize(0),
  48   mWidth(0),
  49   mHeight(0),
  50   mBytesPerPixel(0),
  51   mNumMipLevels(0),
  52   mHasTransparency(false)
  53{
  54   std::fill_n(mMipLevelOffsets, c_maxMipLevels, 0xffffffff);
  55}
  56
  57GBitmap::GBitmap(const GBitmap& rCopy)
  58{
  59   mInternalFormat = rCopy.mInternalFormat;
  60
  61   mByteSize = rCopy.mByteSize;
  62   mBits    = new U8[mByteSize];
  63   dMemcpy(mBits, rCopy.mBits, mByteSize);
  64
  65   mWidth         = rCopy.mWidth;
  66   mHeight        = rCopy.mHeight;
  67   mBytesPerPixel = rCopy.mBytesPerPixel;
  68   mNumMipLevels  = rCopy.mNumMipLevels;
  69   dMemcpy(mMipLevelOffsets, rCopy.mMipLevelOffsets, sizeof(mMipLevelOffsets));
  70
  71   mHasTransparency = rCopy.mHasTransparency;
  72}
  73
  74
  75GBitmap::GBitmap(const U32  in_width,
  76                 const U32  in_height,
  77                 const bool in_extrudeMipLevels,
  78                 const GFXFormat in_format)
  79 : mBits(NULL),
  80   mByteSize(0)
  81{
  82   for (U32 i = 0; i < c_maxMipLevels; i++)
  83      mMipLevelOffsets[i] = 0xffffffff;
  84
  85   allocateBitmap(in_width, in_height, in_extrudeMipLevels, in_format);
  86
  87   mHasTransparency = false;
  88}
  89
  90GBitmap::GBitmap(const U32  in_width,
  91                 const U32  in_height,
  92                 const U8*  data )
  93 : mBits(NULL),
  94   mByteSize(0)
  95{
  96   allocateBitmap(in_width, in_height, false, GFXFormatR8G8B8A8);
  97
  98   mHasTransparency = false;
  99
 100   for (U32 x = 0; x < in_width; x++)
 101   {
 102      for (U32 y = 0; y < in_height; y++)
 103      {
 104         U32 offset = (x + y * in_width) * 4;
 105
 106         ColorI color(data[offset],
 107                      data[offset + 1],
 108                      data[offset + 2],
 109                      data[offset + 3]);
 110
 111         if (color.alpha < 255)
 112            mHasTransparency = true;
 113
 114         setColor(x, y, color);
 115      }
 116   }
 117}
 118
 119
 120//--------------------------------------------------------------------------
 121
 122GBitmap::~GBitmap()
 123{
 124   deleteImage();
 125}
 126
 127//--------------------------------------------------------------------------
 128
 129void GBitmap::sRegisterFormat( const GBitmap::Registration &reg )
 130{
 131   U32 insert = sRegistrations.size();
 132   for ( U32 i = 0; i < sRegistrations.size(); i++ )
 133   {
 134      if ( sRegistrations[i].priority <= reg.priority )
 135      {
 136         insert = i;
 137         break;
 138      }
 139   }
 140
 141   sRegistrations.insert( insert, reg );
 142}
 143
 144const GBitmap::Registration   *GBitmap::sFindRegInfo( const String &extension )
 145{
 146   for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ )
 147   {
 148      const GBitmap::Registration   &reg = GBitmap::sRegistrations[i];
 149      const Vector<String>          &extensions = reg.extensions;
 150
 151      for ( U32 j = 0; j < extensions.size(); ++j )
 152      {    
 153         if ( extensions[j].equal( extension, String::NoCase ) )
 154            return &reg;
 155      }
 156   }
 157
 158   return NULL;
 159}
 160
 161bool GBitmap::sFindFile( const Path &path, Path *outPath )
 162{
 163   PROFILE_SCOPE( GBitmap_sFindFile );
 164
 165   const String origExt( String::ToLower( path.getExtension() ) );
 166
 167   Path tryPath( path );
 168
 169   for ( U32 i = 0; i < sRegistrations.size(); i++ )
 170   {
 171      const Registration &reg = sRegistrations[i];
 172      const Vector<String> &extensions = reg.extensions;
 173
 174      for ( U32 j = 0; j < extensions.size(); ++j )
 175      {
 176         // We've already tried this one.
 177         if ( extensions[j] == origExt )
 178            continue;
 179
 180         tryPath.setExtension( extensions[j] );
 181         if ( !Torque::FS::IsFile( tryPath ) )
 182            continue;
 183
 184         if ( outPath )
 185            *outPath = tryPath;
 186         return true;
 187      }
 188   }
 189
 190   return false;
 191}
 192
 193bool GBitmap::sFindFiles( const Path &path, Vector<Path> *outFoundPaths )
 194{
 195   PROFILE_SCOPE( GBitmap_sFindFiles );
 196   
 197   Path  tryPath( path );
 198
 199   for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ )
 200   {
 201      const GBitmap::Registration   &reg = GBitmap::sRegistrations[i];
 202      const Vector<String>          &extensions = reg.extensions;
 203
 204      for ( U32 j = 0; j < extensions.size(); ++j )
 205      {
 206         tryPath.setExtension( extensions[j] );
 207
 208         if ( Torque::FS::IsFile( tryPath ) )
 209         {
 210            if ( outFoundPaths )
 211               outFoundPaths->push_back( tryPath );
 212            else
 213               return true;
 214         }
 215      }
 216   }
 217
 218   return outFoundPaths ? outFoundPaths->size() > 0 : false;
 219}
 220
 221String GBitmap::sGetExtensionList()
 222{
 223   String list;
 224
 225   for ( U32 i = 0; i < sRegistrations.size(); i++ )
 226   {
 227      const Registration &reg = sRegistrations[i];
 228      for ( U32 j = 0; j < reg.extensions.size(); j++ )
 229      {
 230         list += reg.extensions[j];
 231         list += " ";         
 232      }
 233   }
 234
 235   return list;
 236}
 237
 238//--------------------------------------------------------------------------
 239void GBitmap::deleteImage()
 240{
 241   delete [] mBits;
 242   mBits    = NULL;
 243   mByteSize = 0;
 244
 245   mWidth        = 0;
 246   mHeight       = 0;
 247   mNumMipLevels = 0;
 248}
 249
 250
 251//--------------------------------------------------------------------------
 252
 253void GBitmap::copyRect(const GBitmap *src, const RectI &srcRect, const Point2I &dstPt, const U32 srcMipLevel, const U32 dstMipLevel)
 254{
 255   if(src->getFormat() != getFormat())
 256      return;
 257   if(srcRect.extent.x + srcRect.point.x > src->getWidth(srcMipLevel) || srcRect.extent.y + srcRect.point.y > src->getHeight(srcMipLevel))
 258      return;
 259   if(srcRect.extent.x + dstPt.x > getWidth(dstMipLevel) || srcRect.extent.y + dstPt.y > getHeight(dstMipLevel))
 260      return;
 261
 262   for(U32 i = 0; i < srcRect.extent.y; i++)
 263   {
 264      dMemcpy(getAddress(dstPt.x, dstPt.y + i, dstMipLevel),
 265              src->getAddress(srcRect.point.x, srcRect.point.y + i, srcMipLevel),
 266              mBytesPerPixel * srcRect.extent.x);
 267   }
 268}
 269
 270//--------------------------------------------------------------------------
 271void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool in_extrudeMipLevels, const GFXFormat in_format )
 272{
 273   //-------------------------------------- Some debug checks...
 274   U32 svByteSize = mByteSize;
 275   U8 *svBits = mBits;
 276
 277   AssertFatal(in_width != 0 && in_height != 0, "GBitmap::allocateBitmap: width or height is 0");
 278
 279   if (in_extrudeMipLevels == true) 
 280   {
 281      AssertFatal(isPow2(in_width) == true && isPow2(in_height) == true, "GBitmap::GBitmap: in order to extrude mip levels, bitmap w/h must be pow2");
 282   }
 283
 284   mInternalFormat = in_format;
 285   mWidth          = in_width;
 286   mHeight         = in_height;
 287
 288   mBytesPerPixel = 1;
 289   switch (mInternalFormat) 
 290   {
 291     case GFXFormatA8:
 292     case GFXFormatL8:           mBytesPerPixel = 1;
 293      break;
 294     case GFXFormatR8G8B8:       mBytesPerPixel = 3;
 295      break;
 296     case GFXFormatR8G8B8A8_LINEAR_FORCE:
 297     case GFXFormatR8G8B8X8:
 298     case GFXFormatR8G8B8A8:     mBytesPerPixel = 4;
 299      break;
 300    case GFXFormatL16:
 301     case GFXFormatR5G6B5:
 302     case GFXFormatR5G5B5A1:     mBytesPerPixel = 2;
 303      break;
 304     case GFXFormatR16G16B16A16F:
 305      case GFXFormatR16G16B16A16: mBytesPerPixel = 8;
 306         break;
 307      default:
 308         AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier");
 309         break;
 310   }
 311
 312   // Set up the mip levels, if necessary...
 313   mNumMipLevels       = 1;
 314   U32 allocPixels = in_width * in_height * mBytesPerPixel;
 315   mMipLevelOffsets[0] = 0;
 316
 317
 318   if (in_extrudeMipLevels == true) 
 319   {
 320      U32 currWidth  = in_width;
 321      U32 currHeight = in_height;
 322
 323      while (currWidth != 1 || currHeight != 1)
 324      {
 325         mMipLevelOffsets[mNumMipLevels] = mMipLevelOffsets[mNumMipLevels - 1] +
 326                                         (currWidth * currHeight * mBytesPerPixel);
 327         currWidth  >>= 1;
 328         currHeight >>= 1;
 329         if (currWidth  == 0) currWidth  = 1;
 330         if (currHeight == 0) currHeight = 1;
 331
 332         mNumMipLevels++;
 333         allocPixels += currWidth * currHeight * mBytesPerPixel;
 334      }
 335
 336      U32 expectedMips = mFloor(mLog2(mMax(in_width, in_height))) + 1;
 337      AssertFatal(mNumMipLevels == expectedMips, "GBitmap::allocateBitmap: mipmap count wrong");
 338   }
 339   AssertFatal(mNumMipLevels <= c_maxMipLevels, "GBitmap::allocateBitmap: too many miplevels");
 340
 341   // Set up the memory...
 342   mByteSize = allocPixels;
 343   mBits    = new U8[mByteSize];
 344
 345   dMemset(mBits, 0xFF, mByteSize);
 346
 347   if(svBits != NULL)
 348   {
 349      dMemcpy(mBits, svBits, getMin(mByteSize, svByteSize));
 350      delete[] svBits;
 351   }
 352}
 353
 354//--------------------------------------------------------------------------
 355void GBitmap::allocateBitmapWithMips(const U32 in_width, const U32 in_height, const U32 in_numMips, const GFXFormat in_format)
 356{
 357   //-------------------------------------- Some debug checks...
 358   U32 svByteSize = mByteSize;
 359   U8 *svBits = mBits;
 360
 361   AssertFatal(in_width != 0 && in_height != 0, "GBitmap::allocateBitmap: width or height is 0");
 362
 363   mInternalFormat = in_format;
 364   mWidth = in_width;
 365   mHeight = in_height;
 366
 367   mBytesPerPixel = 1;
 368   switch (mInternalFormat)
 369   {
 370   case GFXFormatA8:
 371   case GFXFormatL8:           mBytesPerPixel = 1;
 372      break;
 373   case GFXFormatR8G8B8:       mBytesPerPixel = 3;
 374      break;
 375   case GFXFormatR8G8B8X8:
 376   case GFXFormatR8G8B8A8:     mBytesPerPixel = 4;
 377      break;
 378   case GFXFormatL16:
 379   case GFXFormatR5G6B5:
 380   case GFXFormatR5G5B5A1:     mBytesPerPixel = 2;
 381      break;
 382   case GFXFormatR16G16B16A16F:
 383   case GFXFormatR16G16B16A16: mBytesPerPixel = 8;
 384      break;
 385   default:
 386      AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier");
 387      break;
 388   }
 389
 390   // Set up the mip levels, if necessary...
 391   mNumMipLevels = 1;
 392   U32 allocPixels = in_width * in_height * mBytesPerPixel;
 393   mMipLevelOffsets[0] = 0;
 394
 395
 396   if (in_numMips != 0)
 397   {
 398      U32 currWidth = in_width;
 399      U32 currHeight = in_height;
 400
 401      do
 402      {
 403         mMipLevelOffsets[mNumMipLevels] = mMipLevelOffsets[mNumMipLevels - 1] +
 404            (currWidth * currHeight * mBytesPerPixel);
 405         currWidth >>= 1;
 406         currHeight >>= 1;
 407         if (currWidth == 0) currWidth = 1;
 408         if (currHeight == 0) currHeight = 1;
 409
 410         mNumMipLevels++;
 411         allocPixels += currWidth * currHeight * mBytesPerPixel;
 412      } while (currWidth != 1 || currHeight != 1 && mNumMipLevels != in_numMips);
 413   }
 414   AssertFatal(mNumMipLevels <= c_maxMipLevels, "GBitmap::allocateBitmap: too many miplevels");
 415
 416   // Set up the memory...
 417   mByteSize = allocPixels;
 418   mBits = new U8[mByteSize];
 419
 420   dMemset(mBits, 0xFF, mByteSize);
 421
 422   if (svBits != NULL)
 423   {
 424      dMemcpy(mBits, svBits, getMin(mByteSize, svByteSize));
 425      delete[] svBits;
 426   }
 427}
 428
 429//--------------------------------------------------------------------------
 430void GBitmap::extrudeMipLevels(bool clearBorders)
 431{
 432   if(mNumMipLevels == 1)
 433      allocateBitmap(getWidth(), getHeight(), true, getFormat());
 434
 435   switch (getFormat())
 436   {
 437      case GFXFormatR5G5B5A1:
 438      {
 439         for(U32 i = 1; i < mNumMipLevels; i++)
 440            bitmapExtrude5551(getBits(i - 1), getWritableBits(i), getHeight(i), getWidth(i));
 441         break;
 442      }
 443
 444      case GFXFormatR8G8B8:
 445      {
 446         for(U32 i = 1; i < mNumMipLevels; i++)
 447            bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1));
 448         break;
 449      }
 450
 451      case GFXFormatR8G8B8A8:
 452      case GFXFormatR8G8B8X8:
 453      {
 454         for(U32 i = 1; i < mNumMipLevels; i++)
 455            bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1));
 456         break;
 457      }
 458
 459      case GFXFormatR16G16B16A16F:
 460      {
 461         for (U32 i = 1; i < mNumMipLevels; i++)
 462            bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1));
 463         break;
 464      }
 465      
 466      default:
 467         break;
 468   }
 469   if (clearBorders)
 470   {
 471      for (U32 i = 1; i<mNumMipLevels; i++)
 472      {
 473         U32 width = getWidth(i);
 474         U32 height = getHeight(i);
 475         if (height<3 || width<3)
 476            // bmp is all borders at this mip level
 477            dMemset(getWritableBits(i),0,width*height*mBytesPerPixel);
 478         else
 479         {
 480            width *= mBytesPerPixel;
 481            U8 * bytes = getWritableBits(i);
 482            U8 * end = bytes + (height-1)*width - mBytesPerPixel; // end = last row, 2nd column
 483            // clear first row sans the last pixel
 484            dMemset(bytes,0,width-mBytesPerPixel);
 485            bytes -= mBytesPerPixel;
 486            while (bytes<end)
 487            {
 488               // clear last pixel of row N-1 and first pixel of row N
 489               bytes += width;
 490               dMemset(bytes,0,mBytesPerPixel*2);
 491            }
 492            // clear last row sans the first pixel
 493            dMemset(bytes+2*mBytesPerPixel,0,width-mBytesPerPixel);
 494         }
 495      }
 496   }
 497}
 498
 499//--------------------------------------------------------------------------
 500void GBitmap::chopTopMips(U32 mipsToChop)
 501{
 502   U32 scalePower = getMin(mipsToChop, getNumMipLevels() - 1);
 503   U32 newMipCount = getNumMipLevels() - scalePower;
 504
 505   U32 realWidth = getMax((U32)1, getWidth() >> scalePower);
 506   U32 realHeight = getMax((U32)1, getHeight() >> scalePower);
 507
 508   U8 *destBits = mBits;
 509
 510   U32 destOffsets[c_maxMipLevels];
 511
 512   for (U32 i = scalePower; i<mNumMipLevels; i++)
 513   {
 514      // Copy to the new bitmap...
 515      dMemcpy(destBits,
 516         getWritableBits(i),
 517         getSurfaceSize(i));
 518
 519      destOffsets[i - scalePower] = destBits - mBits;
 520      destBits += getSurfaceSize(i);
 521   }
 522
 523   dMemcpy(mMipLevelOffsets, destOffsets, sizeof(destOffsets));
 524
 525   mWidth = realWidth;
 526   mHeight = realHeight;
 527   mByteSize = destBits - mBits;
 528   mNumMipLevels = newMipCount;
 529}
 530
 531//--------------------------------------------------------------------------
 532void GBitmap::extrudeMipLevelsDetail()
 533{
 534   AssertFatal(getFormat() == GFXFormatR8G8B8, "Error, only handles RGB for now...");
 535   U32 i,j;
 536
 537   if(mNumMipLevels == 1)
 538      allocateBitmap(getWidth(), getHeight(), true, getFormat());
 539
 540   for (i = 1; i < mNumMipLevels; i++) {
 541      bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1));
 542   }
 543
 544   // Ok, now that we have the levels extruded, we need to move the lower miplevels
 545   //  closer to 0.5.
 546   for (i = 1; i < mNumMipLevels - 1; i++) {
 547      U8* pMipBits = (U8*)getWritableBits(i);
 548      U32 numBytes = getWidth(i) * getHeight(i) * 3;
 549
 550      U32 shift    = i;
 551      U32 start    = ((1 << i) - 1) * 0x80;
 552
 553      for (j = 0; j < numBytes; j++) {
 554         U32 newVal = (start + pMipBits[j]) >> shift;
 555         AssertFatal(newVal <= 255, "Error, oob");
 556         pMipBits[j] = U8(newVal);
 557      }
 558   }
 559   AssertFatal(getWidth(mNumMipLevels - 1) == 1 && getHeight(mNumMipLevels - 1) == 1,
 560               "Error, last miplevel should be 1x1!");
 561   ((U8*)getWritableBits(mNumMipLevels - 1))[0] = 0x80;
 562   ((U8*)getWritableBits(mNumMipLevels - 1))[1] = 0x80;
 563   ((U8*)getWritableBits(mNumMipLevels - 1))[2] = 0x80;
 564}
 565
 566//--------------------------------------------------------------------------
 567bool GBitmap::setFormat(GFXFormat fmt)
 568{
 569   if (getFormat() == fmt)
 570      return true;
 571
 572   PROFILE_SCOPE(GBitmap_setFormat);
 573
 574   // this is a nasty pointer math hack
 575   // is there a quick way to calc pixels of a fully mipped bitmap?
 576   U32 pixels = 0;
 577   for (U32 i=0; i < mNumMipLevels; i++)
 578      pixels += getHeight(i) * getWidth(i);
 579
 580   switch( getFormat() )
 581   {
 582      case GFXFormatR8G8B8:
 583         switch ( fmt )
 584         {
 585            case GFXFormatR5G5B5A1:
 586#ifdef _XBOX
 587               bitmapConvertRGB_to_1555(mBits, pixels);
 588#else
 589               bitmapConvertRGB_to_5551(mBits, pixels);
 590#endif
 591               mInternalFormat = GFXFormatR5G5B5A1;
 592               mBytesPerPixel  = 2;
 593               break;
 594
 595            case GFXFormatR8G8B8A8:
 596            case GFXFormatR8G8B8X8:
 597               // Took this out, it may crash -patw
 598               //AssertFatal( mNumMipLevels == 1, "Do the mip-mapping in hardware." );
 599
 600               bitmapConvertRGB_to_RGBX( &mBits, pixels );
 601               mInternalFormat = fmt;
 602               mBytesPerPixel = 4;
 603               mByteSize = pixels * 4;
 604               break;
 605
 606            default:
 607               AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format.");
 608               return false;
 609         }
 610         break;
 611
 612      case GFXFormatR8G8B8X8:
 613         switch( fmt )
 614         {
 615            // No change needed for this
 616            case GFXFormatR8G8B8A8:
 617               mInternalFormat = GFXFormatR8G8B8A8;
 618               break;
 619
 620            case GFXFormatR8G8B8:
 621               bitmapConvertRGBX_to_RGB( &mBits, pixels );
 622               mInternalFormat = GFXFormatR8G8B8;
 623               mBytesPerPixel = 3;
 624               mByteSize = pixels * 3;
 625               break;
 626
 627            default:
 628               AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format.");
 629               return false;
 630         }
 631         break;
 632
 633      case GFXFormatR8G8B8A8:
 634         switch( fmt )
 635         {
 636            // No change needed for this
 637            case GFXFormatR8G8B8X8:
 638               mInternalFormat = GFXFormatR8G8B8X8;
 639               break;
 640
 641            case GFXFormatR8G8B8:
 642               bitmapConvertRGBX_to_RGB( &mBits, pixels );
 643               mInternalFormat = GFXFormatR8G8B8;
 644               mBytesPerPixel = 3;
 645               mByteSize = pixels * 3;
 646               break;
 647
 648            default:
 649               AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format.");
 650               return false;
 651         }
 652         break;
 653
 654      case GFXFormatA8:
 655         switch( fmt )
 656         {
 657            case GFXFormatR8G8B8A8:
 658               mInternalFormat = GFXFormatR8G8B8A8;
 659               bitmapConvertA8_to_RGBA( &mBits, pixels );
 660               mBytesPerPixel = 4;
 661               mByteSize = pixels * 4;
 662               break;
 663
 664            default:
 665               AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format.");
 666               return false;
 667         }
 668         break;
 669
 670      default:
 671         AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format.");
 672         return false;
 673   }
 674
 675   U32 offset = 0;
 676   for (U32 j=0; j < mNumMipLevels; j++)
 677   {
 678      mMipLevelOffsets[j] = offset;
 679      offset += getHeight(j) * getWidth(j) * mBytesPerPixel;
 680   }
 681
 682   return true;
 683}
 684
 685//------------------------------------------------------------------------------
 686
 687bool GBitmap::checkForTransparency()
 688{
 689   mHasTransparency = false;
 690
 691   ColorI pixel(255, 255, 255, 255);
 692
 693   switch (mInternalFormat)
 694   {
 695      // Non-transparent formats
 696      case GFXFormatL8:
 697     case GFXFormatL16:
 698      case GFXFormatR8G8B8:
 699      case GFXFormatR5G6B5:
 700         break;
 701      // Transparent formats
 702      case GFXFormatA8:
 703      case GFXFormatR8G8B8A8:
 704      case GFXFormatR5G5B5A1:
 705         // Let getColor() do the heavy lifting
 706         for (U32 x = 0; x < mWidth; x++)
 707         {
 708            for (U32 y = 0; y < mHeight; y++)
 709            {
 710               if (getColor(x, y, pixel))
 711               {
 712                  if (pixel.alpha < 255)
 713                  {
 714                     mHasTransparency = true;
 715                     break;
 716                  }
 717               }
 718            }
 719         }
 720
 721         break;
 722      default:
 723         AssertFatal(false, "GBitmap::checkForTransparency: misunderstood format specifier");
 724      break;
 725   }
 726
 727   return mHasTransparency;
 728}
 729
 730//------------------------------------------------------------------------------
 731LinearColorF GBitmap::sampleTexel(F32 u, F32 v, bool retAlpha) const
 732{
 733   LinearColorF col(0.5f, 0.5f, 0.5f);
 734   // normally sampling wraps all the way around at 1.0,
 735   // but locking doesn't support this, and we seem to calc
 736   // the uv based on a clamped 0 - 1...
 737   Point2F max((F32)(getWidth()-1), (F32)(getHeight()-1));
 738   Point2F posf;
 739   posf.x = mClampF(((u) * max.x), 0.0f, max.x);
 740   posf.y = mClampF(((v) * max.y), 0.0f, max.y);
 741   Point2I posi((S32)posf.x, (S32)posf.y);
 742
 743   const U8 *buffer = getBits();
 744   U32 lexelindex = ((posi.y * getWidth()) + posi.x) * mBytesPerPixel;
 745
 746   if(mBytesPerPixel == 2)
 747   {
 748      //U16 *buffer = (U16 *)lockrect->pBits;
 749   }
 750   else if(mBytesPerPixel > 2)
 751   {     
 752      col.red = F32(buffer[lexelindex + 0]) / 255.0f;
 753      col.green = F32(buffer[lexelindex + 1]) / 255.0f;
 754      col.blue = F32(buffer[lexelindex + 2]) / 255.0f;
 755      if (retAlpha)
 756      {
 757         if (getHasTransparency())
 758            col.alpha = F32(buffer[lexelindex + 3]) / 255.0f;
 759         else
 760            col.alpha = 1.0f;
 761      }
 762   }
 763
 764   return col;
 765}
 766
 767//--------------------------------------------------------------------------
 768bool GBitmap::getColor(const U32 x, const U32 y, ColorI& rColor) const
 769{
 770   if (x >= mWidth || y >= mHeight)
 771      return false;
 772
 773   const U8* pLoc = getAddress(x, y);
 774
 775   switch (mInternalFormat) {
 776     case GFXFormatA8:
 777     case GFXFormatL8:
 778      rColor.set( *pLoc, *pLoc, *pLoc, *pLoc );
 779      break;
 780    case GFXFormatL16:
 781       rColor.set(U8(U16((pLoc[0] << 8) + pLoc[1])), 0, 0, 0);
 782     case GFXFormatR8G8B8:
 783     case GFXFormatR8G8B8X8:
 784        rColor.set( pLoc[0], pLoc[1], pLoc[2], 255 );
 785      break;
 786
 787     case GFXFormatR8G8B8A8:
 788      rColor.set( pLoc[0], pLoc[1], pLoc[2], pLoc[3] );
 789      break;
 790
 791     case GFXFormatR5G5B5A1:
 792#if defined(TORQUE_OS_MAC)
 793      rColor.set( (*((U16*)pLoc) >> 0) & 0x1F,
 794                  (*((U16*)pLoc) >> 5) & 0x1F,
 795                  (*((U16*)pLoc) >> 10) & 0x1F,
 796                  ((*((U16*)pLoc) >> 15) & 0x01) ? 255 : 0 );
 797#else
 798      rColor.set( *((U16*)pLoc) >> 11,
 799                  (*((U16*)pLoc) >> 6) & 0x1f,
 800                  (*((U16*)pLoc) >> 1) & 0x1f,
 801                  (*((U16*)pLoc) & 1) ? 255 : 0 );
 802#endif
 803      break;
 804
 805     default:
 806      AssertFatal(false, "Bad internal format");
 807      return false;
 808   }
 809
 810   return true;
 811}
 812
 813
 814//--------------------------------------------------------------------------
 815
 816
 817bool GBitmap::setColor(const U32 x, const U32 y, const ColorI& rColor)
 818{
 819   if (x >= mWidth || y >= mHeight)
 820      return false;
 821
 822   U8* pLoc = getAddress(x, y);
 823
 824   switch (mInternalFormat) {
 825     case GFXFormatA8:
 826     case GFXFormatL8:
 827      *pLoc = rColor.alpha;
 828      break;
 829
 830    case GFXFormatL16:
 831       dMemcpy(pLoc, &rColor, 2 * sizeof(U8));
 832       break;
 833
 834     case GFXFormatR8G8B8:
 835      dMemcpy( pLoc, &rColor, 3 * sizeof( U8 ) );
 836      break;
 837
 838     case GFXFormatR8G8B8A8:
 839     case GFXFormatR8G8B8X8:
 840      dMemcpy( pLoc, &rColor, 4 * sizeof( U8 ) );
 841      break;
 842      
 843     case GFXFormatR5G6B5:
 844      #ifdef TORQUE_OS_MAC
 845         *((U16*)pLoc) = (rColor.red << 11) | (rColor.green << 5) | (rColor.blue << 0) ;
 846      #else
 847         *((U16*)pLoc) = (rColor.blue << 0) | (rColor.green << 5) | (rColor.red << 11);
 848      #endif
 849      break;
 850
 851     case GFXFormatR5G5B5A1:
 852      #ifdef TORQUE_OS_MAC
 853         *((U16*)pLoc) = (((rColor.alpha>0) ? 1 : 0)<<15) | (rColor.blue << 10) | (rColor.green << 5) | (rColor.red << 0);
 854      #else
 855         *((U16*)pLoc) = (rColor.blue << 1) | (rColor.green << 6) | (rColor.red << 11) | ((rColor.alpha>0) ? 1 : 0);
 856      #endif
 857      break;
 858
 859     default:
 860      AssertFatal(false, "Bad internal format");
 861      return false;
 862   }
 863
 864   return true;
 865}
 866
 867//--------------------------------------------------------------------------
 868U8 GBitmap::getChanelValueAt(U32 x, U32 y, U32 chan)
 869{
 870   ColorI pixelColor = ColorI(255,255,255,255);
 871   getColor(x, y, pixelColor);
 872   if (mInternalFormat == GFXFormatL16)
 873   {
 874      chan = 0;
 875   }
 876   switch (chan) {
 877   case 0: return pixelColor.red;
 878   case 1: return pixelColor.green;
 879   case 2: return pixelColor.blue;
 880   default: return pixelColor.alpha;
 881   }
 882}
 883
 884//-----------------------------------------------------------------------------
 885
 886bool GBitmap::combine( const GBitmap *bitmapA, const GBitmap *bitmapB, const TextureOp combineOp )
 887{
 888   // Check bitmapA format
 889   switch( bitmapA->getFormat() )
 890   {
 891   case GFXFormatR8G8B8:
 892   case GFXFormatR8G8B8X8:
 893   case GFXFormatR8G8B8A8:
 894      break;
 895
 896   default:
 897      Con::errorf( "GBitmap::combine - invalid format for bitmapA" );
 898      return false;
 899   }
 900
 901   // Check bitmapB format
 902   switch( bitmapB->getFormat() )
 903   {
 904   case GFXFormatR8G8B8:
 905   case GFXFormatR8G8B8X8:
 906   case GFXFormatR8G8B8A8:
 907      break;
 908
 909   default:
 910      Con::errorf( "GBitmap::combine - invalid format for bitmapB" );
 911      return false;
 912   }
 913
 914   // Determine format of result texture
 915   // CodeReview: This is dependent on the order of the GFXFormat enum. [5/11/2007 Pat]
 916   GFXFormat resFmt = static_cast<GFXFormat>( getMax( bitmapA->getFormat(), bitmapB->getFormat() ) );
 917   U32 resWidth = getMax( bitmapA->getWidth(), bitmapB->getWidth() );
 918   U32 resHeight = getMax( bitmapA->getHeight(), bitmapB->getHeight() );
 919
 920   // Adjust size OF bitmap based on the biggest one
 921   if( bitmapA->getWidth() != bitmapB->getWidth() ||
 922       bitmapA->getHeight() != bitmapB->getHeight() )
 923   {
 924      // Delete old bitmap
 925      deleteImage();
 926
 927      // Allocate new one
 928      allocateBitmap( resWidth, resHeight, false, resFmt );
 929   }
 930   
 931   // Adjust format of result bitmap (if resFmt == getFormat() it will not perform the format convert)
 932   setFormat( resFmt );
 933
 934   // Perform combine
 935   U8 *destBits = getWritableBits();
 936   const U8 *aBits = bitmapA->getBits();
 937   const U8 *bBits = bitmapB->getBits();
 938
 939   for( S32 y = 0; y < getHeight(); y++ )
 940   {
 941      for( S32 x = 0; x < getWidth(); x++ )
 942      {
 943         for( S32 _byte = 0; _byte < mBytesPerPixel; _byte++ )
 944         {
 945            U8 pxA = 0;
 946            U8 pxB = 0;
 947
 948            // Get contributions from A and B
 949            if( y < bitmapA->getHeight() && 
 950                x < bitmapA->getWidth() &&
 951                _byte < bitmapA->mBytesPerPixel )
 952               pxA = *aBits++;
 953
 954            if( y < bitmapB->getHeight() && 
 955               x < bitmapB->getWidth() &&
 956               _byte < bitmapB->mBytesPerPixel )
 957               pxB = *bBits++;
 958
 959            // Combine them (clamp values 0-U8_MAX)
 960            switch( combineOp )
 961            {
 962               case Add:
 963                  *destBits++ = getMin( U8( pxA + pxB ), U8_MAX );
 964                  break;
 965
 966               case Subtract:
 967                  *destBits++ = getMax( U8( pxA - pxB ), U8( 0 ) );
 968                  break;
 969               default:
 970                  AssertFatal(false, "GBitmap::combine - Invalid combineOp");
 971                  break;
 972            }
 973         }
 974      }
 975   }
 976
 977   return true;
 978}
 979
 980void GBitmap::fill( const ColorI &rColor )
 981{
 982   // Set the first pixel using the slow 
 983   // but proper method.
 984   setColor( 0, 0, rColor );
 985   mHasTransparency = rColor.alpha < 255;
 986      
 987   // Now fill the first row of the bitmap by 
 988   // copying the first pixel across the row.
 989   const U32 stride = getWidth() * mBytesPerPixel;
 990   const U8 *src = getBits();
 991   U8 *dest = getWritableBits() + mBytesPerPixel;
 992   const U8 *end = src + stride;
 993   for ( ; dest != end; dest += mBytesPerPixel )
 994      dMemcpy( dest, src, mBytesPerPixel );
 995
 996   // Now copy the first row to all the others.
 997   // 
 998   // TODO: This could adaptively size the copy 
 999   // amount to copy more rows from the source
1000   // and reduce the total number of memcpy calls.
1001   //
1002   dest = getWritableBits() + stride;
1003   end = src + ( stride * getHeight() );
1004   for ( ; dest != end; dest += stride )
1005      dMemcpy( dest, src, stride );
1006}
1007
1008void GBitmap::fillWhite()
1009{
1010   dMemset( getWritableBits(), 255, mByteSize );
1011   mHasTransparency = false;
1012}
1013
1014GBitmap* GBitmap::createPaddedBitmap() const
1015{
1016   if (isPow2(getWidth()) && isPow2(getHeight()))
1017      return NULL;
1018
1019   AssertFatal(getNumMipLevels() == 1,
1020      "Cannot have non-pow2 bitmap with miplevels");
1021
1022   U32 width = getWidth();
1023   U32 height = getHeight();
1024
1025   U32 newWidth  = getNextPow2(getWidth());
1026   U32 newHeight = getNextPow2(getHeight());
1027
1028   GBitmap* pReturn = new GBitmap(newWidth, newHeight, false, getFormat());
1029
1030   for (U32 i = 0; i < height; i++) 
1031   {
1032      U8*       pDest = (U8*)pReturn->getAddress(0, i);
1033      const U8* pSrc  = (const U8*)getAddress(0, i);
1034
1035      dMemcpy(pDest, pSrc, width * mBytesPerPixel);
1036
1037      pDest += width * mBytesPerPixel;
1038      // set the src pixel to the last pixel in the row
1039      const U8 *pSrcPixel = pDest - mBytesPerPixel; 
1040
1041      for(U32 j = width; j < newWidth; j++)
1042         for(U32 k = 0; k < mBytesPerPixel; k++)
1043            *pDest++ = pSrcPixel[k];
1044   }
1045
1046   for(U32 i = height; i < newHeight; i++)
1047   {
1048      U8* pDest = (U8*)pReturn->getAddress(0, i);
1049      U8* pSrc = (U8*)pReturn->getAddress(0, height-1);
1050      dMemcpy(pDest, pSrc, newWidth * mBytesPerPixel);
1051   }
1052
1053   return pReturn;
1054}
1055
1056GBitmap* GBitmap::createPow2Bitmap() const
1057{
1058   if (isPow2(getWidth()) && isPow2(getHeight()))
1059      return NULL;
1060
1061   AssertFatal(getNumMipLevels() == 1,
1062               "Cannot have non-pow2 bitmap with miplevels");
1063
1064   U32 width = getWidth();
1065   U32 height = getHeight();
1066
1067   U32 newWidth  = getNextPow2(getWidth());
1068   U32 newHeight = getNextPow2(getHeight());
1069
1070   GBitmap* pReturn = new GBitmap(newWidth, newHeight, false, getFormat());
1071
1072   U8*       pDest = (U8*)pReturn->getAddress(0, 0);
1073   const U8* pSrc  = (const U8*)getAddress(0, 0);
1074
1075   F32 yCoeff = (F32) height / (F32) newHeight;
1076   F32 xCoeff = (F32) width / (F32) newWidth;
1077
1078   F32 currY = 0.0f;
1079   for (U32 y = 0; y < newHeight; y++)
1080   {
1081      F32 currX = 0.0f;
1082      //U32 yDestOffset = (pReturn->mWidth * pReturn->mBytesPerPixel) * y;
1083      //U32 xDestOffset = 0;
1084      //U32 ySourceOffset = (U32)((mWidth * mBytesPerPixel) * currY);
1085      //F32 xSourceOffset = 0.0f;
1086      for (U32 x = 0; x < newWidth; x++)
1087      {
1088         pDest = (U8*) pReturn->getAddress(x, y);
1089         pSrc = (U8*) getAddress((S32)currX, (S32)currY);
1090         for (U32 p = 0; p < pReturn->mBytesPerPixel; p++) 
1091         {
1092            pDest[p] = pSrc[p];
1093         }
1094         currX += xCoeff;
1095      }
1096      currY += yCoeff;
1097   }
1098
1099   return pReturn;
1100}
1101
1102void GBitmap::copyChannel( U32 index, GBitmap *outBitmap ) const
1103{
1104   AssertFatal( index < mBytesPerPixel, "GBitmap::copyChannel() - Bad channel offset!" );
1105   AssertFatal( outBitmap, "GBitmap::copyChannel() - Null output bitmap!" );   
1106   AssertFatal( outBitmap->getWidth() == getWidth(), "GBitmap::copyChannel() - Width mismatch!" );
1107   AssertFatal( outBitmap->getHeight() == getHeight(), "GBitmap::copyChannel() - Height mismatch!" );
1108
1109   U8 *outBits = outBitmap->getWritableBits();
1110   const U32 outBytesPerPixel = outBitmap->getBytesPerPixel();
1111   const U8 *srcBits = getBits() + index;
1112   const U8 *endBits = getBits() + mByteSize;
1113
1114   for (  ; srcBits < endBits;  )
1115   {
1116      *outBits = *srcBits;
1117      outBits += outBytesPerPixel;
1118      srcBits += mBytesPerPixel;
1119   }
1120}
1121
1122//------------------------------------------------------------------------------
1123
1124bool GBitmap::read(Stream& io_rStream)
1125{
1126   // Handle versioning
1127   U32 version;
1128   io_rStream.read(&version);
1129   AssertFatal(version == csFileVersion, "Bitmap::read: incorrect file version");
1130
1131   //-------------------------------------- Read the object
1132   U32 fmt;
1133   io_rStream.read(&fmt);
1134   mInternalFormat = GFXFormat(fmt);
1135   mBytesPerPixel = 1;
1136   switch (mInternalFormat) {
1137     case GFXFormatA8:
1138     case GFXFormatL8:  mBytesPerPixel = 1;
1139      break;
1140     case GFXFormatR8G8B8:        mBytesPerPixel = 3;
1141      break;
1142     case GFXFormatR8G8B8A8:       mBytesPerPixel = 4;
1143      break;
1144    case GFXFormatL16:
1145     case GFXFormatR5G6B5:
1146     case GFXFormatR5G5B5A1:    mBytesPerPixel = 2;
1147      break;
1148     default:
1149      AssertFatal(false, "GBitmap::read: misunderstood format specifier");
1150      break;
1151   }
1152
1153   io_rStream.read(&mByteSize);
1154
1155   mBits = new U8[mByteSize];
1156   io_rStream.read(mByteSize, mBits);
1157
1158   io_rStream.read(&mWidth);
1159   io_rStream.read(&mHeight);
1160
1161   io_rStream.read(&mNumMipLevels);
1162   for (U32 i = 0; i < c_maxMipLevels; i++)
1163      io_rStream.read(&mMipLevelOffsets[i]);
1164
1165   checkForTransparency();
1166
1167   return (io_rStream.getStatus() == Stream::Ok);
1168}
1169
1170bool GBitmap::write(Stream& io_rStream) const
1171{
1172   // Handle versioning
1173   io_rStream.write(csFileVersion);
1174
1175   //-------------------------------------- Write the object
1176   io_rStream.write(U32(mInternalFormat));
1177
1178   io_rStream.write(mByteSize);
1179   io_rStream.write(mByteSize, mBits);
1180
1181   io_rStream.write(mWidth);
1182   io_rStream.write(mHeight);
1183
1184   io_rStream.write(mNumMipLevels);
1185   for (U32 i = 0; i < c_maxMipLevels; i++)
1186      io_rStream.write(mMipLevelOffsets[i]);
1187
1188   return (io_rStream.getStatus() == Stream::Ok);
1189}
1190
1191//------------------------------------------------------------------------------
1192//-------------------------------------- Persistent I/O
1193//
1194
1195bool  GBitmap::readBitmap( const String &bmType, Stream &ioStream )
1196{
1197   PROFILE_SCOPE(ResourceGBitmap_readBitmap);
1198   const GBitmap::Registration   *regInfo = GBitmap::sFindRegInfo( bmType );
1199
1200   if ( regInfo == NULL )
1201   {
1202      Con::errorf( "[GBitmap::readBitmap] unable to find registration for extension [%s]", bmType.c_str() );
1203      return NULL;
1204   }
1205
1206   return regInfo->readFunc( ioStream, this );
1207}
1208
1209bool  GBitmap::writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel )
1210{
1211   const GBitmap::Registration   *regInfo = GBitmap::sFindRegInfo( bmType );
1212
1213   if ( regInfo == NULL )
1214   {
1215      Con::errorf( "[GBitmap::writeBitmap] unable to find registration for extension [%s]", bmType.c_str() );
1216      return NULL;
1217   }
1218
1219   return regInfo->writeFunc( this, ioStream, (compressionLevel == U32_MAX) ? regInfo->defaultCompression : compressionLevel );
1220}
1221
1222template<> void *Resource<GBitmap>::create(const Torque::Path &path)
1223{
1224   PROFILE_SCOPE( ResourceGBitmap_create );
1225
1226#ifdef TORQUE_DEBUG_RES_MANAGER
1227   Con::printf( "Resource<GBitmap>::create - [%s]", path.getFullPath().c_str() );
1228#endif
1229
1230   FileStream  stream;
1231
1232   stream.open( path.getFullPath(), Torque::FS::File::Read );
1233
1234   if ( stream.getStatus() != Stream::Ok )
1235   {
1236      Con::errorf( "Resource<GBitmap>::create - failed to open '%s'", path.getFullPath().c_str() );
1237      return NULL;
1238   }
1239
1240   GBitmap *bmp = new GBitmap;
1241   const String extension = path.getExtension();
1242   if( !bmp->readBitmap( extension, stream ) )
1243   {
1244      Con::errorf( "Resource<GBitmap>::create - error reading '%s'", path.getFullPath().c_str() );
1245      delete bmp;
1246      bmp = NULL;
1247   }
1248
1249   return bmp;
1250}
1251
1252template<> ResourceBase::Signature  Resource<GBitmap>::signature()
1253{
1254   return MakeFourCC('b','i','t','m');
1255}
1256
1257Resource<GBitmap> GBitmap::load(const Torque::Path &path)
1258{
1259   Resource<GBitmap> ret = _load( path );
1260   if ( ret != NULL )
1261      return ret;
1262
1263   // Do a recursive search.
1264   return _search( path );
1265}
1266
1267Resource<GBitmap> GBitmap::_load(const Torque::Path &path)
1268{
1269   PROFILE_SCOPE( GBitmap_load );
1270
1271   if ( Torque::FS::IsFile( path ) )
1272      return ResourceManager::get().load( path );
1273  
1274   Path foundPath;
1275   if ( GBitmap::sFindFile( path, &foundPath ) )
1276   {
1277      Resource<GBitmap> ret = ResourceManager::get().load( foundPath );
1278      if ( ret != NULL )
1279         return ret;
1280   }
1281
1282   return Resource< GBitmap>( NULL );
1283}
1284
1285Resource<GBitmap> GBitmap::_search(const Torque::Path &path)
1286{
1287   PROFILE_SCOPE( GBitmap_search );
1288
1289   // If unable to load texture in current directory
1290   // look in the parent directory.  But never look in the root.
1291   Path newPath( path );
1292   while ( true )
1293   {
1294      String filePath = newPath.getPath();
1295      String::SizeType slash = filePath.find( '/', filePath.length(), String::Right );
1296
1297      if ( slash == String::NPos )
1298         break;
1299
1300      slash = filePath.find( '/', filePath.length(), String::Right );
1301      if ( slash == String::NPos )
1302         break;
1303
1304      String truncPath = filePath.substr( 0, slash );
1305      newPath.setPath( truncPath );
1306
1307      Resource<GBitmap> ret = _load( newPath );
1308      if ( ret != NULL )
1309         return ret;
1310   }
1311
1312   return Resource< GBitmap>( NULL );
1313}
1314
1315U32 GBitmap::getSurfaceSize(const U32 mipLevel) const
1316{
1317   // Bump by the mip level.
1318   U32 height = getMax(U32(1), mHeight >> mipLevel);
1319   U32 width = getMax(U32(1), mWidth >> mipLevel);
1320
1321   if (mInternalFormat >= GFXFormatBC1 && mInternalFormat <= GFXFormatBC3)
1322   {
1323      // From the directX docs:
1324      // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
1325
1326      U32 sizeMultiple = 0;
1327
1328      switch (mInternalFormat)
1329      {
1330      case GFXFormatBC1:
1331         sizeMultiple = 8;
1332         break;
1333      case GFXFormatBC2:
1334      case GFXFormatBC3:
1335         sizeMultiple = 16;
1336         break;
1337      default:
1338         AssertISV(false, "DDSFile::getSurfaceSize - invalid compressed texture format, we only support DXT1-5 right now.");
1339         break;
1340      }
1341
1342      return getMax(U32(1), width / 4) * getMax(U32(1), height / 4) * sizeMultiple;
1343   }
1344   else
1345   {
1346      return height * width* mBytesPerPixel;
1347   }
1348}
1349
1350DefineEngineFunction( getBitmapInfo, String, ( const char *filename ),,
1351   "Returns image info in the following format: width TAB height TAB bytesPerPixel TAB format. "
1352   "It will return an empty string if the file is not found.\n"
1353   "@ingroup Rendering\n" )
1354{
1355   Resource<GBitmap> image = GBitmap::load( filename );
1356   if ( !image )
1357      return String::EmptyString;
1358
1359   return String::ToString( "%d\t%d\t%d\t%d", image->getWidth(), 
1360                                          image->getHeight(),
1361                                          image->getBytesPerPixel(),
1362                                          image->getFormat());
1363}
1364