void ScreenShot::capture( GuiCanvas *canvas )
{
   AssertFatal( mPending, "ScreenShot::capture() - The capture wasn't pending!" );

   if ( mTiles == 1 )
   {
      _singleCapture( canvas );
      return;
   }

   char filename[256];

   Point2I canvasSize = canvas->getPlatformWindow()->getVideoMode().resolution;
   
   // Calculate the real final size taking overlap in account
   Point2I overlapPixels( canvasSize.x * mPixelOverlap.x, canvasSize.y * mPixelOverlap.y );   

   // Calculate the overlap to be used by the frustum tiling, 
   // so it properly expands the frustum to overlap the amount of pixels we want
   mFrustumOverlap.x = ((F32)canvasSize.x/(canvasSize.x - overlapPixels.x*2)) * ((F32)overlapPixels.x/(F32)canvasSize.x);
   mFrustumOverlap.y = ((F32)canvasSize.y/(canvasSize.y - overlapPixels.y*2)) * ((F32)overlapPixels.y/(F32)canvasSize.y);
   
   //overlapPixels.set(0,0);
   // Get a buffer to write a row of tiles into.
   GBitmap *outBuffer = new GBitmap( canvasSize.x * mTiles - overlapPixels.x * mTiles * 2 , canvasSize.y - overlapPixels.y );

   // Open up the file on disk.
   dSprintf( filename, 256, "%s.%s", mFilename, "png" );
   FileStream fs;
   if ( !fs.open( filename, Torque::FS::File::Write ) )
      Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename );

   // Open a PNG stream for the final image
   DeferredPNGWriter pngWriter;
   pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0);
   
   // Render each tile to generate a huge screenshot.
   for( U32 ty=0; ty < mTiles; ty++ )
   {
      for( S32 tx=0; tx < mTiles; tx++ )
      {
         // Set the current tile offset for tileFrustum().
         mCurrTile.set( tx, mTiles - ty - 1 );

         // Let the canvas render the scene.
         canvas->renderFrame( false );

         // Now grab the current back buffer.
         GBitmap *gb = _captureBackBuffer();

			// The current GFX device couldn't capture the backbuffer or it's unable of doing so.
			if (gb == NULL)
				return;

                  
         // Copy the captured bitmap into its tile
         // within the output bitmap.         
         const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();         
         const U8 *inColor = gb->getBits() + inStride * overlapPixels.y;
         const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel();
         const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();         
         const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2;
         const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel();
         U8 *outColor = outBuffer->getWritableBits() + outOffset;
         for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ )
         {
            dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride );
            
            //Grandient blend the left overlap area of this tile over the previous tile left border
            if (tx && !(ty && row < overlapPixels.y))
            {
               U8 *blendOverlapSrc = (U8*)inColor;
               U8 *blendOverlapDst = outColor - inOverlapOffset;
               for ( U32 px=0; px < overlapPixels.x; px++)
               {
                  F32 blendFactor = (F32)px / (F32)overlapPixels.x;
                  sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);                 

                  blendOverlapSrc += gb->getBytesPerPixel();
                  blendOverlapDst += outBuffer->getBytesPerPixel();                   
               }               
            }

            //Gradient blend against the rows the excess overlap rows already in the buffer            
            if (ty && row < overlapPixels.y)
            {
               F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y;
               U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
               U8 *blendDst = outColor;               
               for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++)
               {                  
                  sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor); 
                  blendSrc += gb->getBytesPerPixel();
                  blendDst += outBuffer->getBytesPerPixel();                   
               }                              
            }

            
            inColor += inStride;
            outColor += outStride;
         }

         delete gb;
      }

      // Write the captured tile row into the PNG stream
      pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y);
   }

   //Close the PNG stream
   pngWriter.end();
   
   // We captured... clear the flag.
   mPending = false;
}
void TerrainFile::import(  const GBitmap &heightMap, 
                           F32 heightScale,
                           const Vector<U8> &layerMap, 
                           const Vector<String> &materials,
                           bool flipYAxis )
{
   AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" );
   AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" );

   const U32 newSize = heightMap.getWidth();
   if ( newSize != mSize )
   {
      mHeightMap.setSize( newSize * newSize );
      mHeightMap.compact();
      mSize = newSize;
   }

   // Convert the height map to heights.
   U16 *oBits = mHeightMap.address();
   if ( heightMap.getFormat() == GFXFormatR5G6B5 )
   {
      const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale );
      const U16 *iBits = (const U16*)heightMap.getBits();
      if ( flipYAxis )
      {
         for ( U32 i = 0; i < mSize * mSize; i++ )
         {
            U16 height = convertBEndianToHost( *iBits );
            *oBits = (U16)mCeil( (F32)height * toFixedPoint );
            ++oBits;
            ++iBits;
         }
      }
      else
      {
         for(S32 y = mSize - 1; y >= 0; y--) {
            for(U32 x = 0; x < mSize; x++) {
               U16 height = convertBEndianToHost( *iBits );
               mHeightMap[x + y * mSize] = (U16)mCeil( (F32)height * toFixedPoint );
               ++iBits;
            }
         }
      }
   }
   else
   {
      const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale );
      const U8 *iBits = heightMap.getBits();
      if ( flipYAxis )
      {
         for ( U32 i = 0; i < mSize * mSize; i++ )
         {
            *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
            ++oBits;
            iBits += heightMap.getBytesPerPixel();
         }
      }
      else
      {
         for(S32 y = mSize - 1; y >= 0; y--) {
            for(U32 x = 0; x < mSize; x++) {
               mHeightMap[x + y * mSize] = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
               iBits += heightMap.getBytesPerPixel();
            }
         }
      }
   }

   // Copy over the layer map.
   AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" );
   mLayerMap = layerMap;
   mLayerMap.compact();

   // Resolve the materials.
   _resolveMaterials( materials );

   // Rebuild the collision grid map.
   _buildGridMap();
}