/src/gui/painting/qpainter.cpp
https://bitbucket.org/manctl/qt · C++ · 9658 lines · 4457 code · 1149 blank · 4052 comment · 1337 complexity · e90d1e4df250518317650084385ab757 MD5 · raw file
Large files are truncated click here to view the full file
- /****************************************************************************
- **
- ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of the QtGui module of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:LGPL$
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and Digia. For licensing terms and
- ** conditions see http://qt.digia.com/licensing. For further information
- ** use the contact form at http://qt.digia.com/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, 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, Digia gives you certain additional
- ** rights. These rights are described in the Digia 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.
- **
- **
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
- // QtCore
- #include <qdebug.h>
- #include <qmath.h>
- #include <qmutex.h>
- // QtGui
- #include "qbitmap.h"
- #include "qimage.h"
- #include "qpaintdevice.h"
- #include "qpaintengine.h"
- #include "qpainter.h"
- #include "qpainter_p.h"
- #include "qpainterpath.h"
- #include "qpicture.h"
- #include "qpixmapcache.h"
- #include "qpolygon.h"
- #include "qtextlayout.h"
- #include "qwidget.h"
- #include "qapplication.h"
- #include "qstyle.h"
- #include "qthread.h"
- #include "qvarlengtharray.h"
- #include "qstatictext.h"
- #include "qglyphrun.h"
- #include <private/qfontengine_p.h>
- #include <private/qpaintengine_p.h>
- #include <private/qemulationpaintengine_p.h>
- #include <private/qpainterpath_p.h>
- #include <private/qtextengine_p.h>
- #include <private/qwidget_p.h>
- #include <private/qpaintengine_raster_p.h>
- #include <private/qmath_p.h>
- #include <private/qstatictext_p.h>
- #include <private/qglyphrun_p.h>
- #include <private/qstylehelper_p.h>
- #include <private/qrawfont_p.h>
- QT_BEGIN_NAMESPACE
- #define QGradient_StretchToDevice 0x10000000
- #define QPaintEngine_OpaqueBackground 0x40000000
- // #define QT_DEBUG_DRAW
- #ifdef QT_DEBUG_DRAW
- bool qt_show_painter_debug_output = true;
- #endif
- extern QPixmap qt_pixmapForBrush(int style, bool invert);
- void qt_format_text(const QFont &font,
- const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect,
- int tabstops, int* tabarray, int tabarraylen,
- QPainter *painter);
- static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe,
- QTextCharFormat::UnderlineStyle underlineStyle,
- QTextItem::RenderFlags flags, qreal width,
- const QTextCharFormat &charFormat);
- // Helper function to calculate left most position, width and flags for decoration drawing
- Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray,
- const QFixedPoint *positions, int glyphCount,
- QFontEngine *fontEngine, const QFont &font,
- const QTextCharFormat &charFormat);
- static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush)
- {
- switch (brush.style()) {
- case Qt::LinearGradientPattern:
- case Qt::RadialGradientPattern:
- case Qt::ConicalGradientPattern:
- return brush.gradient()->coordinateMode();
- default:
- ;
- }
- return QGradient::LogicalMode;
- }
- /* Returns true if the gradient requires stretch to device...*/
- static inline bool check_gradient(const QBrush &brush)
- {
- return coordinateMode(brush) == QGradient::StretchToDeviceMode;
- }
- extern bool qHasPixmapTexture(const QBrush &);
- static inline bool is_brush_transparent(const QBrush &brush) {
- Qt::BrushStyle s = brush.style();
- bool brushBitmap = qHasPixmapTexture(brush)
- ? brush.texture().isQBitmap()
- : (brush.textureImage().depth() == 1);
- return ((s >= Qt::Dense1Pattern && s <= Qt::DiagCrossPattern)
- || (s == Qt::TexturePattern && brushBitmap));
- }
- static inline bool is_pen_transparent(const QPen &pen) {
- return pen.style() > Qt::SolidLine || is_brush_transparent(pen.brush());
- }
- /* Discards the emulation flags that are not relevant for line drawing
- and returns the result
- */
- static inline uint line_emulation(uint emulation)
- {
- return emulation & (QPaintEngine::PrimitiveTransform
- | QPaintEngine::AlphaBlend
- | QPaintEngine::Antialiasing
- | QPaintEngine::BrushStroke
- | QPaintEngine::ConstantOpacity
- | QGradient_StretchToDevice
- | QPaintEngine::ObjectBoundingModeGradients
- | QPaintEngine_OpaqueBackground);
- }
- #ifndef QT_NO_DEBUG
- static bool qt_painter_thread_test(int devType, const char *what, bool extraCondition = false)
- {
- switch (devType) {
- case QInternal::Image:
- case QInternal::Printer:
- case QInternal::Picture:
- // can be drawn onto these devices safely from any thread
- #ifndef Q_WS_WIN
- if (extraCondition)
- #endif
- break;
- default:
- #ifdef Q_WS_X11
- if (QApplication::testAttribute(Qt::AA_X11InitThreads))
- return true;
- #endif
- if (!extraCondition && QThread::currentThread() != qApp->thread()) {
- qWarning("QPainter: It is not safe to use %s outside the GUI thread", what);
- return false;
- }
- break;
- }
- return true;
- }
- #endif
- void QPainterPrivate::checkEmulation()
- {
- Q_ASSERT(extended);
- if (extended->flags() & QPaintEngineEx::DoNotEmulate)
- return;
- bool doEmulation = false;
- if (state->bgMode == Qt::OpaqueMode)
- doEmulation = true;
- const QGradient *bg = state->brush.gradient();
- if (bg && bg->coordinateMode() > QGradient::LogicalMode)
- doEmulation = true;
- const QGradient *pg = qpen_brush(state->pen).gradient();
- if (pg && pg->coordinateMode() > QGradient::LogicalMode)
- doEmulation = true;
- if (doEmulation) {
- if (extended != emulationEngine) {
- if (!emulationEngine)
- emulationEngine = new QEmulationPaintEngine(extended);
- extended = emulationEngine;
- extended->setState(state);
- }
- } else if (emulationEngine == extended) {
- extended = emulationEngine->real_engine;
- }
- }
- QPainterPrivate::~QPainterPrivate()
- {
- delete emulationEngine;
- for (int i=0; i<states.size(); ++i)
- delete states.at(i);
- if (dummyState)
- delete dummyState;
- }
- QTransform QPainterPrivate::viewTransform() const
- {
- if (state->VxF) {
- qreal scaleW = qreal(state->vw)/qreal(state->ww);
- qreal scaleH = qreal(state->vh)/qreal(state->wh);
- return QTransform(scaleW, 0, 0, scaleH,
- state->vx - state->wx*scaleW, state->vy - state->wy*scaleH);
- }
- return QTransform();
- }
- /*
- \internal
- Returns true if using a shared painter; otherwise false.
- */
- bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev)
- {
- Q_ASSERT(q);
- Q_ASSERT(pdev);
- if (pdev->devType() != QInternal::Widget)
- return false;
- QWidget *widget = static_cast<QWidget *>(pdev);
- Q_ASSERT(widget);
- // Someone either called QPainter::setRedirected in the widget's paint event
- // right before this painter was created (or begin was called) or
- // sent a paint event directly to the widget.
- if (!widget->d_func()->redirectDev)
- return false;
- QPainter *sp = widget->d_func()->sharedPainter();
- if (!sp || !sp->isActive())
- return false;
- if (sp->paintEngine()->paintDevice() != widget->d_func()->redirectDev)
- return false;
- // Check if we're attempting to paint outside a paint event.
- if (!sp->d_ptr->engine->hasFeature(QPaintEngine::PaintOutsidePaintEvent)
- && !widget->testAttribute(Qt::WA_PaintOutsidePaintEvent)
- && !widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
- qWarning("QPainter::begin: Widget painting can only begin as a result of a paintEvent");
- return false;
- }
- // Save the current state of the shared painter and assign
- // the current d_ptr to the shared painter's d_ptr.
- sp->save();
- if (!sp->d_ptr->d_ptrs) {
- // Allocate space for 4 d-pointers (enough for up to 4 sub-sequent
- // redirections within the same paintEvent(), which should be enough
- // in 99% of all cases). E.g: A renders B which renders C which renders D.
- sp->d_ptr->d_ptrs_size = 4;
- sp->d_ptr->d_ptrs = (QPainterPrivate **)malloc(4 * sizeof(QPainterPrivate *));
- Q_CHECK_PTR(sp->d_ptr->d_ptrs);
- } else if (sp->d_ptr->refcount - 1 == sp->d_ptr->d_ptrs_size) {
- // However, to support corner cases we grow the array dynamically if needed.
- sp->d_ptr->d_ptrs_size <<= 1;
- const int newSize = sp->d_ptr->d_ptrs_size * sizeof(QPainterPrivate *);
- sp->d_ptr->d_ptrs = q_check_ptr((QPainterPrivate **)realloc(sp->d_ptr->d_ptrs, newSize));
- }
- sp->d_ptr->d_ptrs[++sp->d_ptr->refcount - 2] = q->d_ptr.data();
- q->d_ptr.take();
- q->d_ptr.reset(sp->d_ptr.data());
- Q_ASSERT(q->d_ptr->state);
- // Now initialize the painter with correct widget properties.
- q->initFrom(widget);
- QPoint offset;
- widget->d_func()->redirected(&offset);
- offset += q->d_ptr->engine->coordinateOffset();
- // Update system rect.
- q->d_ptr->state->ww = q->d_ptr->state->vw = widget->width();
- q->d_ptr->state->wh = q->d_ptr->state->vh = widget->height();
- // Update matrix.
- if (q->d_ptr->state->WxF) {
- q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix;
- q->d_ptr->state->redirectionMatrix.translate(-offset.x(), -offset.y());
- q->d_ptr->state->worldMatrix = QTransform();
- q->d_ptr->state->WxF = false;
- } else {
- q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(-offset.x(), -offset.y());
- }
- q->d_ptr->updateMatrix();
- QPaintEnginePrivate *enginePrivate = q->d_ptr->engine->d_func();
- if (enginePrivate->currentClipWidget == widget) {
- enginePrivate->systemStateChanged();
- return true;
- }
- // Update system transform and clip.
- enginePrivate->currentClipWidget = widget;
- enginePrivate->setSystemTransform(q->d_ptr->state->matrix);
- return true;
- }
- void QPainterPrivate::detachPainterPrivate(QPainter *q)
- {
- Q_ASSERT(refcount > 1);
- Q_ASSERT(q);
- QPainterPrivate *original = d_ptrs[--refcount - 1];
- if (inDestructor) {
- inDestructor = false;
- if (original)
- original->inDestructor = true;
- } else if (!original) {
- original = new QPainterPrivate(q);
- }
- d_ptrs[refcount - 1] = 0;
- q->restore();
- q->d_ptr.take();
- q->d_ptr.reset(original);
- if (emulationEngine) {
- extended = emulationEngine->real_engine;
- delete emulationEngine;
- emulationEngine = 0;
- }
- }
- void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperation op)
- {
- #ifdef QT_DEBUG_DRAW
- if (qt_show_painter_debug_output) {
- printf("QPainter::drawHelper\n");
- }
- #endif
- if (originalPath.isEmpty())
- return;
- QPaintEngine::PaintEngineFeatures gradientStretch =
- QPaintEngine::PaintEngineFeatures(QGradient_StretchToDevice
- | QPaintEngine::ObjectBoundingModeGradients);
- const bool mustEmulateObjectBoundingModeGradients = extended
- || ((state->emulationSpecifier & QPaintEngine::ObjectBoundingModeGradients)
- && !engine->hasFeature(QPaintEngine::PatternTransform));
- if (!(state->emulationSpecifier & ~gradientStretch)
- && !mustEmulateObjectBoundingModeGradients) {
- drawStretchedGradient(originalPath, op);
- return;
- } else if (state->emulationSpecifier & QPaintEngine_OpaqueBackground) {
- drawOpaqueBackground(originalPath, op);
- return;
- }
- Q_Q(QPainter);
- qreal strokeOffsetX = 0, strokeOffsetY = 0;
- QPainterPath path = originalPath * state->matrix;
- QRectF pathBounds = path.boundingRect();
- QRectF strokeBounds;
- bool doStroke = (op & StrokeDraw) && (state->pen.style() != Qt::NoPen);
- if (doStroke) {
- qreal penWidth = state->pen.widthF();
- if (penWidth == 0) {
- strokeOffsetX = 1;
- strokeOffsetY = 1;
- } else {
- // In case of complex xform
- if (state->matrix.type() > QTransform::TxScale) {
- QPainterPathStroker stroker;
- stroker.setWidth(penWidth);
- stroker.setJoinStyle(state->pen.joinStyle());
- stroker.setCapStyle(state->pen.capStyle());
- QPainterPath stroke = stroker.createStroke(originalPath);
- strokeBounds = (stroke * state->matrix).boundingRect();
- } else {
- strokeOffsetX = qAbs(penWidth * state->matrix.m11() / qreal(2.0));
- strokeOffsetY = qAbs(penWidth * state->matrix.m22() / qreal(2.0));
- }
- }
- }
- QRect absPathRect;
- if (!strokeBounds.isEmpty()) {
- absPathRect = strokeBounds.intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect();
- } else {
- absPathRect = pathBounds.adjusted(-strokeOffsetX, -strokeOffsetY, strokeOffsetX, strokeOffsetY)
- .intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect();
- }
- if (q->hasClipping()) {
- bool hasPerspectiveTransform = false;
- for (int i = 0; i < state->clipInfo.size(); ++i) {
- const QPainterClipInfo &info = state->clipInfo.at(i);
- if (info.matrix.type() == QTransform::TxProject) {
- hasPerspectiveTransform = true;
- break;
- }
- }
- // avoid mapping QRegions with perspective transforms
- if (!hasPerspectiveTransform) {
- // The trick with txinv and invMatrix is done in order to
- // avoid transforming the clip to logical coordinates, and
- // then back to device coordinates. This is a problem with
- // QRegion/QRect based clips, since they use integer
- // coordinates and converting to/from logical coordinates will
- // lose precision.
- bool old_txinv = txinv;
- QTransform old_invMatrix = invMatrix;
- txinv = true;
- invMatrix = QTransform();
- QPainterPath clipPath = q->clipPath();
- QRectF r = clipPath.boundingRect().intersected(absPathRect);
- absPathRect = r.toAlignedRect();
- txinv = old_txinv;
- invMatrix = old_invMatrix;
- }
- }
- // qDebug("\nQPainterPrivate::draw_helper(), x=%d, y=%d, w=%d, h=%d",
- // devMinX, devMinY, device->width(), device->height());
- // qDebug() << " - matrix" << state->matrix;
- // qDebug() << " - originalPath.bounds" << originalPath.boundingRect();
- // qDebug() << " - path.bounds" << path.boundingRect();
- if (absPathRect.width() <= 0 || absPathRect.height() <= 0)
- return;
- QImage image(absPathRect.width(), absPathRect.height(), QImage::Format_ARGB32_Premultiplied);
- image.fill(0);
- QPainter p(&image);
- p.d_ptr->helper_device = helper_device;
- p.setOpacity(state->opacity);
- p.translate(-absPathRect.x(), -absPathRect.y());
- p.setTransform(state->matrix, true);
- p.setPen(doStroke ? state->pen : QPen(Qt::NoPen));
- p.setBrush((op & FillDraw) ? state->brush : QBrush(Qt::NoBrush));
- p.setBackground(state->bgBrush);
- p.setBackgroundMode(state->bgMode);
- p.setBrushOrigin(state->brushOrigin);
- p.setRenderHint(QPainter::Antialiasing, state->renderHints & QPainter::Antialiasing);
- p.setRenderHint(QPainter::SmoothPixmapTransform,
- state->renderHints & QPainter::SmoothPixmapTransform);
- p.drawPath(originalPath);
- #ifndef QT_NO_DEBUG
- static bool do_fallback_overlay = qgetenv("QT_PAINT_FALLBACK_OVERLAY").size() > 0;
- if (do_fallback_overlay) {
- QImage block(8, 8, QImage::Format_ARGB32_Premultiplied);
- QPainter pt(&block);
- pt.fillRect(0, 0, 8, 8, QColor(196, 0, 196));
- pt.drawLine(0, 0, 8, 8);
- pt.end();
- p.resetTransform();
- p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
- p.setOpacity(0.5);
- p.fillRect(0, 0, image.width(), image.height(), QBrush(block));
- }
- #endif
- p.end();
- q->save();
- state->matrix = QTransform();
- if (extended) {
- extended->transformChanged();
- } else {
- state->dirtyFlags |= QPaintEngine::DirtyTransform;
- updateState(state);
- }
- engine->drawImage(absPathRect,
- image,
- QRectF(0, 0, absPathRect.width(), absPathRect.height()),
- Qt::OrderedDither | Qt::OrderedAlphaDither);
- q->restore();
- }
- void QPainterPrivate::drawOpaqueBackground(const QPainterPath &path, DrawOperation op)
- {
- Q_Q(QPainter);
- q->setBackgroundMode(Qt::TransparentMode);
- if (op & FillDraw && state->brush.style() != Qt::NoBrush) {
- q->fillPath(path, state->bgBrush.color());
- q->fillPath(path, state->brush);
- }
- if (op & StrokeDraw && state->pen.style() != Qt::NoPen) {
- q->strokePath(path, QPen(state->bgBrush.color(), state->pen.width()));
- q->strokePath(path, state->pen);
- }
- q->setBackgroundMode(Qt::OpaqueMode);
- }
- static inline QBrush stretchGradientToUserSpace(const QBrush &brush, const QRectF &boundingRect)
- {
- Q_ASSERT(brush.style() >= Qt::LinearGradientPattern
- && brush.style() <= Qt::ConicalGradientPattern);
- QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
- boundingRect.x(), boundingRect.y());
- QGradient g = *brush.gradient();
- g.setCoordinateMode(QGradient::LogicalMode);
- QBrush b(g);
- b.setTransform(gradientToUser * b.transform());
- return b;
- }
- void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperation op)
- {
- Q_Q(QPainter);
- const qreal sw = helper_device->width();
- const qreal sh = helper_device->height();
- bool changedPen = false;
- bool changedBrush = false;
- bool needsFill = false;
- const QPen pen = state->pen;
- const QBrush brush = state->brush;
- const QGradient::CoordinateMode penMode = coordinateMode(pen.brush());
- const QGradient::CoordinateMode brushMode = coordinateMode(brush);
- QRectF boundingRect;
- // Draw the xformed fill if the brush is a stretch gradient.
- if ((op & FillDraw) && brush.style() != Qt::NoBrush) {
- if (brushMode == QGradient::StretchToDeviceMode) {
- q->setPen(Qt::NoPen);
- changedPen = pen.style() != Qt::NoPen;
- q->scale(sw, sh);
- updateState(state);
- const qreal isw = 1.0 / sw;
- const qreal ish = 1.0 / sh;
- QTransform inv(isw, 0, 0, ish, 0, 0);
- engine->drawPath(path * inv);
- q->scale(isw, ish);
- } else {
- needsFill = true;
- if (brushMode == QGradient::ObjectBoundingMode) {
- Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
- boundingRect = path.boundingRect();
- q->setBrush(stretchGradientToUserSpace(brush, boundingRect));
- changedBrush = true;
- }
- }
- }
- if ((op & StrokeDraw) && pen.style() != Qt::NoPen) {
- // Draw the xformed outline if the pen is a stretch gradient.
- if (penMode == QGradient::StretchToDeviceMode) {
- q->setPen(Qt::NoPen);
- changedPen = true;
- if (needsFill) {
- updateState(state);
- engine->drawPath(path);
- }
- q->scale(sw, sh);
- q->setBrush(pen.brush());
- changedBrush = true;
- updateState(state);
- QPainterPathStroker stroker;
- stroker.setDashPattern(pen.style());
- stroker.setWidth(pen.widthF());
- stroker.setJoinStyle(pen.joinStyle());
- stroker.setCapStyle(pen.capStyle());
- stroker.setMiterLimit(pen.miterLimit());
- QPainterPath stroke = stroker.createStroke(path);
- const qreal isw = 1.0 / sw;
- const qreal ish = 1.0 / sh;
- QTransform inv(isw, 0, 0, ish, 0, 0);
- engine->drawPath(stroke * inv);
- q->scale(isw, ish);
- } else {
- if (!needsFill && brush.style() != Qt::NoBrush) {
- q->setBrush(Qt::NoBrush);
- changedBrush = true;
- }
- if (penMode == QGradient::ObjectBoundingMode) {
- Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
- // avoid computing the bounding rect twice
- if (!needsFill || brushMode != QGradient::ObjectBoundingMode)
- boundingRect = path.boundingRect();
- QPen p = pen;
- p.setBrush(stretchGradientToUserSpace(pen.brush(), boundingRect));
- q->setPen(p);
- changedPen = true;
- } else if (changedPen) {
- q->setPen(pen);
- changedPen = false;
- }
- updateState(state);
- engine->drawPath(path);
- }
- } else if (needsFill) {
- if (pen.style() != Qt::NoPen) {
- q->setPen(Qt::NoPen);
- changedPen = true;
- }
- updateState(state);
- engine->drawPath(path);
- }
- if (changedPen)
- q->setPen(pen);
- if (changedBrush)
- q->setBrush(brush);
- }
- void QPainterPrivate::updateMatrix()
- {
- state->matrix = state->WxF ? state->worldMatrix : QTransform();
- if (state->VxF)
- state->matrix *= viewTransform();
- txinv = false; // no inverted matrix
- state->matrix *= state->redirectionMatrix;
- if (extended)
- extended->transformChanged();
- else
- state->dirtyFlags |= QPaintEngine::DirtyTransform;
- // printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF);
- // qDebug() << " --- using matrix" << state->matrix << redirection_offset;
- }
- /*! \internal */
- void QPainterPrivate::updateInvMatrix()
- {
- Q_ASSERT(txinv == false);
- txinv = true; // creating inverted matrix
- invMatrix = state->matrix.inverted();
- }
- Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush);
- void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
- {
- bool alpha = false;
- bool linearGradient = false;
- bool radialGradient = false;
- bool extendedRadialGradient = false;
- bool conicalGradient = false;
- bool patternBrush = false;
- bool xform = false;
- bool complexXform = false;
- bool skip = true;
- // Pen and brush properties (we have to check both if one changes because the
- // one that's unchanged can still be in a state which requires emulation)
- if (s->state() & (QPaintEngine::DirtyPen | QPaintEngine::DirtyBrush | QPaintEngine::DirtyHints)) {
- // Check Brush stroke emulation
- if (!s->pen.isSolid() && !engine->hasFeature(QPaintEngine::BrushStroke))
- s->emulationSpecifier |= QPaintEngine::BrushStroke;
- else
- s->emulationSpecifier &= ~QPaintEngine::BrushStroke;
- skip = false;
- QBrush penBrush = (qpen_style(s->pen) == Qt::NoPen) ? QBrush(Qt::NoBrush) : qpen_brush(s->pen);
- Qt::BrushStyle brushStyle = qbrush_style(s->brush);
- Qt::BrushStyle penBrushStyle = qbrush_style(penBrush);
- alpha = (penBrushStyle != Qt::NoBrush
- && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255)
- && !penBrush.isOpaque())
- || (brushStyle != Qt::NoBrush
- && (brushStyle < Qt::LinearGradientPattern && s->brush.color().alpha() != 255)
- && !s->brush.isOpaque());
- linearGradient = ((penBrushStyle == Qt::LinearGradientPattern) ||
- (brushStyle == Qt::LinearGradientPattern));
- radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) ||
- (brushStyle == Qt::RadialGradientPattern));
- extendedRadialGradient = radialGradient && (qt_isExtendedRadialGradient(penBrush) || qt_isExtendedRadialGradient(s->brush));
- conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) ||
- (brushStyle == Qt::ConicalGradientPattern));
- patternBrush = (((penBrushStyle > Qt::SolidPattern
- && penBrushStyle < Qt::LinearGradientPattern)
- || penBrushStyle == Qt::TexturePattern) ||
- ((brushStyle > Qt::SolidPattern
- && brushStyle < Qt::LinearGradientPattern)
- || brushStyle == Qt::TexturePattern));
- bool penTextureAlpha = false;
- if (penBrush.style() == Qt::TexturePattern)
- penTextureAlpha = qHasPixmapTexture(penBrush)
- ? (penBrush.texture().depth() > 1) && penBrush.texture().hasAlpha()
- : penBrush.textureImage().hasAlphaChannel();
- bool brushTextureAlpha = false;
- if (s->brush.style() == Qt::TexturePattern) {
- brushTextureAlpha = qHasPixmapTexture(s->brush)
- ? (s->brush.texture().depth() > 1) && s->brush.texture().hasAlpha()
- : s->brush.textureImage().hasAlphaChannel();
- }
- if (((penBrush.style() == Qt::TexturePattern && penTextureAlpha)
- || (s->brush.style() == Qt::TexturePattern && brushTextureAlpha))
- && !engine->hasFeature(QPaintEngine::MaskedBrush))
- s->emulationSpecifier |= QPaintEngine::MaskedBrush;
- else
- s->emulationSpecifier &= ~QPaintEngine::MaskedBrush;
- }
- if (s->state() & (QPaintEngine::DirtyHints
- | QPaintEngine::DirtyOpacity
- | QPaintEngine::DirtyBackgroundMode)) {
- skip = false;
- }
- if (skip)
- return;
- #if 0
- qDebug("QPainterPrivate::updateEmulationSpecifier, state=%p\n"
- " - alpha: %d\n"
- " - linearGradient: %d\n"
- " - radialGradient: %d\n"
- " - conicalGradient: %d\n"
- " - patternBrush: %d\n"
- " - hints: %x\n"
- " - xform: %d\n",
- s,
- alpha,
- linearGradient,
- radialGradient,
- conicalGradient,
- patternBrush,
- uint(s->renderHints),
- xform);
- #endif
- // XForm properties
- if (s->state() & QPaintEngine::DirtyTransform) {
- xform = !s->matrix.isIdentity();
- complexXform = !s->matrix.isAffine();
- } else if (s->matrix.type() >= QTransform::TxTranslate) {
- xform = true;
- complexXform = !s->matrix.isAffine();
- }
- const bool brushXform = (!s->brush.transform().type() == QTransform::TxNone);
- const bool penXform = (!s->pen.brush().transform().type() == QTransform::TxNone);
- const bool patternXform = patternBrush && (xform || brushXform || penXform);
- // Check alphablending
- if (alpha && !engine->hasFeature(QPaintEngine::AlphaBlend))
- s->emulationSpecifier |= QPaintEngine::AlphaBlend;
- else
- s->emulationSpecifier &= ~QPaintEngine::AlphaBlend;
- // Linear gradient emulation
- if (linearGradient && !engine->hasFeature(QPaintEngine::LinearGradientFill))
- s->emulationSpecifier |= QPaintEngine::LinearGradientFill;
- else
- s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill;
- // Radial gradient emulation
- if (extendedRadialGradient || (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill)))
- s->emulationSpecifier |= QPaintEngine::RadialGradientFill;
- else
- s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill;
- // Conical gradient emulation
- if (conicalGradient && !engine->hasFeature(QPaintEngine::ConicalGradientFill))
- s->emulationSpecifier |= QPaintEngine::ConicalGradientFill;
- else
- s->emulationSpecifier &= ~QPaintEngine::ConicalGradientFill;
- // Pattern brushes
- if (patternBrush && !engine->hasFeature(QPaintEngine::PatternBrush))
- s->emulationSpecifier |= QPaintEngine::PatternBrush;
- else
- s->emulationSpecifier &= ~QPaintEngine::PatternBrush;
- // Pattern XForms
- if (patternXform && !engine->hasFeature(QPaintEngine::PatternTransform))
- s->emulationSpecifier |= QPaintEngine::PatternTransform;
- else
- s->emulationSpecifier &= ~QPaintEngine::PatternTransform;
- // Primitive XForms
- if (xform && !engine->hasFeature(QPaintEngine::PrimitiveTransform))
- s->emulationSpecifier |= QPaintEngine::PrimitiveTransform;
- else
- s->emulationSpecifier &= ~QPaintEngine::PrimitiveTransform;
- // Perspective XForms
- if (complexXform && !engine->hasFeature(QPaintEngine::PerspectiveTransform))
- s->emulationSpecifier |= QPaintEngine::PerspectiveTransform;
- else
- s->emulationSpecifier &= ~QPaintEngine::PerspectiveTransform;
- // Constant opacity
- if (state->opacity != 1 && !engine->hasFeature(QPaintEngine::ConstantOpacity))
- s->emulationSpecifier |= QPaintEngine::ConstantOpacity;
- else
- s->emulationSpecifier &= ~QPaintEngine::ConstantOpacity;
- bool gradientStretch = false;
- bool objectBoundingMode = false;
- if (linearGradient || conicalGradient || radialGradient) {
- QGradient::CoordinateMode brushMode = coordinateMode(s->brush);
- QGradient::CoordinateMode penMode = coordinateMode(s->pen.brush());
- gradientStretch |= (brushMode == QGradient::StretchToDeviceMode);
- gradientStretch |= (penMode == QGradient::StretchToDeviceMode);
- objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode);
- objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode);
- }
- if (gradientStretch)
- s->emulationSpecifier |= QGradient_StretchToDevice;
- else
- s->emulationSpecifier &= ~QGradient_StretchToDevice;
- if (objectBoundingMode && !engine->hasFeature(QPaintEngine::ObjectBoundingModeGradients))
- s->emulationSpecifier |= QPaintEngine::ObjectBoundingModeGradients;
- else
- s->emulationSpecifier &= ~QPaintEngine::ObjectBoundingModeGradients;
- // Opaque backgrounds...
- if (s->bgMode == Qt::OpaqueMode &&
- (is_pen_transparent(s->pen) || is_brush_transparent(s->brush)))
- s->emulationSpecifier |= QPaintEngine_OpaqueBackground;
- else
- s->emulationSpecifier &= ~QPaintEngine_OpaqueBackground;
- #if 0
- //won't be correct either way because the device can already have
- // something rendered to it in which case subsequent emulation
- // on a fully transparent qimage and then blitting the results
- // won't produce correct results
- // Blend modes
- if (state->composition_mode > QPainter::CompositionMode_Xor &&
- !engine->hasFeature(QPaintEngine::BlendModes))
- s->emulationSpecifier |= QPaintEngine::BlendModes;
- else
- s->emulationSpecifier &= ~QPaintEngine::BlendModes;
- #endif
- }
- void QPainterPrivate::updateStateImpl(QPainterState *newState)
- {
- // ### we might have to call QPainter::begin() here...
- if (!engine->state) {
- engine->state = newState;
- engine->setDirty(QPaintEngine::AllDirty);
- }
- if (engine->state->painter() != newState->painter)
- // ### this could break with clip regions vs paths.
- engine->setDirty(QPaintEngine::AllDirty);
- // Upon restore, revert all changes since last save
- else if (engine->state != newState)
- newState->dirtyFlags |= QPaintEngine::DirtyFlags(static_cast<QPainterState *>(engine->state)->changeFlags);
- // We need to store all changes made so that restore can deal with them
- else
- newState->changeFlags |= newState->dirtyFlags;
- updateEmulationSpecifier(newState);
- // Unset potential dirty background mode
- newState->dirtyFlags &= ~(QPaintEngine::DirtyBackgroundMode
- | QPaintEngine::DirtyBackground);
- engine->state = newState;
- engine->updateState(*newState);
- engine->clearDirty(QPaintEngine::AllDirty);
- }
- void QPainterPrivate::updateState(QPainterState *newState)
- {
- if (!newState) {
- engine->state = newState;
- } else if (newState->state() || engine->state!=newState) {
- bool setNonCosmeticPen = (newState->renderHints & QPainter::NonCosmeticDefaultPen)
- && newState->pen.widthF() == 0;
- if (setNonCosmeticPen) {
- // Override the default pen's cosmetic state if the
- // NonCosmeticDefaultPen render hint is used.
- QPen oldPen = newState->pen;
- newState->pen.setWidth(1);
- newState->pen.setCosmetic(false);
- newState->dirtyFlags |= QPaintEngine::DirtyPen;
- updateStateImpl(newState);
- // Restore the state pen back to its default to preserve visible
- // state.
- newState->pen = oldPen;
- } else {
- updateStateImpl(newState);
- }
- }
- }
- /*!
- \class QPainter
- \brief The QPainter class performs low-level painting on widgets and
- other paint devices.
- \ingroup painting
- \reentrant
- QPainter provides highly optimized functions to do most of the
- drawing GUI programs require. It can draw everything from simple
- lines to complex shapes like pies and chords. It can also draw
- aligned text and pixmaps. Normally, it draws in a "natural"
- coordinate system, but it can also do view and world
- transformation. QPainter can operate on any object that inherits
- the QPaintDevice class.
- The common use of QPainter is inside a widget's paint event:
- Construct and customize (e.g. set the pen or the brush) the
- painter. Then draw. Remember to destroy the QPainter object after
- drawing. For example:
- \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 0
- The core functionality of QPainter is drawing, but the class also
- provide several functions that allows you to customize QPainter's
- settings and its rendering quality, and others that enable
- clipping. In addition you can control how different shapes are
- merged together by specifying the painter's composition mode.
- The isActive() function indicates whether the painter is active. A
- painter is activated by the begin() function and the constructor
- that takes a QPaintDevice argument. The end() function, and the
- destructor, deactivates it.
- Together with the QPaintDevice and QPaintEngine classes, QPainter
- form the basis for Qt's paint system. QPainter is the class used
- to perform drawing operations. QPaintDevice represents a device
- that can be painted on using a QPainter. QPaintEngine provides the
- interface that the painter uses to draw onto different types of
- devices. If the painter is active, device() returns the paint
- device on which the painter paints, and paintEngine() returns the
- paint engine that the painter is currently operating on. For more
- information, see the \l {Paint System}.
- Sometimes it is desirable to make someone else paint on an unusual
- QPaintDevice. QPainter supports a static function to do this,
- setRedirected().
- \warning When the paintdevice is a widget, QPainter can only be
- used inside a paintEvent() function or in a function called by
- paintEvent(); that is unless the Qt::WA_PaintOutsidePaintEvent
- widget attribute is set. On Mac OS X and Windows, you can only
- paint in a paintEvent() function regardless of this attribute's
- setting.
- \tableofcontents
- \section1 Settings
- There are several settings that you can customize to make QPainter
- draw according to your preferences:
- \list
- \o font() is the font used for drawing text. If the painter
- isActive(), you can retrieve information about the currently set
- font, and its metrics, using the fontInfo() and fontMetrics()
- functions respectively.
- \o brush() defines the color or pattern that is used for filling
- shapes.
- \o pen() defines the color or stipple that is used for drawing
- lines or boundaries.
- \o backgroundMode() defines whether there is a background() or
- not, i.e it is either Qt::OpaqueMode or Qt::TransparentMode.
- \o background() only applies when backgroundMode() is \l
- Qt::OpaqueMode and pen() is a stipple. In that case, it
- describes the color of the background pixels in the stipple.
- \o brushOrigin() defines the origin of the tiled brushes, normally
- the origin of widget's background.
- \o viewport(), window(), worldTransform() make up the painter's coordinate
- transformation system. For more information, see the \l
- {Coordinate Transformations} section and the \l {Coordinate
- System} documentation.
- \o hasClipping() tells whether the painter clips at all. (The paint
- device clips, too.) If the painter clips, it clips to clipRegion().
- \o layoutDirection() defines the layout direction used by the
- painter when drawing text.
- \o worldMatrixEnabled() tells whether world transformation is enabled.
- \o viewTransformEnabled() tells whether view transformation is
- enabled.
- \endlist
- Note that some of these settings mirror settings in some paint
- devices, e.g. QWidget::font(). The QPainter::begin() function (or
- equivalently the QPainter constructor) copies these attributes
- from the paint device.
- You can at any time save the QPainter's state by calling the
- save() function which saves all the available settings on an
- internal stack. The restore() function pops them back.
- \section1 Drawing
- QPainter provides functions to draw most primitives: drawPoint(),
- drawPoints(), drawLine(), drawRect(), drawRoundedRect(),
- drawEllipse(), drawArc(), drawPie(), drawChord(), drawPolyline(),
- drawPolygon(), drawConvexPolygon() and drawCubicBezier(). The two
- convenience functions, drawRects() and drawLines(), draw the given
- number of rectangles or lines in the given array of \l
- {QRect}{QRects} or \l {QLine}{QLines} using the current pen and
- brush.
- The QPainter class also provides the fillRect() function which
- fills the given QRect, with the given QBrush, and the eraseRect()
- function that erases the area inside the given rectangle.
- All of these functions have both integer and floating point
- versions.
- \table 100%
- \row
- \o \inlineimage qpainter-basicdrawing.png
- \o
- \bold {Basic Drawing Example}
- The \l {painting/basicdrawing}{Basic Drawing} example shows how to
- display basic graphics primitives in a variety of styles using the
- QPainter class.
- \endtable
- If you need to draw a complex shape, especially if you need to do
- so repeatedly, consider creating a QPainterPath and drawing it
- using drawPath().
- \table 100%
- \row
- \o
- \bold {Painter Paths example}
- The QPainterPath class provides a container for painting
- operations, enabling graphical shapes to be constructed and
- reused.
- The \l {painting/painterpaths}{Painter Paths} example shows how
- painter paths can be used to build complex shapes for rendering.
- \o \inlineimage qpainter-painterpaths.png
- \endtable
- QPainter also provides the fillPath() function which fills the
- given QPainterPath with the given QBrush, and the strokePath()
- function that draws the outline of the given path (i.e. strokes
- the path).
- See also the \l {demos/deform}{Vector Deformation} demo which
- shows how to use advanced vector techniques to draw text using a
- QPainterPath, the \l {demos/gradients}{Gradients} demo which shows
- the different types of gradients that are available in Qt, and the \l
- {demos/pathstroke}{Path Stroking} demo which shows Qt's built-in
- dash patterns and shows how custom patterns can be used to extend
- the range of available patterns.
- \table
- \header
- \o \l {demos/deform}{Vector Deformation}
- \o \l {demos/gradients}{Gradients}
- \o \l {demos/pathstroke}{Path Stroking}
- \row
- \o \inlineimage qpainter-vectordeformation.png
- \o \inlineimage qpainter-gradients.png
- \o \inlineimage qpainter-pathstroking.png
- \endtable
- There are functions to draw pixmaps/images, namely drawPixmap(),
- drawImage() and drawTiledPixmap(). Both drawPixmap() and drawImage()
- produce the same result, except that drawPixmap() is faster
- on-screen while drawImage() may be faster on a QPrinter or other
- devices.
- Text drawing is done using drawText(). When you need
- fine-grained positioning, boundingRect() tells you where a given
- drawText() command will draw.
- There is a drawPicture() function that draws the contents of an
- entire QPicture. The drawPicture() function is the only function
- that disregards all the painter's settings as QPicture has its own
- settings.
- \section1 Rendering Quality
- To get the optimal rendering result using QPainter, you should use
- the platform independent QImage as paint device; i.e. using QImage
- will ensure that the result has an identical pixel representation
- on any platform.
- The QPainter class also provides a means of controlling the
- rendering quality through its RenderHint enum and the support for
- floating point precision: All the functions for drawing primitives
- has a floating point version. These are often used in combination
- with the \l {RenderHint}{QPainter::Antialiasing} render hint.
- \table 100%
- \row
- \o \inlineimage qpainter-concentriccircles.png
- \o
- \bold {Concentric Circles Example}
- The \l {painting/concentriccircles}{Concentric Circles} example
- shows the improved rendering quality that can be obtained using
- floating point precision and anti-aliasing when drawing custom
- widgets.
- The application's main window displays several widgets which are
- drawn using the various combinations of precision and
- anti-aliasing.
- \endtable
- The RenderHint enum specifies flags to QPainter that may or may
- not be respected by any given engine. \l
- {RenderHint}{QPainter::Antialiasing} indicates that the engine
- should antialias edges of primitives if possible, \l
- {RenderHint}{QPainter::TextAntialiasing} indicates that the engine
- should antialias text if possible, and the \l
- {RenderHint}{QPainter::SmoothPixmapTransform} indicates that the
- engine should use a smooth pixmap transformation algorithm.
- \l {RenderHint}{HighQualityAntialiasing} is an OpenGL-specific rendering hint
- indicating that the engine should use fragment programs and offscreen
- rendering for antialiasing.
- The renderHints() function returns a flag that specifies the
- rendering hints that are set for this painter. Use the
- setRenderHint() function to set or clear the currently set
- RenderHints.
- \section1 Coordinate Transformations
- Normally, the QPainter operates on the device's own coordinate
- system (usually pixels), but QPainter has good support for
- coordinate transformations.
- \table
- \header
- \o nop \o rotate() \o scale() \o translate()
- \row
- \o \inlineimage qpainter-clock.png
- \o \inlineimage qpainter-rotation.png
- \o \inlineimage qpainter-scale.png
- \o \inlineimage qpainter-translation.png
- \endtable
- The most commonly used transformations are scaling, rotation,
- translation and shearing. Use the scale() function to scale the
- coordinate system by a given offset, the rotate() function to
- rotate it clockwise and translate() to translate it (i.e. adding a
- given offset to the points). You can also twist the coordinate
- system around the origin using the shear() function. See the \l
- {demos/affine}{Affine Transformations} demo for a visualization of
- a sheared coordinate system.
- See also the \l {painting/transformations}{Transformations}
- example which shows how transformations influence the way that
- QPainter renders graphics primitives. In particular it shows how
- the order of transformations affects the result.
- \table 100%
- \row
- \o
- \bold {Affine Transformations Demo}
- The \l {demos/affine}{Affine Transformations} demo show Qt's
- ability to perform affine transformations on painting
- operations. The demo also allows the user to experiment with the
- transformation operations and see the results immediately.
- \o \inlineimage qpainter-affinetransformations.png
- \endtable
- All the tranformation operations operate on the transformation
- worldTransform(). A matrix transforms a point in the plane to another
- point. For more information about the transformation matrix, see
- the \l {Coordinate System} and QTransform documentation.
- The setWorldTransform() function can replace or add to the currently
- set worldTransform(). The resetTransform() function resets any
- transformations that were made using translate(), scale(),
- shear(), rotate(), setWorldTransform(), setViewport() and setWindow()
- functions. The deviceTransform() returns the matrix that transforms
- from logical coordinates to device coordinates of the platform
- dependent paint device. The latter function is only needed when
- using platform painting commands on the platform dependent handle,
- and the platform does not do transformations nativly.
- When drawing with QPainter, we specify points using logical
- coordinates which then are converted into the physical coordinates
- of the paint device. The mapping of the logical coordinates to the
- physical coordinates are handled by QPainter's combinedTransform(), a
- combination of viewport() and window() and worldTransform(). The
- viewport() represents the physical coordinates specifying an
- arbitrary rectangle, the window() describes the same rectangle in
- logical coordinates, and the worldTransform() is identical with the
- transformation matrix.
- See also \l {Coordinate System}
- \section1 Clipping
- QPainter can clip any drawing operation to a rectangle, a region,
- or a vector path. The current clip is available using the
- functions clipRegion() and clipPath(). Whether paths or regions are
- preferred (faster) depends on the underlying paintEngine(). For
- example, the QImage paint engine prefers paths while the X11 paint
- engine prefers regions. Setting a clip is done in the painters
- logical coordinates.
- After QPainter's clipping, the paint device may also clip. For
- example, most widgets clip away the pixels used by child widgets,
- and most printers clip away an area near the edges of the paper.
- This additional clipping is not reflected by the return value of
- clipRegion() or hasClipping().
- \section1 Composition Modes
- \target Composition Modes
- QPainter provides the CompositionMode enum which defines the
- Porter-Duff rules for digital image compositing; it describes a
- model for combining the pixels in one image, the source, with the
- pixels in another image, the destination.
- The two most common forms of composition are \l
- {QPainter::CompositionMode}{Source} and \l
- {QPainter::CompositionMode}{SourceOver}. \l
- {QPainter::CompositionMode}{Source} is used to draw opaque objects
- onto a paint device. In this mode, each pixel in the source
- replaces the corresponding pixel in the destination. In \l
- {QPainter::CompositionMode}{SourceOver} composition mode, the
- source object is transparent and is drawn on top of the
- destination.
- Note that composition transformation operates pixelwise. For that
- reason, there is a difference between using the graphic primitive
- itself and its bounding rectangle: The bounding rect contains
- pixels with alpha == 0 (i.e the pixels surrounding the
- primitive). These pixels will overwrite the other image's pixels,
- affectively clearing those, while the primitive only overwrites
- its own area.
- \table 100%
- \row
- \o \inlineimage qpainter-compositiondemo.png
- \o
- \bold {Composition Modes Demo}
- The \l {demos/composition}{Composition Modes} demo, avai…