221 lines
6.8 KiB
C++
221 lines
6.8 KiB
C++
/******************************************************************************
|
|
* QSkinny - Copyright (C) The authors
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*****************************************************************************/
|
|
|
|
#include "QskBoxRenderer.h"
|
|
#include "QskBoxShapeMetrics.h"
|
|
#include "QskBoxBorderMetrics.h"
|
|
#include "QskBoxBorderColors.h"
|
|
#include "QskBoxMetrics.h"
|
|
#include "QskBoxBasicStroker.h"
|
|
#include "QskBoxGradientStroker.h"
|
|
|
|
#include "QskGradient.h"
|
|
#include "QskGradientDirection.h"
|
|
#include "QskFunctions.h"
|
|
|
|
#include <qsggeometry.h>
|
|
|
|
static inline QskVertex::Line* qskAllocateLines(
|
|
QSGGeometry& geometry, int lineCount )
|
|
{
|
|
geometry.allocate( 2 * lineCount ); // 2 points per line
|
|
return reinterpret_cast< QskVertex::Line* >( geometry.vertexData() );
|
|
}
|
|
|
|
static inline QskVertex::ColoredLine* qskAllocateColoredLines(
|
|
QSGGeometry& geometry, int lineCount )
|
|
{
|
|
geometry.allocate( 2 * lineCount ); // 2 points per line
|
|
return reinterpret_cast< QskVertex::ColoredLine* >( geometry.vertexData() );
|
|
}
|
|
|
|
static inline QskGradient qskEffectiveGradient(
|
|
const QRectF& rect, const QskGradient& gradient )
|
|
{
|
|
if ( !gradient.isVisible() )
|
|
return gradient;
|
|
|
|
const auto dir = gradient.linearDirection();
|
|
|
|
auto g = gradient;
|
|
|
|
if ( !dir.isTilted() )
|
|
{
|
|
/*
|
|
Dealing with inverted gradient vectors makes the code even
|
|
more unreadable. So we simply invert stops/vector instead.
|
|
*/
|
|
if ( ( dir.x1() > dir.x2() ) || ( dir.y1() > dir.y2() ) )
|
|
{
|
|
g.setLinearDirection( dir.x2(), dir.y2(), dir.x1(), dir.y1() );
|
|
|
|
if ( !g.isMonochrome() )
|
|
g.setStops( qskRevertedGradientStops( gradient.stops() ) );
|
|
}
|
|
}
|
|
|
|
if ( g.stretchMode() == QskGradient::StretchToSize )
|
|
g.stretchTo( rect );
|
|
|
|
return g;
|
|
}
|
|
|
|
static inline bool qskMaybeSpreading( const QskGradient& gradient )
|
|
{
|
|
if ( gradient.stretchMode() == QskGradient::StretchToSize )
|
|
return !gradient.linearDirection().contains( QRectF( 0, 0, 1, 1 ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QskBoxRenderer::isGradientSupported( const QskGradient& gradient )
|
|
{
|
|
if ( !gradient.isVisible() || gradient.isMonochrome() )
|
|
return true;
|
|
|
|
switch( gradient.type() )
|
|
{
|
|
case QskGradient::Stops:
|
|
{
|
|
// will be rendered as vertical linear gradient
|
|
return true;
|
|
}
|
|
case QskGradient::Linear:
|
|
{
|
|
if ( ( gradient.spreadMode() != QskGradient::PadSpread )
|
|
&& qskMaybeSpreading( gradient ) )
|
|
{
|
|
// shouldn't be hard to implement the other spreadModes TODO ...
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
{
|
|
/*
|
|
At least Conical gradients could be implemented easily TODO..
|
|
|
|
For the moment Radial/Conical gradients have to be done
|
|
with QskGradientMaterial
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void QskBoxRenderer::renderBorderGeometry(
|
|
const QRectF& rect, const QskBoxShapeMetrics& shape,
|
|
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
|
|
{
|
|
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
|
geometry.markVertexDataDirty();
|
|
|
|
const QskBoxMetrics metrics( rect, shape, border );
|
|
const QskBoxBasicStroker stroker( metrics );
|
|
|
|
const auto lines = qskAllocateLines( geometry, stroker.borderCount() );
|
|
if ( lines )
|
|
stroker.setBorderLines( lines );
|
|
}
|
|
|
|
void QskBoxRenderer::renderFillGeometry(
|
|
const QRectF& rect, const QskBoxShapeMetrics& shape, QSGGeometry& geometry )
|
|
{
|
|
renderFillGeometry( rect, shape, QskBoxBorderMetrics(), geometry );
|
|
}
|
|
|
|
void QskBoxRenderer::renderFillGeometry(
|
|
const QRectF& rect, const QskBoxShapeMetrics& shape,
|
|
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
|
|
{
|
|
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
|
geometry.markVertexDataDirty();
|
|
|
|
const QskBoxMetrics metrics( rect, shape, border );
|
|
QskBoxBasicStroker stroker( metrics );
|
|
|
|
if ( auto lines = qskAllocateLines( geometry, stroker.fillCount() ) )
|
|
stroker.setFillLines( lines );
|
|
}
|
|
|
|
void QskBoxRenderer::renderBox( const QRectF& rect,
|
|
const QskBoxShapeMetrics& shape, const QskGradient& gradient,
|
|
QSGGeometry& geometry )
|
|
{
|
|
renderBox( rect, shape, QskBoxBorderMetrics(),
|
|
QskBoxBorderColors(), gradient, geometry );
|
|
}
|
|
|
|
void QskBoxRenderer::renderBox( const QRectF& rect,
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border,
|
|
const QskBoxBorderColors& borderColors, const QskGradient& gradient,
|
|
QSGGeometry& geometry )
|
|
{
|
|
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
|
geometry.markVertexDataDirty();
|
|
|
|
const QskBoxMetrics metrics( rect, shape, border );
|
|
const auto effectiveGradient = qskEffectiveGradient( metrics.innerRect, gradient );
|
|
|
|
if ( metrics.innerRect.isEmpty() ||
|
|
QskBoxRenderer::ColorMap::isGradientSupported( effectiveGradient, metrics.innerRect ) )
|
|
{
|
|
/*
|
|
The gradient can be translated to a QskBoxRenderer::ColorMap and we can do all
|
|
coloring by adding a color info to points of the contour lines.
|
|
The orientation of contour lines does not depend on the direction
|
|
of the gradient vector.
|
|
|
|
This allows using simpler and faster algos.
|
|
*/
|
|
|
|
const QskBoxBasicStroker stroker( metrics, borderColors, effectiveGradient );
|
|
|
|
const int fillCount = stroker.fillCount();
|
|
const int borderCount = stroker.borderCount();
|
|
|
|
auto lines = qskAllocateColoredLines( geometry, borderCount + fillCount );
|
|
|
|
auto fillLines = fillCount ? lines : nullptr;
|
|
auto borderLines = borderCount ? lines + fillCount : nullptr;
|
|
|
|
stroker.setBoxLines( borderLines, fillLines );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
We need to create gradient and contour lines in the correct order
|
|
perpendicular to the gradient vector.
|
|
*/
|
|
const QskBoxBasicStroker borderStroker( metrics, borderColors );
|
|
QskBoxGradientStroker fillStroker( metrics, effectiveGradient );
|
|
|
|
const int fillCount = fillStroker.lineCount();
|
|
const int borderCount = borderStroker.borderCount();
|
|
const int extraLine = ( fillCount && borderCount ) ? 1 : 0;
|
|
|
|
auto lines = qskAllocateColoredLines(
|
|
geometry, fillCount + borderCount + extraLine );
|
|
|
|
if ( fillCount )
|
|
fillStroker.setLines( fillCount, lines );
|
|
|
|
if ( borderCount )
|
|
borderStroker.setBorderLines( lines + fillCount + extraLine );
|
|
|
|
if ( extraLine )
|
|
{
|
|
// dummy line to connect filling and border
|
|
const auto l = lines + fillCount;
|
|
l[0].p1 = l[-1].p2;
|
|
l[0].p2 = l[+1].p1;
|
|
}
|
|
}
|
|
}
|