Qt源码分析--QPainter(1)

59 篇文章 1 订阅
37 篇文章 1 订阅

定义:

QPainter 类在小部件和其它绘画设备上履行低级描绘。

与 QPaintDevice 和 QPaintEngine 类一起,QPainter 构成了 Qt 绘画系统的基础。 QPainter 是用于执行绘图操作的类。 QPaintDevice 表示可以使用 QPainter 绘制的设备。 QPaintEngine 提供了painter用来在不同类型的设备上绘制的接口。 如果painter 处于活动状态,device() 返回painter 在其上绘制的绘制设备,paintEngine() 返回painter 当前正在操作的绘制引擎。

isActive() 函数指示painter是否处于活动状态。 painter由 begin() 函数和接受 QPaintDevice 参数的构造函数激活。 end() 函数和析构函数将其停用。

官方例子:

void SimpleExampleWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setPen(Qt::blue);
    painter.setFont(QFont("Arial", 30));
    painter.drawText(rect(), Qt::AlignCenter, "Qt");
}

源码分析:

1.explicit QPainter(QPaintDevice *);

QPainter::QPainter(QPaintDevice *pd)
    : d_ptr(0)
{
    Q_ASSERT(pd != 0);
    if (!QPainterPrivate::attachPainterPrivate(this, pd)) {
        d_ptr.reset(new QPainterPrivate(this));
        begin(pd);
    }
    Q_ASSERT(d_ptr);
}

bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev)
{
    Q_ASSERT(q);
    Q_ASSERT(pdev);
    QPainter *sp = pdev->sharedPainter();
    if (!sp)
        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->d_ptr->initFrom(pdev);
    QPoint offset;
    pdev->redirected(&offset);
    offset += q->d_ptr->engine->coordinateOffset();
    // Update system rect.
    q->d_ptr->state->ww = q->d_ptr->state->vw = pdev->width();
    q->d_ptr->state->wh = q->d_ptr->state->vh = pdev->height();
    // Update matrix.
    if (q->d_ptr->state->WxF) {
        q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix;
        q->d_ptr->state->redirectionMatrix *= q->d_ptr->hidpiScaleTransform().inverted();
        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->currentClipDevice == pdev) {
        enginePrivate->systemStateChanged();
        return true;
    }
    // Update system transform and clip.
    enginePrivate->currentClipDevice = pdev;
    enginePrivate->setSystemTransform(q->d_ptr->state->matrix);
    return true;
}

attach QPaintDevice到 QPainter.

2.void setPen(const QColor &color);

void QPainter::setPen(const QColor &color)
{
#ifdef QT_DEBUG_DRAW
    if (qt_show_painter_debug_output)
        printf("QPainter::setPen(), color=%04x\n", color.rgb());
#endif
    Q_D(QPainter);
    if (!d->engine) {
        qWarning("QPainter::setPen: Painter not active");
        return;
    }
    QPen pen(color.isValid() ? color : QColor(Qt::black));
    if (d->state->pen == pen)
        return;
    d->state->pen = pen;
    if (d->extended)
        d->extended->penChanged();
    else
        d->state->dirtyFlags |= QPaintEngine::DirtyPen;
}

3.void setFont(const QFont &f);

void QPainter::setFont(const QFont &font)
{
    Q_D(QPainter);
#ifdef QT_DEBUG_DRAW
    if (qt_show_painter_debug_output)
        printf("QPainter::setFont(), family=%s, pointSize=%d\n", font.family().toLatin1().constData(), font.pointSize());
#endif
    if (!d->engine) {
        qWarning("QPainter::setFont: Painter not active");
        return;
    }
    d->state->font = QFont(font.resolve(d->state->deviceFont), device());
    if (!d->extended)
        d->state->dirtyFlags |= QPaintEngine::DirtyFont;
}

4. void drawText(const QRectF &r, int flags, const QString &text, QRectF *br = nullptr);

void QPainter::drawText(const QRectF &r, int flags, const QString &str, QRectF *br)
{
#ifdef QT_DEBUG_DRAW
    if (qt_show_painter_debug_output)
        printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], flags=%d, str='%s'\n",
           r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
