FreeCAD/src/Gui/SoFCOffscreenRenderer.cpp

368 lines
14 KiB
C++

/***************************************************************************
* Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <Inventor/fields/SoSFImage.h>
# include <QBuffer>
# include <QDateTime>
# include <QFile>
# include <QImageWriter>
#endif
//gcc
# include <iomanip>
# include <ios>
# include <sstream>
#include <Base/FileInfo.h>
#include <Base/Exception.h>
#include <Base/Console.h>
#include <App/Application.h>
#include "SoFCOffscreenRenderer.h"
#include "BitmapFactory.h"
using namespace Gui;
using namespace std;
void writeJPEGComment(const std::string&, QByteArray&);
// ---------------------------------------------------------------
SoFCOffscreenRenderer* SoFCOffscreenRenderer::inst = 0;
SoFCOffscreenRenderer& SoFCOffscreenRenderer::instance()
{
if (inst==0)
inst = new SoFCOffscreenRenderer(SbViewportRegion());
return *inst;
}
SoFCOffscreenRenderer::SoFCOffscreenRenderer (const SbViewportRegion &viewportregion)
: SoOffscreenRenderer(viewportregion)
{
}
SoFCOffscreenRenderer::SoFCOffscreenRenderer (SoGLRenderAction *action)
: SoOffscreenRenderer(action)
{
}
SoFCOffscreenRenderer::~SoFCOffscreenRenderer()
{
}
void SoFCOffscreenRenderer::writeToImage (QImage& img) const
{
const unsigned char * bytes = getBuffer();
SbVec2s size = getViewportRegion().getViewportSizePixels();
int numcomponents = (int) this->getComponents();
SoSFImage image;
image.setValue(size, numcomponents, bytes, SoSFImage::NO_COPY);
BitmapFactory().convert(image, img);
}
void SoFCOffscreenRenderer::writeToImageFile(const char* filename, const char* comment, const SbMatrix& mat, const QImage& image)
{
Base::FileInfo file(filename);
if (file.hasExtension("JPG") || file.hasExtension("JPEG")) {
// writing comment in case of jpeg (Qt ignores setText() in case of jpeg)
std::string com;
if (strcmp(comment,"")==0)
com = "Screenshot created by FreeCAD";
else if (strcmp(comment,"$MIBA")==0)
com = createMIBA(mat);
else
com = comment;
// write into memory
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "JPG");
writeJPEGComment(com, ba);
QFile file(QString::fromUtf8(filename));
if (file.open(QFile::WriteOnly)) {
file.write(ba);
file.close();
}
else {
std::stringstream str;
str << "Cannot open file '" << filename << "' for writing.";
throw Base::Exception(str.str());
}
}
else {
// check for all QImage formats
bool supported = false;
QByteArray format;
QList<QByteArray> qtformats = QImageWriter::supportedImageFormats();
for (QList<QByteArray>::Iterator it = qtformats.begin(); it != qtformats.end(); ++it) {
if (file.hasExtension((*it).data())) {
format = *it;
supported = true;
break;
}
}
// Supported by Qt
if (supported) {
QImage img = image;
// set keywords for PNG format
if (file.hasExtension("PNG")) {
img.setText(QLatin1String("Title"), QString::fromUtf8(filename));
img.setText(QLatin1String("Author"), QLatin1String("FreeCAD (http://free-cad.sourceforge.net)"));
if (strcmp(comment,"")==0)
img.setText(QLatin1String("Description"), QLatin1String("Screenshot created by FreeCAD"));
else if (strcmp(comment,"$MIBA")==0)
img.setText(QLatin1String("Description"), QLatin1String(createMIBA(mat).c_str()));
else
img.setText(QLatin1String("Description"), QString::fromUtf8(comment));
img.setText(QLatin1String("Creation Time"), QDateTime::currentDateTime().toString());
img.setText(QLatin1String("Software"),
QString::fromUtf8(App::GetApplication().getExecutableName()));
}
QFile f(QString::fromUtf8(filename));
if (f.open(QFile::WriteOnly)) {
if (img.save(&f, format.data())) {
f.close();
}
else {
f.close();
std::stringstream str;
str << "Cannot save image to file '" << filename << "'.";
throw Base::Exception(str.str());
}
}
else {
std::stringstream str;
str << "Cannot open file '" << filename << "' for writing.";
throw Base::Exception(str.str());
}
}
//
// Use internal buffer instead of QImage
//
else if (isWriteSupported(file.extension().c_str())) {
// Any format which is supported by Coin only
if (!writeToFile(filename, file.extension().c_str()))
throw Base::FileException("Error writing image file", filename);
}
else if (file.hasExtension("EPS") || file.hasExtension("PS")) {
// Any format which is supported by Coin only
#ifdef FC_OS_WIN32
FILE* fd = _wfopen(file.toStdWString().c_str(), L"w");
#else
FILE* fd = fopen(filename, "w");
#endif
bool ok = writeToPostScript(fd);
fclose(fd);
if (!ok)
throw Base::FileException("Error writing image file", filename);
}
else if (file.hasExtension("RGB") || file.hasExtension("SGI")) {
// Any format which is supported by Coin only
#ifdef FC_OS_WIN32
FILE* fd = _wfopen(file.toStdWString().c_str(), L"w");
#else
FILE* fd = fopen(filename, "w");
#endif
bool ok = writeToRGB(fd);
fclose(fd);
if (!ok)
throw Base::FileException("Error writing image file", filename);
}
}
}
QStringList SoFCOffscreenRenderer::getWriteImageFiletypeInfo()
{
QStringList formats;
// get all supported formats by Coin3D
int num = getNumWriteFiletypes();
for (int i=0; i < num; i++) {
#if (COIN_MAJOR_VERSION < 2) // Coin3D <= 1.x
SbList<SbName> extlist;
#elif (COIN_MAJOR_VERSION < 3) // Coin3D <= 2.x
# if (COIN_MINOR_VERSION < 3) // Coin3D <= 2.2.x
SbList<SbName> extlist;
# else // Coin3D >= 2.3.x
SbPList extlist;
# endif
#else // Coin3D >= 3.x
SbPList extlist;
#endif
SbString fullname, description;
getWriteFiletypeInfo(i, extlist, fullname, description);
for (int j=0; j < extlist.getLength(); j++) {
QString ext = QLatin1String((const char*) extlist[j]);
if (formats.indexOf(ext.toUpper()) == -1)
formats << ext.toUpper();
}
}
// add now all further QImage formats
QList<QByteArray> qtformats = QImageWriter::supportedImageFormats();
for (QList<QByteArray>::Iterator it = qtformats.begin(); it != qtformats.end(); ++it) {
// not supported? then append
if (!isWriteSupported((*it).data()) && formats.indexOf(QLatin1String(*it)) == -1)
formats << QLatin1String(*it);
}
// now add PostScript and SGI RGB
if (formats.indexOf(QLatin1String("EPS")) == -1)
formats << QLatin1String("EPS");
else if (formats.indexOf(QLatin1String("SGI")) == -1)
formats << QLatin1String("SGI");
formats.sort();
return formats;
}
std::string SoFCOffscreenRenderer::createMIBA(const SbMatrix& mat) const
{
std::stringstream com;
const std::map<std::string, std::string>& cfg = App::Application::Config();
std::map<std::string, std::string>::const_iterator it;
it = cfg.find("BuildVersionMajor");
std::string major = (it != cfg.end() ? it->second : "");
it = cfg.find("BuildVersionMinor");
std::string minor = (it != cfg.end() ? it->second : "");
com << setw(7) << setfill(' ') << fixed;
com << "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n" ;
com << "<MIBA xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://juergen-riegel.net/Miba/Miba2.xsd\" Version=\"2\"> \n" ;
com << " <View>\n";
com << " <Matrix \n";
com << " a11=\"" << mat[0][0] <<"\" a12=\"" << mat[1][0] <<"\" a13=\"" << mat[2][0] <<"\" a14=\"" << mat[3][0] << "\"\n";
com << " a21=\"" << mat[0][1] <<"\" a22=\"" << mat[1][1] <<"\" a23=\"" << mat[2][1] <<"\" a24=\"" << mat[3][1] << "\"\n";
com << " a31=\"" << mat[0][2] <<"\" a32=\"" << mat[1][2] <<"\" a33=\"" << mat[2][2] <<"\" a34=\"" << mat[3][2] << "\"\n";
com << " a41=\"" << mat[0][3] <<"\" a42=\"" << mat[1][3] <<"\" a43=\"" << mat[2][3] <<"\" a44=\"" << mat[3][3] << "\"\n";
com << " />\n" ;
com << " </View>\n" ;
com << " <Source>\n" ;
com << " <Creator>Unknown</Creator>\n" ;
com << " <CreationDate>" << QDateTime::currentDateTime().toString().toAscii().constData() << "</CreationDate>\n" ;
com << " <CreatingSystem>" << App::GetApplication().getExecutableName() << " " << major << "." << minor << "</CreatingSystem>\n" ;
com << " <PartNumber>Unknown</PartNumber>\n";
com << " <Revision>1.0</Revision>\n";
com << " </Source>\n" ;
com << "</MIBA>\n" ;
return com.str();
}
void writeJPEGComment(const std::string& comment, QByteArray& ba)
{
const unsigned char M_SOF0 = 0xc0;
const unsigned char M_SOF1 = 0xc1;
const unsigned char M_SOF2 = 0xc2;
const unsigned char M_SOF3 = 0xc3;
const unsigned char M_SOF5 = 0xc5;
const unsigned char M_SOF6 = 0xc6;
const unsigned char M_SOF7 = 0xc7;
const unsigned char M_SOF9 = 0xc9;
const unsigned char M_SOF10 = 0xcA;
const unsigned char M_SOF11 = 0xcb;
const unsigned char M_SOF13 = 0xcd;
const unsigned char M_SOF14 = 0xce;
const unsigned char M_SOF15 = 0xcf;
const unsigned char M_SOI = 0xd8;
const unsigned char M_EOI = 0xd9;
const unsigned char M_COM = 0xfe;
union Byte {
char c; unsigned char u;
};
if (comment.empty() || ba.length() < 2)
return;
// first marker
Byte a,b;
a.c = ba[0];
b.c = ba[1];
if (a.u == 0xff && b.u == M_SOI) {
int index = 2;
int len = ba.length();
while (index < len) {
// next marker
a.c = ba[index++];
while (a.u != 0xff && index < len) {
a.c = ba[index++];
}
do {
b.c = ba[index++];
} while (b.u == 0xff && index < len);
switch (b.u) {
case M_SOF0:
case M_SOF1:
case M_SOF2:
case M_SOF3:
case M_SOF5:
case M_SOF6:
case M_SOF7:
case M_SOF9:
case M_SOF10:
case M_SOF11:
case M_SOF13:
case M_SOF14:
case M_SOF15:
case M_EOI:
{
Byte a, b;
a.u = 0xff;
b.u = M_COM;
index -= 2; // insert comment before marker
ba.insert(index++, a.c);
ba.insert(index++, b.c);
int val = comment.size() + 2;
ba.insert(index++,(val >> 8) & 0xff);
ba.insert(index++,val & 0xff);
ba.insert(index, comment.c_str());
index = len; // finished
} break;
case M_COM:
default:
{
Byte a, b;
a.c = ba[index++];
b.c = ba[index++];
int off = ((unsigned int)a.u << 8) + (unsigned int)b.u;
index += off;
index -= 2; // next marker
} break;
}
}
}
}