aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Nichols <andy.nichols@digia.com>2014-08-21 18:53:09 +0200
committerLars Knoll <lars.knoll@digia.com>2014-09-01 13:54:19 +0300
commit47ad428befff796faf2f3bc7bff94f06fad9c2e1 (patch)
tree6e15dddb234bd6d6c320d3c2b4ae8969b7bccbec
parent13f28b3c7abda7faac661fdc2500562ab81aa86d (diff)
Correct painting of RectangleNode
The previous behavior was very wrong compared to how QtQuick normally paints Rectangle elements. The border.width property does not effect the geometry of the Rectangle, where as QPainter::drawRect draws a border 0.5 the border.width around the edge of the rectangle. So now there is special logic to draw the each case correctly using fillRect as much as possible. Change-Id: I4ea2be2226ff3b1a11dc5000522df503c6f43227 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--softwarecontext/rectanglenode.cpp152
-rw-r--r--softwarecontext/rectanglenode.h4
2 files changed, 143 insertions, 13 deletions
diff --git a/softwarecontext/rectanglenode.cpp b/softwarecontext/rectanglenode.cpp
index 8e3548cf23..054ffd3ecf 100644
--- a/softwarecontext/rectanglenode.cpp
+++ b/softwarecontext/rectanglenode.cpp
@@ -18,10 +18,15 @@
**
****************************************************************************/
#include "rectanglenode.h"
+#include <qmath.h>
+
+#include <QtGui/QPainter>
+
RectangleNode::RectangleNode()
: m_penWidth(0)
, m_radius(0)
+ , m_cornerPixmapIsDirty(true)
{
setMaterial((QSGMaterial*)1);
setGeometry((QSGGeometry*)1);
@@ -29,32 +34,47 @@ RectangleNode::RectangleNode()
void RectangleNode::setRect(const QRectF &rect)
{
- m_rect = rect;
+ if (m_rect != rect) {
+ m_rect = rect;
+ }
}
void RectangleNode::setColor(const QColor &color)
{
- m_color = color;
+ if (m_color != color) {
+ m_color = color;
+ m_cornerPixmapIsDirty = true;
+ }
}
void RectangleNode::setPenColor(const QColor &color)
{
- m_penColor = color;
+ if (m_penColor != color) {
+ m_penColor = color;
+ m_cornerPixmapIsDirty = true;
+ }
}
void RectangleNode::setPenWidth(qreal width)
{
- m_penWidth = width;
+ if (m_penWidth != width) {
+ m_penWidth = width;
+ m_cornerPixmapIsDirty = true;
+ }
}
void RectangleNode::setGradientStops(const QGradientStops &stops)
{
m_stops = stops;
+ m_cornerPixmapIsDirty = true;
}
void RectangleNode::setRadius(qreal radius)
{
- m_radius = radius;
+ if (m_radius != radius) {
+ m_radius = radius;
+ m_cornerPixmapIsDirty = true;
+ }
}
void RectangleNode::setAligned(bool /*aligned*/)
@@ -78,16 +98,122 @@ void RectangleNode::update()
} else {
m_brush = QBrush(m_color);
}
+
+ if (m_cornerPixmapIsDirty) {
+ //Generate new corner Pixmap
+ int radius = qRound(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5f, m_radius));
+
+ m_cornerPixmap = QPixmap(radius * 2, radius * 2);
+ m_cornerPixmap.fill(Qt::transparent);
+
+ if (radius > 0) {
+ QPainter cornerPainter(&m_cornerPixmap);
+ cornerPainter.setRenderHint(QPainter::Antialiasing);
+ cornerPainter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ //Paint outer cicle
+ if (m_penWidth > 0) {
+ cornerPainter.setPen(Qt::NoPen);
+ cornerPainter.setBrush(m_penColor);
+ cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius);
+ }
+
+ //Paint inner circle
+ if (radius > m_penWidth) {
+ cornerPainter.setPen(Qt::NoPen);
+ if (m_stops.isEmpty())
+ cornerPainter.setBrush(m_brush);
+ else
+ cornerPainter.setBrush(Qt::transparent);
+
+ QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth);
+ QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins);
+ cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius);
+ }
+ cornerPainter.end();
+ }
+ m_cornerPixmapIsDirty = false;
+ }
}
void RectangleNode::paint(QPainter *painter)
{
- painter->setPen(m_pen);
- painter->setBrush(m_brush);
- if (m_radius)
- painter->drawRoundedRect(m_rect, m_radius, m_radius);
- else if (m_pen.style() == Qt::NoPen && m_stops.isEmpty())
- painter->fillRect(m_rect, m_color);
- else
- painter->drawRect(m_rect);
+ //Radius should never exceeds half of the width or half of the height
+ int radius = qRound(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius));
+
+ QPainter::RenderHints previousRenderHints = painter->renderHints();
+ painter->setRenderHint(QPainter::Antialiasing, false);
+
+ if (m_penWidth > 0) {
+ //Borders can not be more than half the height/width of a rect
+ double borderWidth = qMin(m_penWidth, m_rect.width() * 0.5);
+ double borderHeight = qMin(m_penWidth, m_rect.height() * 0.5);
+
+ //Fill 4 border Rects
+ QRectF borderTop(QPointF(m_rect.x() + radius, m_rect.y()),
+ QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + borderHeight));
+ painter->fillRect(borderTop, m_penColor);
+ QRectF borderBottom(QPointF(m_rect.x() + radius, m_rect.y() + m_rect.height() - borderHeight),
+ QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + m_rect.height()));
+ painter->fillRect(borderBottom, m_penColor);
+ QRectF borderLeft(QPointF(m_rect.x(), m_rect.y() + radius),
+ QPointF(m_rect.x() + borderWidth, m_rect.y() + m_rect.height() - radius));
+ painter->fillRect(borderLeft, m_penColor);
+ QRectF borderRight(QPointF(m_rect.x() + m_rect.width() - borderWidth, m_rect.y() + radius),
+ QPointF(m_rect.x() + m_rect.width(), m_rect.y() + m_rect.height() - radius));
+ painter->fillRect(borderRight, m_penColor);
+ }
+
+ if (radius > 0) {
+ //blit 4 corners to border
+ QRectF topLeftCorner(QPointF(m_rect.x(), m_rect.y()),
+ QPointF(m_rect.x() + radius, m_rect.y() + radius));
+ painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, radius, radius));
+ QRectF topRightCorner(QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y()),
+ QPointF(m_rect.x() + m_rect.width(), m_rect.y() + radius));
+ painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(radius, 0, radius, radius));
+ QRectF bottomLeftCorner(QPointF(m_rect.x(), m_rect.y() + m_rect.height() - radius),
+ QPointF(m_rect.x() + radius, m_rect.y() + m_rect.height()));
+ painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, radius, radius, radius));
+ QRectF bottomRightCorner(QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + m_rect.height() - radius),
+ QPointF(m_rect.x() + m_rect.width(), m_rect.y() + m_rect.height()));
+ painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(radius, radius, radius, radius));
+
+ }
+
+ QRectF brushRect = m_rect.marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth));
+ if (brushRect.width() < 0)
+ brushRect.setWidth(0);
+ if (brushRect.height() < 0)
+ brushRect.setHeight(0);
+ double innerRectRadius = qMax(0.0, radius - m_penWidth);
+
+ //If not completely transparent or has a gradient
+ if (m_color.alpha() > 0 || !m_stops.empty()) {
+ if (innerRectRadius > 0) {
+ //Rounded Rect
+ if (m_stops.empty()) {
+ //Rounded Rects without gradient need 3 blits
+ QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()),
+ QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height()));
+ painter->fillRect(centerRect, m_color);
+ QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius),
+ QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius));
+ painter->fillRect(leftRect, m_color);
+ QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius),
+ QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius));
+ painter->fillRect(rightRect, m_color);
+ } else {
+ //Rounded Rect with gradient (slow)
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_brush);
+ painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius);
+ }
+ } else {
+ //non-rounded rects only need 1 blit
+ painter->fillRect(brushRect, m_brush);
+ }
+ }
+
+ painter->setRenderHints(previousRenderHints);
}
diff --git a/softwarecontext/rectanglenode.h b/softwarecontext/rectanglenode.h
index df906d718a..74c0971c82 100644
--- a/softwarecontext/rectanglenode.h
+++ b/softwarecontext/rectanglenode.h
@@ -24,6 +24,7 @@
#include <QPen>
#include <QBrush>
+#include <QPixmap>
class RectangleNode : public QSGRectangleNode
{
@@ -52,6 +53,9 @@ private:
double m_radius;
QPen m_pen;
QBrush m_brush;
+
+ bool m_cornerPixmapIsDirty;
+ QPixmap m_cornerPixmap;
};
#endif // RECTANGLENODE_H