#endif
    Q_D(QPainter);
    if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
        return;
    if (!d->extended)
        d->updateState(d->state);
    qt_format_text(d->state->font, r, flags, 0, str, br, 0, 0, 0, this);
}
void qt_format_text(const QFont &fnt, const QRectF &_r,
                    int tf, const QTextOption *option, const QString& str, QRectF *brect,
                    int tabstops, int *ta, int tabarraylen,
                    QPainter *painter)
{
    Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=0) ); // we either have an option or flags
    if (option) {
        tf |= option->alignment();
        if (option->wrapMode() != QTextOption::NoWrap)
            tf |= Qt::TextWordWrap;
        if (option->flags() & QTextOption::IncludeTrailingSpaces)
            tf |= Qt::TextIncludeTrailingSpaces;
        if (option->tabStopDistance() >= 0 || !option->tabArray().isEmpty())
            tf |= Qt::TextExpandTabs;
    }
    // we need to copy r here to protect against the case (&r == brect).
    QRectF r(_r);
    bool dontclip  = (tf & Qt::TextDontClip);
    bool wordwrap  = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
    bool singleline = (tf & Qt::TextSingleLine);
    bool showmnemonic = (tf & Qt::TextShowMnemonic);
    bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
    Qt::LayoutDirection layout_direction;
    if (tf & Qt::TextForceLeftToRight)
        layout_direction = Qt::LeftToRight;
    else if (tf & Qt::TextForceRightToLeft)
        layout_direction = Qt::RightToLeft;
    else if (option)
        layout_direction = option->textDirection();
    else if (painter)
        layout_direction = painter->layoutDirection();
    else
        layout_direction = Qt::LeftToRight;
    tf = QGuiApplicationPrivate::visualAlignment(layout_direction, QFlag(tf));
    bool isRightToLeft = layout_direction == Qt::RightToLeft;
    bool expandtabs = ((tf & Qt::TextExpandTabs) &&
                        (((tf & Qt::AlignLeft) && !isRightToLeft) ||
                          ((tf & Qt::AlignRight) && isRightToLeft)));
    if (!painter)
        tf |= Qt::TextDontPrint;
    uint maxUnderlines = 0;
    QFontMetricsF fm(fnt);
    QString text = str;
    int offset = 0;
