📄 qpainter.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.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 GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/// QtCore#include <qdebug.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 <private/qfontengine_p.h>#include <private/qpaintengine_p.h>#include <private/qpainterpath_p.h>#include <private/qtextengine_p.h>#include <private/qwidget_p.h>// Other#include <math.h>// #define QT_DEBUG_DRAW#ifdef QT_DEBUG_DRAWbool qt_show_painter_debug_output = true;#endifextern QPixmap qt_pixmapForBrush(int style, bool invert);void qt_format_text(const QFont &font, const QRectF &_r, int tf, const QString& str, QRectF *brect, int tabstops, int* tabarray, int tabarraylen, QPainter *painter);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; Q_Q(QPainter); int devMinX = 0, devMaxX = 0, devMinY = 0, devMaxY = 0; 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->txop > 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 = penWidth / 2.0 * state->matrix.m11(); strokeOffsetY = penWidth / 2.0 * state->matrix.m22(); } } } const qreal ROUND_UP_TRICK = 0.9999; if (!strokeBounds.isEmpty()) { devMinX = int(strokeBounds.left()); devMaxX = int(strokeBounds.right() + ROUND_UP_TRICK); devMinY = int(strokeBounds.top()); devMaxY = int(strokeBounds.bottom() + ROUND_UP_TRICK); } else { devMinX = int(pathBounds.left() - strokeOffsetX); devMaxX = int(pathBounds.right() + strokeOffsetX + ROUND_UP_TRICK); devMinY = int(pathBounds.top() - strokeOffsetY); devMaxY = int(pathBounds.bottom() + strokeOffsetY + ROUND_UP_TRICK); } QRect absPathRect(devMinX, devMinY, devMaxX - devMinX, devMaxY - devMinY); if (state->clipInfo.size() != 0) { QRegion r = q->clipRegion() * q->deviceMatrix(); absPathRect = absPathRect.intersect(r.boundingRect()); } absPathRect = absPathRect.intersect(QRect(0, 0, device->width(), device->height()));// 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.translate(-absPathRect.x(), -absPathRect.y()); p.setMatrix(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->bgOrigin); p.setRenderHint(QPainter::Antialiasing, state->renderHints & QPainter::Antialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform, state->renderHints & QPainter::SmoothPixmapTransform); p.drawPath(originalPath); p.end(); q->save(); q->setViewTransformEnabled(false); q->setMatrix(QMatrix(1, 0, 0, 1, -redirection_offset.x(), -redirection_offset.y())); updateState(state); engine->drawImage(absPathRect, image, QRectF(0, 0, absPathRect.width(), absPathRect.height()), Qt::OrderedDither | Qt::OrderedAlphaDither); q->restore();}void QPainterPrivate::init(){ Q_Q(QPainter); state->painter = q;}void QPainterPrivate::updateMatrix(){ QMatrix m; if (state->VxF) { qreal scaleW = qreal(state->vw)/qreal(state->ww); qreal scaleH = qreal(state->vh)/qreal(state->wh); m.setMatrix(scaleW, 0, 0, scaleH, state->vx - state->wx*scaleW, state->vy - state->wy*scaleH); } if (state->WxF) { if (state->VxF) m = state->worldMatrix * m; else m = state->worldMatrix; } state->matrix = m; txinv = false; // no inverted matrix state->txop = TxNone; if (state->matrix.m12()==0.0 && state->matrix.m21()==0.0 && state->matrix.m11() >= 0.0 && state->matrix.m22() >= 0.0) { if (state->matrix.m11()==1.0 && state->matrix.m22()==1.0) { if (state->matrix.dx()!=0.0 || state->matrix.dy()!=0.0) state->txop = TxTranslate; } else { state->txop = TxScale; } } else { state->txop = TxRotShear; } if (!redirection_offset.isNull()) { state->txop |= TxTranslate; state->WxF = true; // We want to translate in dev space so we do the adding of the redirection // offset manually. state->matrix = QMatrix(state->matrix.m11(), state->matrix.m12(), state->matrix.m21(), state->matrix.m22(), state->matrix.dx()-redirection_offset.x(), state->matrix.dy()-redirection_offset.y()); } 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 bool invertible; QMatrix m; if (state->VxF) { m.translate(state->vx, state->vy); m.scale(1.0*state->vw/state->ww, 1.0*state->vh/state->wh); m.translate(-state->wx, -state->wy); } if (state->WxF) { if (state->VxF) m = state->worldMatrix * m; else m = state->worldMatrix; } invMatrix = m.inverted(&invertible); // invert matrix}void QPainterPrivate::updateEmulationSpecifier(QPainterState *s){ bool alpha = false; bool linearGradient = false; bool radialGradient = false; bool conicalGradient = false; bool patternBrush = false; bool xform = 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 || s->state() & QPaintEngine::DirtyBrush) { // 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 = s->pen.brush(); alpha |= (!penBrush.isOpaque() || !s->brush.isOpaque()); linearGradient |= ((penBrush.style() == Qt::LinearGradientPattern) || (s->brush.style() == Qt::LinearGradientPattern)); radialGradient |= ((penBrush.style() == Qt::RadialGradientPattern) || (s->brush.style() == Qt::RadialGradientPattern)); conicalGradient |= ((penBrush.style() == Qt::ConicalGradientPattern) || (s->brush.style() == Qt::ConicalGradientPattern)); patternBrush |= (((penBrush.style() > Qt::SolidPattern && penBrush.style() < Qt::LinearGradientPattern) || s->brush.style() == Qt::TexturePattern) || ((s->brush.style() > Qt::SolidPattern && s->brush.style() < Qt::LinearGradientPattern) || s->brush.style() == Qt::TexturePattern)); } if (s->state() & QPaintEngine::DirtyHints) { 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(); } else if (s->txop >= TxTranslate) { xform = true; } // 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 (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 (patternBrush && xform && !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;}void QPainterPrivate::updateState(QPainterState *newState){ if (!newState) { engine->state = newState; } else if (newState->state() || engine->state!=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); engine->state = newState; engine->updateState(*newState); engine->clearDirty(QPaintEngine::AllDirty); }}/*! \class QPainter \brief The QPainter class performs low-level painting on widgets and other paint devices. \ingroup multimedia \mainclass 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -