screenshot.cpp
Engine/source/gfx/screenshot.cpp
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