1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12 */
13 
14 #include <osgDB/WriteFile>
15 
16 #include <osgViewer/Viewer>
17 #include <osgViewer/ViewerEventHandlers>
18 
19 #include <string.h>
20 
21 namespace osgViewer
22 {
23 
24 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
25 //
26 //  WindowCaptureCallback
27 //
28 
29 // From osgscreencapture example
30 /** Callback which will be added to a viewer's camera to do the actual screen capture. */
31 class WindowCaptureCallback : public osg::Camera::DrawCallback
32 {
33     public:
34 
35         enum Mode
36         {
37             READ_PIXELS,
38             SINGLE_PBO,
39             DOUBLE_PBO,
40             TRIPLE_PBO
41         };
42 
43         enum FramePosition
44         {
45             START_FRAME,
46             END_FRAME
47         };
48 
49         WindowCaptureCallback(int numFrames, Mode mode, FramePosition position, GLenum readBuffer);
50 
getFramePosition() const51         FramePosition getFramePosition() const { return _position; }
52 
53         void setCaptureOperation(ScreenCaptureHandler::CaptureOperation* operation);
getCaptureOperation()54         ScreenCaptureHandler::CaptureOperation* getCaptureOperation() { return _contextDataMap.begin()->second->_captureOperation.get(); }
55 
setFramesToCapture(int numFrames)56         void setFramesToCapture(int numFrames) { _numFrames = numFrames; }
getFramesToCapture() const57         int getFramesToCapture() const { return _numFrames; }
58 
59         virtual void operator () (osg::RenderInfo& renderInfo) const;
60 
61         struct OSGVIEWER_EXPORT ContextData : public osg::Referenced
62         {
63             ContextData(osg::GraphicsContext* gc, Mode mode, GLenum readBuffer);
64 
65             void getSize(osg::GraphicsContext* gc, int& width, int& height);
66 
67             void updateTimings(osg::Timer_t tick_start,
68                                osg::Timer_t tick_afterReadPixels,
69                                osg::Timer_t tick_afterMemCpy,
70                                osg::Timer_t tick_afterCaptureOperation,
71                                unsigned int dataSize);
72 
73             void read();
74             void readPixels();
75             void singlePBO(osg::GLExtensions* ext);
76             void multiPBO(osg::GLExtensions* ext);
77 
78             typedef std::vector< osg::ref_ptr<osg::Image> >             ImageBuffer;
79             typedef std::vector< GLuint > PBOBuffer;
80 
81             osg::GraphicsContext*   _gc;
82             unsigned int            _index;
83             Mode                    _mode;
84             GLenum                  _readBuffer;
85 
86             GLenum                  _pixelFormat;
87             GLenum                  _type;
88             int                     _width;
89             int                     _height;
90 
91             unsigned int            _currentImageIndex;
92             ImageBuffer             _imageBuffer;
93 
94             unsigned int            _currentPboIndex;
95             PBOBuffer               _pboBuffer;
96 
97             unsigned int            _reportTimingFrequency;
98             unsigned int            _numTimeValuesRecorded;
99             double                  _timeForReadPixels;
100             double                  _timeForMemCpy;
101             double                  _timeForCaptureOperation;
102             double                  _timeForFullCopy;
103             double                  _timeForFullCopyAndOperation;
104             osg::Timer_t            _previousFrameTick;
105 
106             osg::ref_ptr<ScreenCaptureHandler::CaptureOperation> _captureOperation;
107         };
108 
109         typedef std::map<osg::GraphicsContext*, osg::ref_ptr<ContextData> > ContextDataMap;
110 
111         ContextData* createContextData(osg::GraphicsContext* gc) const;
112         ContextData* getContextData(osg::GraphicsContext* gc) const;
113 
114         Mode                        _mode;
115         FramePosition               _position;
116         GLenum                      _readBuffer;
117         mutable OpenThreads::Mutex  _mutex;
118         mutable ContextDataMap      _contextDataMap;
119         mutable int                 _numFrames;
120 
121         osg::ref_ptr<ScreenCaptureHandler::CaptureOperation> _defaultCaptureOperation;
122 };
123 
124 
ContextData(osg::GraphicsContext * gc,Mode mode,GLenum readBuffer)125 WindowCaptureCallback::ContextData::ContextData(osg::GraphicsContext* gc, Mode mode, GLenum readBuffer)
126     : _gc(gc),
127       _index(_gc->getState()->getContextID()),
128       _mode(mode),
129       _readBuffer(readBuffer),
130       _pixelFormat(GL_RGBA),
131       _type(GL_UNSIGNED_BYTE),
132       _width(0),
133       _height(0),
134       _currentImageIndex(0),
135       _currentPboIndex(0),
136       _reportTimingFrequency(100),
137       _numTimeValuesRecorded(0),
138       _timeForReadPixels(0.0),
139       _timeForMemCpy(0.0),
140       _timeForCaptureOperation(0.0),
141       _timeForFullCopy(0.0),
142       _timeForFullCopyAndOperation(0.0),
143       _previousFrameTick(0)
144 {
145     _previousFrameTick = osg::Timer::instance()->tick();
146 
147     osg::NotifySeverity level = osg::INFO;
148 
149     if (gc->getTraits())
150     {
151         if (gc->getTraits()->alpha)
152         {
153             OSG_NOTIFY(level)<<"ScreenCaptureHandler: Selected GL_RGBA read back format"<<std::endl;
154             _pixelFormat = GL_RGBA;
155         }
156         else
157         {
158             OSG_NOTIFY(level)<<"ScreenCaptureHandler: Selected GL_RGB read back format"<<std::endl;
159             _pixelFormat = GL_RGB;
160         }
161     }
162 
163     getSize(gc, _width, _height);
164 
165     //OSG_NOTICE<<"Window size "<<_width<<", "<<_height<<std::endl;
166 
167     // single buffered image
168     _imageBuffer.push_back(new osg::Image);
169 
170     // double buffer PBO.
171     switch(_mode)
172     {
173         case(READ_PIXELS):
174             OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, without PixelBufferObject."<<std::endl;
175             break;
176         case(SINGLE_PBO):
177             OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, with a single PixelBufferObject."<<std::endl;
178             _pboBuffer.push_back(0);
179             break;
180         case(DOUBLE_PBO):
181             OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, with a double buffer PixelBufferObject."<<std::endl;
182             _pboBuffer.push_back(0);
183             _pboBuffer.push_back(0);
184             break;
185         case(TRIPLE_PBO):
186             OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, with a triple buffer PixelBufferObject."<<std::endl;
187             _pboBuffer.push_back(0);
188             _pboBuffer.push_back(0);
189             _pboBuffer.push_back(0);
190             break;
191         default:
192             break;
193     }
194 }
195 
getSize(osg::GraphicsContext * gc,int & width,int & height)196 void WindowCaptureCallback::ContextData::getSize(osg::GraphicsContext* gc, int& width, int& height)
197 {
198     if (gc->getTraits())
199     {
200         width = gc->getTraits()->width;
201         height = gc->getTraits()->height;
202     }
203 }
204 
updateTimings(osg::Timer_t tick_start,osg::Timer_t tick_afterReadPixels,osg::Timer_t tick_afterMemCpy,osg::Timer_t tick_afterCaptureOperation,unsigned int)205 void WindowCaptureCallback::ContextData::updateTimings(osg::Timer_t tick_start,
206                                                        osg::Timer_t tick_afterReadPixels,
207                                                        osg::Timer_t tick_afterMemCpy,
208                                                        osg::Timer_t tick_afterCaptureOperation,
209                                                        unsigned int /*dataSize*/)
210 {
211     _timeForReadPixels = osg::Timer::instance()->delta_s(tick_start, tick_afterReadPixels);
212     _timeForMemCpy = osg::Timer::instance()->delta_s(tick_afterReadPixels, tick_afterMemCpy);
213     _timeForCaptureOperation = osg::Timer::instance()->delta_s(tick_afterMemCpy, tick_afterCaptureOperation);
214 
215     _timeForFullCopy = osg::Timer::instance()->delta_s(tick_start, tick_afterMemCpy);
216     _timeForFullCopyAndOperation = osg::Timer::instance()->delta_s(tick_start, tick_afterCaptureOperation);
217 }
218 
read()219 void WindowCaptureCallback::ContextData::read()
220 {
221     osg::GLExtensions* ext = osg::GLExtensions::Get(_gc->getState()->getContextID(),true);
222 
223     if (ext->isPBOSupported && !_pboBuffer.empty())
224     {
225         if (_pboBuffer.size()==1)
226         {
227             singlePBO(ext);
228         }
229         else
230         {
231             multiPBO(ext);
232         }
233     }
234     else
235     {
236         readPixels();
237     }
238 }
239 
240 
readPixels()241 void WindowCaptureCallback::ContextData::readPixels()
242 {
243     unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
244     unsigned int nextPboIndex = _pboBuffer.empty() ? 0 : (_currentPboIndex+1)%_pboBuffer.size();
245 
246     int width=0, height=0;
247     getSize(_gc, width, height);
248     if (width!=_width || _height!=height)
249     {
250         //OSG_NOTICE<<"   Window resized "<<width<<", "<<height<<std::endl;
251         _width = width;
252         _height = height;
253     }
254 
255     osg::Image* image = _imageBuffer[_currentImageIndex].get();
256 
257     osg::Timer_t tick_start = osg::Timer::instance()->tick();
258 
259 #if 1
260     image->readPixels(0,0,_width,_height,
261                       _pixelFormat,_type);
262 #endif
263 
264     osg::Timer_t tick_afterReadPixels = osg::Timer::instance()->tick();
265 
266     if (_captureOperation.valid())
267     {
268         (*_captureOperation)(*image, _index);
269     }
270 
271     osg::Timer_t tick_afterCaptureOperation = osg::Timer::instance()->tick();
272     updateTimings(tick_start, tick_afterReadPixels, tick_afterReadPixels, tick_afterCaptureOperation, image->getTotalSizeInBytes());
273 
274     _currentImageIndex = nextImageIndex;
275     _currentPboIndex = nextPboIndex;
276 }
277 
singlePBO(osg::GLExtensions * ext)278 void WindowCaptureCallback::ContextData::singlePBO(osg::GLExtensions* ext)
279 {
280     unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
281 
282     int width=0, height=0;
283     getSize(_gc, width, height);
284     if (width!=_width || _height!=height)
285     {
286         //OSG_NOTICE<<"   Window resized "<<width<<", "<<height<<std::endl;
287         _width = width;
288         _height = height;
289     }
290 
291     GLuint& pbo = _pboBuffer[0];
292 
293     osg::Image* image = _imageBuffer[_currentImageIndex].get();
294     if (image->s() != _width ||
295         image->t() != _height)
296     {
297         //OSG_NOTICE<<"ScreenCaptureHandler: Allocating image "<<std::endl;
298         image->allocateImage(_width, _height, 1, _pixelFormat, _type);
299 
300         if (pbo!=0)
301         {
302             //OSG_NOTICE<<"ScreenCaptureHandler: deleting pbo "<<pbo<<std::endl;
303             ext->glDeleteBuffers (1, &pbo);
304             pbo = 0;
305         }
306     }
307 
308 
309     if (pbo==0)
310     {
311         ext->glGenBuffers(1, &pbo);
312         ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
313         ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
314 
315         //OSG_NOTICE<<"ScreenCaptureHandler: Generating pbo "<<pbo<<std::endl;
316     }
317     else
318     {
319         ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
320     }
321 
322     osg::Timer_t tick_start = osg::Timer::instance()->tick();
323 
324 #if 1
325     glReadPixels(0, 0, _width, _height, _pixelFormat, _type, 0);
326 #endif
327 
328     osg::Timer_t tick_afterReadPixels = osg::Timer::instance()->tick();
329 
330     GLubyte* src = (GLubyte*)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
331                                               GL_READ_ONLY_ARB);
332     if(src)
333     {
334         memcpy(image->data(), src, image->getTotalSizeInBytes());
335         ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
336     }
337 
338     ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
339 
340     osg::Timer_t tick_afterMemCpy = osg::Timer::instance()->tick();
341 
342     if (_captureOperation.valid())
343     {
344         (*_captureOperation)(*image, _index);
345     }
346 
347     osg::Timer_t tick_afterCaptureOperation = osg::Timer::instance()->tick();
348     updateTimings(tick_start, tick_afterReadPixels, tick_afterMemCpy, tick_afterCaptureOperation, image->getTotalSizeInBytes());
349 
350     _currentImageIndex = nextImageIndex;
351 }
352 
multiPBO(osg::GLExtensions * ext)353 void WindowCaptureCallback::ContextData::multiPBO(osg::GLExtensions* ext)
354 {
355     unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
356     unsigned int nextPboIndex = (_currentPboIndex+1)%_pboBuffer.size();
357 
358     int width=0, height=0;
359     getSize(_gc, width, height);
360     if (width!=_width || _height!=height)
361     {
362         //OSG_NOTICE<<"   Window resized "<<width<<", "<<height<<std::endl;
363         _width = width;
364         _height = height;
365     }
366 
367     GLuint& copy_pbo = _pboBuffer[_currentPboIndex];
368     GLuint& read_pbo = _pboBuffer[nextPboIndex];
369 
370     osg::Image* image = _imageBuffer[_currentImageIndex].get();
371     if (image->s() != _width ||
372         image->t() != _height)
373     {
374         //OSG_NOTICE<<"ScreenCaptureHandler: Allocating image "<<std::endl;
375         image->allocateImage(_width, _height, 1, _pixelFormat, _type);
376 
377         if (read_pbo!=0)
378         {
379             //OSG_NOTICE<<"ScreenCaptureHandler: deleting pbo "<<read_pbo<<std::endl;
380             ext->glDeleteBuffers (1, &read_pbo);
381             read_pbo = 0;
382         }
383 
384         if (copy_pbo!=0)
385         {
386             //OSG_NOTICE<<"ScreenCaptureHandler: deleting pbo "<<copy_pbo<<std::endl;
387             ext->glDeleteBuffers (1, &copy_pbo);
388             copy_pbo = 0;
389         }
390     }
391 
392 
393     bool doCopy = copy_pbo!=0;
394     if (copy_pbo==0)
395     {
396         ext->glGenBuffers(1, &copy_pbo);
397         ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, copy_pbo);
398         ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
399 
400         //OSG_NOTICE<<"ScreenCaptureHandler: Generating pbo "<<read_pbo<<std::endl;
401     }
402 
403     if (read_pbo==0)
404     {
405         ext->glGenBuffers(1, &read_pbo);
406         ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, read_pbo);
407         ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
408 
409         //OSG_NOTICE<<"ScreenCaptureHandler: Generating pbo "<<read_pbo<<std::endl;
410     }
411     else
412     {
413         ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, read_pbo);
414     }
415 
416     osg::Timer_t tick_start = osg::Timer::instance()->tick();
417 
418 #if 1
419     glReadPixels(0, 0, _width, _height, _pixelFormat, _type, 0);
420 #endif
421 
422     osg::Timer_t tick_afterReadPixels = osg::Timer::instance()->tick();
423 
424     if (doCopy)
425     {
426 
427         ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, copy_pbo);
428 
429         GLubyte* src = (GLubyte*)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
430                                                   GL_READ_ONLY_ARB);
431         if(src)
432         {
433             memcpy(image->data(), src, image->getTotalSizeInBytes());
434             ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
435         }
436 
437         if (_captureOperation.valid())
438         {
439             (*_captureOperation)(*image, _index);
440         }
441     }
442 
443     ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
444 
445     osg::Timer_t tick_afterMemCpy = osg::Timer::instance()->tick();
446 
447     updateTimings(tick_start, tick_afterReadPixels, tick_afterMemCpy, tick_afterMemCpy, image->getTotalSizeInBytes());
448 
449     _currentImageIndex = nextImageIndex;
450     _currentPboIndex = nextPboIndex;
451 }
452 
WindowCaptureCallback(int numFrames,Mode mode,FramePosition position,GLenum readBuffer)453 WindowCaptureCallback::WindowCaptureCallback(int numFrames, Mode mode, FramePosition position, GLenum readBuffer)
454     : _mode(mode),
455       _position(position),
456       _readBuffer(readBuffer),
457       _numFrames(numFrames)
458 {
459 }
460 
createContextData(osg::GraphicsContext * gc) const461 WindowCaptureCallback::ContextData* WindowCaptureCallback::createContextData(osg::GraphicsContext* gc) const
462 {
463     WindowCaptureCallback::ContextData* cd = new WindowCaptureCallback::ContextData(gc, _mode, _readBuffer);
464     cd->_captureOperation = _defaultCaptureOperation;
465     return cd;
466 }
467 
getContextData(osg::GraphicsContext * gc) const468 WindowCaptureCallback::ContextData* WindowCaptureCallback::getContextData(osg::GraphicsContext* gc) const
469 {
470     OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
471     osg::ref_ptr<ContextData>& data = _contextDataMap[gc];
472     if (!data) data = createContextData(gc);
473 
474     return data.get();
475 }
476 
setCaptureOperation(ScreenCaptureHandler::CaptureOperation * operation)477 void WindowCaptureCallback::setCaptureOperation(ScreenCaptureHandler::CaptureOperation* operation)
478 {
479     _defaultCaptureOperation = operation;
480 
481     // Set the capture operation for each ContextData.
482     for (ContextDataMap::iterator it = _contextDataMap.begin(); it != _contextDataMap.end(); ++it)
483     {
484         it->second->_captureOperation = operation;
485     }
486 }
487 
488 
operator ()(osg::RenderInfo & renderInfo) const489 void WindowCaptureCallback::operator () (osg::RenderInfo& renderInfo) const
490 {
491 #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
492     glReadBuffer(_readBuffer);
493 #endif
494 
495     osg::GraphicsContext* gc = renderInfo.getState()->getGraphicsContext();
496     osg::ref_ptr<ContextData> cd = getContextData(gc);
497     cd->read();
498 
499     // If _numFrames is > 0 it means capture that number of frames.
500     if (_numFrames > 0)
501     {
502         --_numFrames;
503         if (_numFrames == 0)
504         {
505             // the callback must remove itself when it's done.
506             if (_position == START_FRAME)
507                 renderInfo.getCurrentCamera()->setInitialDrawCallback(0);
508             if (_position == END_FRAME)
509                 renderInfo.getCurrentCamera()->setFinalDrawCallback(0);
510         }
511     }
512 
513     int prec = osg::notify(osg::INFO).precision(5);
514     OSG_INFO << "ScreenCaptureHandler: "
515                            << "copy="      << (cd->_timeForFullCopy*1000.0f)             << "ms, "
516                            << "operation=" << (cd->_timeForCaptureOperation*1000.0f)     << "ms, "
517                            << "total="     << (cd->_timeForFullCopyAndOperation*1000.0f) << std::endl;
518     osg::notify(osg::INFO).precision(prec);
519 
520     cd->_timeForFullCopy = 0;
521 }
522 
523 
524 
525 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
526 //
527 //  ScreenCaptureHandler::WriteToFile
528 //
WriteToFile(const std::string & filename,const std::string & extension,SavePolicy savePolicy)529 ScreenCaptureHandler::WriteToFile::WriteToFile(const std::string& filename,
530                                                const std::string& extension,
531                                                SavePolicy savePolicy)
532     : _filename(filename), _extension(extension), _savePolicy(savePolicy)
533 {
534 }
535 
operator ()(const osg::Image & image,const unsigned int context_id)536 void ScreenCaptureHandler::WriteToFile::operator () (const osg::Image& image, const unsigned int context_id)
537 {
538     if (_savePolicy == SEQUENTIAL_NUMBER)
539     {
540         if (_contextSaveCounter.size() <= context_id)
541         {
542             unsigned int oldSize = _contextSaveCounter.size();
543             _contextSaveCounter.resize(context_id + 1);
544             // Initialize all new values to 0 since context ids may not be consecutive.
545             for (unsigned int i = oldSize; i <= context_id; i++)
546                 _contextSaveCounter[i] = 0;
547         }
548     }
549 
550     std::stringstream filename;
551     filename << _filename << "_" << context_id;
552 
553     if (_savePolicy == SEQUENTIAL_NUMBER)
554         filename << "_" << _contextSaveCounter[context_id];
555 
556     filename << "." << _extension;
557 
558     osgDB::writeImageFile(image, filename.str());
559 
560     OSG_INFO<<"ScreenCaptureHandler: Taking a screenshot, saved as '"<<filename.str()<<"'"<<std::endl;
561 
562     if (_savePolicy == SEQUENTIAL_NUMBER)
563     {
564         _contextSaveCounter[context_id]++;
565     }
566 }
567 
568 
569 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
570 //
571 //  ScreenCaptureHandler
572 //
ScreenCaptureHandler(CaptureOperation * defaultOperation,int numFrames)573 ScreenCaptureHandler::ScreenCaptureHandler(CaptureOperation* defaultOperation,
574                                            int numFrames)
575     : _startCapture(false),
576       _stopCapture(false),
577       _keyEventTakeScreenShot('c'),
578       _keyEventToggleContinuousCapture('C'),
579       _callback(new WindowCaptureCallback( numFrames,
580                                            WindowCaptureCallback::READ_PIXELS,
581 //                                          WindowCaptureCallback::SINGLE_PBO,
582 //                                          WindowCaptureCallback::DOUBLE_PBO,
583 //                                          WindowCaptureCallback::TRIPLE_PBO,
584                                            WindowCaptureCallback::END_FRAME, GL_BACK))
585 {
586     if (defaultOperation)
587         setCaptureOperation(defaultOperation);
588     else
589         setCaptureOperation(new WriteToFile("screen_shot", "jpg"));
590 }
591 
setCaptureOperation(CaptureOperation * operation)592 void ScreenCaptureHandler::setCaptureOperation(CaptureOperation* operation)
593 {
594     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
595     callback->setCaptureOperation(operation);
596 }
597 
getCaptureOperation() const598 ScreenCaptureHandler::CaptureOperation* ScreenCaptureHandler::getCaptureOperation() const
599 {
600     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
601     return callback->getCaptureOperation();
602 }
603 
addCallbackToViewer(osgViewer::ViewerBase & viewer)604 void ScreenCaptureHandler::addCallbackToViewer(osgViewer::ViewerBase& viewer)
605 {
606     osg::Camera* camera = findAppropriateCameraForCallback(viewer);
607     if (!camera) return;
608 
609     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
610     if (callback && callback->getFramePosition() == WindowCaptureCallback::START_FRAME)
611     {
612         camera->setInitialDrawCallback(_callback.get());
613     }
614     else
615     {
616         camera->setFinalDrawCallback(_callback.get());
617     }
618 }
619 
removeCallbackFromViewer(osgViewer::ViewerBase & viewer)620 void ScreenCaptureHandler::removeCallbackFromViewer(osgViewer::ViewerBase& viewer)
621 {
622     osg::Camera* camera = findAppropriateCameraForCallback(viewer);
623     if (!camera) return;
624 
625     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
626     if (callback && callback->getFramePosition() == WindowCaptureCallback::START_FRAME)
627     {
628         camera->setInitialDrawCallback(0);
629     }
630     else
631     {
632         camera->setFinalDrawCallback(0);
633     }
634 }
635 
findAppropriateCameraForCallback(osgViewer::ViewerBase & viewer)636 osg::Camera* ScreenCaptureHandler::findAppropriateCameraForCallback(osgViewer::ViewerBase& viewer)
637 {
638     // Select either the first or the last active camera, depending on the
639     // frame position set in the callback.
640     // One case where testing the node mask is important is when the stats
641     // handler has been initialized, but stats are not displayed. In that
642     // case, there is a post render camera on the viewer, but its node mask
643     // is zero, so the callback added to that camera would never be called.
644     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
645 
646     if (callback->getFramePosition() == WindowCaptureCallback::START_FRAME)
647     {
648         osgViewer::ViewerBase::Contexts contexts;
649         viewer.getContexts(contexts);
650         for(osgViewer::ViewerBase::Contexts::iterator itr = contexts.begin();
651             itr != contexts.end();
652             ++itr)
653         {
654             osg::GraphicsContext* context = *itr;
655             osg::GraphicsContext::Cameras& cameras = context->getCameras();
656             osg::Camera* firstCamera = 0;
657             for(osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin();
658                 cam_itr != cameras.end();
659                 ++cam_itr)
660             {
661                 if (firstCamera)
662                 {
663                     if ((*cam_itr)->getRenderOrder() < firstCamera->getRenderOrder())
664                     {
665                         if ((*cam_itr)->getNodeMask() != 0x0)
666                             firstCamera = (*cam_itr);
667                     }
668                     if ((*cam_itr)->getRenderOrder() == firstCamera->getRenderOrder() &&
669                         (*cam_itr)->getRenderOrderNum() < firstCamera->getRenderOrderNum())
670                     {
671                         if ((*cam_itr)->getNodeMask() != 0x0)
672                             firstCamera = (*cam_itr);
673                     }
674                 }
675                 else
676                 {
677                     if ((*cam_itr)->getNodeMask() != 0x0)
678                         firstCamera = *cam_itr;
679                 }
680             }
681 
682             if (firstCamera)
683             {
684                 //OSG_NOTICE<<"ScreenCaptureHandler: First camera "<<firstCamera<<std::endl;
685 
686                 return firstCamera;
687             }
688             else
689             {
690                 OSG_NOTICE<<"ScreenCaptureHandler: No camera found"<<std::endl;
691             }
692         }
693     }
694     else
695     {
696         osgViewer::ViewerBase::Contexts contexts;
697         viewer.getContexts(contexts);
698         for(osgViewer::ViewerBase::Contexts::iterator itr = contexts.begin();
699             itr != contexts.end();
700             ++itr)
701         {
702             osg::GraphicsContext* context = *itr;
703             osg::GraphicsContext::Cameras& cameras = context->getCameras();
704             osg::Camera* lastCamera = 0;
705             for(osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin();
706                 cam_itr != cameras.end();
707                 ++cam_itr)
708             {
709                 if (lastCamera)
710                 {
711                     if ((*cam_itr)->getRenderOrder() > lastCamera->getRenderOrder())
712                     {
713                         if ((*cam_itr)->getNodeMask() != 0x0)
714                             lastCamera = (*cam_itr);
715                     }
716                     if ((*cam_itr)->getRenderOrder() == lastCamera->getRenderOrder() &&
717                         (*cam_itr)->getRenderOrderNum() >= lastCamera->getRenderOrderNum())
718                     {
719                         if ((*cam_itr)->getNodeMask() != 0x0)
720                             lastCamera = (*cam_itr);
721                     }
722                 }
723                 else
724                 {
725                     if ((*cam_itr)->getNodeMask() != 0x0)
726                         lastCamera = *cam_itr;
727                 }
728             }
729 
730             if (lastCamera)
731             {
732                 //OSG_NOTICE<<"ScreenCaptureHandler: Last camera "<<lastCamera<<std::endl;
733 
734                 return lastCamera;
735             }
736             else
737             {
738                 OSG_NOTICE<<"ScreenCaptureHandler: No camera found"<<std::endl;
739             }
740         }
741     }
742 
743     return 0;
744 }
745 
746 // aa will point to an osgViewer::View, so we will take a screenshot
747 // of that view's graphics contexts.
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)748 bool ScreenCaptureHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
749 {
750     osgViewer::ViewerBase* viewer = dynamic_cast<osgViewer::View*>(&aa)->getViewerBase();
751     if (!viewer) return false;
752 
753     switch(ea.getEventType())
754     {
755         case (osgGA::GUIEventAdapter::FRAME):
756         {
757             // Booleans aren't the best way of doing this, but I want to do
758             // the actual adding here because I don't want to require
759             // startCapture() take a viewer as argument, which could not be
760             // the right one.
761             if (_startCapture)
762             {
763                 // Start capturing with the currently set number of frames.
764                 // If set to -1 it will capture continuously, if set to >0
765                 // it will capture that number of frames.
766                 _startCapture = false;
767                 addCallbackToViewer(*viewer);
768             }
769             else if (_stopCapture)
770             {
771                 _stopCapture = false;
772                 removeCallbackFromViewer(*viewer);
773             }
774             break;
775         }
776 
777         case(osgGA::GUIEventAdapter::KEYUP):
778         {
779             if (ea.getKey() == _keyEventTakeScreenShot)
780             {
781                 // Check that we will capture at least one frame.
782                 // Just check for ==0, because >0 is means we're already
783                 // capturing and <0 means it will capture all frames.
784                 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
785                 if (callback->getFramesToCapture() == 0)
786                 {
787                     setFramesToCapture(1);
788                 }
789                 addCallbackToViewer(*viewer);
790                 return true;
791             }
792 
793             if (ea.getKey() == _keyEventToggleContinuousCapture)
794             {
795                 if (getFramesToCapture() < 0)
796                 {
797                     setFramesToCapture(0);
798                     removeCallbackFromViewer(*viewer);
799                 }
800                 else
801                 {
802                     setFramesToCapture(-1);
803                     addCallbackToViewer(*viewer);
804                 }
805                 return true;
806             }
807             break;
808         }
809     default:
810         break;
811     }
812 
813     return false;
814 }
815 
816 /** Capture the given viewer's views on the next frame. */
captureNextFrame(osgViewer::ViewerBase & viewer)817 void ScreenCaptureHandler::captureNextFrame(osgViewer::ViewerBase& viewer)
818 {
819     addCallbackToViewer(viewer);
820 }
821 
822 /** Set the number of frames to capture. */
setFramesToCapture(int numFrames)823 void ScreenCaptureHandler::setFramesToCapture(int numFrames)
824 {
825     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
826     callback->setFramesToCapture(numFrames);
827 }
828 
829 /** Get the number of frames to capture. */
getFramesToCapture() const830 int ScreenCaptureHandler::getFramesToCapture() const
831 {
832     WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
833     return callback->getFramesToCapture();
834 }
835 
836 /** Start capturing at the end of the next frame. */
startCapture()837 void ScreenCaptureHandler::startCapture()
838 {
839     if (getFramesToCapture() != 0)
840         _startCapture = true;
841 }
842 
843 /** Stop capturing. */
stopCapture()844 void ScreenCaptureHandler::stopCapture()
845 {
846     _stopCapture = true;
847 }
848 
849 /** Get the keyboard and mouse usage of this manipulator.*/
getUsage(osg::ApplicationUsage & usage) const850 void ScreenCaptureHandler::getUsage(osg::ApplicationUsage& usage) const
851 {
852     usage.addKeyboardMouseBinding(_keyEventTakeScreenShot,"Take screenshot.");
853     usage.addKeyboardMouseBinding(_keyEventToggleContinuousCapture,"Toggle continuous screen capture.");
854 }
855 
856 }
857