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