screenshot.cpp

Engine/source/gfx/screenshot.cpp

More...

Public Variables

Public Functions

DefineEngineFunction(screenShot , void , (const char *file, const char *format, U32 tileCount, F32 tileOverlap) , (1, 0) , "Takes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> screenshot with optional tiling <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> produce huge <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">screenshots.\n</a>" "@param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> The output image <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">path.\n</a>" "@param format Either JPEG or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PNG.\n</a>" "@param tileCount If greater than 1 will tile the current screen <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> take <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> large format <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">screenshot.\n</a>" "@param tileOverlap The amount of horizontal and vertical overlap between the tiles used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> remove tile edge artifacts from post <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GFX\n</a>" )
sBlendPixelRGB888(U8 * src, U8 * dst, F32 factor)

Detailed Description

Public Variables

ScreenShot * gScreenShot 

Public Functions

DefineEngineFunction(screenShot , void , (const char *file, const char *format, U32 tileCount, F32 tileOverlap) , (1, 0) , "Takes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> screenshot with optional tiling <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> produce huge <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">screenshots.\n</a>" "@param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> The output image <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">path.\n</a>" "@param format Either JPEG or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PNG.\n</a>" "@param tileCount If greater than 1 will tile the current screen <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> take <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> large format <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">screenshot.\n</a>" "@param tileOverlap The amount of horizontal and vertical overlap between the tiles used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> remove tile edge artifacts from post <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GFX\n</a>" )

sBlendPixelRGB888(U8 * src, U8 * dst, F32 factor)

  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/screenshot.h"
 26
 27#include "math/util/frustum.h"
 28#include "core/stream/fileStream.h"
 29#include "gui/core/guiCanvas.h"
 30#include "gfx/bitmap/pngUtils.h"
 31#include "console/engineAPI.h"
 32
 33
 34// Note: This will be initialized by the device.
 35ScreenShot *gScreenShot = NULL;
 36
 37inline void sBlendPixelRGB888( U8* src, U8* dst, F32 factor )
 38{   
 39   U32 inFactor = factor * BIT(8);
 40   U32 outFactor = BIT(8) - inFactor;
 41   
 42   dst[0] = ((U32)src[0]*inFactor + (U32)dst[0]*outFactor) >> 8;
 43   dst[1] = ((U32)src[1]*inFactor + (U32)dst[1]*outFactor) >> 8;
 44   dst[2] = ((U32)src[2]*inFactor + (U32)dst[2]*outFactor) >> 8;
 45}
 46
 47
 48ScreenShot::ScreenShot()
 49   :  mPending( false ),
 50      mWriteJPG( false ),
 51      mTiles( 1 ),
 52      mCurrTile( 0, 0 )
 53{
 54   mFilename[0] = 0;
 55}
 56
 57void ScreenShot::setPending( const char *filename, bool writeJPG, S32 tiles, F32 overlap )
 58{
 59   dStrcpy( mFilename, filename, 256 );
 60   mWriteJPG = writeJPG;
 61   mTiles = getMax( tiles, 1 );
 62   mPixelOverlap.set(getMin(overlap, 0.25f), getMin(overlap, 0.25f));      
 63
 64   mPending = true;
 65}
 66   
 67void ScreenShot::tileFrustum( Frustum& frustum )
 68{
 69   AssertFatal( mPending, "ScreenShot::tileFrustum() - This should only be called during screenshots!" );
 70
 71   // We do not need to make changes on a single tile.
 72   if ( mTiles == 1 )
 73      return;
 74
 75   frustum.tileFrustum(mTiles, mCurrTile, mFrustumOverlap);
 76}
 77
 78void ScreenShot::tileGui( const Point2I &screenSize )
 79{
 80   AssertFatal( mPending, "ScreenShot::tileGui() - This should only be called during screenshots!" );
 81
 82   // We do not need to make changes on a single tile.
 83   if ( mTiles == 1 )
 84      return;
 85   
 86   GFX->setWorldMatrix( MatrixF::Identity );
 87
 88   S32 currTileX = mCurrTile.x;
 89   S32 currTileY = (S32)mTiles - mCurrTile.y - 1; //Vertically flipped tile index
 90
 91   MatrixF tileMat( true );
 92   Point3F tilePos(0,0,0);
 93   tilePos.x = currTileX * (-screenSize.x) + mPixelOverlap.x * screenSize.x * (currTileX * 2 + 1);   
 94   tilePos.y = currTileY * (-screenSize.y) + mPixelOverlap.y * screenSize.y * (currTileY * 2 + 1);
 95   
 96   tileMat.setPosition( tilePos + Point3F(0.5f, 0.5f, 0) );
 97   tileMat.scale( Point3F( (F32)mTiles * (1-mPixelOverlap.x*2), (F32)mTiles * (1-mPixelOverlap.y*2), 0 ) );
 98
 99   GFX->setViewMatrix( tileMat );
100}
101
102void ScreenShot::capture( GuiCanvas *canvas )
103{
104   AssertFatal( mPending, "ScreenShot::capture() - The capture wasn't pending!" );
105
106   if ( mTiles == 1 )
107   {
108      _singleCapture( canvas );
109      return;
110   }
111
112   char filename[256];
113
114   Point2I canvasSize = canvas->getPlatformWindow()->getVideoMode().resolution;
115   
116   // Calculate the real final size taking overlap in account
117   Point2I overlapPixels( canvasSize.x * mPixelOverlap.x, canvasSize.y * mPixelOverlap.y );   
118
119   // Calculate the overlap to be used by the frustum tiling, 
120   // so it properly expands the frustum to overlap the amount of pixels we want
121   mFrustumOverlap.x = ((F32)canvasSize.x/(canvasSize.x - overlapPixels.x*2)) * ((F32)overlapPixels.x/(F32)canvasSize.x);
122   mFrustumOverlap.y = ((F32)canvasSize.y/(canvasSize.y - overlapPixels.y*2)) * ((F32)overlapPixels.y/(F32)canvasSize.y);
123   
124   //overlapPixels.set(0,0);
125   // Get a buffer to write a row of tiles into.
126   GBitmap *outBuffer = new GBitmap( canvasSize.x * mTiles - overlapPixels.x * mTiles * 2 , canvasSize.y - overlapPixels.y );
127
128   // Open up the file on disk.
129   dSprintf( filename, 256, "%s.%s", mFilename, "png" );
130   FileStream fs;
131   if ( !fs.open( filename, Torque::FS::File::Write ) )
132      Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename );
133
134   // Open a PNG stream for the final image
135   DeferredPNGWriter pngWriter;
136   pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0);
137   
138   // Render each tile to generate a huge screenshot.
139   for( U32 ty=0; ty < mTiles; ty++ )
140   {
141      for( S32 tx=0; tx < mTiles; tx++ )
142      {
143         // Set the current tile offset for tileFrustum().
144         mCurrTile.set( tx, mTiles - ty - 1 );
145
146         // Let the canvas render the scene.
147         canvas->renderFrame( false );
148
149         // Now grab the current back buffer.
150         GBitmap *gb = _captureBackBuffer();
151
152         // The current GFX device couldn't capture the backbuffer or it's unable of doing so.
153         if (gb == NULL)
154            return;
155
156                  
157         // Copy the captured bitmap into its tile
158         // within the output bitmap.         
159         const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();         
160         const U8 *inColor = gb->getBits() + inStride * overlapPixels.y;
161         const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel();
162         const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();         
163         const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2;
164         const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel();
165         U8 *outColor = outBuffer->getWritableBits() + outOffset;
166         for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ )
167         {
168            dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride );
169            
170            //Grandient blend the left overlap area of this tile over the previous tile left border
171            if (tx && !(ty && row < overlapPixels.y))
172            {
173               U8 *blendOverlapSrc = (U8*)inColor;
174               U8 *blendOverlapDst = outColor - inOverlapOffset;
175               for ( U32 px=0; px < overlapPixels.x; px++)
176               {
177                  F32 blendFactor = (F32)px / (F32)overlapPixels.x;
178                  sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);                 
179
180                  blendOverlapSrc += gb->getBytesPerPixel();
181                  blendOverlapDst += outBuffer->getBytesPerPixel();                   
182               }               
183            }
184
185            //Gradient blend against the rows the excess overlap rows already in the buffer            
186            if (ty && row < overlapPixels.y)
187            {
188               F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y;
189               U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
190               U8 *blendDst = outColor;               
191               for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++)
192               {                  
193                  sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor); 
194                  blendSrc += gb->getBytesPerPixel();
195                  blendDst += outBuffer->getBytesPerPixel();                   
196               }                              
197            }
198
199            
200            inColor += inStride;
201            outColor += outStride;
202         }
203
204         delete gb;
205      }
206
207      // Write the captured tile row into the PNG stream
208      pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y);
209   }
210
211   //Close the PNG stream
212   pngWriter.end();
213   
214   // We captured... clear the flag.
215   mPending = false;
216}
217
218
219void ScreenShot::_singleCapture( GuiCanvas *canvas )
220{
221   // Let the canvas render the scene.
222   canvas->renderFrame( false );
223
224   // Now grab the current back buffer.
225   GBitmap *bitmap = _captureBackBuffer();
226
227   // The current GFX device couldn't capture the backbuffer or it's unable of doing so.
228   if (bitmap == NULL)
229      return;
230
231   // We captured... clear the flag.
232   mPending = false;
233
234   // We gotta attach the extension ourselves.
235   char filename[256];
236   dSprintf( filename, 256, "%s.%s", mFilename, mWriteJPG ? "jpg" : "png" );
237
238   // Open up the file on disk.
239   FileStream fs;
240   if ( !fs.open( filename, Torque::FS::File::Write ) )
241      Con::errorf( "ScreenShot::_singleCapture() - Failed to open output file '%s'!", filename );
242   else
243   {
244      // Write it and close.
245      if ( mWriteJPG )
246         bitmap->writeBitmap( "jpg", fs );
247      else
248         bitmap->writeBitmap( "png", fs );
249
250      fs.close();
251   }
252
253   // Cleanup.
254   delete bitmap;
255}
256
257
258DefineEngineFunction( screenShot, void, 
259   ( const char *file, const char *format, U32 tileCount, F32 tileOverlap ),
260   ( 1, 0 ),
261   "Takes a screenshot with optional tiling to produce huge screenshots.\n"
262   "@param file The output image file path.\n"
263   "@param format Either JPEG or PNG.\n"
264   "@param tileCount If greater than 1 will tile the current screen size to take a large format screenshot.\n"
265   "@param tileOverlap The amount of horizontal and vertical overlap between the tiles used to remove tile edge artifacts from post effects.\n"
266   "@ingroup GFX\n" )
267{
268   if ( !gScreenShot )
269   {
270      Con::errorf( "Screenshot module not initialized by device" );
271      return;
272   }
273
274   Torque::Path ssPath( file );
275   Torque::FS::CreatePath( ssPath );
276   Torque::FS::FileSystemRef fs = Torque::FS::GetFileSystem(ssPath);
277   Torque::Path newPath = fs->mapTo(ssPath);
278
279   gScreenShot->setPending(   newPath.getFullPath(), 
280                              dStricmp( format, "JPEG" ) == 0,
281                              tileCount, 
282                              tileOverlap );
283}
284
285