1 /****************************************************************************
2 
3  Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.6.3.
6 
7  http://www.libqglviewer.com - contact@libqglviewer.com
8 
9  This file may be used under the terms of the GNU General Public License
10  versions 2.0 or 3.0 as published by the Free Software Foundation and
11  appearing in the LICENSE file included in the packaging of this file.
12  In addition, as a special exception, Gilles Debunne gives you certain
13  additional rights, described in the file GPL_EXCEPTION in this package.
14 
15  libQGLViewer uses dual licensing. Commercial/proprietary software must
16  purchase a libQGLViewer Commercial License.
17 
18  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 
21 *****************************************************************************/
22 
23 #include "qglviewer.h"
24 
25 #ifndef NO_VECTORIAL_RENDER
26 #  include "ui_VRenderInterface.h"
27 #  include "VRender/VRender.h"
28 #endif
29 
30 #include "ui_ImageInterface.h"
31 
32 // Output format list
33 # include <QImageWriter>
34 
35 #include <qfileinfo.h>
36 #include <qfiledialog.h>
37 #include <qmessagebox.h>
38 #include <qapplication.h>
39 #include <qmap.h>
40 #include <qinputdialog.h>
41 #include <qprogressdialog.h>
42 #include <qcursor.h>
43 
44 using namespace std;
45 
46 ////// Static global variables - local to this file //////
47 // List of available output file formats, formatted for QFileDialog.
48 static QString formats;
49 // Converts QFileDialog resulting format to Qt snapshotFormat.
50 static QMap<QString, QString> Qtformat;
51 // Converts Qt snapshotFormat to QFileDialog menu string.
52 static QMap<QString, QString> FDFormatString;
53 // Converts snapshotFormat to file extension
54 static QMap<QString, QString> extension;
55 
56 
57 /*! Sets snapshotFileName(). */
setSnapshotFileName(const QString & name)58 void QGLViewer::setSnapshotFileName(const QString& name)
59 {
60 	snapshotFileName_ = QFileInfo(name).absoluteFilePath();
61 }
62 
63 #ifndef DOXYGEN
snapshotFilename() const64 const QString& QGLViewer::snapshotFilename() const
65 {
66 	qWarning("snapshotFilename is deprecated. Use snapshotFileName() (uppercase N) instead.");
67 	return snapshotFileName();
68 }
69 #endif
70 
71 
72 /*! Opens a dialog that displays the different available snapshot formats.
73 
74 Then calls setSnapshotFormat() with the selected one (unless the user cancels).
75 
76 Returns \c false if the user presses the Cancel button and \c true otherwise. */
openSnapshotFormatDialog()77 bool QGLViewer::openSnapshotFormatDialog()
78 {
79 	bool ok = false;
80 	QStringList list = formats.split(";;", QString::SkipEmptyParts);
81 	int current = list.indexOf(FDFormatString[snapshotFormat()]);
82 	QString format = QInputDialog::getItem(this, "Snapshot format", "Select a snapshot format", list, current, false, &ok);
83 	if (ok)
84 		setSnapshotFormat(Qtformat[format]);
85 	return ok;
86 }
87 
88 
89 // Finds all available Qt output formats, so that they can be available in
90 // saveSnapshot dialog. Initialize snapshotFormat() to the first one.
initializeSnapshotFormats()91 void QGLViewer::initializeSnapshotFormats()
92 {
93 	QList<QByteArray> list = QImageWriter::supportedImageFormats();
94 	QStringList formatList;
95 	for (int i=0; i < list.size(); ++i)
96 		formatList << QString(list.at(i).toUpper());
97 	//        qWarning("Available image formats: ");
98 	//        QStringList::Iterator it = formatList.begin();
99 	//        while( it != formatList.end() )
100 	//  	      qWarning((*it++).);  QT4 change this. qWarning no longer accepts QString
101 
102 #ifndef NO_VECTORIAL_RENDER
103 	// We add the 3 vectorial formats to the list
104 	formatList += "EPS";
105 	formatList += "PS";
106 	formatList += "XFIG";
107 #endif
108 
109 	// Check that the interesting formats are available and add them in "formats"
110 	// Unused formats: XPM XBM PBM PGM
111 	QStringList QtText, MenuText, Ext;
112 	QtText += "JPEG";	MenuText += "JPEG (*.jpg)";		Ext += "jpg";
113 	QtText += "PNG";	MenuText += "PNG (*.png)";      Ext += "png";
114 	QtText += "EPS";	MenuText += "Encapsulated Postscript (*.eps)";	Ext += "eps";
115 	QtText += "PS";	    MenuText += "Postscript (*.ps)"; Ext += "ps";
116 	QtText += "PPM";	MenuText += "24bit RGB Bitmap (*.ppm)";	Ext += "ppm";
117 	QtText += "BMP";	MenuText += "Windows Bitmap (*.bmp)";	Ext += "bmp";
118 	QtText += "XFIG";	MenuText += "XFig (*.fig)";		Ext += "fig";
119 
120 	QStringList::iterator itText = QtText.begin();
121 	QStringList::iterator itMenu = MenuText.begin();
122 	QStringList::iterator itExt  = Ext.begin();
123 
124 	while (itText != QtText.end())
125 	{
126 		//QMessageBox::information(this, "Snapshot ", "Trying format\n"+(*itText));
127 		if (formatList.contains((*itText)))
128 		{
129 			//QMessageBox::information(this, "Snapshot ", "Recognized format\n"+(*itText));
130 			if (formats.isEmpty())
131 				setSnapshotFormat(*itText);
132 			else
133 				formats += ";;";
134 			formats += (*itMenu);
135 			Qtformat[(*itMenu)]  = (*itText);
136 			FDFormatString[(*itText)]  = (*itMenu);
137 			extension[(*itText)] = (*itExt);
138 		}
139 		// Synchronize parsing
140 		itText++;
141 		itMenu++;
142 		itExt++;
143 	}
144 }
145 
146 // Returns false if the user refused to use the fileName
checkFileName(QString & fileName,QWidget * widget,const QString & snapshotFormat)147 static bool checkFileName(QString& fileName, QWidget* widget, const QString& snapshotFormat)
148 {
149 	if (fileName.isEmpty())
150 		return false;
151 
152 	// Check that extension has been provided
153 	QFileInfo info(fileName);
154 
155 	if (info.suffix().isEmpty())
156 	{
157 		// No extension given. Silently add one
158 		if (fileName.right(1) != ".")
159 			fileName += ".";
160 		fileName += extension[snapshotFormat];
161 		info.setFile(fileName);
162 	}
163 	else if (info.suffix() != extension[snapshotFormat])
164 	{
165 		// Extension is not appropriate. Propose a modification
166 		QString modifiedName = info.absolutePath() + '/' + info.baseName() + "." + extension[snapshotFormat];
167 		QFileInfo modifInfo(modifiedName);
168 		int i=(QMessageBox::warning(widget,"Wrong extension",
169 									info.fileName()+" has a wrong extension.\nSave as "+modifInfo.fileName()+" instead ?",
170 									QMessageBox::Yes,
171 									QMessageBox::No,
172 									QMessageBox::Cancel));
173 		if (i==QMessageBox::Cancel)
174 			return false;
175 
176 		if (i==QMessageBox::Yes)
177 		{
178 			fileName = modifiedName;
179 			info.setFile(fileName);
180 		}
181 	}
182 
183 	return true;
184 }
185 
186 #ifndef NO_VECTORIAL_RENDER
187 // static void drawVectorial(void* param)
drawVectorial(void * param)188 void drawVectorial(void* param)
189 {
190 	( (QGLViewer*) param )->drawVectorial();
191 }
192 
193 #ifndef DOXYGEN
194 class ProgressDialog
195 {
196 public:
197 	static void showProgressDialog(QGLWidget* parent);
198 	static void updateProgress(float progress, const QString& stepString);
199 	static void hideProgressDialog();
200 
201 private:
202 	static QProgressDialog* progressDialog;
203 };
204 
205 QProgressDialog* ProgressDialog::progressDialog = NULL;
206 
showProgressDialog(QGLWidget * parent)207 void ProgressDialog::showProgressDialog(QGLWidget* parent)
208 {
209 	progressDialog = new QProgressDialog(parent);
210 	progressDialog->setWindowTitle("Image rendering progress");
211 	progressDialog->setMinimumSize(300, 40);
212 	progressDialog->setCancelButton(NULL);
213 	progressDialog->show();
214 }
215 
updateProgress(float progress,const QString & stepString)216 void ProgressDialog::updateProgress(float progress, const QString& stepString)
217 {
218 	progressDialog->setValue(int(progress*100));
219 	QString message(stepString);
220 	if (message.length() > 33)
221 		message = message.left(17) + "..." + message.right(12);
222 	progressDialog->setLabelText(message);
223 	progressDialog->update();
224 	qApp->processEvents();
225 }
226 
hideProgressDialog()227 void ProgressDialog::hideProgressDialog()
228 {
229 	progressDialog->close();
230 	delete progressDialog;
231 	progressDialog = NULL;
232 }
233 
234 class VRenderInterface: public QDialog, public Ui::VRenderInterface
235 {
VRenderInterface(QWidget * parent)236 public: VRenderInterface(QWidget *parent) : QDialog(parent) { setupUi(this); }
237 };
238 
239 #endif //DOXYGEN
240 
241 // Pops-up a vectorial output option dialog box and save to fileName
242 // Returns -1 in case of Cancel, 0 for success and (todo) error code in case of problem.
saveVectorialSnapshot(const QString & fileName,QGLWidget * widget,const QString & snapshotFormat)243 static int saveVectorialSnapshot(const QString& fileName, QGLWidget* widget, const QString& snapshotFormat)
244 {
245 	static VRenderInterface* VRinterface = NULL;
246 
247 	if (!VRinterface)
248 		VRinterface = new VRenderInterface(widget);
249 
250 
251 	// Configure interface according to selected snapshotFormat
252 	if (snapshotFormat == "XFIG")
253 	{
254 		VRinterface->tightenBBox->setEnabled(false);
255 		VRinterface->colorBackground->setEnabled(false);
256 	}
257 	else
258 	{
259 		VRinterface->tightenBBox->setEnabled(true);
260 		VRinterface->colorBackground->setEnabled(true);
261 	}
262 
263 	if (VRinterface->exec() == QDialog::Rejected)
264 		return -1;
265 
266 	vrender::VRenderParams vparams;
267 	vparams.setFilename(fileName);
268 
269 	if (snapshotFormat == "EPS")	vparams.setFormat(vrender::VRenderParams::EPS);
270 	if (snapshotFormat == "PS")	vparams.setFormat(vrender::VRenderParams::PS);
271 	if (snapshotFormat == "XFIG")	vparams.setFormat(vrender::VRenderParams::XFIG);
272 
273 	vparams.setOption(vrender::VRenderParams::CullHiddenFaces, !(VRinterface->includeHidden->isChecked()));
274 	vparams.setOption(vrender::VRenderParams::OptimizeBackFaceCulling, VRinterface->cullBackFaces->isChecked());
275 	vparams.setOption(vrender::VRenderParams::RenderBlackAndWhite, VRinterface->blackAndWhite->isChecked());
276 	vparams.setOption(vrender::VRenderParams::AddBackground, VRinterface->colorBackground->isChecked());
277 	vparams.setOption(vrender::VRenderParams::TightenBoundingBox, VRinterface->tightenBBox->isChecked());
278 
279 	switch (VRinterface->sortMethod->currentIndex())
280 	{
281 		case 0: vparams.setSortMethod(vrender::VRenderParams::NoSorting); 		break;
282 		case 1: vparams.setSortMethod(vrender::VRenderParams::BSPSort); 		break;
283 		case 2: vparams.setSortMethod(vrender::VRenderParams::TopologicalSort); 	break;
284 		case 3: vparams.setSortMethod(vrender::VRenderParams::AdvancedTopologicalSort);	break;
285 		default:
286 			qWarning("VRenderInterface::saveVectorialSnapshot: Unknown SortMethod");
287 	}
288 
289 	vparams.setProgressFunction(&ProgressDialog::updateProgress);
290 	ProgressDialog::showProgressDialog(widget);
291 	widget->makeCurrent();
292 	widget->raise();
293 	vrender::VectorialRender(drawVectorial, (void*) widget, vparams);
294 	ProgressDialog::hideProgressDialog();
295 	widget->setCursor(QCursor(Qt::ArrowCursor));
296 
297 	// Should return vparams.error(), but this is currently not set.
298 	return 0;
299 }
300 #endif // NO_VECTORIAL_RENDER
301 
302 
303 class ImageInterface: public QDialog, public Ui::ImageInterface
304 {
ImageInterface(QWidget * parent)305 public: ImageInterface(QWidget *parent) : QDialog(parent) { setupUi(this); }
306 };
307 
308 
309 // Pops-up an image settings dialog box and save to fileName.
310 // Returns false in case of problem.
saveImageSnapshot(const QString & fileName)311 bool QGLViewer::saveImageSnapshot(const QString& fileName)
312 {
313 	static ImageInterface* imageInterface = NULL;
314 
315 	if (!imageInterface)
316 		imageInterface = new ImageInterface(this);
317 
318 	imageInterface->imgWidth->setValue(width());
319 	imageInterface->imgHeight->setValue(height());
320 
321 	imageInterface->imgQuality->setValue(snapshotQuality());
322 
323 	if (imageInterface->exec() == QDialog::Rejected)
324 		return true;
325 
326 	// Hide closed dialog
327 	qApp->processEvents();
328 
329 	setSnapshotQuality(imageInterface->imgQuality->value());
330 
331 	QColor previousBGColor = backgroundColor();
332 	if (imageInterface->whiteBackground->isChecked())
333 		setBackgroundColor(Qt::white);
334 
335 	QSize finalSize(imageInterface->imgWidth->value(), imageInterface->imgHeight->value());
336 
337 	qreal oversampling = imageInterface->oversampling->value();
338 	QSize subSize(int(this->width()/oversampling), int(this->height()/oversampling));
339 
340 	qreal aspectRatio = width() / static_cast<qreal>(height());
341 	qreal newAspectRatio = finalSize.width() / static_cast<qreal>(finalSize.height());
342 
343 	qreal zNear = camera()->zNear();
344 	qreal zFar = camera()->zFar();
345 
346 	qreal xMin, yMin;
347 	bool expand = imageInterface->expandFrustum->isChecked();
348 	if (camera()->type() == qglviewer::Camera::PERSPECTIVE)
349 		if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
350 		{
351 			yMin = zNear * tan(camera()->fieldOfView() / 2.0);
352 			xMin = newAspectRatio * yMin;
353 		}
354 		else
355 		{
356 			xMin = zNear * tan(camera()->fieldOfView() / 2.0) * aspectRatio;
357 			yMin = xMin / newAspectRatio;
358 		}
359 	else
360 	{
361 		camera()->getOrthoWidthHeight(xMin, yMin);
362 		if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
363 			xMin = newAspectRatio * yMin;
364 		else
365 			yMin = xMin / newAspectRatio;
366 	}
367 
368 	QImage image(finalSize.width(), finalSize.height(), QImage::Format_ARGB32);
369 
370 	if (image.isNull())
371 	{
372 		QMessageBox::warning(this, "Image saving error",
373 							 "Unable to create resulting image",
374 							 QMessageBox::Ok, QMessageBox::NoButton);
375 		return false;
376 	}
377 
378 	// ProgressDialog disabled since it interfers with the screen grabing mecanism on some platforms. Too bad.
379 	// ProgressDialog::showProgressDialog(this);
380 
381 	qreal scaleX = subSize.width() / static_cast<qreal>(finalSize.width());
382 	qreal scaleY = subSize.height() / static_cast<qreal>(finalSize.height());
383 
384 	qreal deltaX = 2.0 * xMin * scaleX;
385 	qreal deltaY = 2.0 * yMin * scaleY;
386 
387 	int nbX = finalSize.width() / subSize.width();
388 	int nbY = finalSize.height() / subSize.height();
389 
390 	// Extra subimage on the right/bottom border(s) if needed
391 	if (nbX * subSize.width() < finalSize.width())
392 		nbX++;
393 	if (nbY * subSize.height() < finalSize.height())
394 		nbY++;
395 
396 	makeCurrent();
397 
398 	// tileRegion_ is used by startScreenCoordinatesSystem to appropriately set the local
399 	// coordinate system when tiling
400 	tileRegion_ = new TileRegion();
401 	qreal tileXMin, tileWidth, tileYMin, tileHeight;
402 	if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
403 	{
404 		qreal tileTotalWidth = newAspectRatio * height();
405 		tileXMin = (width() - tileTotalWidth) / 2.0;
406 		tileWidth = tileTotalWidth * scaleX;
407 		tileYMin = 0.0;
408 		tileHeight = height() * scaleY;
409 		tileRegion_->textScale = 1.0 / scaleY;
410 	}
411 	else
412 	{
413 		qreal tileTotalHeight = width() / newAspectRatio;
414 		tileYMin = (height() - tileTotalHeight) / 2.0;
415 		tileHeight = tileTotalHeight * scaleY;
416 		tileXMin = 0.0;
417 		tileWidth = width() * scaleX;
418 		tileRegion_->textScale = 1.0 / scaleX;
419 	}
420 
421 	int count=0;
422 	for (int i=0; i<nbX; i++)
423 		for (int j=0; j<nbY; j++)
424 		{
425 			preDraw();
426 
427 			// Change projection matrix
428 			glMatrixMode(GL_PROJECTION);
429 			glLoadIdentity();
430 			if (camera()->type() == qglviewer::Camera::PERSPECTIVE)
431 				glFrustum(-xMin + i*deltaX, -xMin + (i+1)*deltaX, yMin - (j+1)*deltaY, yMin - j*deltaY, zNear, zFar);
432 			else
433 				glOrtho(-xMin + i*deltaX, -xMin + (i+1)*deltaX, yMin - (j+1)*deltaY, yMin - j*deltaY, zNear, zFar);
434 			glMatrixMode(GL_MODELVIEW);
435 
436 			tileRegion_->xMin = tileXMin + i * tileWidth;
437 			tileRegion_->xMax = tileXMin + (i+1) * tileWidth;
438 			tileRegion_->yMin = tileYMin + j * tileHeight;
439 			tileRegion_->yMax = tileYMin + (j+1) * tileHeight;
440 
441 			draw();
442 			postDraw();
443 
444 			// ProgressDialog::hideProgressDialog();
445 			// qApp->processEvents();
446 
447 			QImage snapshot = grabFrameBuffer(true);
448 
449 			// ProgressDialog::showProgressDialog(this);
450 			// ProgressDialog::updateProgress(count / (qreal)(nbX*nbY),
451 			// "Generating image ["+QString::number(count)+"/"+QString::number(nbX*nbY)+"]");
452 			// qApp->processEvents();
453 
454 			QImage subImage = snapshot.scaled(subSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
455 
456 			// Copy subImage in image
457 			for (int ii=0; ii<subSize.width(); ii++)
458 			{
459 				int fi = i*subSize.width() + ii;
460 				if (fi == image.width())
461 					break;
462 				for (int jj=0; jj<subSize.height(); jj++)
463 				{
464 					int fj = j*subSize.height() + jj;
465 					if (fj == image.height())
466 						break;
467 					image.setPixel(fi, fj, subImage.pixel(ii,jj));
468 				}
469 			}
470 			count++;
471 		}
472 
473 	bool saveOK = image.save(fileName, snapshotFormat().toLatin1().constData(), snapshotQuality());
474 
475 	// ProgressDialog::hideProgressDialog();
476 	// setCursor(QCursor(Qt::ArrowCursor));
477 
478 	delete tileRegion_;
479 	tileRegion_ = NULL;
480 
481 	if (imageInterface->whiteBackground->isChecked())
482 		setBackgroundColor(previousBGColor);
483 
484 	return saveOK;
485 }
486 
487 
488 /*! Saves a snapshot of the current image displayed by the widget.
489 
490  Options are set using snapshotFormat(), snapshotFileName() and snapshotQuality(). For non vectorial
491  image formats, the image size is equal to the current viewer's dimensions (see width() and
492  height()). See snapshotFormat() for details on supported formats.
493 
494  If \p automatic is \c false (or if snapshotFileName() is empty), a file dialog is opened to ask for
495  the file name.
496 
497  When \p automatic is \c true, the file name is set to \c NAME-NUMBER, where \c NAME is
498  snapshotFileName() and \c NUMBER is snapshotCounter(). The snapshotCounter() is automatically
499  incremented after each snapshot saving. This is useful to create videos from your application:
500  \code
501  void Viewer::init()
502  {
503    resize(720, 576); // PAL DV format (use 720x480 for NTSC DV)
504    connect(this, SIGNAL(drawFinished(bool)), SLOT(saveSnapshot(bool)));
505  }
506  \endcode
507  Then call draw() in a loop (for instance using animate() and/or a camera() KeyFrameInterpolator
508  replay) to create your image sequence.
509 
510  If you want to create a Quicktime VR panoramic sequence, simply use code like this:
511  \code
512  void Viewer::createQuicktime()
513  {
514    const int nbImages = 36;
515    for (int i=0; i<nbImages; ++i)
516 	 {
517 	   camera()->setOrientation(2.0*M_PI/nbImages, 0.0); // Theta-Phi orientation
518 	   showEntireScene();
519 	   update(); // calls draw(), which emits drawFinished(), which calls saveSnapshot()
520 	 }
521  }
522  \endcode
523 
524  If snapshotCounter() is negative, no number is appended to snapshotFileName() and the
525  snapshotCounter() is not incremented. This is useful to force the creation of a file, overwriting
526  the previous one.
527 
528  When \p overwrite is set to \c false (default), a window asks for confirmation if the file already
529  exists. In \p automatic mode, the snapshotCounter() is incremented (if positive) until a
530  non-existing file name is found instead. Otherwise the file is overwritten without confirmation.
531 
532  The VRender library was written by Cyril Soler (Cyril dot Soler at imag dot fr). If the generated
533  PS or EPS file is not properly displayed, remove the anti-aliasing option in your postscript viewer.
534 
535  \note In order to correctly grab the frame buffer, the QGLViewer window is raised in front of
536  other windows by this method. */
saveSnapshot(bool automatic,bool overwrite)537 void QGLViewer::saveSnapshot(bool automatic, bool overwrite)
538 {
539 	// Ask for file name
540 	if (snapshotFileName().isEmpty() || !automatic)
541 	{
542 		QString fileName;
543 		QString selectedFormat = FDFormatString[snapshotFormat()];
544 		fileName = QFileDialog::getSaveFileName(this, "Choose a file name to save under", snapshotFileName(), formats, &selectedFormat,
545 												overwrite?QFileDialog::DontConfirmOverwrite:QFlags<QFileDialog::Option>(0));
546 		setSnapshotFormat(Qtformat[selectedFormat]);
547 
548 		if (checkFileName(fileName, this, snapshotFormat()))
549 			setSnapshotFileName(fileName);
550 		else
551 			return;
552 	}
553 
554 	QFileInfo fileInfo(snapshotFileName());
555 
556 	if ((automatic) && (snapshotCounter() >= 0))
557 	{
558 		// In automatic mode, names have a number appended
559 		const QString baseName = fileInfo.baseName();
560 		QString count;
561 		count.sprintf("%.04d", snapshotCounter_++);
562 		QString suffix;
563 		suffix = fileInfo.suffix();
564 		if (suffix.isEmpty())
565 			suffix = extension[snapshotFormat()];
566 		fileInfo.setFile(fileInfo.absolutePath()+ '/' + baseName + '-' + count + '.' + suffix);
567 
568 		if (!overwrite)
569 			while (fileInfo.exists())
570 			{
571 				count.sprintf("%.04d", snapshotCounter_++);
572 				fileInfo.setFile(fileInfo.absolutePath() + '/' +baseName + '-' + count + '.' + fileInfo.suffix());
573 			}
574 	}
575 
576 	bool saveOK;
577 #ifndef NO_VECTORIAL_RENDER
578 	if ( (snapshotFormat() == "EPS") || (snapshotFormat() == "PS") || (snapshotFormat() == "XFIG") )
579 		// Vectorial snapshot. -1 means cancel, 0 is ok, >0 (should be) an error
580 		saveOK = (saveVectorialSnapshot(fileInfo.filePath(), this, snapshotFormat()) <= 0);
581 	else
582 #endif
583 		if (automatic)
584 		{
585 			QImage snapshot = frameBufferSnapshot();
586 			saveOK = snapshot.save(fileInfo.filePath(), snapshotFormat().toLatin1().constData(), snapshotQuality());
587 		}
588 		else
589 			saveOK = saveImageSnapshot(fileInfo.filePath());
590 
591 	if (!saveOK)
592 		QMessageBox::warning(this, "Snapshot problem", "Unable to save snapshot in\n"+fileInfo.filePath());
593 }
594 
frameBufferSnapshot()595 QImage QGLViewer::frameBufferSnapshot()
596 {
597 	// Viewer must be on top of other windows.
598 	makeCurrent();
599 	raise();
600 	// Hack: Qt has problems if the frame buffer is grabbed after QFileDialog is displayed.
601 	// We grab the frame buffer before, even if it might be not necessary (vectorial rendering).
602 	// The problem could not be reproduced on a simple example to submit a Qt bug.
603 	// However, only grabs the backgroundImage in the eponym example. May come from the driver.
604 	return grabFrameBuffer(true);
605 }
606 
607 /*! Same as saveSnapshot(), except that it uses \p fileName instead of snapshotFileName().
608 
609  If \p fileName is empty, opens a file dialog to select the name.
610 
611  Snapshot settings are set from snapshotFormat() and snapshotQuality().
612 
613  Asks for confirmation when \p fileName already exists and \p overwrite is \c false (default).
614 
615  \attention If \p fileName is a char* (as is "myFile.jpg"), it may be casted into a \c bool, and the
616  other saveSnapshot() method may be used instead. Pass QString("myFile.jpg") as a parameter to
617  prevent this. */
saveSnapshot(const QString & fileName,bool overwrite)618 void QGLViewer::saveSnapshot(const QString& fileName, bool overwrite)
619 {
620 	const QString previousName = snapshotFileName();
621 	const int previousCounter = snapshotCounter();
622 	setSnapshotFileName(fileName);
623 	setSnapshotCounter(-1);
624 	saveSnapshot(true, overwrite);
625 	setSnapshotFileName(previousName);
626 	setSnapshotCounter(previousCounter);
627 }
628 
629 /*! Takes a snapshot of the current display and pastes it to the clipboard.
630 
631 This action is activated by the KeyboardAction::SNAPSHOT_TO_CLIPBOARD enum, binded to \c Ctrl+C by default.
632 */
snapshotToClipboard()633 void QGLViewer::snapshotToClipboard()
634 {
635 	QClipboard *cb = QApplication::clipboard();
636 	cb->setImage(frameBufferSnapshot());
637 }
638 
639