245 QPainter *sp = pdev->sharedPainter();
252 ++sp->d_ptr->refcount;
253 sp->d_ptr->d_ptrs.push_back(std::move(q->d_ptr));
254 q->d_ptr.reset(sp->d_ptr.get());
256 Q_ASSERT(q->d_ptr->state);
259 q->d_ptr->initFrom(pdev);
261 pdev->redirected(&offset);
262 offset += q->d_ptr->engine->coordinateOffset();
265 q->d_ptr->state->ww = q->d_ptr->state->vw = pdev->width();
266 q->d_ptr->state->wh = q->d_ptr->state->vh = pdev->height();
269 if (q->d_ptr->state->WxF) {
270 q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix;
271 q->d_ptr->state->redirectionMatrix *= q->d_ptr->hidpiScaleTransform().inverted();
272 q->d_ptr->state->redirectionMatrix.translate(-offset.x(), -offset.y());
273 q->d_ptr->state->worldMatrix = QTransform();
274 q->d_ptr->state->WxF =
false;
276 q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(-offset.x(), -offset.y());
278 q->d_ptr->updateMatrix();
280 QPaintEnginePrivate *enginePrivate = q->d_ptr->engine->d_func();
281 if (enginePrivate->currentClipDevice == pdev) {
282 enginePrivate->systemStateChanged();
287 enginePrivate->currentClipDevice = pdev;
288 enginePrivate->setSystemTransform(q->d_ptr->state->matrix);
322 if constexpr (qt_show_painter_debug_output) {
323 printf(
"QPainter::drawHelper\n");
327 if (originalPath.isEmpty())
330 QPaintEngine::PaintEngineFeatures gradientStretch =
332 | QPaintEngine::ObjectBoundingModeGradients);
334 const bool mustEmulateObjectBoundingModeGradients = extended
335 || ((state->emulationSpecifier & QPaintEngine::ObjectBoundingModeGradients)
336 && !engine->hasFeature(QPaintEngine::PatternTransform));
338 if (!(state->emulationSpecifier & ~gradientStretch)
339 && !mustEmulateObjectBoundingModeGradients) {
349 qreal strokeOffsetX = 0, strokeOffsetY = 0;
351 QPainterPath path = originalPath * state->matrix;
352 QRectF pathBounds = path.boundingRect();
354 bool doStroke = (op & StrokeDraw) && (state->pen.style() != Qt::NoPen);
356 qreal penWidth = state->pen.widthF();
362 if (state->matrix.type() > QTransform::TxScale) {
364 stroker.setWidth(penWidth);
365 stroker.setJoinStyle(state->pen.joinStyle());
366 stroker.setCapStyle(state->pen.capStyle());
367 QPainterPath stroke = stroker.createStroke(originalPath);
368 strokeBounds = (stroke * state->matrix).boundingRect();
370 strokeOffsetX = qAbs(penWidth * state->matrix.m11() / 2.0);
371 strokeOffsetY = qAbs(penWidth * state->matrix.m22() / 2.0);
377 if (!strokeBounds.isEmpty()) {
378 absPathRect = strokeBounds.intersected(QRectF(0, 0,
device->width(),
device->height())).toAlignedRect();
380 absPathRect = pathBounds.adjusted(-strokeOffsetX, -strokeOffsetY, strokeOffsetX, strokeOffsetY)
381 .intersected(QRectF(0, 0,
device->width(),
device->height())).toAlignedRect();
384 if (q->hasClipping()) {
385 bool hasPerspectiveTransform =
false;
386 for (
const QPainterClipInfo &info : std::as_const(state->clipInfo)) {
387 if (info.matrix.type() == QTransform::TxProject) {
388 hasPerspectiveTransform =
true;
393 if (!hasPerspectiveTransform) {
400 bool old_txinv =
txinv;
401 QTransform old_invMatrix = invMatrix;
403 invMatrix = QTransform();
404 QPainterPath clipPath = q->clipPath();
405 QRectF r = clipPath.boundingRect().intersected(absPathRect);
406 absPathRect = r.toAlignedRect();
408 invMatrix = old_invMatrix;
418 if (absPathRect.width() <= 0 || absPathRect.height() <= 0)
421 QImage image(absPathRect.width(), absPathRect.height(), QImage::Format_ARGB32_Premultiplied);
428 p.setOpacity(state->opacity);
429 p.translate(-absPathRect.x(), -absPathRect.y());
430 p.setTransform(state->matrix,
true);
431 p.setPen(doStroke ? state->pen : QPen(Qt::NoPen));
432 p.setBrush((op & FillDraw) ? state->brush : QBrush(Qt::NoBrush));
433 p.setBackground(state->bgBrush);
434 p.setBackgroundMode(state->bgMode);
435 p.setBrushOrigin(state->brushOrigin);
437 p.setRenderHint(QPainter::Antialiasing, state->renderHints & QPainter::Antialiasing);
438 p.setRenderHint(QPainter::SmoothPixmapTransform,
439 state->renderHints & QPainter::SmoothPixmapTransform);
441 p.drawPath(originalPath);
444 static bool do_fallback_overlay = !qEnvironmentVariableIsEmpty(
"QT_PAINT_FALLBACK_OVERLAY");
445 if (do_fallback_overlay) {
446 QImage block(8, 8, QImage::Format_ARGB32_Premultiplied);
448 pt.fillRect(0, 0, 8, 8, QColor(196, 0, 196));
449 pt.drawLine(0, 0, 8, 8);
452 p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
454 p.fillRect(0, 0, image.width(), image.height(), QBrush(block));
461 state->matrix = QTransform();
465 state->dirtyFlags |= QPaintEngine::DirtyTransform;
468 engine->drawImage(absPathRect,
470 QRectF(0, 0, absPathRect.width(), absPathRect.height()),
471 Qt::OrderedDither | Qt::OrderedAlphaDither);
520 bool changedPen =
false;
521 bool changedBrush =
false;
522 bool needsFill =
false;
524 const QPen pen = state->pen;
525 const QBrush brush = state->brush;
527 const QGradient::CoordinateMode penMode = coordinateMode(pen.brush());
528 const QGradient::CoordinateMode brushMode = coordinateMode(brush);
533 if ((op & FillDraw) && brush.style() != Qt::NoBrush) {
534 if (brushMode == QGradient::StretchToDeviceMode) {
535 q->setPen(Qt::NoPen);
536 changedPen = pen.style() != Qt::NoPen;
540 const qreal isw = 1.0 / sw;
541 const qreal ish = 1.0 / sh;
542 QTransform inv(isw, 0, 0, ish, 0, 0);
543 engine->drawPath(path * inv);
548 if (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode) {
549 Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
550 boundingRect = path.boundingRect();
551 q->setBrush(stretchGradientToUserSpace(brush, boundingRect));
557 if ((op & StrokeDraw) && pen.style() != Qt::NoPen) {
559 if (penMode == QGradient::StretchToDeviceMode) {
560 q->setPen(Qt::NoPen);
565 engine->drawPath(path);
569 q->setBrush(pen.brush());
574 stroker.setDashPattern(pen.style());
575 stroker.setWidth(pen.widthF());
576 stroker.setJoinStyle(pen.joinStyle());
577 stroker.setCapStyle(pen.capStyle());
578 stroker.setMiterLimit(pen.miterLimit());
579 QPainterPath stroke = stroker.createStroke(path);
581 const qreal isw = 1.0 / sw;
582 const qreal ish = 1.0 / sh;
583 QTransform inv(isw, 0, 0, ish, 0, 0);
584 engine->drawPath(stroke * inv);
587 if (!needsFill && brush.style() != Qt::NoBrush) {
588 q->setBrush(Qt::NoBrush);
592 if (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode) {
593 Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
596 if (!needsFill || (brushMode != QGradient::ObjectBoundingMode && brushMode != QGradient::ObjectMode))
597 boundingRect = path.boundingRect();
600 p.setBrush(stretchGradientToUserSpace(pen.brush(), boundingRect));
603 }
else if (changedPen) {
609 engine->drawPath(path);
611 }
else if (needsFill) {
612 if (pen.style() != Qt::NoPen) {
613 q->setPen(Qt::NoPen);
618 engine->drawPath(path);
660 bool linearGradient =
false;
661 bool radialGradient =
false;
662 bool extendedRadialGradient =
false;
663 bool conicalGradient =
false;
664 bool patternBrush =
false;
666 bool complexXform =
false;
672 if (s->state() & (QPaintEngine::DirtyPen | QPaintEngine::DirtyBrush | QPaintEngine::DirtyHints)) {
674 if (!s->pen.isSolid() && !engine->hasFeature(QPaintEngine::BrushStroke))
675 s->emulationSpecifier |= QPaintEngine::BrushStroke;
677 s->emulationSpecifier &= ~QPaintEngine::BrushStroke;
681 QBrush penBrush = (qpen_style(s->pen) == Qt::NoPen) ? QBrush(Qt::NoBrush) : qpen_brush(s->pen);
682 Qt::BrushStyle brushStyle = qbrush_style(s->brush);
683 Qt::BrushStyle penBrushStyle = qbrush_style(penBrush);
684 alpha = (penBrushStyle != Qt::NoBrush
685 && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255)
686 && !penBrush.isOpaque())
687 || (brushStyle != Qt::NoBrush
688 && (brushStyle < Qt::LinearGradientPattern && s->brush.color().alpha() != 255)
689 && !s->brush.isOpaque());
690 linearGradient = ((penBrushStyle == Qt::LinearGradientPattern) ||
691 (brushStyle == Qt::LinearGradientPattern));
692 radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) ||
693 (brushStyle == Qt::RadialGradientPattern));
694 extendedRadialGradient = radialGradient && (qt_isExtendedRadialGradient(penBrush) || qt_isExtendedRadialGradient(s->brush));
695 conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) ||
696 (brushStyle == Qt::ConicalGradientPattern));
697 patternBrush = (((penBrushStyle > Qt::SolidPattern
698 && penBrushStyle < Qt::LinearGradientPattern)
699 || penBrushStyle == Qt::TexturePattern) ||
700 ((brushStyle > Qt::SolidPattern
701 && brushStyle < Qt::LinearGradientPattern)
702 || brushStyle == Qt::TexturePattern));
704 bool penTextureAlpha =
false;
705 if (penBrush.style() == Qt::TexturePattern)
706 penTextureAlpha = qHasPixmapTexture(penBrush)
707 ? (penBrush.texture().depth() > 1) && penBrush.texture().hasAlpha()
708 : penBrush.textureImage().hasAlphaChannel();
709 bool brushTextureAlpha =
false;
710 if (s->brush.style() == Qt::TexturePattern) {
711 brushTextureAlpha = qHasPixmapTexture(s->brush)
712 ? (s->brush.texture().depth() > 1) && s->brush.texture().hasAlpha()
713 : s->brush.textureImage().hasAlphaChannel();
715 if (((penBrush.style() == Qt::TexturePattern && penTextureAlpha)
716 || (s->brush.style() == Qt::TexturePattern && brushTextureAlpha))
717 && !engine->hasFeature(QPaintEngine::MaskedBrush))
718 s->emulationSpecifier |= QPaintEngine::MaskedBrush;
720 s->emulationSpecifier &= ~QPaintEngine::MaskedBrush;
723 if (s->state() & (QPaintEngine::DirtyHints
724 | QPaintEngine::DirtyOpacity
725 | QPaintEngine::DirtyBackgroundMode)) {
733 qDebug(
"QPainterPrivate::updateEmulationSpecifier, state=%p\n"
735 " - linearGradient: %d\n"
736 " - radialGradient: %d\n"
737 " - conicalGradient: %d\n"
738 " - patternBrush: %d\n"
747 uint(s->renderHints),
752 if (s->state() & QPaintEngine::DirtyTransform) {
753 xform = !s->matrix.isIdentity();
754 complexXform = !s->matrix.isAffine();
755 }
else if (s->matrix.type() >= QTransform::TxTranslate) {
757 complexXform = !s->matrix.isAffine();
760 const bool brushXform = (s->brush.transform().type() != QTransform::TxNone);
761 const bool penXform = (s->pen.brush().transform().type() != QTransform::TxNone);
763 const bool patternXform = patternBrush && (xform || brushXform || penXform);
766 if (alpha && !engine->hasFeature(QPaintEngine::AlphaBlend))
767 s->emulationSpecifier |= QPaintEngine::AlphaBlend;
769 s->emulationSpecifier &= ~QPaintEngine::AlphaBlend;
772 if (linearGradient && !engine->hasFeature(QPaintEngine::LinearGradientFill))
773 s->emulationSpecifier |= QPaintEngine::LinearGradientFill;
775 s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill;
778 if (extendedRadialGradient || (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill)))
779 s->emulationSpecifier |= QPaintEngine::RadialGradientFill;
781 s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill;
784 if (conicalGradient && !engine->hasFeature(QPaintEngine::ConicalGradientFill))
785 s->emulationSpecifier |= QPaintEngine::ConicalGradientFill;
787 s->emulationSpecifier &= ~QPaintEngine::ConicalGradientFill;
790 if (patternBrush && !engine->hasFeature(QPaintEngine::PatternBrush))
791 s->emulationSpecifier |= QPaintEngine::PatternBrush;
793 s->emulationSpecifier &= ~QPaintEngine::PatternBrush;
796 if (patternXform && !engine->hasFeature(QPaintEngine::PatternTransform))
797 s->emulationSpecifier |= QPaintEngine::PatternTransform;
799 s->emulationSpecifier &= ~QPaintEngine::PatternTransform;
802 if (xform && !engine->hasFeature(QPaintEngine::PrimitiveTransform))
803 s->emulationSpecifier |= QPaintEngine::PrimitiveTransform;
805 s->emulationSpecifier &= ~QPaintEngine::PrimitiveTransform;
808 if (complexXform && !engine->hasFeature(QPaintEngine::PerspectiveTransform))
809 s->emulationSpecifier |= QPaintEngine::PerspectiveTransform;
811 s->emulationSpecifier &= ~QPaintEngine::PerspectiveTransform;
814 if (state->opacity != 1 && !engine->hasFeature(QPaintEngine::ConstantOpacity))
815 s->emulationSpecifier |= QPaintEngine::ConstantOpacity;
817 s->emulationSpecifier &= ~QPaintEngine::ConstantOpacity;
819 bool gradientStretch =
false;
820 bool objectBoundingMode =
false;
821 if (linearGradient || conicalGradient || radialGradient) {
822 QGradient::CoordinateMode brushMode = coordinateMode(s->brush);
823 QGradient::CoordinateMode penMode = coordinateMode(s->pen.brush());
825 gradientStretch |= (brushMode == QGradient::StretchToDeviceMode);
826 gradientStretch |= (penMode == QGradient::StretchToDeviceMode);
828 objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode);
829 objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode);
836 if (objectBoundingMode && !engine->hasFeature(QPaintEngine::ObjectBoundingModeGradients))
837 s->emulationSpecifier |= QPaintEngine::ObjectBoundingModeGradients;
839 s->emulationSpecifier &= ~QPaintEngine::ObjectBoundingModeGradients;
842 if (s->bgMode == Qt::OpaqueMode &&
843 (is_pen_transparent(s->pen) || is_brush_transparent(s->brush)))
854 if (state->composition_mode > QPainter::CompositionMode_Xor &&
855 !engine->hasFeature(QPaintEngine::BlendModes))
856 s->emulationSpecifier |= QPaintEngine::BlendModes;
858 s->emulationSpecifier &= ~QPaintEngine::BlendModes;
1583void QPainter::restore()
1586 if constexpr (qt_show_painter_debug_output)
1587 printf(
"QPainter::restore()\n");
1590 if (d->savedStates.empty()) {
1591 qWarning(
"QPainter::restore: Unbalanced save/restore");
1593 }
else if (!d->engine) {
1594 qWarning(
"QPainter::restore: Painter not active");
1598 const auto tmp = std::exchange(d->state, std::move(d->savedStates.top()));
1599 d->savedStates.pop();
1603 d->checkEmulation();
1604 d->extended->setState(d->state.get());
1610 if (!d->state->clipInfo.isEmpty()
1611 && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) {
1613 tmp->dirtyFlags = QPaintEngine::DirtyClipPath;
1614 tmp->clipOperation = Qt::NoClip;
1615 tmp->clipPath = QPainterPath();
1616 d->engine->updateState(*tmp);
1618 for (
const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
1619 tmp->matrix = info.matrix;
1620 tmp->clipOperation = info.operation;
1621 if (info.clipType == QPainterClipInfo::RectClip) {
1622 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1623 tmp->clipRegion = info.rect;
1624 }
else if (info.clipType == QPainterClipInfo::RegionClip) {
1625 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1626 tmp->clipRegion = info.region;
1628 tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform;
1629 tmp->clipPath = info.path;
1631 d->engine->updateState(*tmp);
1636 d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1637 tmp->changeFlags &= ~uint(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1638 tmp->changeFlags |= QPaintEngine::DirtyTransform;
1641 d->updateState(d->state.get());
1680bool QPainter::begin(QPaintDevice *pd)
1684 if (pd->painters > 0) {
1685 qWarning(
"QPainter::begin: A paint device can only be painted by one painter at a time.");
1689 if (d_ptr->engine) {
1690 qWarning(
"QPainter::begin: Painter already active");
1694 if (QPainterPrivate::attachPainterPrivate(
this, pd))
1699 d->helper_device = pd;
1700 d->original_device = pd;
1702 QPoint redirectionOffset;
1703 QPaintDevice *rpd = pd->redirected(&redirectionOffset);
1708 if constexpr (qt_show_painter_debug_output)
1709 printf(
"QPainter::begin(), device=%p, type=%d\n", pd, pd->devType());
1712 if (pd->devType() == QInternal::Pixmap)
1713 static_cast<QPixmap *>(pd)->detach();
1714 else if (pd->devType() == QInternal::Image)
1715 static_cast<QImage *>(pd)->detach();
1717 d->engine.reset(pd->paintEngine());
1720 qWarning(
"QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());
1726 d->extended = d->engine->isExtended() ?
static_cast<QPaintEngineEx *>(d->engine.get()) :
nullptr;
1727 if (d->emulationEngine)
1728 d->emulationEngine->real_engine = d->extended;
1731 Q_ASSERT(!d->state);
1732 d->state.reset(d->extended ? d->extended->createState(
nullptr) :
new QPainterState);
1733 d->state->painter =
this;
1735 d->state->redirectionMatrix.translate(-redirectionOffset.x(), -redirectionOffset.y());
1736 d->state->brushOrigin = QPointF();
1740 d->extended->setState(d->state.get());
1742 d->engine->state = d->state.get();
1744 switch (pd->devType()) {
1745 case QInternal::Pixmap:
1747 QPixmap *pm =
static_cast<QPixmap *>(pd);
1750 qWarning(
"QPainter::begin: Cannot paint on a null pixmap");
1751 qt_cleanup_painter_state(d);
1755 if (pm->depth() == 1) {
1756 d->state->pen = QPen(Qt::color1);
1757 d->state->brush = QBrush(Qt::color0);
1761 case QInternal::Image:
1763 QImage *img =
static_cast<QImage *>(pd);
1765 if (img->isNull()) {
1766 qWarning(
"QPainter::begin: Cannot paint on a null image");
1767 qt_cleanup_painter_state(d);
1769 }
else if (img->format() == QImage::Format_Indexed8 ||
1770 img->format() == QImage::Format_CMYK8888) {
1772 qWarning() <<
"QPainter::begin: Cannot paint on an image with the"
1775 qt_cleanup_painter_state(d);
1778 if (img->depth() == 1) {
1779 d->state->pen = QPen(Qt::color1);
1780 d->state->brush = QBrush(Qt::color0);
1787 if (d->state->ww == 0)
1788 d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024;
1790 d->engine->setPaintDevice(pd);
1792 bool begun = d->engine->begin(pd);
1794 qWarning(
"QPainter::begin(): Returned false");
1795 if (d->engine->isActive()) {
1798 qt_cleanup_painter_state(d);
1802 d->engine->setActive(begun);
1805 switch (d->original_device->devType()) {
1806 case QInternal::Widget:
1807 d->initFrom(d->original_device);
1811 d->state->layoutDirection = Qt::LayoutDirectionAuto;
1813 d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device());
1817 QRect systemRect = d->engine->systemRect();
1818 if (!systemRect.isEmpty()) {
1819 d->state->ww = d->state->vw = systemRect.width();
1820 d->state->wh = d->state->vh = systemRect.height();
1822 d->state->ww = d->state->vw = pd->metric(QPaintDevice::PdmWidth);
1823 d->state->wh = d->state->vh = pd->metric(QPaintDevice::PdmHeight);
1826 const QPoint coordinateOffset = d->engine->coordinateOffset();
1827 d->state->redirectionMatrix.translate(-coordinateOffset.x(), -coordinateOffset.y());
1829 Q_ASSERT(d->engine->isActive());
1831 if (!d->state->redirectionMatrix.isIdentity() || !qFuzzyCompare(d->effectiveDevicePixelRatio(), qreal(1.0)))
1834 Q_ASSERT(d->engine->isActive());
1835 d->state->renderHints = QPainter::TextAntialiasing;
1836 ++d->device->painters;
1838 d->state->emulationSpecifier = 0;
1840 switch (d->original_device->devType()) {
1841 case QInternal::Widget:
1845 d->initFrom(d->original_device);
2503QRegion QPainter::clipRegion()
const
2505 Q_D(
const QPainter);
2507 qWarning(
"QPainter::clipRegion: Painter not active");
2512 bool lastWasNothing =
true;
2515 const_cast<QPainter *>(
this)->d_ptr->updateInvMatrix();
2518 for (
const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
2519 switch (info.clipType) {
2521 case QPainterClipInfo::RegionClip: {
2522 QTransform matrix = (info.matrix * d->invMatrix);
2523 if (lastWasNothing) {
2524 region = info.region * matrix;
2525 lastWasNothing =
false;
2528 if (info.operation == Qt::IntersectClip)
2529 region &= info.region * matrix;
2530 else if (info.operation == Qt::NoClip) {
2531 lastWasNothing =
true;
2534 region = info.region * matrix;
2538 case QPainterClipInfo::PathClip: {
2539 QTransform matrix = (info.matrix * d->invMatrix);
2540 if (lastWasNothing) {
2541 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2542 info.path.fillRule());
2543 lastWasNothing =
false;
2546 if (info.operation == Qt::IntersectClip) {
2547 region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2548 info.path.fillRule());
2549 }
else if (info.operation == Qt::NoClip) {
2550 lastWasNothing =
true;
2553 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2554 info.path.fillRule());
2559 case QPainterClipInfo::RectClip: {
2560 QTransform matrix = (info.matrix * d->invMatrix);
2561 if (lastWasNothing) {
2562 region = QRegion(info.rect) * matrix;
2563 lastWasNothing =
false;
2566 if (info.operation == Qt::IntersectClip) {
2568 if (matrix.type() <= QTransform::TxScale)
2569 region &= matrix.mapRect(info.rect);
2571 region &= matrix.map(QRegion(info.rect));
2572 }
else if (info.operation == Qt::NoClip) {
2573 lastWasNothing =
true;
2576 region = QRegion(info.rect) * matrix;
2581 case QPainterClipInfo::RectFClip: {
2582 QTransform matrix = (info.matrix * d->invMatrix);
2583 if (lastWasNothing) {
2584 region = QRegion(info.rectf.toRect()) * matrix;
2585 lastWasNothing =
false;
2588 if (info.operation == Qt::IntersectClip) {
2590 if (matrix.type() <= QTransform::TxScale)
2591 region &= matrix.mapRect(info.rectf.toRect());
2593 region &= matrix.map(QRegion(info.rectf.toRect()));
2594 }
else if (info.operation == Qt::NoClip) {
2595 lastWasNothing =
true;
2598 region = QRegion(info.rectf.toRect()) * matrix;
2727void QPainter::setClipRect(
const QRectF &rect, Qt::ClipOperation op)
2733 qWarning(
"QPainter::setClipRect: Painter not active");
2736 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2737 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2738 op = Qt::ReplaceClip;
2740 qreal right = rect.x() + rect.width();
2741 qreal bottom = rect.y() + rect.height();
2742 qreal pts[] = { rect.x(), rect.y(),
2746 QVectorPath vp(pts, 4,
nullptr, QVectorPath::RectangleHint);
2747 d->state->clipEnabled =
true;
2748 d->extended->clip(vp, op);
2749 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2750 d->state->clipInfo.clear();
2751 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2752 d->state->clipOperation = op;
2756 if (qreal(
int(rect.top())) == rect.top()
2757 && qreal(
int(rect.bottom())) == rect.bottom()
2758 && qreal(
int(rect.left())) == rect.left()
2759 && qreal(
int(rect.right())) == rect.right())
2761 setClipRect(rect.toRect(), op);
2765 if (rect.isEmpty()) {
2766 setClipRegion(QRegion(), op);
2772 setClipPath(path, op);
2782void QPainter::setClipRect(
const QRect &rect, Qt::ClipOperation op)
2787 qWarning(
"QPainter::setClipRect: Painter not active");
2790 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2792 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2793 op = Qt::ReplaceClip;
2796 d->state->clipEnabled =
true;
2797 d->extended->clip(rect, op);
2798 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2799 d->state->clipInfo.clear();
2800 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2801 d->state->clipOperation = op;
2805 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2806 op = Qt::ReplaceClip;
2808 d->state->clipRegion = rect;
2809 d->state->clipOperation = op;
2810 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2811 d->state->clipInfo.clear();
2812 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2813 d->state->clipEnabled =
true;
2814 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2815 d->updateState(d->state);
2836void QPainter::setClipRegion(
const QRegion &r, Qt::ClipOperation op)
2840 QRect rect = r.boundingRect();
2841 if constexpr (qt_show_painter_debug_output)
2842 printf(
"QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
2843 r.rectCount(), rect.x(), rect.y(), rect.width(), rect.height());
2846 qWarning(
"QPainter::setClipRegion: Painter not active");
2849 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2851 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2852 op = Qt::ReplaceClip;
2855 d->state->clipEnabled =
true;
2856 d->extended->clip(r, op);
2857 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2858 d->state->clipInfo.clear();
2859 d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix));
2860 d->state->clipOperation = op;
2864 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2865 op = Qt::ReplaceClip;
2867 d->state->clipRegion = r;
2868 d->state->clipOperation = op;
2869 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2870 d->state->clipInfo.clear();
2871 d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix));
2872 d->state->clipEnabled =
true;
2873 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2874 d->updateState(d->state);
3048void QPainter::setClipPath(
const QPainterPath &path, Qt::ClipOperation op)
3051 if constexpr (qt_show_painter_debug_output) {
3052 QRectF b = path.boundingRect();
3053 printf(
"QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n",
3054 path.elementCount(), op, b.x(), b.y(), b.width(), b.height());
3060 qWarning(
"QPainter::setClipPath: Painter not active");
3064 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
3065 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
3066 op = Qt::ReplaceClip;
3069 d->state->clipEnabled =
true;
3070 d->extended->clip(path, op);
3071 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3072 d->state->clipInfo.clear();
3073 d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix));
3074 d->state->clipOperation = op;
3078 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
3079 op = Qt::ReplaceClip;
3081 d->state->clipPath = path;
3082 d->state->clipOperation = op;
3083 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3084 d->state->clipInfo.clear();
3085 d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix));
3086 d->state->clipEnabled =
true;
3087 d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled;
3088 d->updateState(d->state);
3297void QPainter::drawRects(
const QRectF *rects,
int rectCount)
3300 if constexpr (qt_show_painter_debug_output)
3301 printf(
"QPainter::drawRects(), count=%d\n", rectCount);
3306 qWarning(
"QPainter::drawRects: Painter not active");
3314 d->extended->drawRects(rects, rectCount);
3318 d->updateState(d->state);
3320 if (!d->state->emulationSpecifier) {
3321 d->engine->drawRects(rects, rectCount);
3325 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3326 && d->state->matrix.type() == QTransform::TxTranslate) {
3327 for (
int i=0; i<rectCount; ++i) {
3328 QRectF r(rects[i].x() + d->state->matrix.dx(),
3329 rects[i].y() + d->state->matrix.dy(),
3332 d->engine->drawRects(&r, 1);
3335 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3336 for (
int i=0; i<rectCount; ++i) {
3337 QPainterPath rectPath;
3338 rectPath.addRect(rects[i]);
3339 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3342 QPainterPath rectPath;
3343 for (
int i=0; i<rectCount; ++i)
3344 rectPath.addRect(rects[i]);
3345 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3357void QPainter::drawRects(
const QRect *rects,
int rectCount)
3360 if constexpr (qt_show_painter_debug_output)
3361 printf(
"QPainter::drawRects(), count=%d\n", rectCount);
3366 qWarning(
"QPainter::drawRects: Painter not active");
3374 d->extended->drawRects(rects, rectCount);
3378 d->updateState(d->state);
3380 if (!d->state->emulationSpecifier) {
3381 d->engine->drawRects(rects, rectCount);
3385 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3386 && d->state->matrix.type() == QTransform::TxTranslate) {
3387 for (
int i=0; i<rectCount; ++i) {
3388 QRectF r(rects[i].x() + d->state->matrix.dx(),
3389 rects[i].y() + d->state->matrix.dy(),
3393 d->engine->drawRects(&r, 1);
3396 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3397 for (
int i=0; i<rectCount; ++i) {
3398 QPainterPath rectPath;
3399 rectPath.addRect(rects[i]);
3400 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3403 QPainterPath rectPath;
3404 for (
int i=0; i<rectCount; ++i)
3405 rectPath.addRect(rects[i]);
3407 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3457void QPainter::drawPoints(
const QPointF *points,
int pointCount)
3460 if constexpr (qt_show_painter_debug_output)
3461 printf(
"QPainter::drawPoints(), count=%d\n", pointCount);
3466 qWarning(
"QPainter::drawPoints: Painter not active");
3470 if (pointCount <= 0)
3474 d->extended->drawPoints(points, pointCount);
3478 d->updateState(d->state);
3480 if (!d->state->emulationSpecifier) {
3481 d->engine->drawPoints(points, pointCount);
3485 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3486 && d->state->matrix.type() == QTransform::TxTranslate) {
3488 for (
int i=0; i<pointCount; ++i) {
3489 QPointF pt(points[i].x() + d->state->matrix.dx(),
3490 points[i].y() + d->state->matrix.dy());
3491 d->engine->drawPoints(&pt, 1);
3494 QPen pen = d->state->pen;
3495 bool flat_pen = pen.capStyle() == Qt::FlatCap;
3498 pen.setCapStyle(Qt::SquareCap);
3502 for (
int i=0; i<pointCount; ++i) {
3503 path.moveTo(points[i].x(), points[i].y());
3504 path.lineTo(points[i].x() + 0.0001, points[i].y());
3506 d->draw_helper(path, QPainterPrivate::StrokeDraw);
3519void QPainter::drawPoints(
const QPoint *points,
int pointCount)
3522 if constexpr (qt_show_painter_debug_output)
3523 printf(
"QPainter::drawPoints(), count=%d\n", pointCount);
3528 qWarning(
"QPainter::drawPoints: Painter not active");
3532 if (pointCount <= 0)
3536 d->extended->drawPoints(points, pointCount);
3540 d->updateState(d->state);
3542 if (!d->state->emulationSpecifier) {
3543 d->engine->drawPoints(points, pointCount);
3547 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3548 && d->state->matrix.type() == QTransform::TxTranslate) {
3550 for (
int i=0; i<pointCount; ++i) {
3551 QPointF pt(points[i].x() + d->state->matrix.dx(),
3552 points[i].y() + d->state->matrix.dy());
3553 d->engine->drawPoints(&pt, 1);
3556 QPen pen = d->state->pen;
3557 bool flat_pen = (pen.capStyle() == Qt::FlatCap);
3560 pen.setCapStyle(Qt::SquareCap);
3564 for (
int i=0; i<pointCount; ++i) {
3565 path.moveTo(points[i].x(), points[i].y());
3566 path.lineTo(points[i].x() + 0.0001, points[i].y());
3568 d->draw_helper(path, QPainterPrivate::StrokeDraw);
4880void QPainter::drawPixmap(
const QPointF &p,
const QPixmap &pm)
4882#if defined QT_DEBUG_DRAW
4883 if constexpr (qt_show_painter_debug_output)
4884 printf(
"QPainter::drawPixmap(), p=[%.2f,%.2f], pix=[%d,%d]\n",
4886 pm.width(), pm.height());
4891 if (!d->engine || pm.isNull())
4895 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawPixmap()");
4899 d->extended->drawPixmap(p, pm);
4907 int h = pm.height();
4913 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap()) {
4914 fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
4917 d->updateState(d->state);
4919 if ((d->state->matrix.type() > QTransform::TxTranslate
4920 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
4921 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
4922 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
4927 if (d->state->matrix.type() <= QTransform::TxScale) {
4928 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
4933 setBackgroundMode(Qt::TransparentMode);
4934 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
4935 QBrush brush(d->state->pen.color(), pm);
4938 setBrushOrigin(QPointF(0, 0));
4940 drawRect(pm.rect());
4943 if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
4944 x += d->state->matrix.dx();
4945 y += d->state->matrix.dy();
4947 qreal scale = pm.devicePixelRatio();
4948 d->engine->drawPixmap(QRectF(x, y, w / scale, h / scale), pm, QRectF(0, 0, w, h));
4952void QPainter::drawPixmap(
const QRectF &r,
const QPixmap &pm,
const QRectF &sr)
4954#if defined QT_DEBUG_DRAW
4955 if constexpr (qt_show_painter_debug_output)
4956 printf(
"QPainter::drawPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], source=[%.2f,%.2f,%.2f,%.2f]\n",
4957 r.x(), r.y(), r.width(), r.height(),
4958 pm.width(), pm.height(),
4959 sr.x(), sr.y(), sr.width(), sr.height());
4963 if (!d->engine || pm.isNull())
4966 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawPixmap()");
4971 qreal w = r.width();
4972 qreal h = r.height();
4975 qreal sw = sr.width();
4976 qreal sh = sr.height();
4981 const qreal pmscale = pm.devicePixelRatio();
4985 sw = pm.width() - sx;
4988 sh = pm.height() - sy;
4996 qreal w_ratio = sx * w/sw;
5004 qreal h_ratio = sy * h/sh;
5011 if (sw + sx > pm.width()) {
5012 qreal delta = sw - (pm.width() - sx);
5013 qreal w_ratio = delta * w/sw;
5018 if (sh + sy > pm.height()) {
5019 qreal delta = sh - (pm.height() - sy);
5020 qreal h_ratio = delta * h/sh;
5025 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5029 d->extended->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
5034 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap())
5035 fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
5037 d->updateState(d->state);
5039 if ((d->state->matrix.type() > QTransform::TxTranslate
5040 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5041 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5042 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity))
5043 || ((sw != w || sh != h) && !d->engine->hasFeature(QPaintEngine::PixmapTransform)))
5048 if (d->state->matrix.type() <= QTransform::TxScale) {
5049 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5054 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5062 scale(w / sw, h / sh);
5063 setBackgroundMode(Qt::TransparentMode);
5064 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5067 if (sw == pm.width() && sh == pm.height())
5068 brush = QBrush(d->state->pen.color(), pm);
5070 brush = QBrush(d->state->pen.color(), pm.copy(sx, sy, sw, sh));
5075 drawRect(QRectF(0, 0, sw, sh));
5078 if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5079 x += d->state->matrix.dx();
5080 y += d->state->matrix.dy();
5082 d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
5189void QPainter::drawImage(
const QPointF &p,
const QImage &image)
5193 if (!d->engine || image.isNull())
5197 d->extended->drawImage(p, image);
5204 int w = image.width();
5205 int h = image.height();
5206 qreal scale = image.devicePixelRatio();
5208 d->updateState(d->state);
5210 if (((d->state->matrix.type() > QTransform::TxTranslate)
5211 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5212 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5213 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
5218 if (d->state->matrix.type() <= QTransform::TxScale) {
5219 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5224 setBackgroundMode(Qt::TransparentMode);
5225 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5226 QBrush brush(image);
5229 setBrushOrigin(QPointF(0, 0));
5230 drawRect(QRect(QPoint(0, 0), image.size() / scale));
5235 if (d->state->matrix.type() == QTransform::TxTranslate
5236 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5237 x += d->state->matrix.dx();
5238 y += d->state->matrix.dy();
5241 d->engine->drawImage(QRectF(x, y, w / scale, h / scale), image, QRectF(0, 0, w, h), Qt::AutoColor);
5244void QPainter::drawImage(
const QRectF &targetRect,
const QImage &image,
const QRectF &sourceRect,
5245 Qt::ImageConversionFlags flags)
5249 if (!d->engine || image.isNull())
5252 qreal x = targetRect.x();
5253 qreal y = targetRect.y();
5254 qreal w = targetRect.width();
5255 qreal h = targetRect.height();
5256 qreal sx = sourceRect.x();
5257 qreal sy = sourceRect.y();
5258 qreal sw = sourceRect.width();
5259 qreal sh = sourceRect.height();
5260 qreal imageScale = image.devicePixelRatio();
5264 sw = image.width() - sx;
5267 sh = image.height() - sy;
5270 w = sw / imageScale;
5272 h = sh / imageScale;
5275 qreal w_ratio = sx * w/sw;
5283 qreal h_ratio = sy * h/sh;
5290 if (sw + sx > image.width()) {
5291 qreal delta = sw - (image.width() - sx);
5292 qreal w_ratio = delta * w/sw;
5297 if (sh + sy > image.height()) {
5298 qreal delta = sh - (image.height() - sy);
5299 qreal h_ratio = delta * h/sh;
5304 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5308 d->extended->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
5312 d->updateState(d->state);
5314 if (((d->state->matrix.type() > QTransform::TxTranslate || (sw != w || sh != h))
5315 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5316 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5317 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
5322 if (d->state->matrix.type() <= QTransform::TxScale) {
5323 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5328 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5335 scale(w / sw, h / sh);
5336 setBackgroundMode(Qt::TransparentMode);
5337 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5338 QBrush brush(image);
5341 setBrushOrigin(QPointF(-sx, -sy));
5343 drawRect(QRectF(0, 0, sw, sh));
5348 if (d->state->matrix.type() == QTransform::TxTranslate
5349 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5350 x += d->state->matrix.dx();
5351 y += d->state->matrix.dy();
5354 d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
5369void QPainter::drawGlyphRun(
const QPointF &position,
const QGlyphRun &glyphRun)
5374 qWarning(
"QPainter::drawGlyphRun: Painter not active");
5378 QRawFont font = glyphRun.rawFont();
5379 if (!font.isValid())
5382 QGlyphRunPrivate *glyphRun_d = QGlyphRunPrivate::get(glyphRun);
5384 const quint32 *glyphIndexes = glyphRun_d->glyphIndexData;
5385 const QPointF *glyphPositions = glyphRun_d->glyphPositionData;
5387 int count = qMin(glyphRun_d->glyphIndexDataSize, glyphRun_d->glyphPositionDataSize);
5388 QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count);
5390 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
5391 bool engineRequiresPretransformedGlyphPositions = d->extended
5392 ? d->extended->requiresPretransformedGlyphPositions(fontD->fontEngine, d->state->matrix)
5393 : d->engine->type() != QPaintEngine::CoreGraphics && !d->state->matrix.isAffine();
5395 for (
int i=0; i<count; ++i) {
5396 QPointF processedPosition = position + glyphPositions[i];
5397 if (engineRequiresPretransformedGlyphPositions)
5398 processedPosition = d->state->transform().map(processedPosition);
5399 fixedPointPositions[i] = QFixedPoint::fromPointF(processedPosition);
5402 d->drawGlyphs(engineRequiresPretransformedGlyphPositions
5403 ? d->state->transform().map(position)
5406 fixedPointPositions.data(),
5409 glyphRun.overline(),
5410 glyphRun.underline(),
5411 glyphRun.strikeOut());
5415 const quint32 *glyphArray,
5427 if (extended !=
nullptr && state->matrix.isAffine()) {
5428 QStaticTextItem staticTextItem;
5429 staticTextItem.color = state->pen.color();
5430 staticTextItem.font = state->font;
5431 staticTextItem.setFontEngine(fontEngine);
5432 staticTextItem.numGlyphs = glyphCount;
5433 staticTextItem.glyphs =
reinterpret_cast<glyph_t *>(
const_cast<glyph_t *>(glyphArray));
5434 staticTextItem.glyphPositions = positions;
5436 staticTextItem.usesRawFont =
true;
5438 extended->drawStaticTextItem(&staticTextItem);
5441 textItem.fontEngine = fontEngine;
5443 QVarLengthArray<QFixed, 128> advances(glyphCount);
5444 QVarLengthArray<QGlyphJustification, 128> glyphJustifications(glyphCount);
5445 QVarLengthArray<QGlyphAttributes, 128> glyphAttributes(glyphCount);
5446 memset(glyphAttributes.data(), 0, glyphAttributes.size() *
sizeof(QGlyphAttributes));
5447 memset(
static_cast<
void *>(advances.data()), 0, advances.size() *
sizeof(QFixed));
5448 memset(
static_cast<
void *>(glyphJustifications.data()), 0, glyphJustifications.size() *
sizeof(QGlyphJustification));
5450 textItem.glyphs.numGlyphs = glyphCount;
5451 textItem.glyphs.glyphs =
const_cast<glyph_t *>(glyphArray);
5452 textItem.glyphs.offsets = positions;
5453 textItem.glyphs.advances = advances.data();
5454 textItem.glyphs.justifications = glyphJustifications.data();
5455 textItem.glyphs.attributes = glyphAttributes.data();
5457 engine->drawTextItem(QPointF(0, 0), textItem);
5460 qt_draw_decoration_for_glyphs(q,
5539void QPainter::drawStaticText(
const QPointF &topLeftPosition,
const QStaticText &staticText)
5542 if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen)
5545 QStaticTextPrivate *staticText_d =
5546 const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(&staticText));
5548 QFontPrivate *fp = QFontPrivate::get(font());
5549 QFontPrivate *stfp = QFontPrivate::get(staticText_d->font);
5550 if (font() != staticText_d->font || fp ==
nullptr || stfp ==
nullptr || fp->dpi != stfp->dpi) {
5551 staticText_d->font = font();
5552 staticText_d->needsRelayout =
true;
5553 }
else if (stfp->engineData ==
nullptr || stfp->engineData->fontCacheId != QFontCache::instance()->id()) {
5554 staticText_d->needsRelayout =
true;
5557 QFontEngine *fe = staticText_d->font.d->engineForScript(QChar::Script_Common);
5558 if (fe->type() == QFontEngine::Multi)
5559 fe =
static_cast<QFontEngineMulti *>(fe)->engine(0);
5564 if (d->extended ==
nullptr
5565 || !d->state->matrix.isAffine()
5566 || !fe->supportsTransformation(d->state->matrix)) {
5567 staticText_d->paintText(topLeftPosition,
this, pen().color());
5571 bool engineRequiresPretransform = d->extended->requiresPretransformedGlyphPositions(fe, d->state->matrix);
5572 if (staticText_d->untransformedCoordinates && engineRequiresPretransform) {
5575 staticText_d->untransformedCoordinates =
false;
5576 staticText_d->needsRelayout =
true;
5577 }
else if (!staticText_d->untransformedCoordinates && !engineRequiresPretransform) {
5580 staticText_d->untransformedCoordinates =
true;
5581 staticText_d->needsRelayout =
true;
5586 QPointF transformedPosition = topLeftPosition;
5587 if (!staticText_d->untransformedCoordinates)
5588 transformedPosition = transformedPosition * d->state->matrix;
5589 QTransform oldMatrix;
5593 if (d->state->matrix.isTranslating() && !staticText_d->untransformedCoordinates) {
5594 qreal m11 = d->state->matrix.m11();
5595 qreal m12 = d->state->matrix.m12();
5596 qreal m13 = d->state->matrix.m13();
5597 qreal m21 = d->state->matrix.m21();
5598 qreal m22 = d->state->matrix.m22();
5599 qreal m23 = d->state->matrix.m23();
5600 qreal m33 = d->state->matrix.m33();
5602 oldMatrix = d->state->matrix;
5603 d->state->matrix.setMatrix(m11, m12, m13,
5610 bool staticTextNeedsReinit = staticText_d->needsRelayout;
5611 if (!staticText_d->untransformedCoordinates && staticText_d->matrix != d->state->matrix) {
5612 staticText_d->matrix = d->state->matrix;
5613 staticTextNeedsReinit =
true;
5617 if (staticTextNeedsReinit)
5618 staticText_d->init();
5620 if (transformedPosition != staticText_d->position) {
5621 QFixed fx = QFixed::fromReal(transformedPosition.x());
5622 QFixed fy = QFixed::fromReal(transformedPosition.y());
5623 QFixed oldX = QFixed::fromReal(staticText_d->position.x());
5624 QFixed oldY = QFixed::fromReal(staticText_d->position.y());
5625 for (
int item=0; item<staticText_d->itemCount;++item) {
5626 QStaticTextItem *textItem = staticText_d->items + item;
5627 for (
int i=0; i<textItem->numGlyphs; ++i) {
5628 textItem->glyphPositions[i].x += fx - oldX;
5629 textItem->glyphPositions[i].y += fy - oldY;
5631 textItem->userDataNeedsUpdate =
true;
5634 staticText_d->position = transformedPosition;
5637 QPen oldPen = d->state->pen;
5638 QColor currentColor = oldPen.color();
5639 static const QColor bodyIndicator(0, 0, 0, 0);
5640 for (
int i=0; i<staticText_d->itemCount; ++i) {
5641 QStaticTextItem *item = staticText_d->items + i;
5642 if (item->color.isValid() && currentColor != item->color
5643 && item->color != bodyIndicator) {
5644 setPen(item->color);
5645 currentColor = item->color;
5646 }
else if (item->color == bodyIndicator) {
5648 currentColor = oldPen.color();
5650 d->extended->drawStaticTextItem(item);
5652 qt_draw_decoration_for_glyphs(
this,
5655 item->glyphPositions,
5658 staticText_d->font.underline(),
5659 staticText_d->font.overline(),
5660 staticText_d->font.strikeOut());
5662 if (currentColor != oldPen.color())
5665 if (!staticText_d->untransformedCoordinates && oldMatrix.isTranslating())
5666 d->state->matrix = oldMatrix;
5672void QPainter::drawText(
const QPointF &p,
const QString &str,
int tf,
int justificationPadding)
5675 if constexpr (qt_show_painter_debug_output)
5676 printf(
"QPainter::drawText(), pos=[%.2f,%.2f], str='%s'\n", p.x(), p.y(), str.toLatin1().constData());
5681 if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
5684 Q_DECL_UNINITIALIZED QStackTextEngine engine(str, d->state->font);
5685 engine.option.setTextDirection(d->state->layoutDirection);
5686 if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
5687 engine.ignoreBidi =
true;
5688 engine.option.setTextDirection((tf & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
5692 line.length = str.size();
5693 engine.shapeLine(line);
5695 int nItems = engine.layoutData->items.size();
5696 QVarLengthArray<
int> visualOrder(nItems);
5697 QVarLengthArray<uchar> levels(nItems);
5698 for (
int i = 0; i < nItems; ++i)
5699 levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
5700 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
5702 if (justificationPadding > 0) {
5703 engine.option.setAlignment(Qt::AlignJustify);
5704 engine.forceJustification =
true;
5706 line.width = justificationPadding;
5707 engine.justify(line);
5709 QFixed x = QFixed::fromReal(p.x());
5711 for (
int i = 0; i < nItems; ++i) {
5712 int item = visualOrder[i];
5713 const QScriptItem &si = engine.layoutData->items.at(item);
5714 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
5718 QFont f = engine.font(si);
5719 QTextItemInt gf(si, &f);
5720 gf.glyphs = engine.shapedGlyphs(&si);
5721 gf.chars = engine.layoutData->string.unicode() + si.position;
5722 gf.num_chars = engine.length(item);
5723 if (engine.forceJustification) {
5724 for (
int j=0; j<gf.glyphs.numGlyphs; ++j)
5725 gf.width += gf.glyphs.effectiveAdvance(j);
5727 gf.width = si.width;
5729 gf.logClusters = engine.logClusters(&si);
5731 drawTextItem(QPointF(x.toReal(), p.y()), gf);
5999 const qreal radiusBase = qMax(qreal(1), maxRadius);
6001 QString key =
"WaveUnderline-"_L1
6002 % pen.color().name()
6003 % HexString<qreal>(radiusBase)
6004 % HexString<qreal>(pen.widthF());
6007 if (QPixmapCache::find(key, &pixmap))
6010 const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399));
6011 const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod);
6012 const qreal radius = qFloor(radiusBase * 2) / 2.;
6019 while (xs < width) {
6022 path.quadTo(xs - halfPeriod / 2, ys, xs, 0);
6025 pixmap = QPixmap(width, radius * 2);
6026 pixmap.fill(Qt::transparent);
6029 wavePen.setCapStyle(Qt::SquareCap);
6033 const qreal maxPenWidth = .8 * radius;
6034 if (wavePen.widthF() > maxPenWidth)
6035 wavePen.setWidthF(maxPenWidth);
6038 imgPainter.setPen(wavePen);
6039 imgPainter.setRenderHint(QPainter::Antialiasing);
6040 imgPainter.translate(0, radius);
6041 imgPainter.drawPath(path);
6044 QPixmapCache::insert(key, pixmap);
6050 QTextCharFormat::UnderlineStyle underlineStyle,
6051 QTextItem::RenderFlags flags, qreal width,
6052 const QTextCharFormat &charFormat)
6054 if (underlineStyle == QTextCharFormat::NoUnderline
6055 && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
6058 const QPen oldPen = painter->pen();
6059 const QBrush oldBrush = painter->brush();
6060 painter->setBrush(Qt::NoBrush);
6062 pen.setStyle(Qt::SolidLine);
6063 pen.setWidthF(fe->lineThickness().toReal());
6064 pen.setCapStyle(Qt::FlatCap);
6066 QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y());
6068 const qreal underlineOffset = fe->underlinePosition().toReal();
6070 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
6071 QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
6073 underlineStyle = QTextCharFormat::UnderlineStyle(theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt());
6074 if (underlineStyle == QTextCharFormat::SpellCheckUnderline)
6075 underlineStyle = QTextCharFormat::WaveUnderline;
6078 if (underlineStyle == QTextCharFormat::WaveUnderline) {
6080 painter->translate(0, pos.y() + 1);
6081 qreal maxHeight = fe->descent().toReal() - qreal(1);
6083 QColor uc = charFormat.underlineColor();
6088 const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()), maxHeight / qreal(2.)), pen);
6089 const int descent = qFloor(maxHeight);
6091 painter->setBrushOrigin(painter->brushOrigin().x(), 0);
6092 painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
6094 }
else if (underlineStyle != QTextCharFormat::NoUnderline) {
6095 const bool isAntialiasing = painter->renderHints().testFlag(QPainter::Antialiasing);
6096 if (!isAntialiasing)
6097 pen.setWidthF(qMax(fe->lineThickness().round(), QFixed(1)).toReal());
6098 const qreal lineThicknessOffset = pen.widthF() / 2.0;
6102 qreal adjustedUnderlineOffset =
std::ceil(underlineOffset) + lineThicknessOffset;
6103 if (underlineOffset <= fe->descent().toReal())
6104 adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, fe->descent().toReal() - lineThicknessOffset);
6105 const qreal underlinePos = pos.y() + adjustedUnderlineOffset;
6106 QColor uc = charFormat.underlineColor();
6110 pen.setStyle((Qt::PenStyle)(underlineStyle));
6111 painter->setPen(pen);
6112 QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos);
6114 textEngine->addUnderline(painter, underline);
6116 painter->drawLine(underline);
6118 if (!isAntialiasing)
6119 pen.setWidthF(fe->lineThickness().toReal());
6122 pen.setStyle(Qt::SolidLine);
6123 pen.setColor(oldPen.color());
6125 if (flags & QTextItem::StrikeOut) {
6126 QLineF strikeOutLine = line;
6127 strikeOutLine.translate(0., - fe->ascent().toReal() / 3.);
6128 QColor uc = charFormat.underlineColor();
6131 painter->setPen(pen);
6133 textEngine->addStrikeOut(painter, strikeOutLine);
6135 painter->drawLine(strikeOutLine);
6138 if (flags & QTextItem::Overline) {
6139 QLineF overline = line;
6140 overline.translate(0., - fe->ascent().toReal());
6141 QColor uc = charFormat.underlineColor();
6144 painter->setPen(pen);
6146 textEngine->addOverline(painter, overline);
6148 painter->drawLine(overline);
6151 painter->setPen(oldPen);
6152 painter->setBrush(oldBrush);
6205 if constexpr (qt_show_painter_debug_output)
6206 printf(
"QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
6207 p.x(), p.y(), qPrintable(_ti.text()));
6215 QTextItemInt &ti =
const_cast<QTextItemInt &>(
static_cast<
const QTextItemInt &>(_ti));
6217 if (!extended && state->bgMode == Qt::OpaqueMode) {
6218 QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent).toReal());
6219 q->fillRect(rect, state->bgBrush);
6222 if (q->pen().style() == Qt::NoPen)
6225 const QPainter::RenderHints oldRenderHints = state->renderHints;
6226 if (!(state->renderHints & QPainter::Antialiasing) && state->matrix.type() >= QTransform::TxScale) {
6231 const QTransform &m = state->matrix;
6232 if (state->matrix.type() < QTransform::TxShear) {
6233 bool isPlain90DegreeRotation =
6234 (qFuzzyIsNull(m.m11())
6235 && qFuzzyIsNull(m.m12() - qreal(1))
6236 && qFuzzyIsNull(m.m21() + qreal(1))
6237 && qFuzzyIsNull(m.m22())
6240 (qFuzzyIsNull(m.m11() + qreal(1))
6241 && qFuzzyIsNull(m.m12())
6242 && qFuzzyIsNull(m.m21())
6243 && qFuzzyIsNull(m.m22() + qreal(1))
6246 (qFuzzyIsNull(m.m11())
6247 && qFuzzyIsNull(m.m12() + qreal(1))
6248 && qFuzzyIsNull(m.m21() - qreal(1))
6249 && qFuzzyIsNull(m.m22())
6252 aa = !isPlain90DegreeRotation;
6255 q->setRenderHint(QPainter::Antialiasing,
true);
6261 if (!ti.glyphs.numGlyphs) {
6262 drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
6263 ti.flags, ti.width.toReal(), ti.charFormat);
6264 }
else if (ti.fontEngine->type() == QFontEngine::Multi) {
6265 QFontEngineMulti *multi =
static_cast<QFontEngineMulti *>(ti.fontEngine);
6268 int which = glyphs.glyphs[0] >> 24;
6273 bool rtl = ti.flags & QTextItem::RightToLeft;
6275 x += ti.width.toReal();
6279 for (end = 0; end < ti.glyphs.numGlyphs; ++end) {
6280 const int e = glyphs.glyphs[end] >> 24;
6285 multi->ensureEngineAt(which);
6286 QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
6289 for (i = start; i < end; ++i) {
6290 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6291 ti2.width += ti.glyphs.effectiveAdvance(i);
6295 x -= ti2.width.toReal();
6300 engine->drawTextItem(QPointF(x, y), ti2);
6301 drawTextItemDecoration(q,
QPointF(x, y), ti2.fontEngine, textEngine, ti2.underlineStyle,
6302 ti2.flags, ti2.width.toReal(), ti2.charFormat);
6305 x += ti2.width.toReal();
6308 const int hi = which << 24;
6309 for (i = start; i < end; ++i) {
6310 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6318 multi->ensureEngineAt(which);
6319 QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
6322 for (i = start; i < end; ++i) {
6323 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6324 ti2.width += ti.glyphs.effectiveAdvance(i);
6328 x -= ti2.width.toReal();
6333 engine->drawTextItem(QPointF(x,y), ti2);
6334 drawTextItemDecoration(q,
QPointF(x, y), ti2.fontEngine, textEngine, ti2.underlineStyle,
6335 ti2.flags, ti2.width.toReal(), ti2.charFormat);
6338 const int hi = which << 24;
6339 for (i = start; i < end; ++i)
6340 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6346 engine->drawTextItem(p, ti);
6347 drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
6348 ti.flags, ti.width.toReal(), ti.charFormat);
6351 if (state->renderHints != oldRenderHints) {
6352 state->renderHints = oldRenderHints;
6356 state->dirtyFlags |= QPaintEngine::DirtyHints;
6484void QPainter::drawTiledPixmap(
const QRectF &r,
const QPixmap &pixmap,
const QPointF &sp)
6487 if constexpr (qt_show_painter_debug_output)
6488 printf(
"QPainter::drawTiledPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], offset=[%.2f,%.2f]\n",
6489 r.x(), r.y(), r.width(), r.height(),
6490 pixmap.width(), pixmap.height(),
6495 if (!d->engine || pixmap.isNull() || r.isEmpty())
6499 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawTiledPixmap()");
6502 const qreal sw = pixmap.width() / pixmap.devicePixelRatio();
6503 const qreal sh = pixmap.height() / pixmap.devicePixelRatio();
6507 sx = qRound(sw) - qRound(-sx) % qRound(sw);
6509 sx = qRound(sx) % qRound(sw);
6511 sy = qRound(sh) - -qRound(sy) % qRound(sh);
6513 sy = qRound(sy) % qRound(sh);
6517 d->extended->drawTiledPixmap(r, pixmap, QPointF(sx, sy));
6521 if (d->state->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
6522 fillRect(r, d->state->bgBrush);
6524 d->updateState(d->state);
6525 if ((d->state->matrix.type() > QTransform::TxTranslate
6526 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
6527 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
6530 setBackgroundMode(Qt::TransparentMode);
6531 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
6532 setBrush(QBrush(d->state->pen.color(), pixmap));
6537 if (d->state->matrix.type() <= QTransform::TxScale) {
6538 const QPointF p = roundInDeviceCoordinates(r.topLeft(), d->state->matrix);
6540 if (d->state->matrix.type() <= QTransform::TxTranslate) {
6545 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6546 drawRect(QRectF(p, r.size()));
6548 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6557 if (d->state->matrix.type() == QTransform::TxTranslate
6558 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
6559 x += d->state->matrix.dx();
6560 y += d->state->matrix.dy();
6563 d->engine->drawTiledPixmap(QRectF(x, y, r.width(), r.height()), pixmap, QPointF(sx, sy));
7188 const QTextOption *option,
7196 Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=
nullptr) );
7198 if (_r.isEmpty() && !(tf & Qt::TextDontClip)) {
7202 tf |= Qt::TextDontPrint;
7206 alignment |= option->alignment();
7207 if (option->wrapMode() != QTextOption::NoWrap)
7208 tf |= Qt::TextWordWrap;
7210 if (option->flags() & QTextOption::IncludeTrailingSpaces)
7211 tf |= Qt::TextIncludeTrailingSpaces;
7213 if (option->tabStopDistance() >= 0 || !option->tabArray().isEmpty())
7214 tf |= Qt::TextExpandTabs;
7220 bool dontclip = (tf & Qt::TextDontClip);
7221 bool wordwrap = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
7222 bool singleline = (tf & Qt::TextSingleLine);
7223 bool showmnemonic = (tf & Qt::TextShowMnemonic);
7224 bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
7226 Qt::LayoutDirection layout_direction;
7227 if (tf & Qt::TextForceLeftToRight)
7228 layout_direction = Qt::LeftToRight;
7229 else if (tf & Qt::TextForceRightToLeft)
7230 layout_direction = Qt::RightToLeft;
7232 layout_direction = option->textDirection();
7234 layout_direction = painter->layoutDirection();
7236 layout_direction = Qt::LeftToRight;
7238 alignment = QGuiApplicationPrivate::visualAlignment(layout_direction, QFlag(alignment));
7240 bool isRightToLeft = layout_direction == Qt::RightToLeft;
7241 bool expandtabs = ((tf & Qt::TextExpandTabs) &&
7242 (((alignment & Qt::AlignLeft) && !isRightToLeft) ||
7243 ((alignment & Qt::AlignRight) && isRightToLeft)));
7246 tf |= Qt::TextDontPrint;
7248 uint maxUnderlines = 0;
7250 QFontMetricsF fm(fnt);
7254 bool hasMoreLengthVariants =
false;
7257 int old_offset = offset;
7258 for (; offset < text.size(); offset++) {
7259 QChar chr = text.at(offset);
7260 if (chr == u'\r' || (singleline && chr == u'\n')) {
7261 text[offset] = u' ';
7262 }
else if (chr == u'\n') {
7263 text[offset] = QChar::LineSeparator;
7264 }
else if (chr == u'&') {
7266 }
else if (chr == u'\t') {
7268 text[offset] = u' ';
7269 }
else if (!tabarraylen && !tabstops) {
7270 tabstops = qRound(fm.horizontalAdvance(u'x')*8);
7272 }
else if (chr == u'\x9c') {
7274 hasMoreLengthVariants =
true;
7279 QList<QTextLayout::FormatRange> underlineFormats;
7280 int length = offset - old_offset;
7281 if ((hidemnmemonic || showmnemonic) && maxUnderlines > 0) {
7282 QChar *cout = text.data() + old_offset;
7283 QChar *cout0 = cout;
7293 if (*cin != u'&' && !hidemnmemonic && !(tf & Qt::TextDontPrint)) {
7294 QTextLayout::FormatRange range;
7295 range.start = cout - cout0;
7297 range.format.setFontUnderline(
true);
7298 underlineFormats.append(range);
7301 }
else if (hidemnmemonic && *cin == u'(' && l >= 4 &&
7302 cin[1] == u'&' && cin[2] != u'&' &&
7305 while ((cout - n) > cout0 && (cout - n - 1)->isSpace())
7324 QString finalText = text.mid(old_offset, length);
7325 Q_DECL_UNINITIALIZED QStackTextEngine engine(finalText, fnt);
7327 engine.option = *option;
7330 if (engine.option.tabStopDistance() < 0 && tabstops > 0)
7331 engine.option.setTabStopDistance(tabstops);
7333 if (engine.option.tabs().isEmpty() && ta) {
7335 tabs.reserve(tabarraylen);
7336 for (
int i = 0; i < tabarraylen; i++)
7337 tabs.append(qreal(ta[i]));
7338 engine.option.setTabArray(tabs);
7341 engine.option.setTextDirection(layout_direction);
7342 if (alignment & Qt::AlignJustify)
7343 engine.option.setAlignment(Qt::AlignJustify);
7345 engine.option.setAlignment(Qt::AlignLeft);
7347 if (!option && (tf & Qt::TextWrapAnywhere))
7348 engine.option.setWrapMode(QTextOption::WrapAnywhere);
7350 if (tf & Qt::TextJustificationForced)
7351 engine.forceJustification =
true;
7353 textLayout.setCacheEnabled(
true);
7354 textLayout.setFormats(underlineFormats);
7356 if (finalText.isEmpty()) {
7357 height = fm.height();
7359 tf |= Qt::TextDontPrint;
7361 qreal lineWidth = 0x01000000;
7362 if (wordwrap || (tf & Qt::TextJustificationForced))
7363 lineWidth = qMax<qreal>(0, r.width());
7365 tf |= Qt::TextIncludeTrailingSpaces;
7366 textLayout.beginLayout();
7368 qreal leading = fm.leading();
7372 QTextLine l = textLayout.createLine();
7376 l.setLineWidth(lineWidth);
7380 height = qCeil(height);
7382 if (alignment & Qt::AlignBaseline && l.lineNumber() == 0)
7383 height -= l.ascent();
7385 l.setPosition(
QPointF(0., height));
7386 height += textLayout.engine()->lines[l.lineNumber()].height().toReal();
7387 width = qMax(width, l.naturalTextWidth());
7388 if (!dontclip && !brect && height >= r.height())
7391 textLayout.endLayout();
7396 if (alignment & Qt::AlignBottom)
7397 yoff = r.height() - height;
7398 else if (alignment & Qt::AlignVCenter)
7399 yoff = (r.height() - height)/2;
7401 if (alignment & Qt::AlignRight)
7402 xoff = r.width() - width;
7403 else if (alignment & Qt::AlignHCenter)
7404 xoff = (r.width() - width)/2;
7406 QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
7408 if (hasMoreLengthVariants && !(tf & Qt::TextLongestVariant) && !r.contains(bounds)) {
7410 goto start_lengthVariant;
7415 if (!(tf & Qt::TextDontPrint)) {
7416 bool restore =
false;
7417 if (!dontclip && !r.contains(bounds)) {
7420 painter->setClipRect(r, Qt::IntersectClip);
7423 for (
int i = 0; i < textLayout.lineCount(); i++) {
7424 QTextLine line = textLayout.lineAt(i);
7426 eng->enableDelayDecorations();
7428 qreal advance = line.horizontalAdvance();
7430 if (alignment & Qt::AlignRight) {
7431 xoff = r.width() - advance -
7432 eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
7433 }
else if (alignment & Qt::AlignHCenter) {
7434 xoff = (r.width() - advance) / 2;
7437 line.draw(painter,
QPointF(r.x() + xoff, r.y() + yoff));
7438 eng->drawDecorations(painter);
7516QPainterState::QPainterState(
const QPainterState *s)
7517 : brushOrigin(s->brushOrigin), font(s->font), deviceFont(s->deviceFont),
7518 pen(s->pen), brush(s->brush), bgBrush(s->bgBrush),
7519 clipRegion(s->clipRegion), clipPath(s->clipPath),
7520 clipOperation(s->clipOperation),
7521 renderHints(s->renderHints), clipInfo(s->clipInfo),
7522 worldMatrix(s->worldMatrix), matrix(s->matrix), redirectionMatrix(s->redirectionMatrix),
7523 wx(s->wx), wy(s->wy), ww(s->ww), wh(s->wh),
7524 vx(s->vx), vy(s->vy), vw(s->vw), vh(s->vh),
7525 opacity(s->opacity), WxF(s->WxF), VxF(s->VxF),
7526 clipEnabled(s->clipEnabled), bgMode(s->bgMode), painter(s->painter),
7527 layoutDirection(s->layoutDirection),
7528 composition_mode(s->composition_mode),
7529 emulationSpecifier(s->emulationSpecifier), changeFlags(0)
7531 dirtyFlags = s->dirtyFlags;
8152void QPainter::drawPixmapFragments(
const PixmapFragment *fragments,
int fragmentCount,
8153 const QPixmap &pixmap, PixmapFragmentHints hints)
8157 if (!d->engine || pixmap.isNull())
8161 for (
int i = 0; i < fragmentCount; ++i) {
8162 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8163 fragments[i].width, fragments[i].height);
8164 if (!(QRectF(pixmap.rect()).contains(sourceRect)))
8165 qWarning(
"QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle");
8169 if (d->engine->isExtended()) {
8170 d->extended->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
8172 qreal oldOpacity = opacity();
8173 QTransform oldTransform = transform();
8175 for (
int i = 0; i < fragmentCount; ++i) {
8176 QTransform transform = oldTransform;
8179 if (fragments[i].rotation == 0) {
8180 xOffset = fragments[i].x;
8181 yOffset = fragments[i].y;
8183 transform.translate(fragments[i].x, fragments[i].y);
8184 transform.rotate(fragments[i].rotation);
8186 setOpacity(oldOpacity * fragments[i].opacity);
8187 setTransform(transform);
8189 qreal w = fragments[i].scaleX * fragments[i].width;
8190 qreal h = fragments[i].scaleY * fragments[i].height;
8191 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8192 fragments[i].width, fragments[i].height);
8193 drawPixmap(QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pixmap, sourceRect);
8196 setOpacity(oldOpacity);
8197 setTransform(oldTransform);