saveSnapshot.cpp
1 /****************************************************************************
2 
3  Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.3.10.
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 # if QT_VERSION >= 0x040000
27 # include "ui_VRenderInterface.Qt4.h"
28 # else
29 # include <qcheckbox.h>
30 # include <qcombobox.h>
31 # include "VRenderInterface.Qt3.h"
32 # endif
33 # include "VRender/VRender.h"
34 #endif
35 
36 #if QT_VERSION >= 0x040000
37 # include "ui_ImageInterface.Qt4.h"
38 #else
39 # include <qspinbox.h>
40 # include <qcheckbox.h>
41 # include "ImageInterface.Qt3.h"
42 #endif
43 
44 // Output format list
45 #if QT_VERSION < 0x040000
46 # include <qimage.h>
47 #else
48 # include <QImageWriter>
49 #endif
50 
51 #include <qfileinfo.h>
52 #include <qfiledialog.h>
53 #include <qmessagebox.h>
54 #include <qapplication.h>
55 #include <qmap.h>
56 #include <qinputdialog.h>
57 #include <qprogressdialog.h>
58 #include <qcursor.h>
59 
60 using namespace std;
61 
63 // List of available output file formats, formatted for QFileDialog.
64 static QString formats;
65 // Converts QFileDialog resulting format to Qt snapshotFormat.
66 static QMap<QString, QString> Qtformat;
67 // Converts Qt snapshotFormat to QFileDialog menu string.
68 static QMap<QString, QString> FDFormatString;
69 // Converts snapshotFormat to file extension
70 static QMap<QString, QString> extension;
71 
72 
74 void QGLViewer::setSnapshotFileName(const QString& name)
75 {
76 #if QT_VERSION >= 0x040000
77  snapshotFileName_ = QFileInfo(name).absoluteFilePath();
78 #else
79  snapshotFileName_ = QFileInfo(name).absFilePath();
80 #endif
81 }
82 
83 #ifndef DOXYGEN
84 const QString& QGLViewer::snapshotFilename() const
85 {
86  qWarning("snapshotFilename is deprecated. Use snapshotFileName() (uppercase N) instead.");
87  return snapshotFileName();
88 }
89 #endif
90 
91 
98 {
99  bool ok = false;
100 #if QT_VERSION >= 0x040000
101  QStringList list = formats.split(";;", QString::SkipEmptyParts);
102  int current = list.indexOf(FDFormatString[snapshotFormat()]);
103  QString format = QInputDialog::getItem(this, "Snapshot format", "Select a snapshot format", list, current, false, &ok);
104 #else
105  QStringList list = QStringList::split(";;", formats);
106  int current = list.findIndex(FDFormatString[snapshotFormat()]);
107  QString format = QInputDialog::getItem("Snapshot format", "Select a snapshot format", list, current, false, &ok, this);
108 #endif
109  if (ok)
110  setSnapshotFormat(Qtformat[format]);
111  return ok;
112 }
113 
114 
115 // Finds all available Qt output formats, so that they can be available in
116 // saveSnapshot dialog. Initialize snapshotFormat() to the first one.
117 void QGLViewer::initializeSnapshotFormats()
118 {
119 #if QT_VERSION >= 0x040000
120  QList<QByteArray> list = QImageWriter::supportedImageFormats();
121  QStringList formatList;
122  for (int i=0; i < list.size(); ++i)
123  formatList << QString(list.at(i).toUpper());
124 #else
125  QStringList formatList = QImage::outputFormatList();
126 #endif
127  // qWarning("Available image formats: ");
128  // QStringList::Iterator it = formatList.begin();
129  // while( it != formatList.end() )
130  // qWarning((*it++).); QT4 change this. qWarning no longer accepts QString
131 
132 #ifndef NO_VECTORIAL_RENDER
133  // We add the 3 vectorial formats to the list
134  formatList += "EPS";
135  formatList += "PS";
136  formatList += "XFIG";
137 #endif
138 
139  // Check that the interesting formats are available and add them in "formats"
140  // Unused formats: XPM XBM PBM PGM
141  QStringList QtText, MenuText, Ext;
142  QtText += "JPEG"; MenuText += "JPEG (*.jpg)"; Ext += "jpg";
143  QtText += "PNG"; MenuText += "PNG (*.png)"; Ext += "png";
144  QtText += "EPS"; MenuText += "Encapsulated Postscript (*.eps)"; Ext += "eps";
145  QtText += "PS"; MenuText += "Postscript (*.ps)"; Ext += "ps";
146  QtText += "PPM"; MenuText += "24bit RGB Bitmap (*.ppm)"; Ext += "ppm";
147  QtText += "BMP"; MenuText += "Windows Bitmap (*.bmp)"; Ext += "bmp";
148  QtText += "XFIG"; MenuText += "XFig (*.fig)"; Ext += "fig";
149 
150 #if QT_VERSION < 0x030000
151  QStringList::Iterator itText = QtText.begin();
152  QStringList::Iterator itMenu = MenuText.begin();
153  QStringList::Iterator itExt = Ext.begin();
154 #else
155  QStringList::iterator itText = QtText.begin();
156  QStringList::iterator itMenu = MenuText.begin();
157  QStringList::iterator itExt = Ext.begin();
158 #endif
159  while (itText != QtText.end())
160  {
161  //QMessageBox::information(this, "Snapshot ", "Trying format\n"+(*itText));
162  if (formatList.contains((*itText)))
163  {
164  //QMessageBox::information(this, "Snapshot ", "Recognized format\n"+(*itText));
165  if (formats.isEmpty())
166  setSnapshotFormat(*itText);
167  else
168  formats += ";;";
169  formats += (*itMenu);
170  Qtformat[(*itMenu)] = (*itText);
171  FDFormatString[(*itText)] = (*itMenu);
172  extension[(*itText)] = (*itExt);
173  }
174  // Synchronize parsing
175  itText++;
176  itMenu++;
177  itExt++;
178  }
179 }
180 
181 // Returns false if the user refused to use the fileName
182 static bool checkFileName(QString& fileName, QWidget* widget, const QString& snapshotFormat)
183 {
184  if (fileName.isEmpty())
185  return false;
186 
187  // Check that extension has been provided
188  QFileInfo info(fileName);
189 
190 #if QT_VERSION >= 0x040000
191  if (info.suffix().isEmpty())
192 #else
193  if (info.extension(false).isEmpty())
194 #endif
195  {
196  // No extension given. Silently add one
197  if (fileName.right(1) != ".")
198  fileName += ".";
199  fileName += extension[snapshotFormat];
200  info.setFile(fileName);
201  }
202 #if QT_VERSION >= 0x040000
203  else if (info.suffix() != extension[snapshotFormat])
204 #else
205  else if (info.extension(false) != extension[snapshotFormat])
206 #endif
207  {
208  // Extension is not appropriate. Propose a modification
209 #if QT_VERSION >= 0x040000
210  QString modifiedName = info.absolutePath() + '/' + info.baseName() + "." + extension[snapshotFormat];
211 #else
212 # if QT_VERSION >= 0x030000
213  QString modifiedName = info.dirPath() + '/' + info.baseName(true) + '.' + extension[snapshotFormat];
214 # else
215  QString modifiedName = info.dirPath() + '/' + info.baseName() + '.' + extension[snapshotFormat];
216 # endif
217 #endif
218  QFileInfo modifInfo(modifiedName);
219  int i=(QMessageBox::warning(widget,"Wrong extension",
220  info.fileName()+" has a wrong extension.\nSave as "+modifInfo.fileName()+" instead ?",
221  QMessageBox::Yes,
222  QMessageBox::No,
223  QMessageBox::Cancel));
224  if (i==QMessageBox::Cancel)
225  return false;
226 
227  if (i==QMessageBox::Yes)
228  {
229  fileName = modifiedName;
230  info.setFile(fileName);
231  }
232  }
233 
234  return true;
235 }
236 
237 #ifndef NO_VECTORIAL_RENDER
238 // static void drawVectorial(void* param)
239 void drawVectorial(void* param)
240 {
241  ( (QGLViewer*) param )->drawVectorial();
242 }
243 
244 #ifndef DOXYGEN
246 {
247 public:
248  static void showProgressDialog(QGLWidget* parent);
249  static void updateProgress(float progress, const QString& stepString);
250  static void hideProgressDialog();
251 
252 private:
253  static QProgressDialog* progressDialog;
254 };
255 
256 QProgressDialog* ProgressDialog::progressDialog = NULL;
257 
258 void ProgressDialog::showProgressDialog(QGLWidget* parent)
259 {
260  progressDialog = new QProgressDialog(parent);
261 #if QT_VERSION >= 0x040000
262  progressDialog->setWindowTitle("Image rendering progress");
263 #else
264  progressDialog->setCaption("Image rendering progress");
265 #endif
266  progressDialog->setMinimumSize(300, 40);
267  progressDialog->setCancelButton(NULL);
268  progressDialog->show();
269 }
270 
271 void ProgressDialog::updateProgress(float progress, const QString& stepString)
272 {
273 #if QT_VERSION >= 0x040000
274  progressDialog->setValue(int(progress*100));
275 #else
276  progressDialog->setProgress(int(progress*100));
277 #endif
278  QString message(stepString);
279  if (message.length() > 33)
280  message = message.left(17) + "..." + message.right(12);
281  progressDialog->setLabelText(message);
282  progressDialog->update();
283  qApp->processEvents();
284 }
285 
286 void ProgressDialog::hideProgressDialog()
287 {
288  progressDialog->close();
289  delete progressDialog;
290  progressDialog = NULL;
291 }
292 
293 #if QT_VERSION >= 0x040000
294 class VRenderInterface: public QDialog, public Ui::VRenderInterface
295 {
296  public: VRenderInterface(QWidget *parent) : QDialog(parent) { setupUi(this); }
297 };
298 #endif
299 
300 #endif
301 
302 // Pops-up a vectorial output option dialog box and save to fileName
303 // Returns -1 in case of Cancel, 0 for success and (todo) error code in case of problem.
304 static int saveVectorialSnapshot(const QString& fileName, QGLWidget* widget, const QString& snapshotFormat)
305 {
306  static VRenderInterface* VRinterface = NULL;
307 
308  if (!VRinterface)
309 #if QT_VERSION >= 0x030000
310  VRinterface = new VRenderInterface(widget);
311 #else
312  VRinterface = new VRenderInterface(widget, "", true); // Make the dialog modal
313 #endif
314 
315 
316  // Configure interface according to selected snapshotFormat
317  if (snapshotFormat == "XFIG")
318  {
319  VRinterface->tightenBBox->setEnabled(false);
320  VRinterface->colorBackground->setEnabled(false);
321  }
322  else
323  {
324  VRinterface->tightenBBox->setEnabled(true);
325  VRinterface->colorBackground->setEnabled(true);
326  }
327 
328  if (VRinterface->exec() == QDialog::Rejected)
329  return -1;
330 
331  vrender::VRenderParams vparams;
332  vparams.setFilename(fileName);
333 
334  if (snapshotFormat == "EPS") vparams.setFormat(vrender::VRenderParams::EPS);
335  if (snapshotFormat == "PS") vparams.setFormat(vrender::VRenderParams::PS);
336  if (snapshotFormat == "XFIG") vparams.setFormat(vrender::VRenderParams::XFIG);
337 
338  vparams.setOption(vrender::VRenderParams::CullHiddenFaces, !(VRinterface->includeHidden->isChecked()));
339  vparams.setOption(vrender::VRenderParams::OptimizeBackFaceCulling, VRinterface->cullBackFaces->isChecked());
340  vparams.setOption(vrender::VRenderParams::RenderBlackAndWhite, VRinterface->blackAndWhite->isChecked());
341  vparams.setOption(vrender::VRenderParams::AddBackground, VRinterface->colorBackground->isChecked());
342  vparams.setOption(vrender::VRenderParams::TightenBoundingBox, VRinterface->tightenBBox->isChecked());
343 
344 #if QT_VERSION >= 0x040000
345  switch (VRinterface->sortMethod->currentIndex())
346 #else
347  switch (VRinterface->sortMethod->currentItem())
348 #endif
349  {
350  case 0: vparams.setSortMethod(vrender::VRenderParams::NoSorting); break;
351  case 1: vparams.setSortMethod(vrender::VRenderParams::BSPSort); break;
352  case 2: vparams.setSortMethod(vrender::VRenderParams::TopologicalSort); break;
353  case 3: vparams.setSortMethod(vrender::VRenderParams::AdvancedTopologicalSort); break;
354  default:
355  qWarning("VRenderInterface::saveVectorialSnapshot: Unknown SortMethod");
356  }
357 
358  vparams.setProgressFunction(&ProgressDialog::updateProgress);
359  ProgressDialog::showProgressDialog(widget);
360  widget->makeCurrent();
361  widget->raise();
362  vrender::VectorialRender(drawVectorial, (void*) widget, vparams);
363  ProgressDialog::hideProgressDialog();
364 #if QT_VERSION < 0x030000
365  widget->setCursor(Qt::arrowCursor);
366 #else
367  widget->setCursor(QCursor(Qt::ArrowCursor));
368 #endif
369 
370  // Should return vparams.error(), but this is currently not set.
371  return 0;
372 }
373 #endif // NO_VECTORIAL_RENDER
374 
375 
376 #if QT_VERSION >= 0x040000
377 class ImageInterface: public QDialog, public Ui::ImageInterface
378 {
379  public: ImageInterface(QWidget *parent) : QDialog(parent) { setupUi(this); }
380 };
381 #endif
382 
383 
384 // Pops-up an image settings dialog box and save to fileName.
385 // Returns false in case of problem.
386 bool QGLViewer::saveImageSnapshot(const QString& fileName)
387 {
388  static ImageInterface* imageInterface = NULL;
389 
390  if (!imageInterface)
391 #if QT_VERSION >= 0x030000
392  imageInterface = new ImageInterface(this);
393 #else
394  imageInterface = new ImageInterface(this, "", true); // Make the dialog modal
395 #endif
396 
397  // 1 means never set : use current window size as default
398  if ((imageInterface->imgWidth->value() == 1) && (imageInterface->imgHeight->value() == 1))
399  {
400  imageInterface->imgWidth->setValue(width());
401  imageInterface->imgHeight->setValue(height());
402  }
403 
404  imageInterface->imgQuality->setValue(snapshotQuality());
405 
406  if (imageInterface->exec() == QDialog::Rejected)
407  return true;
408 
409  // Hide closed dialog
410  qApp->processEvents();
411 
412  setSnapshotQuality(imageInterface->imgQuality->value());
413 
414  QColor previousBGColor = backgroundColor();
415  if (imageInterface->whiteBackground->isChecked())
416  setBackgroundColor(Qt::white);
417 
418  QSize finalSize(imageInterface->imgWidth->value(), imageInterface->imgHeight->value());
419 
420  double oversampling = imageInterface->oversampling->value();
421  QSize subSize(int(this->width()/oversampling), int(this->height()/oversampling));
422 
423  double aspectRatio = width() / static_cast<double>(height());
424  double newAspectRatio = finalSize.width() / static_cast<double>(finalSize.height());
425 
426  double zNear = camera()->zNear();
427  double zFar = camera()->zFar();
428 
429  double xMin, yMin;
430  bool expand = imageInterface->expandFrustum->isChecked();
431  if (camera()->type() == qglviewer::Camera::PERSPECTIVE)
432  if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
433  {
434  yMin = zNear * tan(camera()->fieldOfView() / 2.0);
435  xMin = newAspectRatio * yMin;
436  }
437  else
438  {
439  xMin = zNear * tan(camera()->fieldOfView() / 2.0) * aspectRatio;
440  yMin = xMin / newAspectRatio;
441  }
442  else
443  {
444  camera()->getOrthoWidthHeight(xMin, yMin);
445  if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
446  xMin = newAspectRatio * yMin;
447  else
448  yMin = xMin / newAspectRatio;
449  }
450 
451 #if QT_VERSION >= 0x040000
452  QImage image(finalSize.width(), finalSize.height(), QImage::Format_ARGB32);
453 #else
454  QImage image(finalSize.width(), finalSize.height(), 32);
455 #endif
456 
457  if (image.isNull())
458  {
459  QMessageBox::warning(this, "Image saving error",
460  "Unable to create resulting image",
461  QMessageBox::Ok, QMessageBox::NoButton);
462  return false;
463  }
464 
465  // ProgressDialog disabled since it interfers with the screen grabing mecanism on some platforms. Too bad.
466  // ProgressDialog::showProgressDialog(this);
467 
468  double scaleX = subSize.width() / static_cast<double>(finalSize.width());
469  double scaleY = subSize.height() / static_cast<double>(finalSize.height());
470 
471  double deltaX = 2.0 * xMin * scaleX;
472  double deltaY = 2.0 * yMin * scaleY;
473 
474  int nbX = finalSize.width() / subSize.width();
475  int nbY = finalSize.height() / subSize.height();
476 
477  // Extra subimage on the right/bottom border(s) if needed
478  if (nbX * subSize.width() < finalSize.width())
479  nbX++;
480  if (nbY * subSize.height() < finalSize.height())
481  nbY++;
482 
483  makeCurrent();
484 
485  // tileRegion_ is used by startScreenCoordinatesSystem to appropriately set the local
486  // coordinate system when tiling
487  tileRegion_ = new TileRegion();
488  double tileXMin, tileWidth, tileYMin, tileHeight;
489  if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
490  {
491  double tileTotalWidth = newAspectRatio * height();
492  tileXMin = (width() - tileTotalWidth) / 2.0;
493  tileWidth = tileTotalWidth * scaleX;
494  tileYMin = 0.0;
495  tileHeight = height() * scaleY;
496  tileRegion_->textScale = 1.0 / scaleY;
497  }
498  else
499  {
500  double tileTotalHeight = width() / newAspectRatio;
501  tileYMin = (height() - tileTotalHeight) / 2.0;
502  tileHeight = tileTotalHeight * scaleY;
503  tileXMin = 0.0;
504  tileWidth = width() * scaleX;
505  tileRegion_->textScale = 1.0 / scaleX;
506  }
507 
508  int count=0;
509  for (int i=0; i<nbX; i++)
510  for (int j=0; j<nbY; j++)
511  {
512  preDraw();
513 
514  // Change projection matrix
515  glMatrixMode(GL_PROJECTION);
516  glLoadIdentity();
517  if (camera()->type() == qglviewer::Camera::PERSPECTIVE)
518  glFrustum(-xMin + i*deltaX, -xMin + (i+1)*deltaX, yMin - (j+1)*deltaY, yMin - j*deltaY, zNear, zFar);
519  else
520  glOrtho(-xMin + i*deltaX, -xMin + (i+1)*deltaX, yMin - (j+1)*deltaY, yMin - j*deltaY, zNear, zFar);
521  glMatrixMode(GL_MODELVIEW);
522 
523  tileRegion_->xMin = tileXMin + i * tileWidth;
524  tileRegion_->xMax = tileXMin + (i+1) * tileWidth;
525  tileRegion_->yMin = tileYMin + j * tileHeight;
526  tileRegion_->yMax = tileYMin + (j+1) * tileHeight;
527 
528  draw();
529  postDraw();
530 
531  // ProgressDialog::hideProgressDialog();
532  // qApp->processEvents();
533 
534  QImage snapshot = grabFrameBuffer(true);
535 
536  // ProgressDialog::showProgressDialog(this);
537  // ProgressDialog::updateProgress(count / (float)(nbX*nbY),
538  // "Generating image ["+QString::number(count)+"/"+QString::number(nbX*nbY)+"]");
539  // qApp->processEvents();
540 
541 #if QT_VERSION >= 0x040000
542  QImage subImage = snapshot.scaled(subSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
543 #else
544 # if QT_VERSION >= 0x030000
545  QImage subImage = snapshot.scale(subSize, QImage::ScaleFree);
546 # else
547  QImage subImage = snapshot.smoothScale(subSize.width(), subSize.height());
548 # endif
549 #endif
550 
551  // Copy subImage in image
552  for (int ii=0; ii<subSize.width(); ii++)
553  {
554  int fi = i*subSize.width() + ii;
555  if (fi == image.width())
556  break;
557  for (int jj=0; jj<subSize.height(); jj++)
558  {
559  int fj = j*subSize.height() + jj;
560  if (fj == image.height())
561  break;
562  image.setPixel(fi, fj, subImage.pixel(ii,jj));
563  }
564  }
565  count++;
566  }
567 
568 #if QT_VERSION >= 0x040000
569  bool saveOK = image.save(fileName, snapshotFormat().toLatin1().constData(), snapshotQuality());
570 #else
571  bool saveOK = image.save(fileName, snapshotFormat(), snapshotQuality());
572 #endif
573 
574  // ProgressDialog::hideProgressDialog();
575 
576  // #if QT_VERSION < 0x030000
577  // setCursor(Qt::arrowCursor);
578  // #else
579  // setCursor(QCursor(Qt::ArrowCursor));
580  // #endif
581 
582  delete tileRegion_;
583  tileRegion_ = NULL;
584 
585  if (imageInterface->whiteBackground->isChecked())
586  setBackgroundColor(previousBGColor);
587 
588  return saveOK;
589 }
590 
591 
641 void QGLViewer::saveSnapshot(bool automatic, bool overwrite)
642 {
643  // Ask for file name
644  if (snapshotFileName().isEmpty() || !automatic)
645  {
646  QString fileName;
647 #if QT_VERSION < 0x030000
648  if (openSnapshotFormatDialog())
649  fileName = QFileDialog::getSaveFileName(snapshotFileName(), FDFormatString[snapshotFormat()]+";;All files (*.*)", this, "Save dialog");
650  else
651  return;
652 #else
653  QString selectedFormat = FDFormatString[snapshotFormat()];
654 # if QT_VERSION >= 0x040000
655  fileName = QFileDialog::getSaveFileName(this, "Choose a file name to save under", snapshotFileName(), formats, &selectedFormat,
656  overwrite?QFileDialog::DontConfirmOverwrite:QFlag(0));
657 # else
658  fileName = QFileDialog::getSaveFileName(snapshotFileName(), formats, this,
659  "Save Snapshot dialog", "Choose a file name to save under", &selectedFormat);
660 # endif
661  setSnapshotFormat(Qtformat[selectedFormat]);
662 #endif
663 
664  if (checkFileName(fileName, this, snapshotFormat()))
665  setSnapshotFileName(fileName);
666  else
667  return;
668  }
669 
670  QFileInfo fileInfo(snapshotFileName());
671 
672  if ((automatic) && (snapshotCounter() >= 0))
673  {
674  // In automatic mode, names have a number appended
675  const QString baseName = fileInfo.baseName();
676  QString count;
677  count.sprintf("%.04d", snapshotCounter_++);
678  QString suffix;
679 #if QT_VERSION >= 0x040000
680  suffix = fileInfo.suffix();
681  if (suffix.isEmpty())
682  suffix = extension[snapshotFormat()];
683  fileInfo.setFile(fileInfo.absolutePath()+ '/' + baseName + '-' + count + '.' + suffix);
684 #else
685  suffix = fileInfo.extension();
686  if (suffix.isEmpty())
687  suffix = extension[snapshotFormat()];
688  fileInfo.setFile(fileInfo.dirPath()+ '/' + baseName + '-' + count + '.' + suffix);
689 #endif
690 
691  if (!overwrite)
692  while (fileInfo.exists())
693  {
694  count.sprintf("%.04d", snapshotCounter_++);
695 #if QT_VERSION >= 0x040000
696  fileInfo.setFile(fileInfo.absolutePath() + '/' +baseName + '-' + count + '.' + fileInfo.suffix());
697 #else
698  fileInfo.setFile(fileInfo.dirPath() + '/' + baseName + '-' + count + '.' + fileInfo.extension());
699 #endif
700  }
701  }
702 
703 #if QT_VERSION < 0x040000
704  if ((fileInfo.exists()) && (!overwrite) &&
705  (QMessageBox::warning(this,"Overwrite file ?",
706  "File "+fileInfo.fileName()+" already exists.\nOverwrite ?",
707  QMessageBox::Yes,
708  QMessageBox::Cancel) == QMessageBox::Cancel))
709  return;
710 #endif
711 
712  bool saveOK;
713 #ifndef NO_VECTORIAL_RENDER
714  if ( (snapshotFormat() == "EPS") || (snapshotFormat() == "PS") || (snapshotFormat() == "XFIG") )
715  // Vectorial snapshot. -1 means cancel, 0 is ok, >0 (should be) an error
716  saveOK = (saveVectorialSnapshot(fileInfo.filePath(), this, snapshotFormat()) <= 0);
717  else
718 #endif
719  if (automatic)
720  {
721  QImage snapshot = frameBufferSnapshot();
722 #if QT_VERSION >= 0x040000
723  saveOK = snapshot.save(fileInfo.filePath(), snapshotFormat().toLatin1().constData(), snapshotQuality());
724 #else
725  saveOK = snapshot.save(fileInfo.filePath(), snapshotFormat(), snapshotQuality());
726 #endif
727  }
728  else
729  saveOK = saveImageSnapshot(fileInfo.filePath());
730 
731  if (!saveOK)
732  QMessageBox::warning(this, "Snapshot problem", "Unable to save snapshot in\n"+fileInfo.filePath());
733 }
734 
735 QImage QGLViewer::frameBufferSnapshot()
736 {
737  // Viewer must be on top of other windows.
738  makeCurrent();
739  raise();
740  // Hack: Qt has problems if the frame buffer is grabbed after QFileDialog is displayed.
741  // We grab the frame buffer before, even if it might be not necessary (vectorial rendering).
742  // The problem could not be reproduced on a simple example to submit a Qt bug.
743  // However, only grabs the backgroundImage in the eponym example. May come from the driver.
744  return grabFrameBuffer(true);
745 }
746 
758 void QGLViewer::saveSnapshot(const QString& fileName, bool overwrite)
759 {
760  const QString previousName = snapshotFileName();
761  const int previousCounter = snapshotCounter();
762  setSnapshotFileName(fileName);
763  setSnapshotCounter(-1);
764  saveSnapshot(true, overwrite);
765  setSnapshotFileName(previousName);
766  setSnapshotCounter(previousCounter);
767 }
768 
774 {
775  QClipboard *cb = QApplication::clipboard();
776  cb->setImage(frameBufferSnapshot());
777 }
778 
779 #if QT_VERSION < 0x030000
780 // This code is largely inspired from Qt's method available in version 3
781 // Copyright Trolltech AS
782 QImage QGLViewer::grabFrameBuffer(bool withAlpha)
783 {
784  makeCurrent();
785  QImage res;
786  int w = width();
787  int h = height();
788  if (format().rgba())
789  {
790  res = QImage(w, h, 32);
791  glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, res.bits());
792  if (QImage::systemByteOrder() == QImage::BigEndian)
793  {
794  // OpenGL gives RGBA; Qt wants ARGB
795  uint *p = (uint*)res.bits();
796  uint *end = p + w*h;
797  if (withAlpha && format().alpha())
798  {
799  while (p < end)
800  {
801  uint a = *p << 24;
802  *p = (*p >> 8) | a;
803  p++;
804  }
805  }
806  else
807  {
808  while (p < end)
809  *p++ >>= 8;
810  }
811  }
812  else
813  {
814  // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB
815  res = res.swapRGB();
816  }
817  res.setAlphaBuffer(withAlpha && format().alpha());
818  }
819  else
820  {
821 #if defined (Q_WS_WIN)
822  res = QImage(w, h, 8);
823  glReadPixels(0, 0, w, h, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, res.bits());
824  //int palSize = 0;
825  //const QRgb* pal = QColor::palette(&palSize);
826  //if (pal && palSize)
827  //{
828  // res.setNumColors(palSize);
829  // for (int i = 0; i < palSize; i++)
830  // res.setColor(i, pal[i]);
831  //}
832 #endif
833  }
834 
835  return res.mirror();
836 }
837 
838 #endif // QT_VERSION < 0x030000