Merge remote-tracking branch 'origin/master' into material-theme
This commit is contained in:
commit
c4dc6e8c2f
|
|
@ -26,6 +26,5 @@ SUBDIRS += \
|
|||
boxes \
|
||||
buttons \
|
||||
frames \
|
||||
gbenchmark \
|
||||
glabels \
|
||||
messageboxQml
|
||||
|
|
|
|||
|
|
@ -1,178 +0,0 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Benchmark.h"
|
||||
|
||||
#include <QskGraphic.h>
|
||||
#include <QskGraphicIO.h>
|
||||
#include <QskColorFilter.h>
|
||||
#include <QskTextureRenderer.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#include <QPainter>
|
||||
#include <QQuickWindow>
|
||||
#include <QStringList>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
bool Benchmark::run( const QString& dirName )
|
||||
{
|
||||
QDir svgDir( dirName );
|
||||
|
||||
QStringList svgFiles = svgDir.entryList( QStringList() << "*.svg", QDir::Files );
|
||||
if ( svgFiles.isEmpty() )
|
||||
return false;
|
||||
|
||||
const char qvgPath[] = "/tmp/benchmark/qvg";
|
||||
if ( !svgDir.mkpath( qvgPath ) )
|
||||
return false;
|
||||
|
||||
QStringList qvgFiles = svgFiles;
|
||||
for ( int i = 0; i < qvgFiles.size(); i++ )
|
||||
{
|
||||
svgFiles[ i ].prepend( "/" );
|
||||
svgFiles[ i ].prepend( dirName );
|
||||
|
||||
qvgFiles[ i ].replace( ".svg", ".qvg" );
|
||||
qvgFiles[ i ].prepend( "/" );
|
||||
qvgFiles[ i ].prepend( qvgPath );
|
||||
}
|
||||
|
||||
QVector< QskGraphic > graphics( qvgFiles.size() );
|
||||
QVector< QSvgRenderer* > renderers( svgFiles.size() );
|
||||
|
||||
qint64 msElapsed[ 6 ];
|
||||
|
||||
QElapsedTimer timer;
|
||||
|
||||
{
|
||||
// compile step
|
||||
|
||||
timer.start();
|
||||
|
||||
for ( int i = 0; i < svgFiles.size(); i++ )
|
||||
{
|
||||
renderers[ i ] = new QSvgRenderer();
|
||||
if ( !renderers[ i ]->load( svgFiles[ i ] ) )
|
||||
{
|
||||
qCritical() << "Can't load" << svgFiles[ i ];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
msElapsed[ 0 ] = timer.elapsed();
|
||||
}
|
||||
|
||||
{
|
||||
// converting into graphics and storing to disk
|
||||
|
||||
timer.start();
|
||||
|
||||
for ( int i = 0; i < renderers.size(); i++ )
|
||||
{
|
||||
QPainter painter( &graphics[ i ] );
|
||||
renderers[ i ]->render( &painter );
|
||||
painter.end();
|
||||
}
|
||||
|
||||
msElapsed[ 1 ] = timer.elapsed();
|
||||
}
|
||||
|
||||
{
|
||||
// writing them to disk
|
||||
|
||||
timer.start();
|
||||
|
||||
for ( int i = 0; i < graphics.size(); i++ )
|
||||
{
|
||||
QskGraphicIO::write( graphics[ i ], qvgFiles[ i ] );
|
||||
}
|
||||
|
||||
msElapsed[ 2 ] = timer.elapsed();
|
||||
}
|
||||
|
||||
{
|
||||
// loading qvg files to memory
|
||||
|
||||
timer.start();
|
||||
|
||||
for ( int i = 0; i < qvgFiles.size(); i++ )
|
||||
{
|
||||
graphics[ i ] = QskGraphicIO::read( qvgFiles[ i ] );
|
||||
if ( graphics[ i ].isNull() )
|
||||
{
|
||||
qCritical() << "Can't load" << qvgFiles[ i ];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
msElapsed[ 3 ] = timer.elapsed();
|
||||
}
|
||||
|
||||
{
|
||||
// creating textures using OpenGL
|
||||
|
||||
timer.start();
|
||||
|
||||
const QSize targetSize( 200, 200 );
|
||||
const QskColorFilter colorFilter;
|
||||
|
||||
for ( int i = 0; i < qvgFiles.size(); i++ )
|
||||
{
|
||||
using namespace QskTextureRenderer;
|
||||
|
||||
const auto textureId = createTextureFromGraphic(
|
||||
nullptr, OpenGL, targetSize, graphics[ i ], colorFilter,
|
||||
Qt::IgnoreAspectRatio );
|
||||
|
||||
if ( textureId == 0 )
|
||||
{
|
||||
qCritical() << "Can't render texture for" << qvgFiles[ i ];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
msElapsed[ 4 ] = timer.elapsed();
|
||||
}
|
||||
|
||||
{
|
||||
// creating textures using Raster
|
||||
|
||||
timer.start();
|
||||
|
||||
const QSize targetSize( 200, 200 );
|
||||
const QskColorFilter colorFilter;
|
||||
|
||||
for ( int i = 0; i < qvgFiles.size(); i++ )
|
||||
{
|
||||
using namespace QskTextureRenderer;
|
||||
|
||||
const auto textureId = createTextureFromGraphic(
|
||||
nullptr, Raster, targetSize, graphics[ i ], colorFilter,
|
||||
Qt::IgnoreAspectRatio );
|
||||
|
||||
if ( textureId == 0 )
|
||||
{
|
||||
qCritical() << "Can't render texture for" << qvgFiles[ i ];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
msElapsed[ 5 ] = timer.elapsed();
|
||||
}
|
||||
|
||||
qDebug() << "#Icons:" << svgFiles.count() <<
|
||||
"Compiled:" << msElapsed[ 0 ] <<
|
||||
"Converted:" << msElapsed[ 1 ] <<
|
||||
"Stored:" << msElapsed[ 2 ] <<
|
||||
"Loaded:" << msElapsed[ 3 ] <<
|
||||
"Rendered OpenGL:" << msElapsed[ 4 ] <<
|
||||
"Rendered Raster:" << msElapsed[ 5 ];
|
||||
|
||||
svgDir.rmdir( qvgPath );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class QString;
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
bool run( const QString& svgDir );
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
CONFIG += qskexample
|
||||
|
||||
QT += svg
|
||||
|
||||
HEADERS += \
|
||||
Benchmark.h
|
||||
|
||||
SOURCES += \
|
||||
Benchmark.cpp \
|
||||
main.cpp
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Benchmark.h"
|
||||
|
||||
#include <QskLinearBox.h>
|
||||
#include <QskPushButton.h>
|
||||
#include <QskWindow.h>
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QGuiApplication>
|
||||
|
||||
class Button : public QskPushButton
|
||||
{
|
||||
public:
|
||||
Button( const QString& testDir )
|
||||
: m_testDir( testDir )
|
||||
{
|
||||
setText( QString( "Run: " ) + testDir );
|
||||
setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
Benchmark::run( m_testDir );
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_testDir;
|
||||
};
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription( "Benchmark for creating textures from SVGs" );
|
||||
parser.addHelpOption();
|
||||
parser.addPositionalArgument( "svgdir", "Directory with SVG files.", "[pathname]" );
|
||||
|
||||
parser.process( app );
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
||||
if ( args.count() != 1 )
|
||||
parser.showHelp( 1 );
|
||||
|
||||
auto button = new Button( args[ 0 ] );
|
||||
button->setLayoutAlignmentHint( Qt::AlignCenter );
|
||||
QObject::connect( button, &Button::clicked, button, &Button::run );
|
||||
|
||||
auto box = new QskLinearBox();
|
||||
box->addItem( button );
|
||||
|
||||
QskWindow window;
|
||||
window.setColor( Qt::white );
|
||||
window.resize( 400, 400 );
|
||||
window.addItem( box );
|
||||
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
#include "QskColorFilter.h"
|
||||
#include "QskFunctions.h"
|
||||
#include "QskGraphic.h"
|
||||
#include "QskTextureNode.h"
|
||||
|
||||
QskGraphicLabelSkinlet::QskGraphicLabelSkinlet( QskSkin* skin )
|
||||
: Inherited( skin )
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "QskArcNode.h"
|
||||
#include "QskAspect.h"
|
||||
#include "QskArcMetrics.h"
|
||||
#include "QskBoxBorderColors.h"
|
||||
#include "QskBoxBorderMetrics.h"
|
||||
#include "QskBoxClipNode.h"
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
#include "QskTextNode.h"
|
||||
#include "QskTextOptions.h"
|
||||
#include "QskSkinStateChanger.h"
|
||||
#include "QskTextureRenderer.h"
|
||||
|
||||
#include <qquickwindow.h>
|
||||
#include <qsgsimplerectnode.h>
|
||||
|
|
@ -111,18 +113,17 @@ static inline QSGNode* qskUpdateGraphicNode(
|
|||
if ( control == nullptr )
|
||||
return nullptr;
|
||||
|
||||
auto mode = QskTextureRenderer::OpenGL;
|
||||
|
||||
auto graphicNode = static_cast< QskGraphicNode* >( node );
|
||||
if ( graphicNode == nullptr )
|
||||
graphicNode = new QskGraphicNode();
|
||||
|
||||
if ( control->testUpdateFlag( QskControl::PreferRasterForTextures ) )
|
||||
mode = QskTextureRenderer::Raster;
|
||||
const bool useRaster = control->testUpdateFlag( QskControl::PreferRasterForTextures );
|
||||
graphicNode->setRenderHint( useRaster ? QskPaintedNode::Raster : QskPaintedNode::OpenGL );
|
||||
|
||||
graphicNode->setMirrored( mirrored );
|
||||
|
||||
const auto r = qskSceneAlignedRect( control, rect );
|
||||
graphicNode->setGraphic( control->window(), graphic,
|
||||
colorFilter, mode, r, mirrored );
|
||||
graphicNode->setGraphic( control->window(), graphic, colorFilter, r );
|
||||
|
||||
return graphicNode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,16 +54,30 @@ QSize QskGraphicTextureFactory::size() const
|
|||
return m_size;
|
||||
}
|
||||
|
||||
|
||||
QSGTexture* QskGraphicTextureFactory::createTexture( QQuickWindow* window ) const
|
||||
{
|
||||
using namespace QskTextureRenderer;
|
||||
class PaintHelper : public QskTextureRenderer::PaintHelper
|
||||
{
|
||||
public:
|
||||
PaintHelper( const QskGraphic& graphic, const QskColorFilter& filter )
|
||||
: m_graphic( graphic )
|
||||
, m_filter( filter )
|
||||
{
|
||||
}
|
||||
|
||||
const uint textureId = createTextureFromGraphic(
|
||||
window, QskTextureRenderer::OpenGL, m_size, m_graphic, m_colorFilter,
|
||||
Qt::IgnoreAspectRatio );
|
||||
void paint( QPainter* painter, const QSize& size ) override
|
||||
{
|
||||
const QRect rect( 0, 0, size.width(), size.height() );
|
||||
m_graphic.render( painter, rect, m_filter );
|
||||
}
|
||||
|
||||
return textureFromId( window, textureId, m_size );
|
||||
private:
|
||||
const QskGraphic& m_graphic;
|
||||
const QskColorFilter& m_filter;
|
||||
};
|
||||
|
||||
PaintHelper helper( m_graphic, m_colorFilter );
|
||||
return QskTextureRenderer::createPaintedTexture( window, m_size, &helper );
|
||||
}
|
||||
|
||||
QSize QskGraphicTextureFactory::textureSize() const
|
||||
|
|
|
|||
|
|
@ -4,7 +4,19 @@
|
|||
*****************************************************************************/
|
||||
|
||||
#include "QskArcNode.h"
|
||||
#include "QskArcMetrics.h"
|
||||
#include "QskArcRenderer.h"
|
||||
#include "QskGradient.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
class ArcData
|
||||
{
|
||||
public:
|
||||
const QskArcMetrics& metrics;
|
||||
const QskGradient& gradient;
|
||||
};
|
||||
}
|
||||
|
||||
QskArcNode::QskArcNode()
|
||||
{
|
||||
|
|
@ -17,26 +29,28 @@ QskArcNode::~QskArcNode()
|
|||
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics,
|
||||
const QskGradient& gradient, QQuickWindow* window )
|
||||
{
|
||||
m_metrics = metrics;
|
||||
m_gradient = gradient;
|
||||
|
||||
update( window, QskTextureRenderer::AutoDetect, rect.toRect() );
|
||||
const ArcData arcData { metrics, gradient };
|
||||
update( window, rect, QSizeF(), &arcData );
|
||||
}
|
||||
|
||||
void QskArcNode::paint( QPainter* painter, const QSizeF& size )
|
||||
void QskArcNode::paint( QPainter* painter, const QSize& size, const void* nodeData )
|
||||
{
|
||||
const qreal w = m_metrics.width();
|
||||
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
|
||||
|
||||
const qreal w = arcData->metrics.width();
|
||||
const QRectF rect( 0.5 * w, 0.5 * w, size.width() - w, size.height() - w );
|
||||
|
||||
QskArcRenderer renderer;
|
||||
renderer.renderArc( rect, m_metrics, m_gradient, painter );
|
||||
renderer.renderArc( rect, arcData->metrics, arcData->gradient, painter );
|
||||
}
|
||||
|
||||
QskHashValue QskArcNode::hash() const
|
||||
QskHashValue QskArcNode::hash( const void* nodeData ) const
|
||||
{
|
||||
auto h = m_metrics.hash();
|
||||
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
|
||||
|
||||
for( const auto& stop : qAsConst( m_gradient.stops() ) )
|
||||
auto h = arcData->metrics.hash();
|
||||
|
||||
for( const auto& stop : qAsConst( arcData->gradient.stops() ) )
|
||||
h = stop.hash( h );
|
||||
|
||||
return h;
|
||||
|
|
|
|||
|
|
@ -6,10 +6,13 @@
|
|||
#ifndef QSK_ARC_NODE_H
|
||||
#define QSK_ARC_NODE_H
|
||||
|
||||
#include "QskArcMetrics.h"
|
||||
#include "QskGradient.h"
|
||||
#include "QskPaintedNode.h"
|
||||
|
||||
class QskArcMetrics;
|
||||
class QskGradient;
|
||||
|
||||
// should be a QSGGeometryNode, TODO ..
|
||||
|
||||
class QSK_EXPORT QskArcNode : public QskPaintedNode
|
||||
{
|
||||
public:
|
||||
|
|
@ -19,12 +22,9 @@ class QSK_EXPORT QskArcNode : public QskPaintedNode
|
|||
void setArcData( const QRectF&, const QskArcMetrics&,
|
||||
const QskGradient&, QQuickWindow* );
|
||||
|
||||
void paint( QPainter* painter, const QSizeF& size ) override;
|
||||
QskHashValue hash() const override;
|
||||
|
||||
private:
|
||||
QskArcMetrics m_metrics;
|
||||
QskGradient m_gradient;
|
||||
protected:
|
||||
void paint( QPainter*, const QSize&, const void* nodeData ) override;
|
||||
QskHashValue hash( const void* nodeData ) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,27 +8,17 @@
|
|||
#include "QskColorFilter.h"
|
||||
#include "QskPainterCommand.h"
|
||||
|
||||
static inline QskHashValue qskHash(
|
||||
const QskGraphic& graphic, const QskColorFilter& colorFilter,
|
||||
QskTextureRenderer::RenderMode renderMode )
|
||||
namespace
|
||||
{
|
||||
QskHashValue hash = 12000;
|
||||
|
||||
const auto& substitutions = colorFilter.substitutions();
|
||||
if ( substitutions.size() > 0 )
|
||||
class GraphicData
|
||||
{
|
||||
hash = qHashBits( substitutions.constData(),
|
||||
substitutions.size() * sizeof( substitutions[ 0 ] ), hash );
|
||||
}
|
||||
|
||||
hash = graphic.hash( hash );
|
||||
hash = qHash( renderMode, hash );
|
||||
|
||||
return hash;
|
||||
public:
|
||||
const QskGraphic& graphic;
|
||||
const QskColorFilter& colorFilter;
|
||||
};
|
||||
}
|
||||
|
||||
QskGraphicNode::QskGraphicNode()
|
||||
: m_hash( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -36,14 +26,10 @@ QskGraphicNode::~QskGraphicNode()
|
|||
{
|
||||
}
|
||||
|
||||
void QskGraphicNode::setGraphic(
|
||||
QQuickWindow* window, const QskGraphic& graphic, const QskColorFilter& colorFilter,
|
||||
QskTextureRenderer::RenderMode renderMode, const QRectF& rect,
|
||||
Qt::Orientations mirrored )
|
||||
void QskGraphicNode::setGraphic( QQuickWindow* window, const QskGraphic& graphic,
|
||||
const QskColorFilter& colorFilter, const QRectF& rect )
|
||||
{
|
||||
bool isTextureDirty = isNull();
|
||||
|
||||
QSize textureSize;
|
||||
QSizeF size;
|
||||
|
||||
if ( graphic.commandTypes() == QskGraphic::RasterData )
|
||||
{
|
||||
|
|
@ -52,34 +38,43 @@ void QskGraphicNode::setGraphic(
|
|||
There is no benefit in rescaling it into the target rectangle
|
||||
by the CPU and creating a new texture.
|
||||
*/
|
||||
textureSize = graphic.defaultSize().toSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
textureSize = rect.size().toSize();
|
||||
|
||||
if ( !isTextureDirty )
|
||||
{
|
||||
const auto oldRect = QskTextureNode::rect();
|
||||
isTextureDirty = ( rect.width() != static_cast< int >( oldRect.width() ) ) ||
|
||||
( rect.height() != static_cast< int >( oldRect.height() ) );
|
||||
}
|
||||
size = graphic.defaultSize();
|
||||
}
|
||||
|
||||
const auto hash = qskHash( graphic, colorFilter, renderMode );
|
||||
if ( hash != m_hash )
|
||||
{
|
||||
m_hash = hash;
|
||||
isTextureDirty = true;
|
||||
}
|
||||
|
||||
auto textureId = QskTextureNode::textureId();
|
||||
|
||||
if ( isTextureDirty )
|
||||
{
|
||||
textureId = QskTextureRenderer::createTextureFromGraphic(
|
||||
window, renderMode, textureSize, graphic, colorFilter, Qt::IgnoreAspectRatio );
|
||||
}
|
||||
|
||||
QskTextureNode::setTexture( window, rect, textureId, mirrored );
|
||||
const GraphicData graphicData { graphic, colorFilter };
|
||||
update( window, rect, size, &graphicData );
|
||||
}
|
||||
|
||||
void QskGraphicNode::paint( QPainter* painter, const QSize& size, const void* nodeData )
|
||||
{
|
||||
const auto graphicData = reinterpret_cast< const GraphicData* >( nodeData );
|
||||
|
||||
const auto& graphic = graphicData->graphic;
|
||||
const auto& colorFilter = graphicData->colorFilter;
|
||||
|
||||
if ( graphic.commandTypes() == QskGraphic::RasterData )
|
||||
{
|
||||
qDebug() << size;
|
||||
}
|
||||
|
||||
const QRectF rect( 0, 0, size.width(), size.height() );
|
||||
graphic.render( painter, rect, colorFilter, Qt::IgnoreAspectRatio );
|
||||
}
|
||||
|
||||
QskHashValue QskGraphicNode::hash( const void* nodeData ) const
|
||||
{
|
||||
const auto graphicData = reinterpret_cast< const GraphicData* >( nodeData );
|
||||
|
||||
const auto& graphic = graphicData->graphic;
|
||||
|
||||
QskHashValue hash = 12000;
|
||||
|
||||
const auto& substitutions = graphicData->colorFilter.substitutions();
|
||||
if ( substitutions.size() > 0 )
|
||||
{
|
||||
hash = qHashBits( substitutions.constData(),
|
||||
substitutions.size() * sizeof( substitutions[ 0 ] ), hash );
|
||||
}
|
||||
|
||||
return graphic.hash( hash );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,29 +6,23 @@
|
|||
#ifndef QSK_GRAPHIC_NODE_H
|
||||
#define QSK_GRAPHIC_NODE_H
|
||||
|
||||
#include "QskTextureRenderer.h"
|
||||
#include "QskTextureNode.h"
|
||||
#include "QskPaintedNode.h"
|
||||
|
||||
class QskGraphic;
|
||||
class QskColorFilter;
|
||||
class QQuickWindow;
|
||||
|
||||
class QSK_EXPORT QskGraphicNode : public QskTextureNode
|
||||
class QSK_EXPORT QskGraphicNode : public QskPaintedNode
|
||||
{
|
||||
public:
|
||||
QskGraphicNode();
|
||||
~QskGraphicNode() override;
|
||||
|
||||
void setGraphic( QQuickWindow*,
|
||||
const QskGraphic&, const QskColorFilter&,
|
||||
QskTextureRenderer::RenderMode, const QRectF&,
|
||||
Qt::Orientations mirrored = Qt::Orientations() );
|
||||
void setGraphic( QQuickWindow*, const QskGraphic&,
|
||||
const QskColorFilter&, const QRectF& );
|
||||
|
||||
private:
|
||||
void setTexture( QQuickWindow*,
|
||||
const QRectF&, uint id, Qt::Orientations ) = delete;
|
||||
|
||||
QskHashValue m_hash;
|
||||
virtual void paint( QPainter*, const QSize&, const void* nodeData ) override;
|
||||
virtual QskHashValue hash( const void* nodeData ) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,24 +4,44 @@
|
|||
*****************************************************************************/
|
||||
|
||||
#include "QskPaintedNode.h"
|
||||
#include "QskSGNode.h"
|
||||
#include "QskTextureRenderer.h"
|
||||
|
||||
class QskPaintedNode::PaintHelper : public QskTextureRenderer::PaintHelper
|
||||
#include <qsgimagenode.h>
|
||||
#include <qquickwindow.h>
|
||||
#include <qimage.h>
|
||||
#include <qpainter.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qsgplaintexture_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
static inline QSGImageNode::TextureCoordinatesTransformMode
|
||||
qskEffectiveTransformMode( const Qt::Orientations mirrored )
|
||||
{
|
||||
public:
|
||||
inline PaintHelper( QskPaintedNode* node )
|
||||
: m_node( node )
|
||||
{
|
||||
}
|
||||
QSGImageNode::TextureCoordinatesTransformMode mode;
|
||||
|
||||
void paint( QPainter* painter, const QSize& size ) override
|
||||
{
|
||||
m_node->paint( painter, size );
|
||||
}
|
||||
if ( mirrored & Qt::Vertical )
|
||||
mode |= QSGImageNode::MirrorVertically;
|
||||
|
||||
private:
|
||||
QskPaintedNode* m_node;
|
||||
};
|
||||
if ( mirrored & Qt::Horizontal )
|
||||
mode |= QSGImageNode::MirrorHorizontally;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const quint8 imageRole = 250; // reserved for internal use
|
||||
|
||||
inline QSGImageNode* findImageNode( const QSGNode* parentNode )
|
||||
{
|
||||
auto node = QskSGNode::findChildNode(
|
||||
const_cast< QSGNode* >( parentNode ), imageRole );
|
||||
|
||||
return static_cast< QSGImageNode* >( node );
|
||||
}
|
||||
}
|
||||
|
||||
QskPaintedNode::QskPaintedNode()
|
||||
{
|
||||
|
|
@ -31,33 +51,185 @@ QskPaintedNode::~QskPaintedNode()
|
|||
{
|
||||
}
|
||||
|
||||
void QskPaintedNode::update( QQuickWindow* window,
|
||||
QskTextureRenderer::RenderMode renderMode, const QRect& rect )
|
||||
void QskPaintedNode::setRenderHint( RenderHint renderHint )
|
||||
{
|
||||
bool isTextureDirty = isNull();
|
||||
m_renderHint = renderHint;
|
||||
}
|
||||
|
||||
if ( !isTextureDirty )
|
||||
QskPaintedNode::RenderHint QskPaintedNode::renderHint() const
|
||||
{
|
||||
return m_renderHint;
|
||||
}
|
||||
|
||||
void QskPaintedNode::setMirrored( Qt::Orientations orientations )
|
||||
{
|
||||
if ( orientations != m_mirrored )
|
||||
{
|
||||
const auto oldRect = QskTextureNode::rect();
|
||||
isTextureDirty = ( rect.width() != static_cast< int >( oldRect.width() ) ) ||
|
||||
( rect.height() != static_cast< int >( oldRect.height() ) );
|
||||
m_mirrored = orientations;
|
||||
|
||||
if ( auto imageNode = findImageNode( this ) )
|
||||
{
|
||||
imageNode->setTextureCoordinatesTransform(
|
||||
qskEffectiveTransformMode( orientations ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Orientations QskPaintedNode::mirrored() const
|
||||
{
|
||||
return m_mirrored;
|
||||
}
|
||||
|
||||
QSize QskPaintedNode::textureSize() const
|
||||
{
|
||||
if ( const auto imageNode = findImageNode( this ) )
|
||||
{
|
||||
if ( auto texture = imageNode->texture() )
|
||||
return texture->textureSize();
|
||||
}
|
||||
|
||||
const auto newHash = hash();
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QRectF QskPaintedNode::rect() const
|
||||
{
|
||||
const auto imageNode = findImageNode( this );
|
||||
return imageNode ? imageNode->rect() : QRectF();
|
||||
}
|
||||
|
||||
void QskPaintedNode::update( QQuickWindow* window,
|
||||
const QRectF& rect, const QSizeF& size, const void* nodeData )
|
||||
{
|
||||
auto imageNode = findImageNode( this );
|
||||
|
||||
if ( rect.isEmpty() )
|
||||
{
|
||||
if ( imageNode )
|
||||
{
|
||||
removeChildNode( imageNode );
|
||||
delete imageNode;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( imageNode == nullptr )
|
||||
{
|
||||
imageNode = window->createImageNode();
|
||||
|
||||
imageNode->setOwnsTexture( true );
|
||||
QskSGNode::setNodeRole( imageNode, imageRole );
|
||||
|
||||
appendChildNode( imageNode );
|
||||
}
|
||||
|
||||
QSize imageSize;
|
||||
|
||||
{
|
||||
auto scaledSize = size.isEmpty() ? rect.size() : size;
|
||||
scaledSize *= window->effectiveDevicePixelRatio();
|
||||
|
||||
imageSize = scaledSize.toSize();
|
||||
}
|
||||
|
||||
bool isTextureDirty = false;
|
||||
|
||||
const auto newHash = hash( nodeData );
|
||||
if ( ( newHash == 0 ) || ( newHash != m_hash ) )
|
||||
{
|
||||
m_hash = newHash;
|
||||
isTextureDirty = true;
|
||||
}
|
||||
|
||||
auto textureId = QskTextureNode::textureId();
|
||||
|
||||
if ( isTextureDirty )
|
||||
else
|
||||
{
|
||||
PaintHelper helper( this );
|
||||
textureId = QskTextureRenderer::createTexture(
|
||||
window, renderMode, rect.size(), &helper );
|
||||
isTextureDirty = ( imageSize != textureSize() );
|
||||
}
|
||||
|
||||
QskTextureNode::setTexture( window, rect, textureId );
|
||||
|
||||
if ( isTextureDirty )
|
||||
updateTexture( window, imageSize, nodeData );
|
||||
|
||||
imageNode->setRect( rect );
|
||||
imageNode->setTextureCoordinatesTransform(
|
||||
qskEffectiveTransformMode( m_mirrored ) );
|
||||
}
|
||||
|
||||
void QskPaintedNode::updateTexture( QQuickWindow* window,
|
||||
const QSize& size, const void* nodeData )
|
||||
{
|
||||
auto imageNode = findImageNode( this );
|
||||
|
||||
if ( ( m_renderHint == OpenGL ) && QskTextureRenderer::isOpenGLWindow( window ) )
|
||||
{
|
||||
const auto textureId = createTextureGL( window, size, nodeData );
|
||||
|
||||
auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() );
|
||||
if ( texture == nullptr )
|
||||
{
|
||||
texture = new QSGPlainTexture;
|
||||
texture->setHasAlphaChannel( true );
|
||||
texture->setOwnsTexture( true );
|
||||
|
||||
imageNode->setTexture( texture );
|
||||
}
|
||||
|
||||
QskTextureRenderer::setTextureId( window, textureId, size, texture );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto image = createImage( window, size, nodeData );
|
||||
|
||||
if ( auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ) )
|
||||
texture->setImage( image );
|
||||
else
|
||||
imageNode->setTexture( window->createTextureFromImage( image ) );
|
||||
}
|
||||
}
|
||||
|
||||
QImage QskPaintedNode::createImage( QQuickWindow* window,
|
||||
const QSize& size, const void* nodeData )
|
||||
{
|
||||
QImage image( size, QImage::Format_RGBA8888_Premultiplied );
|
||||
image.fill( Qt::transparent );
|
||||
|
||||
QPainter painter( &image );
|
||||
|
||||
/*
|
||||
setting a devicePixelRatio for the image only works for
|
||||
value >= 1.0. So we have to scale manually.
|
||||
*/
|
||||
const auto ratio = window->effectiveDevicePixelRatio();
|
||||
painter.scale( ratio, ratio );
|
||||
|
||||
paint( &painter, size / ratio, nodeData );
|
||||
|
||||
painter.end();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
quint32 QskPaintedNode::createTextureGL(
|
||||
QQuickWindow* window, const QSize& size, const void* nodeData )
|
||||
{
|
||||
class PaintHelper : public QskTextureRenderer::PaintHelper
|
||||
{
|
||||
public:
|
||||
PaintHelper( QskPaintedNode* node, const void* nodeData )
|
||||
: m_node( node )
|
||||
, m_nodeData( nodeData )
|
||||
{
|
||||
}
|
||||
|
||||
void paint( QPainter* painter, const QSize& size ) override
|
||||
{
|
||||
m_node->paint( painter, size, m_nodeData );
|
||||
}
|
||||
|
||||
private:
|
||||
QskPaintedNode* m_node;
|
||||
const void* m_nodeData;
|
||||
};
|
||||
|
||||
PaintHelper helper( this, nodeData );
|
||||
return createPaintedTextureGL( window, size, &helper );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,31 +6,62 @@
|
|||
#ifndef QSK_PAINTED_NODE_H
|
||||
#define QSK_PAINTED_NODE_H
|
||||
|
||||
#include "QskTextureNode.h"
|
||||
#include "QskTextureRenderer.h"
|
||||
#include "QskGlobal.h"
|
||||
#include <qsgnode.h>
|
||||
|
||||
class QSK_EXPORT QskPaintedNode : public QskTextureNode
|
||||
class QQuickWindow;
|
||||
class QPainter;
|
||||
class QImage;
|
||||
|
||||
class QSK_EXPORT QskPaintedNode : public QSGNode
|
||||
{
|
||||
public:
|
||||
/*
|
||||
Raster usually provides a better antialiasing and is less buggy,
|
||||
while OpenGL might be faster - depending on the content that has
|
||||
to be painted.
|
||||
|
||||
Since Qt 5.10 X11 is back and could be an interesting option
|
||||
with good quality and hardware accelerated performance. TODO ...
|
||||
|
||||
OpenGL might be ignored depending on the backend used by the
|
||||
application.
|
||||
*/
|
||||
enum RenderHint
|
||||
{
|
||||
Raster,
|
||||
OpenGL
|
||||
};
|
||||
|
||||
QskPaintedNode();
|
||||
~QskPaintedNode() override;
|
||||
|
||||
void update( QQuickWindow*,
|
||||
QskTextureRenderer::RenderMode, const QRect& );
|
||||
void setRenderHint( RenderHint );
|
||||
RenderHint renderHint() const;
|
||||
|
||||
void setMirrored( Qt::Orientations );
|
||||
Qt::Orientations mirrored() const;
|
||||
|
||||
QRectF rect() const;
|
||||
QSize textureSize() const;
|
||||
|
||||
virtual void paint( QPainter*, const QSize&, const void* nodeData ) = 0;
|
||||
|
||||
protected:
|
||||
virtual void paint( QPainter*, const QSizeF& ) = 0;
|
||||
void update( QQuickWindow*, const QRectF&, const QSizeF&, const void* nodeData );
|
||||
|
||||
// a hash value of '0' always results in repainting
|
||||
virtual QskHashValue hash() const = 0;
|
||||
virtual QskHashValue hash( const void* nodeData ) const = 0;
|
||||
|
||||
private:
|
||||
class PaintHelper;
|
||||
void updateTexture( QQuickWindow*, const QSize&, const void* nodeData );
|
||||
|
||||
void setTexture( QQuickWindow*,
|
||||
const QRectF&, uint id, Qt::Orientations ) = delete;
|
||||
QImage createImage( QQuickWindow*, const QSize&, const void* nodeData );
|
||||
quint32 createTextureGL( QQuickWindow*, const QSize&, const void* nodeData );
|
||||
|
||||
QskHashValue m_hash;
|
||||
RenderHint m_renderHint = OpenGL;
|
||||
Qt::Orientations m_mirrored;
|
||||
QskHashValue m_hash = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,393 +0,0 @@
|
|||
#include "QskTextureNode.h"
|
||||
#include "QskFunctions.h"
|
||||
|
||||
#include <qopenglfunctions.h>
|
||||
#include <qsggeometry.h>
|
||||
#include <qsgmaterial.h>
|
||||
#include <qquickwindow.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qsgnode_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
#include <qsgtexture.h>
|
||||
#include <qsgtexturematerial.h>
|
||||
#include <qsgtexture_platform.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qrhi_p.h>
|
||||
#include <private/qrhigles2_p_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
static void qskUpdateGLTextureId( QRhiTexture* rhiTexture, uint textureId )
|
||||
{
|
||||
// hack time: we do not want to create a new QSGTexture object for each texture
|
||||
|
||||
class Texture : public QRhiTexture
|
||||
{
|
||||
public:
|
||||
GLuint texture;
|
||||
// ...
|
||||
};
|
||||
|
||||
GLuint id = rhiTexture->nativeTexture().object;
|
||||
|
||||
if ( id )
|
||||
{
|
||||
auto funcs = QOpenGLContext::currentContext()->functions();
|
||||
funcs->glDeleteTextures( 1, &id );
|
||||
}
|
||||
|
||||
auto glTexture = static_cast< Texture* >( rhiTexture );
|
||||
glTexture->texture = textureId;
|
||||
|
||||
Q_ASSERT( rhiTexture->nativeTexture().object == textureId );
|
||||
}
|
||||
|
||||
using TextureMaterial = QSGTextureMaterial;
|
||||
using OpaqueTextureMaterial = QSGOpaqueTextureMaterial;
|
||||
|
||||
static inline void qskDeleteTexture( const TextureMaterial& material )
|
||||
{
|
||||
delete material.texture();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
namespace
|
||||
{
|
||||
class MaterialShader final : public QSGMaterialShader
|
||||
{
|
||||
public:
|
||||
MaterialShader( bool isOpaque );
|
||||
|
||||
char const* const* attributeNames() const override;
|
||||
void updateState( const RenderState&, QSGMaterial*, QSGMaterial* ) override;
|
||||
|
||||
protected:
|
||||
void initialize() override;
|
||||
|
||||
private:
|
||||
int m_matrixId = -1;
|
||||
int m_opacityId = -1;
|
||||
|
||||
const bool m_isOpaque : 1;
|
||||
};
|
||||
|
||||
class Material : public QSGMaterial
|
||||
{
|
||||
public:
|
||||
Material( bool isOpaque );
|
||||
|
||||
QSGMaterialType* type() const override;
|
||||
QSGMaterialShader* createShader() const override;
|
||||
|
||||
void setTextureId( uint );
|
||||
uint textureId() const;
|
||||
|
||||
int compare( const QSGMaterial* ) const override;
|
||||
|
||||
private:
|
||||
uint m_textureId = 0;
|
||||
const bool m_isOpaque : 1;
|
||||
};
|
||||
|
||||
class TextureMaterial final : public Material
|
||||
{
|
||||
public:
|
||||
TextureMaterial()
|
||||
: Material( false )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class OpaqueTextureMaterial final : public Material
|
||||
{
|
||||
public:
|
||||
OpaqueTextureMaterial()
|
||||
: Material( true )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
MaterialShader::MaterialShader( bool isOpaque )
|
||||
: m_isOpaque( isOpaque )
|
||||
{
|
||||
setShaderSourceFile( QOpenGLShader::Vertex,
|
||||
QStringLiteral( ":/qt-project.org/scenegraph/shaders/opaquetexture.vert" ) );
|
||||
|
||||
const auto fragmentShaderFile = m_isOpaque
|
||||
? QStringLiteral( ":/qt-project.org/scenegraph/shaders/opaquetexture.frag" )
|
||||
: QStringLiteral( ":/qt-project.org/scenegraph/shaders/texture.frag" );
|
||||
|
||||
setShaderSourceFile( QOpenGLShader::Fragment, fragmentShaderFile );
|
||||
}
|
||||
|
||||
char const* const* MaterialShader::attributeNames() const
|
||||
{
|
||||
static char const* const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", 0 };
|
||||
return attr;
|
||||
}
|
||||
|
||||
void MaterialShader::updateState(
|
||||
const RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial )
|
||||
{
|
||||
if ( !m_isOpaque && state.isOpacityDirty() )
|
||||
program()->setUniformValue( m_opacityId, state.opacity() );
|
||||
|
||||
auto* materialOld = static_cast< Material* >( oldMaterial );
|
||||
auto* materialNew = static_cast< Material* >( newMaterial );
|
||||
|
||||
if ( ( materialOld == nullptr ) ||
|
||||
( materialOld->textureId() != materialNew->textureId() ) )
|
||||
{
|
||||
auto funcs = QOpenGLContext::currentContext()->functions();
|
||||
funcs->glBindTexture( GL_TEXTURE_2D, materialNew->textureId() );
|
||||
}
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
program()->setUniformValue( m_matrixId, state.combinedMatrix() );
|
||||
}
|
||||
|
||||
void MaterialShader::initialize()
|
||||
{
|
||||
m_matrixId = program()->uniformLocation( "qt_Matrix" );
|
||||
|
||||
if ( !m_isOpaque )
|
||||
m_opacityId = program()->uniformLocation( "opacity" );
|
||||
}
|
||||
|
||||
Material::Material( bool isOpaque )
|
||||
: m_isOpaque( isOpaque )
|
||||
{
|
||||
setFlag( Blending, true ); // alpha blending
|
||||
}
|
||||
|
||||
void Material::setTextureId( uint id )
|
||||
{
|
||||
m_textureId = id;
|
||||
}
|
||||
|
||||
uint Material::textureId() const
|
||||
{
|
||||
return m_textureId;
|
||||
}
|
||||
|
||||
QSGMaterialType* Material::type() const
|
||||
{
|
||||
if ( m_isOpaque )
|
||||
{
|
||||
static QSGMaterialType typeOpaque;
|
||||
return &typeOpaque;
|
||||
}
|
||||
else
|
||||
{
|
||||
static QSGMaterialType type;
|
||||
return &type;
|
||||
}
|
||||
}
|
||||
|
||||
QSGMaterialShader* Material::createShader() const
|
||||
{
|
||||
return new MaterialShader( m_isOpaque );
|
||||
}
|
||||
|
||||
int Material::compare( const QSGMaterial* other ) const
|
||||
{
|
||||
const auto otherMaterial = static_cast< const Material* >( other );
|
||||
|
||||
if ( m_textureId == otherMaterial->m_textureId )
|
||||
return 0;
|
||||
|
||||
return ( m_textureId > otherMaterial->m_textureId ) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void qskDeleteTexture( const TextureMaterial& material )
|
||||
{
|
||||
if ( material.textureId() > 0 )
|
||||
{
|
||||
/*
|
||||
In certain environments we have the effect, that at
|
||||
program termination the context is already gone
|
||||
*/
|
||||
if ( auto context = QOpenGLContext::currentContext() )
|
||||
{
|
||||
GLuint id = material.textureId();
|
||||
|
||||
auto funcs = context->functions();
|
||||
funcs->glDeleteTextures( 1, &id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class QskTextureNodePrivate final : public QSGGeometryNodePrivate
|
||||
{
|
||||
public:
|
||||
QskTextureNodePrivate()
|
||||
: geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 )
|
||||
{
|
||||
}
|
||||
|
||||
void setTextureId( QQuickWindow*, uint id );
|
||||
|
||||
void updateTextureGeometry()
|
||||
{
|
||||
QRectF r( 0, 0, 1, 1 );
|
||||
|
||||
if ( this->mirrored & Qt::Horizontal )
|
||||
{
|
||||
r.setLeft( 1 );
|
||||
r.setRight( 0 );
|
||||
}
|
||||
|
||||
if ( mirrored & Qt::Vertical )
|
||||
{
|
||||
r.setTop( 1 );
|
||||
r.setBottom( 0 );
|
||||
}
|
||||
|
||||
QSGGeometry::updateTexturedRectGeometry( &geometry, rect, r );
|
||||
}
|
||||
|
||||
QSGGeometry geometry;
|
||||
|
||||
OpaqueTextureMaterial opaqueMaterial;
|
||||
TextureMaterial material;
|
||||
|
||||
QRectF rect;
|
||||
Qt::Orientations mirrored;
|
||||
};
|
||||
|
||||
QskTextureNode::QskTextureNode()
|
||||
: QSGGeometryNode( *new QskTextureNodePrivate )
|
||||
{
|
||||
Q_D( QskTextureNode );
|
||||
|
||||
setGeometry( &d->geometry );
|
||||
|
||||
setMaterial( &d->material );
|
||||
setOpaqueMaterial( &d->opaqueMaterial );
|
||||
}
|
||||
|
||||
QskTextureNode::~QskTextureNode()
|
||||
{
|
||||
Q_D( const QskTextureNode );
|
||||
qskDeleteTexture( d->material );
|
||||
}
|
||||
|
||||
void QskTextureNode::setTexture( QQuickWindow* window,
|
||||
const QRectF& rect, uint textureId,
|
||||
Qt::Orientations mirrored )
|
||||
{
|
||||
Q_D( QskTextureNode );
|
||||
|
||||
if ( ( d->rect != rect ) || ( d->mirrored != mirrored ) )
|
||||
{
|
||||
d->rect = rect;
|
||||
d->mirrored = mirrored;
|
||||
|
||||
d->updateTextureGeometry();
|
||||
markDirty( DirtyGeometry );
|
||||
}
|
||||
|
||||
if ( textureId != this->textureId() )
|
||||
{
|
||||
d->setTextureId( window, textureId );
|
||||
markDirty( DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
void QskTextureNodePrivate::setTextureId( QQuickWindow*, uint textureId )
|
||||
{
|
||||
qskDeleteTexture( this->material );
|
||||
|
||||
this->material.setTextureId( textureId );
|
||||
this->opaqueMaterial.setTextureId( textureId );
|
||||
}
|
||||
|
||||
uint QskTextureNode::textureId() const
|
||||
{
|
||||
Q_D( const QskTextureNode );
|
||||
return d->material.textureId();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
void QskTextureNodePrivate::setTextureId( QQuickWindow* window, uint textureId )
|
||||
{
|
||||
auto texture = this->material.texture();
|
||||
|
||||
if ( texture )
|
||||
{
|
||||
// we do not want to create a new QSGTexture object only
|
||||
// to replace the textureId
|
||||
|
||||
switch( window->rendererInterface()->graphicsApi() )
|
||||
{
|
||||
case QSGRendererInterface::OpenGL:
|
||||
{
|
||||
qskUpdateGLTextureId( texture->rhiTexture(), textureId );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
delete texture;
|
||||
texture = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( textureId > 0 && texture == nullptr )
|
||||
{
|
||||
texture = QNativeInterface::QSGOpenGLTexture::fromNative(
|
||||
static_cast< GLuint >( textureId ), window,
|
||||
this->rect.size().toSize(), QQuickWindow::TextureHasAlphaChannel );
|
||||
|
||||
}
|
||||
|
||||
this->material.setTexture( texture );
|
||||
this->opaqueMaterial.setTexture( texture );
|
||||
}
|
||||
|
||||
uint QskTextureNode::textureId() const
|
||||
{
|
||||
Q_D( const QskTextureNode );
|
||||
|
||||
if ( auto texture = d->material.texture() )
|
||||
{
|
||||
const auto nativeTexture = texture->rhiTexture()->nativeTexture();
|
||||
return nativeTexture.object;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool QskTextureNode::isNull() const
|
||||
{
|
||||
return textureId() == 0;
|
||||
}
|
||||
|
||||
QRectF QskTextureNode::rect() const
|
||||
{
|
||||
Q_D( const QskTextureNode );
|
||||
return d->rect;
|
||||
}
|
||||
|
||||
Qt::Orientations QskTextureNode::mirrored() const
|
||||
{
|
||||
Q_D( const QskTextureNode );
|
||||
return d->mirrored;
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_TEXTURE_NODE_H
|
||||
#define QSK_TEXTURE_NODE_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
|
||||
#include <qnamespace.h>
|
||||
#include <qsgnode.h>
|
||||
|
||||
class QQuickWindow;
|
||||
class QskTextureNodePrivate;
|
||||
|
||||
class QSK_EXPORT QskTextureNode : public QSGGeometryNode
|
||||
{
|
||||
public:
|
||||
QskTextureNode();
|
||||
~QskTextureNode() override;
|
||||
|
||||
bool isNull() const;
|
||||
|
||||
void setTexture( QQuickWindow*, const QRectF&, uint id,
|
||||
Qt::Orientations mirrored = Qt::Orientations() );
|
||||
|
||||
uint textureId() const;
|
||||
QRectF rect() const;
|
||||
Qt::Orientations mirrored() const;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE( QskTextureNode )
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -4,92 +4,178 @@
|
|||
*****************************************************************************/
|
||||
|
||||
#include "QskTextureRenderer.h"
|
||||
#include "QskColorFilter.h"
|
||||
#include "QskGraphic.h"
|
||||
#include "QskSetup.h"
|
||||
|
||||
#include <qopenglcontext.h>
|
||||
#include <qopenglextrafunctions.h>
|
||||
#include <qopenglframebufferobject.h>
|
||||
#include <qopenglfunctions.h>
|
||||
#include <qopenglpaintdevice.h>
|
||||
#include <qopengltexture.h>
|
||||
|
||||
#include <qimage.h>
|
||||
#include <qpainter.h>
|
||||
|
||||
#include <qquickwindow.h>
|
||||
#include <qsgtexture.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qsgplaintexture_p.h>
|
||||
#include <private/qopenglframebufferobject_p.h>
|
||||
#include <private/qquickwindow_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
#include <qsgtexture_platform.h>
|
||||
#include <qquickopenglutils.h>
|
||||
#endif
|
||||
|
||||
static inline bool qskHasOpenGLRenderer( const QQuickWindow* window )
|
||||
static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo )
|
||||
{
|
||||
/*
|
||||
See https://bugreports.qt.io/browse/QTBUG-103929
|
||||
|
||||
As we create a FBO for each update of a node we can't live
|
||||
without having this ( ugly ) workaround.
|
||||
*/
|
||||
class MyFBO
|
||||
{
|
||||
public:
|
||||
virtual ~MyFBO() = default;
|
||||
QScopedPointer< QOpenGLFramebufferObjectPrivate > d_ptr;
|
||||
};
|
||||
|
||||
static_assert( sizeof( MyFBO ) == sizeof( QOpenGLFramebufferObject ),
|
||||
"Bad cast: QOpenGLFramebufferObject does not match" );
|
||||
|
||||
auto& attachment = reinterpret_cast< MyFBO* >( &fbo )->d_ptr->colorAttachments[0];
|
||||
auto guard = attachment.guard;
|
||||
|
||||
const auto textureId = fbo.takeTexture();
|
||||
|
||||
if ( guard )
|
||||
{
|
||||
class MyGuard : public QOpenGLSharedResourceGuard
|
||||
{
|
||||
public:
|
||||
void invalidateTexture() { invalidateResource(); }
|
||||
};
|
||||
|
||||
reinterpret_cast< MyGuard* >( guard )->invalidateTexture();
|
||||
}
|
||||
|
||||
attachment.guard = guard;
|
||||
|
||||
return textureId;
|
||||
}
|
||||
|
||||
bool QskTextureRenderer::isOpenGLWindow( const QQuickWindow* window )
|
||||
{
|
||||
if ( window == nullptr )
|
||||
return false;
|
||||
|
||||
const auto renderer = window->rendererInterface();
|
||||
return renderer->graphicsApi() == QSGRendererInterface::OpenGL;
|
||||
switch( renderer->graphicsApi() )
|
||||
{
|
||||
case QSGRendererInterface::OpenGL:
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
case QSGRendererInterface::OpenGLRhi:
|
||||
#endif
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static uint qskCreateTextureOpenGL( QQuickWindow* window,
|
||||
const QSize& size, QskTextureRenderer::PaintHelper* helper )
|
||||
void QskTextureRenderer::setTextureId( QQuickWindow* window,
|
||||
quint32 textureId, const QSize& size, QSGTexture* texture )
|
||||
{
|
||||
const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0;
|
||||
auto plainTexture = qobject_cast< QSGPlainTexture* >( texture );
|
||||
if ( plainTexture == nullptr )
|
||||
return;
|
||||
|
||||
const int width = ratio * size.width();
|
||||
const int height = ratio * size.height();
|
||||
auto rhi = QQuickWindowPrivate::get( window )->rhi;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
plainTexture->setTextureFromNativeTexture(
|
||||
rhi, quint64( textureId ), 0, size, {}, {} );
|
||||
#else
|
||||
if ( rhi )
|
||||
{
|
||||
// enabled with: "export QSG_RHI=1"
|
||||
plainTexture->setTextureFromNativeObject( rhi,
|
||||
QQuickWindow::NativeObjectTexture, &textureId, 0, size, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
plainTexture->setTextureId( textureId );
|
||||
plainTexture->setTextureSize( size );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
quint32 QskTextureRenderer::createPaintedTextureGL(
|
||||
QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper )
|
||||
{
|
||||
/*
|
||||
Binding GL_ARRAY_BUFFER/GL_ELEMENT_ARRAY_BUFFER to 0 seems to be enough.
|
||||
|
||||
However - as we do not know what is finally painted and what the
|
||||
OpenGL paint engine is doing with better reinitialize everything.
|
||||
|
||||
Hope this has no side effects as the context will leave the function
|
||||
in a modified state. Otherwise we could try to change the buffers
|
||||
only and reset them, before leaving.
|
||||
|
||||
QQuickFramebufferObject does the FBO rendering early
|
||||
( QQuickWindow::beforeRendering ). But so far doing it below updatePaintNode
|
||||
seems to work as well. Let's see if we run into issues ...
|
||||
*/
|
||||
|
||||
window->beginExternalCommands();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
QQuickOpenGLUtils::resetOpenGLState();
|
||||
#else
|
||||
window->resetOpenGLState();
|
||||
#endif
|
||||
|
||||
auto context = QOpenGLContext::currentContext();
|
||||
|
||||
QOpenGLFramebufferObjectFormat format1;
|
||||
format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil );
|
||||
|
||||
// ### TODO: get samples from window instead
|
||||
format1.setSamples( QOpenGLContext::currentContext()->format().samples() );
|
||||
format1.setSamples( context->format().samples() );
|
||||
|
||||
QOpenGLFramebufferObject multisampledFbo( width, height, format1 );
|
||||
QOpenGLFramebufferObject multisampledFbo( size, format1 );
|
||||
|
||||
QOpenGLPaintDevice pd( width, height );
|
||||
QOpenGLPaintDevice pd( size );
|
||||
pd.setPaintFlipped( true );
|
||||
|
||||
{
|
||||
QPainter painter( &pd );
|
||||
painter.scale( ratio, ratio );
|
||||
|
||||
painter.setCompositionMode( QPainter::CompositionMode_Source );
|
||||
painter.fillRect( 0, 0, width, height, Qt::transparent );
|
||||
painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent );
|
||||
painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
|
||||
|
||||
helper->paint( &painter, size );
|
||||
const auto ratio = window->effectiveDevicePixelRatio();
|
||||
|
||||
#if 1
|
||||
if ( format1.samples() > 0 )
|
||||
{
|
||||
/*
|
||||
Multisampling in the window surface might get lost
|
||||
as a side effect of rendering to the FBO.
|
||||
weired, needs to be investigated more
|
||||
*/
|
||||
painter.setRenderHint( QPainter::Antialiasing, true );
|
||||
}
|
||||
#endif
|
||||
painter.scale( ratio, ratio );
|
||||
helper->paint( &painter, size / ratio );
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObjectFormat format2;
|
||||
format2.setAttachment( QOpenGLFramebufferObject::NoAttachment );
|
||||
|
||||
QOpenGLFramebufferObject fbo( width, height, format2 );
|
||||
QOpenGLFramebufferObject fbo( size, format2 );
|
||||
|
||||
const QRect fboRect( 0, 0, width, height );
|
||||
const QRect fboRect( 0, 0, size.width(), size.height() );
|
||||
|
||||
QOpenGLFramebufferObject::blitFramebuffer(
|
||||
&fbo, fboRect, &multisampledFbo, fboRect );
|
||||
|
||||
return fbo.takeTexture();
|
||||
window->endExternalCommands();
|
||||
|
||||
return qskTakeTexture( fbo );
|
||||
}
|
||||
|
||||
static uint qskCreateTextureRaster( QQuickWindow* window,
|
||||
static QSGTexture* qskCreateTextureRaster( QQuickWindow* window,
|
||||
const QSize& size, QskTextureRenderer::PaintHelper* helper )
|
||||
{
|
||||
const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0;
|
||||
|
|
@ -109,130 +195,26 @@ static uint qskCreateTextureRaster( QQuickWindow* window,
|
|||
helper->paint( &painter, size );
|
||||
}
|
||||
|
||||
const auto target = QOpenGLTexture::Target2D;
|
||||
return window->createTextureFromImage( image, QQuickWindow::TextureHasAlphaChannel );
|
||||
}
|
||||
|
||||
auto context = QOpenGLContext::currentContext();
|
||||
if ( context == nullptr )
|
||||
return 0;
|
||||
|
||||
auto& f = *context->functions();
|
||||
|
||||
GLint oldTexture; // we can't rely on having OpenGL Direct State Access
|
||||
f.glGetIntegerv( QOpenGLTexture::BindingTarget2D, &oldTexture );
|
||||
|
||||
GLuint textureId;
|
||||
f.glGenTextures( 1, &textureId );
|
||||
|
||||
f.glBindTexture( target, textureId );
|
||||
|
||||
f.glTexParameteri( target, GL_TEXTURE_MIN_FILTER, QOpenGLTexture::Nearest );
|
||||
f.glTexParameteri( target, GL_TEXTURE_MAG_FILTER, QOpenGLTexture::Nearest );
|
||||
|
||||
f.glTexParameteri( target, GL_TEXTURE_WRAP_S, QOpenGLTexture::ClampToEdge );
|
||||
f.glTexParameteri( target, GL_TEXTURE_WRAP_T, QOpenGLTexture::ClampToEdge );
|
||||
|
||||
if ( QOpenGLTexture::hasFeature( QOpenGLTexture::ImmutableStorage ) )
|
||||
QSGTexture* QskTextureRenderer::createPaintedTexture(
|
||||
QQuickWindow* window, const QSize& size, PaintHelper* helper )
|
||||
{
|
||||
if ( isOpenGLWindow( window ) )
|
||||
{
|
||||
auto& ef = *context->extraFunctions();
|
||||
ef.glTexStorage2D( target, 1,
|
||||
QOpenGLTexture::RGBA8_UNorm, image.width(), image.height() );
|
||||
const auto textureId = createPaintedTextureGL( window, size, helper );
|
||||
|
||||
f.glTexSubImage2D( target, 0, 0, 0, image.width(), image.height(),
|
||||
QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits() );
|
||||
auto texture = new QSGPlainTexture;
|
||||
texture->setHasAlphaChannel( true );
|
||||
texture->setOwnsTexture( true );
|
||||
|
||||
setTextureId( window, textureId, size, texture );
|
||||
|
||||
return texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
f.glTexImage2D( target, 0, QOpenGLTexture::RGBA8_UNorm,
|
||||
image.width(), image.height(), 0,
|
||||
QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits() );
|
||||
}
|
||||
|
||||
f.glBindTexture( target, oldTexture );
|
||||
|
||||
return textureId;
|
||||
}
|
||||
|
||||
QSGTexture* QskTextureRenderer::textureFromId(
|
||||
QQuickWindow* window, uint textureId, const QSize& size )
|
||||
{
|
||||
const auto flags = static_cast< QQuickWindow::CreateTextureOptions >(
|
||||
QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture );
|
||||
|
||||
QSGTexture* texture;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
texture = QNativeInterface::QSGOpenGLTexture::fromNative(
|
||||
textureId, window, size, flags );
|
||||
|
||||
#else
|
||||
|
||||
const int nativeLayout = 0; // VkImageLayout in case of Vulkan
|
||||
|
||||
texture = window->createTextureFromNativeObject(
|
||||
QQuickWindow::NativeObjectTexture, &textureId, nativeLayout, size, flags );
|
||||
#endif
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
uint QskTextureRenderer::createTexture(
|
||||
QQuickWindow* window, RenderMode renderMode,
|
||||
const QSize& size, PaintHelper* helper )
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
// Qt6.0.0 is buggy when using FBOs. So let's disable it for the moment TODO ...
|
||||
renderMode = Raster;
|
||||
#endif
|
||||
|
||||
if ( renderMode != Raster )
|
||||
{
|
||||
if ( !qskHasOpenGLRenderer( window ) )
|
||||
renderMode = Raster;
|
||||
}
|
||||
|
||||
if ( renderMode == AutoDetect )
|
||||
{
|
||||
if ( qskSetup->testItemUpdateFlag( QskQuickItem::PreferRasterForTextures ) )
|
||||
renderMode = Raster;
|
||||
else
|
||||
renderMode = OpenGL;
|
||||
}
|
||||
|
||||
if ( renderMode == Raster )
|
||||
return qskCreateTextureRaster( window, size, helper );
|
||||
else
|
||||
return qskCreateTextureOpenGL( window, size, helper );
|
||||
}
|
||||
|
||||
uint QskTextureRenderer::createTextureFromGraphic(
|
||||
QQuickWindow* window, RenderMode renderMode, const QSize& size,
|
||||
const QskGraphic& graphic, const QskColorFilter& colorFilter,
|
||||
Qt::AspectRatioMode aspectRatioMode )
|
||||
{
|
||||
class PaintHelper : public QskTextureRenderer::PaintHelper
|
||||
{
|
||||
public:
|
||||
PaintHelper( const QskGraphic& graphic,
|
||||
const QskColorFilter& filter, Qt::AspectRatioMode aspectRatioMode )
|
||||
: m_graphic( graphic )
|
||||
, m_filter( filter )
|
||||
, m_aspectRatioMode( aspectRatioMode )
|
||||
{
|
||||
}
|
||||
|
||||
void paint( QPainter* painter, const QSize& size ) override
|
||||
{
|
||||
const QRect rect( 0, 0, size.width(), size.height() );
|
||||
m_graphic.render( painter, rect, m_filter, m_aspectRatioMode );
|
||||
}
|
||||
|
||||
private:
|
||||
const QskGraphic& m_graphic;
|
||||
const QskColorFilter& m_filter;
|
||||
const Qt::AspectRatioMode m_aspectRatioMode;
|
||||
};
|
||||
|
||||
PaintHelper helper( graphic, colorFilter, aspectRatioMode );
|
||||
return createTexture( window, renderMode, size, &helper );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,35 +7,15 @@
|
|||
#define QSK_TEXTURE_RENDERER_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QskGraphic;
|
||||
class QskColorFilter;
|
||||
|
||||
class QPainter;
|
||||
class QSize;
|
||||
class QPainter;
|
||||
class QSGTexture;
|
||||
class QQuickWindow;
|
||||
|
||||
namespace QskTextureRenderer
|
||||
{
|
||||
/*
|
||||
Raster usually provides a better antialiasing and is less buggy,
|
||||
while OpenGL might be faster - depending on the content that has
|
||||
to be painted.
|
||||
|
||||
Since Qt 5.10 X11 is back and could be an interesting option
|
||||
with good quality and hardware accelerated performance. TODO ...
|
||||
*/
|
||||
enum RenderMode
|
||||
{
|
||||
AutoDetect, // depends on QskSetup::controlFlags()
|
||||
|
||||
Raster,
|
||||
OpenGL
|
||||
};
|
||||
|
||||
class QSK_EXPORT PaintHelper
|
||||
class PaintHelper
|
||||
{
|
||||
public:
|
||||
PaintHelper() = default;
|
||||
|
|
@ -47,15 +27,16 @@ namespace QskTextureRenderer
|
|||
Q_DISABLE_COPY( PaintHelper )
|
||||
};
|
||||
|
||||
QSK_EXPORT uint createTexture(
|
||||
QQuickWindow*, RenderMode, const QSize&, PaintHelper* );
|
||||
bool isOpenGLWindow( const QQuickWindow* );
|
||||
|
||||
QSK_EXPORT uint createTextureFromGraphic(
|
||||
QQuickWindow*, RenderMode, const QSize&, const QskGraphic&,
|
||||
const QskColorFilter&, Qt::AspectRatioMode );
|
||||
void setTextureId( QQuickWindow*,
|
||||
quint32 textureId, const QSize&, QSGTexture* );
|
||||
|
||||
QSK_EXPORT QSGTexture* textureFromId(
|
||||
QQuickWindow*, uint textureId, const QSize& );
|
||||
quint32 createPaintedTextureGL(
|
||||
QQuickWindow*, const QSize&, QskTextureRenderer::PaintHelper* );
|
||||
|
||||
QSK_EXPORT QSGTexture* createPaintedTexture(
|
||||
QQuickWindow* window, const QSize& size, PaintHelper* helper );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ TARGET = $$qskLibraryTarget(qskinny)
|
|||
|
||||
QT += quick quick-private
|
||||
|
||||
greaterThan( QT_MAJOR_VERSION, 5 ) {
|
||||
QT += opengl-private
|
||||
}
|
||||
|
||||
contains(QSK_CONFIG, QskDll): DEFINES += QSK_MAKEDLL
|
||||
|
||||
QSK_SUBDIRS = common graphic nodes controls layouts dialogs inputpanel
|
||||
|
|
@ -107,7 +111,6 @@ HEADERS += \
|
|||
nodes/QskSGNode.h \
|
||||
nodes/QskTextNode.h \
|
||||
nodes/QskTextRenderer.h \
|
||||
nodes/QskTextureNode.h \
|
||||
nodes/QskTextureRenderer.h \
|
||||
nodes/QskTickmarksNode.h \
|
||||
nodes/QskVertex.h
|
||||
|
|
@ -128,7 +131,6 @@ SOURCES += \
|
|||
nodes/QskSGNode.cpp \
|
||||
nodes/QskTextNode.cpp \
|
||||
nodes/QskTextRenderer.cpp \
|
||||
nodes/QskTextureNode.cpp \
|
||||
nodes/QskTextureRenderer.cpp \
|
||||
nodes/QskTickmarksNode.cpp \
|
||||
nodes/QskVertex.cpp
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
LD_LIBRARY_PATH=/home/uwe/qskinny/config/qsk1/Qt/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
|
||||
export LD_LIBRARY_PATH
|
||||
QT_PLUGIN_PATH=/home/uwe/qskinny/config/qsk1/Qt/plugins${QT_PLUGIN_PATH:+:$QT_PLUGIN_PATH}
|
||||
export QT_PLUGIN_PATH
|
||||
exec "$@"
|
||||
Loading…
Reference in New Issue