start_lengthVariant:
    bool hasMoreLengthVariants = false;
    // compatible behaviour to the old implementation. Replace
    // tabs by spaces
    int old_offset = offset;
    for (; offset < text.length(); offset++) {
        QChar chr = text.at(offset);
        if (chr == QLatin1Char('\r') || (singleline && chr == QLatin1Char('\n'))) {
            text[offset] = QLatin1Char(' ');
        } else if (chr == QLatin1Char('\n')) {
            text[offset] = QChar::LineSeparator;
        } else if (chr == QLatin1Char('&')) {
            ++maxUnderlines;
        } else if (chr == QLatin1Char('\t')) {
            if (!expandtabs) {
                text[offset] = QLatin1Char(' ');
            } else if (!tabarraylen && !tabstops) {
                tabstops = qRound(fm.horizontalAdvance(QLatin1Char('x'))*8);
            }
        } else if (chr == QChar(ushort(0x9c))) {
            // string with multiple length variants
            hasMoreLengthVariants = true;
            break;
        }
    }
    QVector<QTextLayout::FormatRange> underlineFormats;
    int length = offset - old_offset;
    if ((hidemnmemonic || showmnemonic) && maxUnderlines > 0) {
        QChar *cout = text.data() + old_offset;
        QChar *cout0 = cout;
        QChar *cin = cout;
        int l = length;
        while (l) {
            if (*cin == QLatin1Char('&')) {
                ++cin;
                --length;
                --l;
                if (!l)
                    break;
                if (*cin != QLatin1Char('&') && !hidemnmemonic && !(tf & Qt::TextDontPrint)) {
                    QTextLayout::FormatRange range;
                    range.start = cout - cout0;
                    range.length = 1;
                    range.format.setFontUnderline(true);
                    underlineFormats.append(range);
                }
#ifdef Q_OS_MAC
            } else if (hidemnmemonic && *cin == QLatin1Char('(') && l >= 4 &&
                       cin[1] == QLatin1Char('&') && cin[2] != QLatin1Char('&') &&
                       cin[3] == QLatin1Char(')')) {
                int n = 0;
                while ((cout - n) > cout0 && (cout - n - 1)->isSpace())
                    ++n;
                cout -= n;
                cin += 4;
                length -= n + 4;
                l -= 4;
                continue;
#endif //Q_OS_MAC
            }
            *cout = *cin;
            ++cout;
            ++cin;
            --l;
        }
    }
    qreal height = 0;
    qreal width = 0;
    QString finalText = text.mid(old_offset, length);
    QStackTextEngine engine(finalText, fnt);
    if (option) {
        engine.option = *option;
    }
    if (engine.option.tabStopDistance() < 0 && tabstops > 0)
        engine.option.setTabStopDistance(tabstops);
    if (engine.option.tabs().isEmpty() && ta) {
        QList<qreal> tabs;
        tabs.reserve(tabarraylen);
        for (int i = 0; i < tabarraylen; i++)
            tabs.append(qreal(ta[i]));
        engine.option.setTabArray(tabs);
    }
    engine.option.setTextDirection(layout_direction);
    if (tf & Qt::AlignJustify)
        engine.option.setAlignment(Qt::AlignJustify);
    else
        engine.option.setAlignment(Qt::AlignLeft); // do not do alignment twice
    if (!option && (tf & Qt::TextWrapAnywhere))
        engine.option.setWrapMode(QTextOption::WrapAnywhere);
    if (tf & Qt::TextJustificationForced)
        engine.forceJustification = true;
    QTextLayout textLayout(&engine);
    textLayout.setCacheEnabled(true);
    textLayout.setFormats(underlineFormats);
    if (finalText.isEmpty()) {
        height = fm.height();
        width = 0;
        tf |= Qt::TextDontPrint;
    } else {
        qreal lineWidth = 0x01000000;
        if (wordwrap || (tf & Qt::TextJustificationForced))
            lineWidth = qMax<qreal>(0, r.width());
        if(!wordwrap)
            tf |= Qt::TextIncludeTrailingSpaces;
        textLayout.beginLayout();
        qreal leading = fm.leading();
        height = -leading;
        while (1) {
            QTextLine l = textLayout.createLine();
            if (!l.isValid())
                break;
            l.setLineWidth(lineWidth);
            height += leading;
            // Make sure lines are positioned on whole pixels
            height = qCeil(height);
            l.setPosition(QPointF(0., height));
            height += textLayout.engine()->lines[l.lineNumber()].height().toReal();
            width = qMax(width, l.naturalTextWidth());
            if (!dontclip && !brect && height >= r.height())
                break;
        }
        textLayout.endLayout();
    }
    qreal yoff = 0;
    qreal xoff = 0;
    if (tf & Qt::AlignBottom)
        yoff = r.height() - height;
    else if (tf & Qt::AlignVCenter)
        yoff = (r.height() - height)/2;
    if (tf & Qt::AlignRight)
        xoff = r.width() - width;
    else if (tf & Qt::AlignHCenter)
        xoff = (r.width() - width)/2;
    QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
    if (hasMoreLengthVariants && !(tf & Qt::TextLongestVariant) && !r.contains(bounds)) {
        offset++;
        goto start_lengthVariant;
    }
    if (brect)
        *brect = bounds;
    if (!(tf & Qt::TextDontPrint)) {
        bool restore = false;
        if (!dontclip && !r.contains(bounds)) {
            restore = true;
            painter->save();
            painter->setClipRect(r, Qt::IntersectClip);
        }
        for (int i = 0; i < textLayout.lineCount(); i++) {
            QTextLine line = textLayout.lineAt(i);
            QTextEngine *eng = textLayout.engine();
            eng->enableDelayDecorations();
            qreal advance = line.horizontalAdvance();
            xoff = 0;
            if (tf & Qt::AlignRight) {
                xoff = r.width() - advance -
                    eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
            }
            else if (tf & Qt::AlignHCenter)
                xoff = (r.width() - advance) / 2;
            line.draw(painter, QPointF(r.x() + xoff, r.y() + yoff));
            eng->drawDecorations(painter);
        }
        if (restore) {
            painter->restore();
        }
    }
}

设置好画笔和字体后,最后用QTextLine画文字。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值