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