QSGNode *FilteredImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
    QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode*>(oldNode);
    QImage *image = !m_filteredImage.isNull() ? &m_filteredImage : &m_image;

    if(image->isNull())
    {
        delete node;
        return 0;
    }

    if (!node)
    {
        node = new QSGSimpleTextureNode();
    }

    if (m_imageChanged || !node->texture())
    {
        m_imageChanged = false;
        delete node->texture();

        node->setTexture(window()->createTextureFromImage(*image));

        node->setRect(0,0,implicitWidth(),implicitHeight());

        node->markDirty(QSGNode::DirtyMaterial | QSGNode::DirtyGeometry);
    }

    return node;
}
QSGNode* ARCameraQml::updatePaintNode(QSGNode* node, UpdatePaintNodeData* )
{
    using namespace QScrollEngine;
    _context->lock();
    QSGSimpleTextureNode* textureNode = dynamic_cast<QSGSimpleTextureNode*>(node);
    if (_context->openGLContext() != QOpenGLContext::currentContext()) {
        _clearFBOs();
        _context->setOpenGLContext(QOpenGLContext::currentContext());
        _context->setPostEffectUsed(false);
        _context->setEnableClearing(false);
        _surface.setQScrollEngineContext(_context);
        _arSystem.setContext(_context);
        if (textureNode == nullptr)
            textureNode = new QSGSimpleTextureNode();
        _context->unlock();
        _initialize(textureNode);
        _context->lock();
    } else if (textureNode == nullptr) {
        _context->unlock();
        textureNode = new QSGSimpleTextureNode();
        _initialize(textureNode);
        _context->lock();
    }
    GLint defaultFBO = 0;
    _context->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
    AR::FrameProvider* frameProvider = _surface.frameProvider();
    if (frameProvider) {
        AR::IPoint resolution = frameProvider->originalTextureSize();
        if ((resolution != _currentResolution) || (_currentBoundingRect != boundingRect())) {
            _context->unlock();
            _initialize(textureNode);
            _context->lock();
        }
    }
    textureNode->markDirty(QSGNode::DirtyForceUpdate);
    _updateActions();
    if (_scene == nullptr) {
        _context->unlock();
        emit needToScene();
        return textureNode;
    }
    if (_arSystem.reconstructor3D()->isRunning()) {
        _context->unlock();
        _context->glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
        textureNode->setRect(_currentTextureRect);
        qDebug() << "Thread is running.";
        return textureNode;
    }
    _scene->setFrameProvider(nullptr);
    _scene->beginUpdate();
    _context->glEnable(GL_CULL_FACE);
    _context->glFrontFace(GL_CW);
    if (!_surface.isActive()) {
        _surface.scheduleOpenGLContextUpdate();
        QObject* glThreadCallback = (_surface.property("_q_GLThreadCallback")).value<QObject*>();
        if (glThreadCallback) {
            QEvent event(QEvent::User);
            glThreadCallback->event(&event);
        }
        if (!_context->postEffectUsed())
            _FBOs[0]->bind();
        _context->beginPaint();
    } else {
        bool frameAvailable = _surface.isFrameAvailable();
        _surface.provideFrame();
        if (frameProvider) {
            _scene->setFrameProvider(frameProvider);
            frameProvider->setUsedTransform(false);
            if (frameAvailable) {
                QTime timer;
                timer.start();
                _arSystem.tracking(frameProvider, _context, _FBOs[1], _FBOs[2]);
                int time = timer.elapsed();
                _textStatus = QString::number(frameProvider->timeProvideLuminanceFrame()) + "/" + QString::number(time);
                QMatrix4x4 matrix = _arSystem.matrixWorld();
                QQuaternion orientation;
                QOtherMathFunctions::matrixToQuaternion(matrix, orientation);
                QVector3D position(matrix(0, 3), matrix(1, 3), matrix(2, 3));
                _scene->position = position;
                _scene->orientation = orientation;
                emit textStatusChanged();
            }
            frameProvider->setUsedTransform(true);
            if (!_context->postEffectUsed())
                _FBOs[0]->bind();
            _context->beginPaint();
            _context->glDisable(GL_DEPTH_TEST);
            _context->glDepthMask(GL_FALSE);
            _context->glViewport(0, 0, _currentResolution.x, _currentResolution.y);
            frameProvider->bindColorShader(_context);
            _context->glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
            using namespace TMath;
            QMatrix2x2 transform;
            if (_mirrored) {
                QMatrix2x2 frameTransform = frameProvider->matrixTexture();
                transform(0, 0) = frameTransform(0, 0);
                transform(0, 1) = - frameTransform(0, 1);
                transform(1, 0) = - frameTransform(1, 0);
                transform(1, 1) = frameTransform(1, 1);
            } else {
                transform = frameProvider->matrixTexture();
            }
            _arSystem.drawResultOnGL(_context, transform);
        } else {
            if (!_context->postEffectUsed())
                _FBOs[0]->bind();
            _context->beginPaint();
        }
    }
    _context->unlock();
    _scene->endUpdate();
    State currentState = State(_arSystem.state());
    if (currentState != _currentState) {
        _currentState = currentState;
        emit arCameraStateChanged();
    }
    if ((currentState == TrackingNow) || (currentState == Reconstruction3D))
        _context->drawScenes();
    _context->endPaint(_FBOs[0]->handle());
    _context->glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
    textureNode->setRect(_currentTextureRect);
    return textureNode;
}