TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(1, m_decodeRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);
    SkData* data = m_generator->refEncodedData();
    EXPECT_EQ(nullptr, data);

    // LocalFrame can now be decoded completely.
    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();
    // addNewData is calling m_generator->setData with allDataReceived == false, which means that
    // refEncodedData should return null.
    data = m_generator->refEncodedData();
    EXPECT_EQ(nullptr, data);
    OwnPtr<WebThread> thread = adoptPtr(Platform::current()->createThread("DecodeThread"));
    thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&decodeThreadMain, AllowCrossThreadAccess(m_generator.get()))));
    thread.clear();
    EXPECT_EQ(2, m_decodeRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);

    // Decoder created again.
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(3, m_decodeRequestCount);

    addNewData(true);
    data = m_generator->refEncodedData();
    ASSERT_TRUE(data);
    // To prevent data writting, SkData::unique() should be false.
    ASSERT_TRUE(!data->unique());

    // Thread will also ref and unref the data.
    thread = adoptPtr(Platform::current()->createThread("RefEncodedDataThread"));
    thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&decodeThreadWithRefEncodedMain, AllowCrossThreadAccess(m_generator.get()))));
    thread.clear();
    EXPECT_EQ(4, m_decodeRequestCount);

    data->unref();
    // m_generator is holding the only reference to SkData now.
    ASSERT_TRUE(data->unique());

    data = m_generator->refEncodedData();
    ASSERT_TRUE(data && !data->unique());

    // Delete generator, and SkData should have the only reference.
    m_generator = nullptr;
    ASSERT_TRUE(data->unique());
    data->unref();
}
TEST_F(ImageFrameGeneratorTest, incompleteDecode)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(1, m_decodeRequestCount);

    addNewData();
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(2, m_decodeRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);
}
Пример #3
0
void Magick::Options::fontFamily(const std::string &family_)
{
  if (family_.length() == 0)
    {
      _drawInfo->family=(char *) RelinquishMagickMemory(_drawInfo->font);
      (void) RemoveImageOption(imageInfo(),"family");
    }
  else
    {
      Magick::CloneString(&_drawInfo->family,family_);
      (void) SetImageOption(imageInfo(),"family",family_.c_str());
    }
}
Пример #4
0
status_t
LoadImageDebugInfoJob::Do()
{
    // get an image info for the image
    AutoLocker<Team> locker(fImage->GetTeam());
    ImageInfo imageInfo(fImage->Info());
    locker.Unlock();

    // create the debug info
    ImageDebugInfo* debugInfo;
    status_t error = fImage->GetTeam()->DebugInfo()->LoadImageDebugInfo(
                         imageInfo, fImage->ImageFile(), debugInfo);

    // set the result
    locker.Lock();
    if (error == B_OK) {
        error = fImage->SetImageDebugInfo(debugInfo, IMAGE_DEBUG_INFO_LOADED);
        debugInfo->ReleaseReference();
    } else {
        fImage->SetImageDebugInfo(NULL, IMAGE_DEBUG_INFO_UNAVAILABLE);
        delete debugInfo;
    }

    return error;
}
Пример #5
0
void Scene::contextMenuEvent(QGraphicsSceneContextMenuEvent * event) {
  event->accept();
  QMenu* menu = new QMenu();
  QAction *ImageInfoAction = menu->addAction(QIcon(":/info.svg"),
                             tr("Image info"));
  connect(ImageInfoAction, SIGNAL(triggered()), this, SLOT(imageInfo()));
  menu->addSeparator();

  QAction* rotateCWAction = menu->addAction(QIcon(":/rotateCW.svg"),
                            tr("Rotate CW"));
  connect(rotateCWAction, SIGNAL(triggered()), this, SLOT(rotateCW()));

  QAction* rotateCCWAction = menu->addAction(QIcon(":/rotateCCW.svg"),
                             tr("Rotate CCW"));
  connect(rotateCCWAction, SIGNAL(triggered()), this, SLOT(rotateCCW()));

  QAction* rotateHalfAction = menu->addAction(QIcon(":/rotateCCW.svg"),
                              tr("Rotate 180°"));
  connect(rotateHalfAction, SIGNAL(triggered()), this, SLOT(rotateHalf()));
  QAction* detectOrientationAction = menu->addAction(QIcon(":/orientation.svg"),
                                                     tr("Detect orientation"));
  detectOrientationAction->setToolTip(
    tr("Page orientation detection (four 90 degree angles)"));
  connect(detectOrientationAction, SIGNAL(triggered()), this,
          SLOT(detectOrientation()));
  menu->exec(event->screenPos());
  menu->deleteLater();
}
Пример #6
0
void Magick::Options::setOption(const char *name,const Color &value_)
{
  std::string
    option;

  option=value_;
  (void) SetImageOption(imageInfo(),name,option.c_str());
}
static void decodeThreadWithRefEncodedMain(ImageFrameGenerator* generator)
{
    // Image must be complete - refEncodedData otherwise returns null.
    char buffer[100 * 100 * 4];
    SkData* data = generator->refEncodedData();
    generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    data->unref();
}
TEST_F(ImageFrameGeneratorTest, frameHasAlpha)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(imageInfo(), 1, buffer, 100 * 4);
    EXPECT_TRUE(m_generator->hasAlpha(1));
    EXPECT_EQ(1, m_frameBufferRequestCount);

    ImageDecoder* tempDecoder = 0;
    EXPECT_TRUE(ImageDecodingStore::instance()->lockDecoder(m_generator.get(), fullSize(), &tempDecoder));
    ASSERT_TRUE(tempDecoder);
    static_cast<MockImageDecoder*>(tempDecoder)->setFrameHasAlpha(false);
    ImageDecodingStore::instance()->unlockDecoder(m_generator.get(), tempDecoder);

    setFrameStatus(ImageFrame::FrameComplete);
    m_generator->decodeAndScale(imageInfo(), 1, buffer, 100 * 4);
    EXPECT_EQ(2, m_frameBufferRequestCount);
    EXPECT_FALSE(m_generator->hasAlpha(1));
}
TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesComplete)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(1, m_frameBufferRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);

    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();

    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(2, m_frameBufferRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);

    // Decoder created again.
    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(3, m_frameBufferRequestCount);
}
TEST_F(ImageFrameGeneratorTest, frameHasAlpha)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_TRUE(m_generator->hasAlpha(0));
    EXPECT_EQ(1, m_decodeRequestCount);

    ImageDecoder* tempDecoder = 0;
    EXPECT_TRUE(ImageDecodingStore::instance().lockDecoder(m_generator.get(), fullSize(), &tempDecoder));
    ASSERT_TRUE(tempDecoder);
    tempDecoder->frameBufferAtIndex(0)->setHasAlpha(false);
    ImageDecodingStore::instance().unlockDecoder(m_generator.get(), tempDecoder);
    EXPECT_EQ(2, m_decodeRequestCount);

    setFrameStatus(ImageFrame::FrameComplete);
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(3, m_decodeRequestCount);
    EXPECT_FALSE(m_generator->hasAlpha(0));
}
TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(1, m_decodeRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);

    // LocalFrame can now be decoded completely.
    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();
    OwnPtr<WebThread> thread = adoptPtr(Platform::current()->createThread("DecodeThread"));
    thread->postTask(FROM_HERE, new Task(threadSafeBind(&decodeThreadMain, AllowCrossThreadAccess(m_generator.get()))));
    thread.clear();
    EXPECT_EQ(2, m_decodeRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);

    // Decoder created again.
    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(3, m_decodeRequestCount);
}
TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded)
{
    setFrameStatus(ImageFrame::FramePartial);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(1, m_frameBufferRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);

    // LocalFrame can now be decoded completely.
    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();
    OwnPtr<base::Thread> thread = adoptPtr(new base::Thread("DecodeThread"));
    thread->Start();
    thread->message_loop()->PostTask(FROM_HERE, base::Bind(&decodeThreadMain, m_generator.get()));
    thread.clear();
    EXPECT_EQ(2, m_frameBufferRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);

    // Decoder created again.
    m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4);
    EXPECT_EQ(3, m_frameBufferRequestCount);
}
TEST_F(ImageFrameGeneratorTest, removeMultiFrameDecoder)
{
    setFrameCount(3);
    setFrameStatus(ImageFrame::FrameComplete);

    char buffer[100 * 100 * 4];
    m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(1, m_decodeRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);

    setFrameStatus(ImageFrame::FrameComplete);

    m_generator->decodeAndScale(1, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(2, m_decodeRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);

    setFrameStatus(ImageFrame::FrameComplete);

    // Multi frame decoder should be removed.
    m_generator->decodeAndScale(2, imageInfo(), buffer, 100 * 4);
    EXPECT_EQ(3, m_decodeRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);
}
void BrowserUtils::generateIconFromFile(const QString inFile, const QString outFile, const QSize imageSize)
{
	QImage inImage(inFile);
	if (inImage.isNull()) {
		qWarning() << "generateIconFromFile - failed to open source file";
		Q_EMIT iconGenerated(false, outFile);
		return;
	}
	const int nMargin = 4;// Must agree with pixel data in image files
	const int nIconSize = 64;// Width & height of output image
	const int nIconWidth = nIconSize-2*nMargin;// Width of icon image within file
	const int nIconHeight = nIconSize-2*nMargin;
	QImage outImage(nIconSize, nIconSize, QImage::Format_ARGB32_Premultiplied);
	outImage.fill(0);
	QPainter painter(&outImage);
	painter.setRenderHint(QPainter::SmoothPixmapTransform);
	QRectF source(0.0, 0.0, imageSize.width(), imageSize.height());
	QRectF target(nMargin, nMargin, nIconWidth, nIconHeight);
	QRectF size(0.0, 0.0, nIconSize, nIconSize);
	painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
	painter.drawImage(target, inImage, source);
	painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
	QImage maskImage(kIconMaskFile);
	painter.drawImage(target, maskImage, target);
	painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
	QImage overlayImage(kIconOverlayFile);
	painter.drawImage(size, overlayImage, size);

	QFileInfo imageInfo(outFile);
	QDir imageDir(imageInfo.path());
	if (!imageDir.exists()) {
		imageDir.mkpath(".");
	}

	bool saved = outImage.save(outFile);
	Q_EMIT iconGenerated(saved, outFile);
}
void ScanDarkSourceCommand::do_processing()
{
    emit started();

    if ( ! _sources.isEmpty() ) {

        QList<QString>      imagePaths;
        QList<ImageInfo>    imageInfos;

        _progressMessage = tr("Looking for RAW files...");
        emit statusChanged(this);

        int found=0;

        foreach (QString path, _sources) {

            /*
             * If current dark source folder is missing,
             * we skip it and store its path for error display
             */
            if ( ! QDir(path).exists() ) {

                _missingDirsPaths << path;
                continue;
            }

            /*
             * retrieve paths of all RAW files located in current dark source folder,
             * including subdirectories
             */
            QDirIterator it(path,
                            QStringList() << "*.CR2" << "*.CRW",
                            QDir::NoDotAndDotDot | QDir::Files,
                            QDirIterator::Subdirectories);

            while (it.hasNext()) {

                imagePaths << it.next();
                _progressMessage = tr("%1 RAW file(s) found").arg(++found);
                emit statusChanged(this);

            }
        }

        _progressMessage = tr("%1 RAW file(s) found").arg(found);
        emit statusChanged(this);

        int scanned = 0;

        /*
         * Retrieve EXIF metadata for each RAW file
         */
        foreach (QString filePath, imagePaths) {

            ImageInfo imageInfo(filePath);

            if ( ! ExifReader::retrieveExifMetadata(imageInfo) ) {

                _badFilesPaths << imageInfo.getPath();

            }
            else if ( imageInfo.getExposure() < 1 ) {

                _shortFilesPaths << imageInfo.getPath();

            } else {

                imageInfos << imageInfo;
            }

            _progressMessage = tr("Scanned file %1 / %2").arg(++scanned).arg(imagePaths.count());
            emit statusChanged(this);
        }
Пример #16
0
void Magick::Options::textEncoding(const std::string &encoding_)
{
  CloneString(&_drawInfo->encoding,encoding_.c_str());
  (void) SetImageOption(imageInfo(),"encoding",encoding_.c_str());
}
static void decodeThreadMain(ImageFrameGenerator* generator)
{
    char buffer[100 * 100 * 4];
    generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4);
}
Пример #18
0
bool UserScript::toolOperations()
{
    QString script = settings()[QLatin1String("Script")].toString();

    if (script.isEmpty())
    {
        setErrorDescription(i18n("User Script: No script."));
        return false;
    }

    // Replace all occurences of $INPUT and $OUTPUT in script to file names. Case sensitive.
    script.replace(QLatin1String("$INPUT"),  QLatin1Char('"') + inputUrl().toLocalFile()  + QLatin1Char('"'));
    script.replace(QLatin1String("$OUTPUT"), QLatin1Char('"') + outputUrl().toLocalFile() + QLatin1Char('"'));

    QString shellScript;

#ifndef WIN32
    QString envCmd  = QLatin1String("export ");
#else
    QString envCmd  = QLatin1String("set ");
#endif // WIN32

    QString tagPath = TagsCache::instance()->tagPaths(imageInfo().tagIds(), TagsCache::NoLeadingSlash,
                                                      TagsCache::NoHiddenTags).join(QLatin1Char(';'));

    // Populate env variables from metadata
    shellScript.append(envCmd + QString::fromUtf8("COLORLABEL=\"%1\"\n").arg(imageInfo().colorLabel()));
    shellScript.append(envCmd + QString::fromUtf8("PICKLABEL=\"%1\"\n") .arg(imageInfo().pickLabel()));
    shellScript.append(envCmd + QString::fromUtf8("COMMENTS=\"%1\"\n")  .arg(imageInfo().comment()));
    shellScript.append(envCmd + QString::fromUtf8("RATING=\"%1\"\n")    .arg(imageInfo().rating()));
    shellScript.append(envCmd + QString::fromUtf8("TITLE=\"%1\"\n")     .arg(imageInfo().title()));
    shellScript.append(envCmd + QString::fromUtf8("TAGSPATH=\"%1\"\n")  .arg(tagPath));
    shellScript.append(script);

    // Empties d->image, not to pass it to the next tool in chain
    setImageData(DImg());

    QProcess process(this);

    // call the shell script
#ifndef WIN32
    int returncode = process.execute(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << shellScript);
#else
    int returncode = process.execute(QLatin1String("cmd.exe"), QStringList() << QLatin1String("/c") << shellScript);
#endif // WIN32

    if (returncode == -2)
    {
        setErrorDescription(i18n("User Script: Failed to start script."));
        return false;
    }

    if (returncode == -1)
    {
        setErrorDescription(i18n("User Script: Script process crashed."));
        return false;
    }

    if (returncode == 127)
    {
        setErrorDescription(i18n("User Script: Command not found."));
        return false;
    }

    return true;
}
Пример #19
0
MainWindow::MainWindow(QWidget *parent, const QString &fileName)
  : QMainWindow(parent) {
  setupUi(this);

  _zoom = new QLabel();
  _zoom->setToolTip(QString("Zoom factor"));
  _zoom->setFrameStyle(QFrame::Sunken);
  _zoom->setAlignment(Qt::AlignHCenter);
  _zoom->setMaximumWidth(50);
  this->statusBar()->addPermanentWidget(_zoom, 1);

  setAcceptDrops(true);

  fileWatcher = 0;
  imageItem = 0;

  gViewResult->viewport()->setGeometry(QRect(0,0,0,0));
  imageScene = new Scene();
  connect(imageScene, SIGNAL(dropedFilename(QString)),
          this, SLOT(openImage(QString)));
  connect(imageScene, SIGNAL(sceneScaleChanged(qreal)),
          this, SLOT(changeSceneScale(qreal)));
  connect(imageScene, SIGNAL(rotateImage(int)),
          this, SLOT(rotate(int)));
  connect(imageScene, SIGNAL(imageInfoTriggered()), this, SLOT(imageInfo()));
  connect(imageScene, SIGNAL(detectOrientationSignal()), this,
          SLOT(on_actionDetectOrientation_triggered()));

  gViewResult->setScene(imageScene);
  gViewResult->setRenderHint(QPainter::Antialiasing);
  gViewResult->setCacheMode(QGraphicsView::CacheBackground);
  gViewResult->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
  gViewResult->setOptimizationFlags(QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing);
  gViewResult->viewport()->setSizeIncrement(gViewResult->sceneRect().width(),gViewResult->sceneRect().height());
  gViewResult->viewport()->setSizeIncrement(gViewResult->sceneRect().width(),gViewResult->sceneRect().height());

  connect(actionAbout, SIGNAL(triggered()), this, SLOT(about()));
  connect(actionAbout_Qt, SIGNAL(triggered()), this, SLOT(aboutQt()));

  for (int i = 0; i < MaxRecentFiles; ++i) {
    recentFileActs[i] = new QAction(this);
    recentFileActs[i]->setVisible(false);
    connect(recentFileActs[i], SIGNAL(triggered()),
            this, SLOT(openRecentFile()));
  }

  fSeparatorAct = menuFile->addSeparator();
  for (int i = 0; i < MaxRecentFiles; ++i)
    menuFile->addAction(recentFileActs[i]);
  updateRecentFileActions();

  readSettings(true);
  if (fileName.isEmpty()) {
    // Open last file on init if there was no argument
    recentFile = recentFileActs[0]->data().toString();
  } else {
    recentFile = fileName;
    setZoom(1.0);
  }
  if (!recentFile.isEmpty())
    openImage(recentFile);

  // default values for Clean Dark Background
  blackval = 70;
  whiteval = 180;
  thresh = 60;
}
Пример #20
0
	frameZeroSize(0),            // Size of frame 0
	frameZeroOffset(0),          // Frame 0 offset
	headerSize(0),               // size of header in bytes
	metaDataSize(0),             // size of meta data block (images)
	imageInfoSize(0),            // size of image info structure
	imageSeqSize(0),             // Size of image sequence
	offsetBytes(0),              //  number of bytes offset for the file
	setupDataSize(0),            // Size of xml setup block
	versionNum(0),               // Version number
	cropRowLow(0),               // The crop row value on the bottom
	cropRowHigh(0),              // The crop row value in the top
	cropColHigh(0),              // The crop value on the far right
	cropColLow(0),               // The crop value on the far left
	metaData(0),                 // Offset of meta data block
	setupData(0),                // Offset of xml setup block	
	imageInfo(0),                // Offset of image info structure
	imageSeq(0),                 // Offset for image sequence
	width(0),                    // The width of the frame in pixels
	height(0),                   // The height of the frame in pixels
	frameNum(0),                 // The Number of frames.
	frameSize(0),                // Size of an image in...
	frameSizeArray(),            // Size of all 180 frames
	frameOffset()                // Offset of all 180 frames
{

	FILE* fp;
	size_t fb;

	fileName = fileNameParam;

	/*
QSGNode* BrowserUtils::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData)
{
	Q_UNUSED(updatePaintNodeData);

	if (!(m_webview && (flags() & QQuickItem::ItemHasContents))) {
		return oldNode;
	}
	setFlag(QQuickItem::ItemHasContents, false);
#if 0
	QQuickWebPage* page = m_webview->page();
	qreal xmin = qMin(page->width(), m_webview->width());
	qreal ymin = qMin(m_webview->height(), page->height());
#else
	// Here the screenshot of the page might be too large if the page is tiny
	qreal xmin = m_webview->width();
	qreal ymin = m_webview->height();
#endif
	ymin = qMin(static_cast<int>(ymin), m_imageSize.height());
	xmin = qMin(static_cast<int>(xmin), m_imageSize.width());

	QSize size(xmin, ymin);

	QSGNode* node = QQuickItemPrivate::get(m_webview)->itemNode();
	QSGNode* parent = node->QSGNode::parent();
	QSGNode* previousSibling = node->previousSibling();
	if (parent) {
		parent->removeChildNode(node);
	}
	QSGRootNode root;
	root.appendChildNode(node);

	QSGRenderer* renderer;
#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
	renderer = QQuickItemPrivate::get(this)->sceneGraphContext()->createRenderer();
#else
	renderer = QQuickItemPrivate::get(this)->sceneGraphRenderContext()->createRenderer();
#endif
	renderer->setRootNode(static_cast<QSGRootNode*>(&root));

	QOpenGLFramebufferObject fbo(size);

	renderer->setDeviceRect(size);
	renderer->setViewportRect(size);
	renderer->setProjectionMatrixToRect(QRectF(QPointF(), size));
	renderer->setClearColor(Qt::transparent);

	renderer->renderScene(BindableFbo(&fbo));

	fbo.release();

	QImage image = fbo.toImage().scaled(m_imageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
	QFileInfo imageInfo(m_outFile);
	QDir imageDir(imageInfo.path());
	if (!imageDir.exists()) {
		imageDir.mkpath(".");
	}

	bool saved = image.save(m_outFile);

	root.removeChildNode(node);
	renderer->setRootNode(0);
	delete renderer;

	if (parent) {
		if (previousSibling) {
			parent->insertChildNodeAfter(node, previousSibling);
		} else {
			parent->prependChildNode(node);
		}
	}

	Q_EMIT webViewSaved(saved, m_outFile);

	return oldNode;
}