/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtGui module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qpaintengineex_p.h" | |
#include "qpainter_p.h" | |
#include "qstroker_p.h" | |
#include "qbezier_p.h" | |
#include <private/qpainterpath_p.h> | |
#include <qvarlengtharray.h> | |
#include <qdebug.h> | |
QT_BEGIN_NAMESPACE | |
/******************************************************************************* | |
* | |
* class QVectorPath | |
* | |
*/ | |
QVectorPath::~QVectorPath() | |
{ | |
if (m_hints & ShouldUseCacheHint) { | |
CacheEntry *e = m_cache; | |
while (e) { | |
if (e->data) | |
e->cleanup(e->engine, e->data); | |
CacheEntry *n = e->next; | |
delete e; | |
e = n; | |
} | |
} | |
} | |
QRectF QVectorPath::controlPointRect() const | |
{ | |
if (m_hints & ControlPointRect) | |
return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2)); | |
if (m_count == 0) { | |
m_cp_rect.x1 = m_cp_rect.x2 = m_cp_rect.y1 = m_cp_rect.y2 = 0; | |
m_hints |= ControlPointRect; | |
return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2)); | |
} | |
Q_ASSERT(m_points && m_count > 0); | |
const qreal *pts = m_points; | |
m_cp_rect.x1 = m_cp_rect.x2 = *pts; | |
++pts; | |
m_cp_rect.y1 = m_cp_rect.y2 = *pts; | |
++pts; | |
const qreal *epts = m_points + (m_count << 1); | |
while (pts < epts) { | |
qreal x = *pts; | |
if (x < m_cp_rect.x1) m_cp_rect.x1 = x; | |
else if (x > m_cp_rect.x2) m_cp_rect.x2 = x; | |
++pts; | |
qreal y = *pts; | |
if (y < m_cp_rect.y1) m_cp_rect.y1 = y; | |
else if (y > m_cp_rect.y2) m_cp_rect.y2 = y; | |
++pts; | |
} | |
m_hints |= ControlPointRect; | |
return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2)); | |
} | |
QVectorPath::CacheEntry *QVectorPath::addCacheData(QPaintEngineEx *engine, void *data, | |
qvectorpath_cache_cleanup cleanup) const{ | |
Q_ASSERT(!lookupCacheData(engine)); | |
if ((m_hints & IsCachedHint) == 0) { | |
m_cache = 0; | |
m_hints |= IsCachedHint; | |
} | |
CacheEntry *e = new CacheEntry; | |
e->engine = engine; | |
e->data = data; | |
e->cleanup = cleanup; | |
e->next = m_cache; | |
m_cache = e; | |
return m_cache; | |
} | |
const QVectorPath &qtVectorPathForPath(const QPainterPath &path) | |
{ | |
Q_ASSERT(path.d_func()); | |
return path.d_func()->vectorPath(); | |
} | |
#ifndef QT_NO_DEBUG_STREAM | |
QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path) | |
{ | |
QRectF rf = path.controlPointRect(); | |
s << "QVectorPath(size:" << path.elementCount() | |
<< " hints:" << hex << path.hints() | |
<< rf << ')'; | |
return s; | |
} | |
#endif | |
/******************************************************************************* | |
* | |
* class QPaintEngineExPrivate: | |
* | |
*/ | |
struct StrokeHandler { | |
StrokeHandler(int reserve) : pts(reserve), types(reserve) {} | |
QDataBuffer<qreal> pts; | |
QDataBuffer<QPainterPath::ElementType> types; | |
}; | |
QPaintEngineExPrivate::QPaintEngineExPrivate() | |
: dasher(&stroker), | |
strokeHandler(0), | |
activeStroker(0), | |
strokerPen(Qt::NoPen) | |
{ | |
} | |
QPaintEngineExPrivate::~QPaintEngineExPrivate() | |
{ | |
delete strokeHandler; | |
} | |
void QPaintEngineExPrivate::replayClipOperations() | |
{ | |
Q_Q(QPaintEngineEx); | |
QPainter *p = q->painter(); | |
if (!p || !p->d_ptr) | |
return; | |
QList<QPainterClipInfo> clipInfo = p->d_ptr->state->clipInfo; | |
QTransform transform = q->state()->matrix; | |
for (int i = 0; i < clipInfo.size(); ++i) { | |
const QPainterClipInfo &info = clipInfo.at(i); | |
if (info.matrix != q->state()->matrix) { | |
q->state()->matrix = info.matrix; | |
q->transformChanged(); | |
} | |
switch (info.clipType) { | |
case QPainterClipInfo::RegionClip: | |
q->clip(info.region, info.operation); | |
break; | |
case QPainterClipInfo::PathClip: | |
q->clip(info.path, info.operation); | |
break; | |
case QPainterClipInfo::RectClip: | |
q->clip(info.rect, info.operation); | |
break; | |
case QPainterClipInfo::RectFClip: { | |
qreal right = info.rectf.x() + info.rectf.width(); | |
qreal bottom = info.rectf.y() + info.rectf.height(); | |
qreal pts[] = { info.rectf.x(), info.rectf.y(), | |
right, info.rectf.y(), | |
right, bottom, | |
info.rectf.x(), bottom }; | |
QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint); | |
q->clip(vp, info.operation); | |
break; | |
} | |
} | |
} | |
if (transform != q->state()->matrix) { | |
q->state()->matrix = transform; | |
q->transformChanged(); | |
} | |
} | |
bool QPaintEngineExPrivate::hasClipOperations() const | |
{ | |
Q_Q(const QPaintEngineEx); | |
QPainter *p = q->painter(); | |
if (!p || !p->d_ptr) | |
return false; | |
QList<QPainterClipInfo> clipInfo = p->d_ptr->state->clipInfo; | |
return !clipInfo.isEmpty(); | |
} | |
/******************************************************************************* | |
* | |
* class QPaintEngineEx: | |
* | |
*/ | |
static QPainterPath::ElementType qpaintengineex_ellipse_types[] = { | |
QPainterPath::MoveToElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement | |
}; | |
static QPainterPath::ElementType qpaintengineex_line_types_16[] = { | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, | |
QPainterPath::MoveToElement, QPainterPath::LineToElement | |
}; | |
static QPainterPath::ElementType qpaintengineex_rect4_types_32[] = { | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 1 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 2 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 3 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 4 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 5 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 6 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 7 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 8 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 9 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 10 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 11 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 12 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 13 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 14 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 15 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 16 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 17 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 18 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 19 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 20 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 21 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 22 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 23 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 24 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 25 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 26 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 27 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 28 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 29 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 30 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 31 | |
QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 32 | |
}; | |
static QPainterPath::ElementType qpaintengineex_roundedrect_types[] = { | |
QPainterPath::MoveToElement, | |
QPainterPath::LineToElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::LineToElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::LineToElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::LineToElement, | |
QPainterPath::CurveToElement, | |
QPainterPath::CurveToDataElement, | |
QPainterPath::CurveToDataElement | |
}; | |
static void qpaintengineex_moveTo(qreal x, qreal y, void *data) { | |
((StrokeHandler *) data)->pts.add(x); | |
((StrokeHandler *) data)->pts.add(y); | |
((StrokeHandler *) data)->types.add(QPainterPath::MoveToElement); | |
} | |
static void qpaintengineex_lineTo(qreal x, qreal y, void *data) { | |
((StrokeHandler *) data)->pts.add(x); | |
((StrokeHandler *) data)->pts.add(y); | |
((StrokeHandler *) data)->types.add(QPainterPath::LineToElement); | |
} | |
static void qpaintengineex_cubicTo(qreal c1x, qreal c1y, qreal c2x, qreal c2y, qreal ex, qreal ey, void *data) { | |
((StrokeHandler *) data)->pts.add(c1x); | |
((StrokeHandler *) data)->pts.add(c1y); | |
((StrokeHandler *) data)->types.add(QPainterPath::CurveToElement); | |
((StrokeHandler *) data)->pts.add(c2x); | |
((StrokeHandler *) data)->pts.add(c2y); | |
((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement); | |
((StrokeHandler *) data)->pts.add(ex); | |
((StrokeHandler *) data)->pts.add(ey); | |
((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement); | |
} | |
QPaintEngineEx::QPaintEngineEx() | |
: QPaintEngine(*new QPaintEngineExPrivate, AllFeatures) | |
{ | |
extended = true; | |
} | |
QPaintEngineEx::QPaintEngineEx(QPaintEngineExPrivate &data) | |
: QPaintEngine(data, AllFeatures) | |
{ | |
extended = true; | |
} | |
QPainterState *QPaintEngineEx::createState(QPainterState *orig) const | |
{ | |
if (!orig) | |
return new QPainterState; | |
return new QPainterState(orig); | |
} | |
Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp | |
void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) | |
{ | |
#ifdef QT_DEBUG_DRAW | |
qDebug() << "QPaintEngineEx::stroke()" << pen; | |
#endif | |
Q_D(QPaintEngineEx); | |
if (path.isEmpty()) | |
return; | |
if (!d->strokeHandler) { | |
d->strokeHandler = new StrokeHandler(path.elementCount()+4); | |
d->stroker.setMoveToHook(qpaintengineex_moveTo); | |
d->stroker.setLineToHook(qpaintengineex_lineTo); | |
d->stroker.setCubicToHook(qpaintengineex_cubicTo); | |
} | |
if (!qpen_fast_equals(pen, d->strokerPen)) { | |
d->strokerPen = pen; | |
d->stroker.setJoinStyle(pen.joinStyle()); | |
d->stroker.setCapStyle(pen.capStyle()); | |
d->stroker.setMiterLimit(pen.miterLimit()); | |
qreal penWidth = pen.widthF(); | |
if (penWidth == 0) | |
d->stroker.setStrokeWidth(1); | |
else | |
d->stroker.setStrokeWidth(penWidth); | |
Qt::PenStyle style = pen.style(); | |
if (style == Qt::SolidLine) { | |
d->activeStroker = &d->stroker; | |
} else if (style == Qt::NoPen) { | |
d->activeStroker = 0; | |
} else { | |
d->dasher.setDashPattern(pen.dashPattern()); | |
d->dasher.setDashOffset(pen.dashOffset()); | |
d->activeStroker = &d->dasher; | |
} | |
} | |
if (!d->activeStroker) { | |
return; | |
} | |
if (pen.style() > Qt::SolidLine) { | |
if (pen.isCosmetic()) { | |
d->activeStroker->setClipRect(d->exDeviceRect); | |
} else { | |
QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect)); | |
d->activeStroker->setClipRect(clipRect); | |
} | |
} | |
const QPainterPath::ElementType *types = path.elements(); | |
const qreal *points = path.points(); | |
int pointCount = path.elementCount(); | |
const qreal *lastPoint = points + (pointCount<<1); | |
d->strokeHandler->types.reset(); | |
d->strokeHandler->pts.reset(); | |
// Some engines might decide to optimize for the non-shape hint later on... | |
uint flags = QVectorPath::WindingFill; | |
if (path.elementCount() > 2) | |
flags |= QVectorPath::NonConvexShapeMask; | |
if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin) | |
flags |= QVectorPath::CurvedShapeMask; | |
// ### Perspective Xforms are currently not supported... | |
if (!pen.isCosmetic()) { | |
// We include cosmetic pens in this case to avoid having to | |
// change the current transform. Normal transformed, | |
// non-cosmetic pens will be transformed as part of fill | |
// later, so they are also covered here.. | |
d->activeStroker->setCurveThresholdFromTransform(state()->matrix); | |
d->activeStroker->begin(d->strokeHandler); | |
if (types) { | |
while (points < lastPoint) { | |
switch (*types) { | |
case QPainterPath::MoveToElement: | |
d->activeStroker->moveTo(points[0], points[1]); | |
points += 2; | |
++types; | |
break; | |
case QPainterPath::LineToElement: | |
d->activeStroker->lineTo(points[0], points[1]); | |
points += 2; | |
++types; | |
break; | |
case QPainterPath::CurveToElement: | |
d->activeStroker->cubicTo(points[0], points[1], | |
points[2], points[3], | |
points[4], points[5]); | |
points += 6; | |
types += 3; | |
flags |= QVectorPath::CurvedShapeMask; | |
break; | |
default: | |
break; | |
} | |
} | |
if (path.hasImplicitClose()) | |
d->activeStroker->lineTo(path.points()[0], path.points()[1]); | |
} else { | |
d->activeStroker->moveTo(points[0], points[1]); | |
points += 2; | |
while (points < lastPoint) { | |
d->activeStroker->lineTo(points[0], points[1]); | |
points += 2; | |
} | |
if (path.hasImplicitClose()) | |
d->activeStroker->lineTo(path.points()[0], path.points()[1]); | |
} | |
d->activeStroker->end(); | |
if (!d->strokeHandler->types.size()) // an empty path... | |
return; | |
QVectorPath strokePath(d->strokeHandler->pts.data(), | |
d->strokeHandler->types.size(), | |
d->strokeHandler->types.data(), | |
flags); | |
fill(strokePath, pen.brush()); | |
} else { | |
// For cosmetic pens we need a bit of trickery... We to process xform the input points | |
if (state()->matrix.type() >= QTransform::TxProject) { | |
QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath()); | |
d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform()); | |
} else { | |
d->activeStroker->setCurveThresholdFromTransform(QTransform()); | |
d->activeStroker->begin(d->strokeHandler); | |
if (types) { | |
while (points < lastPoint) { | |
switch (*types) { | |
case QPainterPath::MoveToElement: { | |
QPointF pt = (*(QPointF *) points) * state()->matrix; | |
d->activeStroker->moveTo(pt.x(), pt.y()); | |
points += 2; | |
++types; | |
break; | |
} | |
case QPainterPath::LineToElement: { | |
QPointF pt = (*(QPointF *) points) * state()->matrix; | |
d->activeStroker->lineTo(pt.x(), pt.y()); | |
points += 2; | |
++types; | |
break; | |
} | |
case QPainterPath::CurveToElement: { | |
QPointF c1 = ((QPointF *) points)[0] * state()->matrix; | |
QPointF c2 = ((QPointF *) points)[1] * state()->matrix; | |
QPointF e = ((QPointF *) points)[2] * state()->matrix; | |
d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); | |
points += 6; | |
types += 3; | |
flags |= QVectorPath::CurvedShapeMask; | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
if (path.hasImplicitClose()) { | |
QPointF pt = * ((QPointF *) path.points()) * state()->matrix; | |
d->activeStroker->lineTo(pt.x(), pt.y()); | |
} | |
} else { | |
QPointF p = ((QPointF *)points)[0] * state()->matrix; | |
d->activeStroker->moveTo(p.x(), p.y()); | |
points += 2; | |
while (points < lastPoint) { | |
QPointF p = ((QPointF *)points)[0] * state()->matrix; | |
d->activeStroker->lineTo(p.x(), p.y()); | |
points += 2; | |
} | |
if (path.hasImplicitClose()) | |
d->activeStroker->lineTo(p.x(), p.y()); | |
} | |
d->activeStroker->end(); | |
} | |
QVectorPath strokePath(d->strokeHandler->pts.data(), | |
d->strokeHandler->types.size(), | |
d->strokeHandler->types.data(), | |
flags); | |
QTransform xform = state()->matrix; | |
state()->matrix = QTransform(); | |
transformChanged(); | |
QBrush brush = pen.brush(); | |
if (qbrush_style(brush) != Qt::SolidPattern) | |
brush.setTransform(brush.transform() * xform); | |
fill(strokePath, brush); | |
state()->matrix = xform; | |
transformChanged(); | |
} | |
} | |
void QPaintEngineEx::draw(const QVectorPath &path) | |
{ | |
const QBrush &brush = state()->brush; | |
if (qbrush_style(brush) != Qt::NoBrush) | |
fill(path, brush); | |
const QPen &pen = state()->pen; | |
if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) | |
stroke(path, pen); | |
} | |
void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op) | |
{ | |
qreal right = r.x() + r.width(); | |
qreal bottom = r.y() + r.height(); | |
qreal pts[] = { qreal(r.x()), qreal(r.y()), | |
right, qreal(r.y()), | |
right, bottom, | |
qreal(r.x()), bottom, | |
qreal(r.x()), qreal(r.y()) }; | |
QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint); | |
clip(vp, op); | |
} | |
void QPaintEngineEx::clip(const QRegion ®ion, Qt::ClipOperation op) | |
{ | |
if (region.rectCount() == 1) | |
clip(region.boundingRect(), op); | |
QVector<QRect> rects = region.rects(); | |
if (rects.size() <= 32) { | |
qreal pts[2*32*4]; | |
int pos = 0; | |
for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) { | |
qreal x1 = i->x(); | |
qreal y1 = i->y(); | |
qreal x2 = i->x() + i->width(); | |
qreal y2 = i->y() + i->height(); | |
pts[pos++] = x1; | |
pts[pos++] = y1; | |
pts[pos++] = x2; | |
pts[pos++] = y1; | |
pts[pos++] = x2; | |
pts[pos++] = y2; | |
pts[pos++] = x1; | |
pts[pos++] = y2; | |
} | |
QVectorPath vp(pts, rects.size() * 4, qpaintengineex_rect4_types_32); | |
clip(vp, op); | |
} else { | |
QVarLengthArray<qreal> pts(rects.size() * 2 * 4); | |
QVarLengthArray<QPainterPath::ElementType> types(rects.size() * 4); | |
int ppos = 0; | |
int tpos = 0; | |
for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) { | |
qreal x1 = i->x(); | |
qreal y1 = i->y(); | |
qreal x2 = i->x() + i->width(); | |
qreal y2 = i->y() + i->height(); | |
pts[ppos++] = x1; | |
pts[ppos++] = y1; | |
pts[ppos++] = x2; | |
pts[ppos++] = y1; | |
pts[ppos++] = x2; | |
pts[ppos++] = y2; | |
pts[ppos++] = x1; | |
pts[ppos++] = y2; | |
types[tpos++] = QPainterPath::MoveToElement; | |
types[tpos++] = QPainterPath::LineToElement; | |
types[tpos++] = QPainterPath::LineToElement; | |
types[tpos++] = QPainterPath::LineToElement; | |
} | |
QVectorPath vp(pts.data(), rects.size() * 4, types.data()); | |
clip(vp, op); | |
} | |
} | |
void QPaintEngineEx::clip(const QPainterPath &path, Qt::ClipOperation op) | |
{ | |
if (path.isEmpty()) { | |
QVectorPath vp(0, 0); | |
clip(vp, op); | |
} else { | |
clip(qtVectorPathForPath(path), op); | |
} | |
} | |
void QPaintEngineEx::fillRect(const QRectF &r, const QBrush &brush) | |
{ | |
qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(), | |
r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() }; | |
QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint); | |
fill(vp, brush); | |
} | |
void QPaintEngineEx::fillRect(const QRectF &r, const QColor &color) | |
{ | |
fillRect(r, QBrush(color)); | |
} | |
void QPaintEngineEx::drawRects(const QRect *rects, int rectCount) | |
{ | |
for (int i=0; i<rectCount; ++i) { | |
const QRect &r = rects[i]; | |
// ### Is there a one off here? | |
qreal right = r.x() + r.width(); | |
qreal bottom = r.y() + r.height(); | |
qreal pts[] = { qreal(r.x()), qreal(r.y()), | |
right, qreal(r.y()), | |
right, bottom, | |
qreal(r.x()), bottom, | |
qreal(r.x()), qreal(r.y()) }; | |
QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint); | |
draw(vp); | |
} | |
} | |
void QPaintEngineEx::drawRects(const QRectF *rects, int rectCount) | |
{ | |
for (int i=0; i<rectCount; ++i) { | |
const QRectF &r = rects[i]; | |
qreal right = r.x() + r.width(); | |
qreal bottom = r.y() + r.height(); | |
qreal pts[] = { r.x(), r.y(), | |
right, r.y(), | |
right, bottom, | |
r.x(), bottom, | |
r.x(), r.y() }; | |
QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint); | |
draw(vp); | |
} | |
} | |
void QPaintEngineEx::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, | |
Qt::SizeMode mode) | |
{ | |
qreal x1 = rect.left(); | |
qreal x2 = rect.right(); | |
qreal y1 = rect.top(); | |
qreal y2 = rect.bottom(); | |
if (mode == Qt::RelativeSize) { | |
xRadius = xRadius * rect.width() / 200.; | |
yRadius = yRadius * rect.height() / 200.; | |
} | |
xRadius = qMin(xRadius, rect.width() / 2); | |
yRadius = qMin(yRadius, rect.height() / 2); | |
qreal pts[] = { | |
x1 + xRadius, y1, // MoveTo | |
x2 - xRadius, y1, // LineTo | |
x2 - (1 - KAPPA) * xRadius, y1, // CurveTo | |
x2, y1 + (1 - KAPPA) * yRadius, | |
x2, y1 + yRadius, | |
x2, y2 - yRadius, // LineTo | |
x2, y2 - (1 - KAPPA) * yRadius, // CurveTo | |
x2 - (1 - KAPPA) * xRadius, y2, | |
x2 - xRadius, y2, | |
x1 + xRadius, y2, // LineTo | |
x1 + (1 - KAPPA) * xRadius, y2, // CurveTo | |
x1, y2 - (1 - KAPPA) * yRadius, | |
x1, y2 - yRadius, | |
x1, y1 + yRadius, // LineTo | |
x1, y1 + (1 - KAPPA) * yRadius, // CurveTo | |
x1 + (1 - KAPPA) * xRadius, y1, | |
x1 + xRadius, y1 | |
}; | |
QVectorPath path(pts, 17, qpaintengineex_roundedrect_types, QVectorPath::RoundedRectHint); | |
draw(path); | |
} | |
void QPaintEngineEx::drawLines(const QLine *lines, int lineCount) | |
{ | |
int elementCount = lineCount << 1; | |
while (elementCount > 0) { | |
int count = qMin(elementCount, 32); | |
qreal pts[64]; | |
int count2 = count<<1; | |
#ifdef Q_WS_MAC | |
for (int i=0; i<count2; i+=2) { | |
pts[i] = ((int *) lines)[i+1]; | |
pts[i+1] = ((int *) lines)[i]; | |
} | |
#else | |
for (int i=0; i<count2; ++i) | |
pts[i] = ((int *) lines)[i]; | |
#endif | |
QVectorPath path(pts, count, qpaintengineex_line_types_16, QVectorPath::LinesHint); | |
stroke(path, state()->pen); | |
elementCount -= 32; | |
lines += 16; | |
} | |
} | |
void QPaintEngineEx::drawLines(const QLineF *lines, int lineCount) | |
{ | |
int elementCount = lineCount << 1; | |
while (elementCount > 0) { | |
int count = qMin(elementCount, 32); | |
QVectorPath path((qreal *) lines, count, qpaintengineex_line_types_16, | |
QVectorPath::LinesHint); | |
stroke(path, state()->pen); | |
elementCount -= 32; | |
lines += 16; | |
} | |
} | |
void QPaintEngineEx::drawEllipse(const QRectF &r) | |
{ | |
qreal pts[26]; // QPointF[13] without constructors... | |
union { | |
qreal *ptr; | |
QPointF *points; | |
} x; | |
x.ptr = pts; | |
int point_count = 0; | |
x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count); | |
QVectorPath vp((qreal *) pts, point_count, qpaintengineex_ellipse_types, QVectorPath::EllipseHint); | |
draw(vp); | |
} | |
void QPaintEngineEx::drawEllipse(const QRect &r) | |
{ | |
drawEllipse(QRectF(r)); | |
} | |
void QPaintEngineEx::drawPath(const QPainterPath &path) | |
{ | |
if (!path.isEmpty()) | |
draw(qtVectorPathForPath(path)); | |
} | |
void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount) | |
{ | |
QPen pen = state()->pen; | |
if (pen.capStyle() == Qt::FlatCap) | |
pen.setCapStyle(Qt::SquareCap); | |
if (pen.brush().isOpaque()) { | |
while (pointCount > 0) { | |
int count = qMin(pointCount, 16); | |
qreal pts[64]; | |
int oset = -1; | |
for (int i=0; i<count; ++i) { | |
pts[++oset] = points[i].x(); | |
pts[++oset] = points[i].y(); | |
pts[++oset] = points[i].x() + 1/63.; | |
pts[++oset] = points[i].y(); | |
} | |
QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint); | |
stroke(path, pen); | |
pointCount -= 16; | |
points += 16; | |
} | |
} else { | |
for (int i=0; i<pointCount; ++i) { | |
qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + 1/63., points[i].y() }; | |
QVectorPath path(pts, 2, 0); | |
stroke(path, pen); | |
} | |
} | |
} | |
void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount) | |
{ | |
QPen pen = state()->pen; | |
if (pen.capStyle() == Qt::FlatCap) | |
pen.setCapStyle(Qt::SquareCap); | |
if (pen.brush().isOpaque()) { | |
while (pointCount > 0) { | |
int count = qMin(pointCount, 16); | |
qreal pts[64]; | |
int oset = -1; | |
for (int i=0; i<count; ++i) { | |
pts[++oset] = points[i].x(); | |
pts[++oset] = points[i].y(); | |
pts[++oset] = points[i].x() + 1/63.; | |
pts[++oset] = points[i].y(); | |
} | |
QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint); | |
stroke(path, pen); | |
pointCount -= 16; | |
points += 16; | |
} | |
} else { | |
for (int i=0; i<pointCount; ++i) { | |
qreal pts[] = { qreal(points[i].x()), qreal(points[i].y()), | |
qreal(points[i].x() +1/63.), qreal(points[i].y()) }; | |
QVectorPath path(pts, 2, 0); | |
stroke(path, pen); | |
} | |
} | |
} | |
void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) | |
{ | |
QVectorPath path((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode)); | |
if (mode == PolylineMode) | |
stroke(path, state()->pen); | |
else | |
draw(path); | |
} | |
void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) | |
{ | |
int count = pointCount<<1; | |
QVarLengthArray<qreal> pts(count); | |
#ifdef Q_WS_MAC | |
for (int i=0; i<count; i+=2) { | |
pts[i] = ((int *) points)[i+1]; | |
pts[i+1] = ((int *) points)[i]; | |
} | |
#else | |
for (int i=0; i<count; ++i) | |
pts[i] = ((int *) points)[i]; | |
#endif | |
QVectorPath path(pts.data(), pointCount, 0, QVectorPath::polygonFlags(mode)); | |
if (mode == PolylineMode) | |
stroke(path, state()->pen); | |
else | |
draw(path); | |
} | |
void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm) | |
{ | |
drawPixmap(QRectF(pos, pm.size()), pm, pm.rect()); | |
} | |
void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image) | |
{ | |
drawImage(QRectF(pos, image.size()), image, image.rect()); | |
} | |
void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) | |
{ | |
QBrush brush(state()->pen.color(), pixmap); | |
QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y()); | |
brush.setTransform(xform); | |
qreal pts[] = { r.x(), r.y(), | |
r.x() + r.width(), r.y(), | |
r.x() + r.width(), r.y() + r.height(), | |
r.x(), r.y() + r.height() }; | |
QVectorPath path(pts, 4, 0, QVectorPath::RectangleHint); | |
fill(path, brush); | |
} | |
void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, | |
const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/) | |
{ | |
if (pixmap.isNull()) | |
return; | |
qreal oldOpacity = state()->opacity; | |
QTransform oldTransform = state()->matrix; | |
for (int i = 0; i < fragmentCount; ++i) { | |
QTransform transform = oldTransform; | |
transform.translate(fragments[i].x, fragments[i].y); | |
transform.rotate(fragments[i].rotation); | |
state()->opacity = oldOpacity * fragments[i].opacity; | |
state()->matrix = transform; | |
opacityChanged(); | |
transformChanged(); | |
qreal w = fragments[i].scaleX * fragments[i].width; | |
qreal h = fragments[i].scaleY * fragments[i].height; | |
QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop, | |
fragments[i].width, fragments[i].height); | |
drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, sourceRect); | |
} | |
state()->opacity = oldOpacity; | |
state()->matrix = oldTransform; | |
opacityChanged(); | |
transformChanged(); | |
} | |
void QPaintEngineEx::setState(QPainterState *s) | |
{ | |
QPaintEngine::state = s; | |
} | |
void QPaintEngineEx::updateState(const QPaintEngineState &) | |
{ | |
// do nothing... | |
} | |
QT_END_NAMESPACE |