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;
1596void QPainter::restore()
1599 if constexpr (qt_show_painter_debug_output)
1600 printf(
"QPainter::restore()\n");
1603 if (d->savedStates.empty()) {
1604 qWarning(
"QPainter::restore: Unbalanced save/restore");
1606 }
else if (!d->engine) {
1607 qWarning(
"QPainter::restore: Painter not active");
1611 const auto tmp = std::exchange(d->state, std::move(d->savedStates.top()));
1612 d->savedStates.pop();
1616 d->checkEmulation();
1617 d->extended->setState(d->state.get());
1623 if (!d->state->clipInfo.isEmpty()
1624 && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) {
1626 tmp->dirtyFlags = QPaintEngine::DirtyClipPath;
1627 tmp->clipOperation = Qt::NoClip;
1628 tmp->clipPath = QPainterPath();
1629 d->engine->updateState(*tmp);
1631 for (
const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
1632 tmp->matrix = info.matrix;
1633 tmp->clipOperation = info.operation;
1634 if (info.clipType == QPainterClipInfo::RectClip) {
1635 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1636 tmp->clipRegion = info.rect;
1637 }
else if (info.clipType == QPainterClipInfo::RegionClip) {
1638 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1639 tmp->clipRegion = info.region;
1641 tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform;
1642 tmp->clipPath = info.path;
1644 d->engine->updateState(*tmp);
1649 d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1650 tmp->changeFlags &= ~uint(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1651 tmp->changeFlags |= QPaintEngine::DirtyTransform;
1654 d->updateState(d->state.get());
1693bool QPainter::begin(QPaintDevice *pd)
1697 if (pd->painters > 0) {
1698 qWarning(
"QPainter::begin: A paint device can only be painted by one painter at a time.");
1702 if (d_ptr->engine) {
1703 qWarning(
"QPainter::begin: Painter already active");
1707 if (QPainterPrivate::attachPainterPrivate(
this, pd))
1712 d->helper_device = pd;
1713 d->original_device = pd;
1715 QPoint redirectionOffset;
1716 QPaintDevice *rpd = pd->redirected(&redirectionOffset);
1721 if constexpr (qt_show_painter_debug_output)
1722 printf(
"QPainter::begin(), device=%p, type=%d\n", pd, pd->devType());
1725 if (pd->devType() == QInternal::Pixmap)
1726 static_cast<QPixmap *>(pd)->detach();
1727 else if (pd->devType() == QInternal::Image)
1728 static_cast<QImage *>(pd)->detach();
1730 d->engine.reset(pd->paintEngine());
1733 qWarning(
"QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());
1739 d->extended = d->engine->isExtended() ?
static_cast<QPaintEngineEx *>(d->engine.get()) :
nullptr;
1740 if (d->emulationEngine)
1741 d->emulationEngine->real_engine = d->extended;
1744 Q_ASSERT(!d->state);
1745 d->state.reset(d->extended ? d->extended->createState(
nullptr) :
new QPainterState);
1746 d->state->painter =
this;
1748 d->state->redirectionMatrix.translate(-redirectionOffset.x(), -redirectionOffset.y());
1749 d->state->brushOrigin = QPointF();
1753 d->extended->setState(d->state.get());
1755 d->engine->state = d->state.get();
1757 switch (pd->devType()) {
1758 case QInternal::Pixmap:
1760 QPixmap *pm =
static_cast<QPixmap *>(pd);
1763 qWarning(
"QPainter::begin: Cannot paint on a null pixmap");
1764 qt_cleanup_painter_state(d);
1768 if (pm->depth() == 1) {
1769 d->state->pen = QPen(Qt::color1);
1770 d->state->brush = QBrush(Qt::color0);
1774 case QInternal::Image:
1776 QImage *img =
static_cast<QImage *>(pd);
1778 if (img->isNull()) {
1779 qWarning(
"QPainter::begin: Cannot paint on a null image");
1780 qt_cleanup_painter_state(d);
1782 }
else if (img->format() == QImage::Format_Indexed8 ||
1783 img->format() == QImage::Format_CMYK8888) {
1785 qWarning() <<
"QPainter::begin: Cannot paint on an image with the"
1788 qt_cleanup_painter_state(d);
1791 if (img->depth() == 1) {
1792 d->state->pen = QPen(Qt::color1);
1793 d->state->brush = QBrush(Qt::color0);
1800 if (d->state->ww == 0)
1801 d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024;
1803 d->engine->setPaintDevice(pd);
1805 bool begun = d->engine->begin(pd);
1807 qWarning(
"QPainter::begin(): Returned false");
1808 if (d->engine->isActive()) {
1811 qt_cleanup_painter_state(d);
1815 d->engine->setActive(begun);
1818 switch (d->original_device->devType()) {
1819 case QInternal::Widget:
1820 d->initFrom(d->original_device);
1824 d->state->layoutDirection = Qt::LayoutDirectionAuto;
1826 d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device());
1830 QRect systemRect = d->engine->systemRect();
1831 if (!systemRect.isEmpty()) {
1832 d->state->ww = d->state->vw = systemRect.width();
1833 d->state->wh = d->state->vh = systemRect.height();
1835 d->state->ww = d->state->vw = pd->metric(QPaintDevice::PdmWidth);
1836 d->state->wh = d->state->vh = pd->metric(QPaintDevice::PdmHeight);
1839 const QPoint coordinateOffset = d->engine->coordinateOffset();
1840 d->state->redirectionMatrix.translate(-coordinateOffset.x(), -coordinateOffset.y());
1842 Q_ASSERT(d->engine->isActive());
1844 if (!d->state->redirectionMatrix.isIdentity() || !qFuzzyCompare(d->effectiveDevicePixelRatio(), qreal(1.0)))
1847 Q_ASSERT(d->engine->isActive());
1848 d->state->renderHints = QPainter::TextAntialiasing;
1849 ++d->device->painters;
1851 d->state->emulationSpecifier = 0;
1853 switch (d->original_device->devType()) {
1854 case QInternal::Widget:
1858 d->initFrom(d->original_device);
2520QRegion QPainter::clipRegion()
const
2522 Q_D(
const QPainter);
2524 qWarning(
"QPainter::clipRegion: Painter not active");
2529 bool lastWasNothing =
true;
2532 const_cast<QPainter *>(
this)->d_ptr->updateInvMatrix();
2535 for (
const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
2536 switch (info.clipType) {
2538 case QPainterClipInfo::RegionClip: {
2539 QTransform matrix = (info.matrix * d->invMatrix);
2540 if (lastWasNothing) {
2541 region = info.region * matrix;
2542 lastWasNothing =
false;
2545 if (info.operation == Qt::IntersectClip)
2546 region &= info.region * matrix;
2547 else if (info.operation == Qt::NoClip) {
2548 lastWasNothing =
true;
2551 region = info.region * matrix;
2555 case QPainterClipInfo::PathClip: {
2556 QTransform matrix = (info.matrix * d->invMatrix);
2557 if (lastWasNothing) {
2558 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2559 info.path.fillRule());
2560 lastWasNothing =
false;
2563 if (info.operation == Qt::IntersectClip) {
2564 region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2565 info.path.fillRule());
2566 }
else if (info.operation == Qt::NoClip) {
2567 lastWasNothing =
true;
2570 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2571 info.path.fillRule());
2576 case QPainterClipInfo::RectClip: {
2577 QTransform matrix = (info.matrix * d->invMatrix);
2578 if (lastWasNothing) {
2579 region = QRegion(info.rect) * matrix;
2580 lastWasNothing =
false;
2583 if (info.operation == Qt::IntersectClip) {
2585 if (matrix.type() <= QTransform::TxScale)
2586 region &= matrix.mapRect(info.rect);
2588 region &= matrix.map(QRegion(info.rect));
2589 }
else if (info.operation == Qt::NoClip) {
2590 lastWasNothing =
true;
2593 region = QRegion(info.rect) * matrix;
2598 case QPainterClipInfo::RectFClip: {
2599 QTransform matrix = (info.matrix * d->invMatrix);
2600 if (lastWasNothing) {
2601 region = QRegion(info.rectf.toRect()) * matrix;
2602 lastWasNothing =
false;
2605 if (info.operation == Qt::IntersectClip) {
2607 if (matrix.type() <= QTransform::TxScale)
2608 region &= matrix.mapRect(info.rectf.toRect());
2610 region &= matrix.map(QRegion(info.rectf.toRect()));
2611 }
else if (info.operation == Qt::NoClip) {
2612 lastWasNothing =
true;
2615 region = QRegion(info.rectf.toRect()) * matrix;
2744void QPainter::setClipRect(
const QRectF &rect, Qt::ClipOperation op)
2750 qWarning(
"QPainter::setClipRect: Painter not active");
2753 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2754 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2755 op = Qt::ReplaceClip;
2757 qreal right = rect.x() + rect.width();
2758 qreal bottom = rect.y() + rect.height();
2759 qreal pts[] = { rect.x(), rect.y(),
2763 QVectorPath vp(pts, 4,
nullptr, QVectorPath::RectangleHint);
2764 d->state->clipEnabled =
true;
2765 d->extended->clip(vp, op);
2766 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2767 d->state->clipInfo.clear();
2768 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2769 d->state->clipOperation = op;
2773 if (qreal(
int(rect.top())) == rect.top()
2774 && qreal(
int(rect.bottom())) == rect.bottom()
2775 && qreal(
int(rect.left())) == rect.left()
2776 && qreal(
int(rect.right())) == rect.right())
2778 setClipRect(rect.toRect(), op);
2782 if (rect.isEmpty()) {
2783 setClipRegion(QRegion(), op);
2789 setClipPath(path, op);
2799void QPainter::setClipRect(
const QRect &rect, Qt::ClipOperation op)
2804 qWarning(
"QPainter::setClipRect: Painter not active");
2807 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2809 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2810 op = Qt::ReplaceClip;
2813 d->state->clipEnabled =
true;
2814 d->extended->clip(rect, op);
2815 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2816 d->state->clipInfo.clear();
2817 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2818 d->state->clipOperation = op;
2822 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2823 op = Qt::ReplaceClip;
2825 d->state->clipRegion = rect;
2826 d->state->clipOperation = op;
2827 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2828 d->state->clipInfo.clear();
2829 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2830 d->state->clipEnabled =
true;
2831 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2832 d->updateState(d->state);
2853void QPainter::setClipRegion(
const QRegion &r, Qt::ClipOperation op)
2857 QRect rect = r.boundingRect();
2858 if constexpr (qt_show_painter_debug_output)
2859 printf(
"QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
2860 r.rectCount(), rect.x(), rect.y(), rect.width(), rect.height());
2863 qWarning(
"QPainter::setClipRegion: Painter not active");
2866 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2868 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2869 op = Qt::ReplaceClip;
2872 d->state->clipEnabled =
true;
2873 d->extended->clip(r, op);
2874 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2875 d->state->clipInfo.clear();
2876 d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix));
2877 d->state->clipOperation = op;
2881 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2882 op = Qt::ReplaceClip;
2884 d->state->clipRegion = r;
2885 d->state->clipOperation = op;
2886 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2887 d->state->clipInfo.clear();
2888 d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix));
2889 d->state->clipEnabled =
true;
2890 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2891 d->updateState(d->state);
3065void QPainter::setClipPath(
const QPainterPath &path, Qt::ClipOperation op)
3068 if constexpr (qt_show_painter_debug_output) {
3069 QRectF b = path.boundingRect();
3070 printf(
"QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n",
3071 path.elementCount(), op, b.x(), b.y(), b.width(), b.height());
3077 qWarning(
"QPainter::setClipPath: Painter not active");
3081 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
3082 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
3083 op = Qt::ReplaceClip;
3086 d->state->clipEnabled =
true;
3087 d->extended->clip(path, op);
3088 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3089 d->state->clipInfo.clear();
3090 d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix));
3091 d->state->clipOperation = op;
3095 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
3096 op = Qt::ReplaceClip;
3098 d->state->clipPath = path;
3099 d->state->clipOperation = op;
3100 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3101 d->state->clipInfo.clear();
3102 d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix));
3103 d->state->clipEnabled =
true;
3104 d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled;
3105 d->updateState(d->state);
3314void QPainter::drawRects(
const QRectF *rects,
int rectCount)
3317 if constexpr (qt_show_painter_debug_output)
3318 printf(
"QPainter::drawRects(), count=%d\n", rectCount);
3323 qWarning(
"QPainter::drawRects: Painter not active");
3331 d->extended->drawRects(rects, rectCount);
3335 d->updateState(d->state);
3337 if (!d->state->emulationSpecifier) {
3338 d->engine->drawRects(rects, rectCount);
3342 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3343 && d->state->matrix.type() == QTransform::TxTranslate) {
3344 for (
int i=0; i<rectCount; ++i) {
3345 QRectF r(rects[i].x() + d->state->matrix.dx(),
3346 rects[i].y() + d->state->matrix.dy(),
3349 d->engine->drawRects(&r, 1);
3352 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3353 for (
int i=0; i<rectCount; ++i) {
3354 QPainterPath rectPath;
3355 rectPath.addRect(rects[i]);
3356 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3359 QPainterPath rectPath;
3360 for (
int i=0; i<rectCount; ++i)
3361 rectPath.addRect(rects[i]);
3362 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3374void QPainter::drawRects(
const QRect *rects,
int rectCount)
3377 if constexpr (qt_show_painter_debug_output)
3378 printf(
"QPainter::drawRects(), count=%d\n", rectCount);
3383 qWarning(
"QPainter::drawRects: Painter not active");
3391 d->extended->drawRects(rects, rectCount);
3395 d->updateState(d->state);
3397 if (!d->state->emulationSpecifier) {
3398 d->engine->drawRects(rects, rectCount);
3402 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3403 && d->state->matrix.type() == QTransform::TxTranslate) {
3404 for (
int i=0; i<rectCount; ++i) {
3405 QRectF r(rects[i].x() + d->state->matrix.dx(),
3406 rects[i].y() + d->state->matrix.dy(),
3410 d->engine->drawRects(&r, 1);
3413 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3414 for (
int i=0; i<rectCount; ++i) {
3415 QPainterPath rectPath;
3416 rectPath.addRect(rects[i]);
3417 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3420 QPainterPath rectPath;
3421 for (
int i=0; i<rectCount; ++i)
3422 rectPath.addRect(rects[i]);
3424 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3474void QPainter::drawPoints(
const QPointF *points,
int pointCount)
3477 if constexpr (qt_show_painter_debug_output)
3478 printf(
"QPainter::drawPoints(), count=%d\n", pointCount);
3483 qWarning(
"QPainter::drawPoints: Painter not active");
3487 if (pointCount <= 0)
3491 d->extended->drawPoints(points, pointCount);
3495 d->updateState(d->state);
3497 if (!d->state->emulationSpecifier) {
3498 d->engine->drawPoints(points, pointCount);
3502 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3503 && d->state->matrix.type() == QTransform::TxTranslate) {
3505 for (
int i=0; i<pointCount; ++i) {
3506 QPointF pt(points[i].x() + d->state->matrix.dx(),
3507 points[i].y() + d->state->matrix.dy());
3508 d->engine->drawPoints(&pt, 1);
3511 QPen pen = d->state->pen;
3512 bool flat_pen = pen.capStyle() == Qt::FlatCap;
3515 pen.setCapStyle(Qt::SquareCap);
3519 for (
int i=0; i<pointCount; ++i) {
3520 path.moveTo(points[i].x(), points[i].y());
3521 path.lineTo(points[i].x() + 0.0001, points[i].y());
3523 d->draw_helper(path, QPainterPrivate::StrokeDraw);
3536void QPainter::drawPoints(
const QPoint *points,
int pointCount)
3539 if constexpr (qt_show_painter_debug_output)
3540 printf(
"QPainter::drawPoints(), count=%d\n", pointCount);
3545 qWarning(
"QPainter::drawPoints: Painter not active");
3549 if (pointCount <= 0)
3553 d->extended->drawPoints(points, pointCount);
3557 d->updateState(d->state);
3559 if (!d->state->emulationSpecifier) {
3560 d->engine->drawPoints(points, pointCount);
3564 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3565 && d->state->matrix.type() == QTransform::TxTranslate) {
3567 for (
int i=0; i<pointCount; ++i) {
3568 QPointF pt(points[i].x() + d->state->matrix.dx(),
3569 points[i].y() + d->state->matrix.dy());
3570 d->engine->drawPoints(&pt, 1);
3573 QPen pen = d->state->pen;
3574 bool flat_pen = (pen.capStyle() == Qt::FlatCap);
3577 pen.setCapStyle(Qt::SquareCap);
3581 for (
int i=0; i<pointCount; ++i) {
3582 path.moveTo(points[i].x(), points[i].y());
3583 path.lineTo(points[i].x() + 0.0001, points[i].y());
3585 d->draw_helper(path, QPainterPrivate::StrokeDraw);
4906void QPainter::drawPixmap(
const QPointF &p,
const QPixmap &pm)
4908#if defined QT_DEBUG_DRAW
4909 if constexpr (qt_show_painter_debug_output)
4910 printf(
"QPainter::drawPixmap(), p=[%.2f,%.2f], pix=[%d,%d]\n",
4912 pm.width(), pm.height());
4917 if (!d->engine || pm.isNull())
4921 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawPixmap()");
4925 d->extended->drawPixmap(p, pm);
4933 int h = pm.height();
4939 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap()) {
4940 fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
4943 d->updateState(d->state);
4945 if ((d->state->matrix.type() > QTransform::TxTranslate
4946 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
4947 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
4948 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
4953 if (d->state->matrix.type() <= QTransform::TxScale) {
4954 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
4959 setBackgroundMode(Qt::TransparentMode);
4960 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
4961 QBrush brush(d->state->pen.color(), pm);
4964 setBrushOrigin(QPointF(0, 0));
4966 drawRect(pm.rect());
4969 if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
4970 x += d->state->matrix.dx();
4971 y += d->state->matrix.dy();
4973 qreal scale = pm.devicePixelRatio();
4974 d->engine->drawPixmap(QRectF(x, y, w / scale, h / scale), pm, QRectF(0, 0, w, h));
4978void QPainter::drawPixmap(
const QRectF &r,
const QPixmap &pm,
const QRectF &sr)
4980#if defined QT_DEBUG_DRAW
4981 if constexpr (qt_show_painter_debug_output)
4982 printf(
"QPainter::drawPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], source=[%.2f,%.2f,%.2f,%.2f]\n",
4983 r.x(), r.y(), r.width(), r.height(),
4984 pm.width(), pm.height(),
4985 sr.x(), sr.y(), sr.width(), sr.height());
4989 if (!d->engine || pm.isNull())
4992 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawPixmap()");
4997 qreal w = r.width();
4998 qreal h = r.height();
5001 qreal sw = sr.width();
5002 qreal sh = sr.height();
5007 const qreal pmscale = pm.devicePixelRatio();
5011 sw = pm.width() - sx;
5014 sh = pm.height() - sy;
5022 qreal w_ratio = sx * w/sw;
5030 qreal h_ratio = sy * h/sh;
5037 if (sw + sx > pm.width()) {
5038 qreal delta = sw - (pm.width() - sx);
5039 qreal w_ratio = delta * w/sw;
5044 if (sh + sy > pm.height()) {
5045 qreal delta = sh - (pm.height() - sy);
5046 qreal h_ratio = delta * h/sh;
5051 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5055 d->extended->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
5060 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap())
5061 fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
5063 d->updateState(d->state);
5065 if ((d->state->matrix.type() > QTransform::TxTranslate
5066 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5067 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5068 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity))
5069 || ((sw != w || sh != h) && !d->engine->hasFeature(QPaintEngine::PixmapTransform)))
5074 if (d->state->matrix.type() <= QTransform::TxScale) {
5075 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5080 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5088 scale(w / sw, h / sh);
5089 setBackgroundMode(Qt::TransparentMode);
5090 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5093 if (sw == pm.width() && sh == pm.height())
5094 brush = QBrush(d->state->pen.color(), pm);
5096 brush = QBrush(d->state->pen.color(), pm.copy(sx, sy, sw, sh));
5101 drawRect(QRectF(0, 0, sw, sh));
5104 if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5105 x += d->state->matrix.dx();
5106 y += d->state->matrix.dy();
5108 d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
5215void QPainter::drawImage(
const QPointF &p,
const QImage &image)
5219 if (!d->engine || image.isNull())
5223 d->extended->drawImage(p, image);
5230 int w = image.width();
5231 int h = image.height();
5232 qreal scale = image.devicePixelRatio();
5234 d->updateState(d->state);
5236 if (((d->state->matrix.type() > QTransform::TxTranslate)
5237 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5238 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5239 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
5244 if (d->state->matrix.type() <= QTransform::TxScale) {
5245 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5250 setBackgroundMode(Qt::TransparentMode);
5251 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5252 QBrush brush(image);
5255 setBrushOrigin(QPointF(0, 0));
5256 drawRect(QRect(QPoint(0, 0), image.size() / scale));
5261 if (d->state->matrix.type() == QTransform::TxTranslate
5262 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5263 x += d->state->matrix.dx();
5264 y += d->state->matrix.dy();
5267 d->engine->drawImage(QRectF(x, y, w / scale, h / scale), image, QRectF(0, 0, w, h), Qt::AutoColor);
5270void QPainter::drawImage(
const QRectF &targetRect,
const QImage &image,
const QRectF &sourceRect,
5271 Qt::ImageConversionFlags flags)
5275 if (!d->engine || image.isNull())
5278 qreal x = targetRect.x();
5279 qreal y = targetRect.y();
5280 qreal w = targetRect.width();
5281 qreal h = targetRect.height();
5282 qreal sx = sourceRect.x();
5283 qreal sy = sourceRect.y();
5284 qreal sw = sourceRect.width();
5285 qreal sh = sourceRect.height();
5286 qreal imageScale = image.devicePixelRatio();
5290 sw = image.width() - sx;
5293 sh = image.height() - sy;
5296 w = sw / imageScale;
5298 h = sh / imageScale;
5301 qreal w_ratio = sx * w/sw;
5309 qreal h_ratio = sy * h/sh;
5316 if (sw + sx > image.width()) {
5317 qreal delta = sw - (image.width() - sx);
5318 qreal w_ratio = delta * w/sw;
5323 if (sh + sy > image.height()) {
5324 qreal delta = sh - (image.height() - sy);
5325 qreal h_ratio = delta * h/sh;
5330 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5334 d->extended->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
5338 d->updateState(d->state);
5340 if (((d->state->matrix.type() > QTransform::TxTranslate || (sw != w || sh != h))
5341 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5342 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5343 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
5348 if (d->state->matrix.type() <= QTransform::TxScale) {
5349 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5354 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5361 scale(w / sw, h / sh);
5362 setBackgroundMode(Qt::TransparentMode);
5363 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5364 QBrush brush(image);
5367 setBrushOrigin(QPointF(-sx, -sy));
5369 drawRect(QRectF(0, 0, sw, sh));
5374 if (d->state->matrix.type() == QTransform::TxTranslate
5375 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5376 x += d->state->matrix.dx();
5377 y += d->state->matrix.dy();
5380 d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
5395void QPainter::drawGlyphRun(
const QPointF &position,
const QGlyphRun &glyphRun)
5400 qWarning(
"QPainter::drawGlyphRun: Painter not active");
5404 QRawFont font = glyphRun.rawFont();
5405 if (!font.isValid())
5408 QGlyphRunPrivate *glyphRun_d = QGlyphRunPrivate::get(glyphRun);
5410 const quint32 *glyphIndexes = glyphRun_d->glyphIndexData;
5411 const QPointF *glyphPositions = glyphRun_d->glyphPositionData;
5413 int count = qMin(glyphRun_d->glyphIndexDataSize, glyphRun_d->glyphPositionDataSize);
5414 QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count);
5416 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
5417 bool engineRequiresPretransformedGlyphPositions = d->extended
5418 ? d->extended->requiresPretransformedGlyphPositions(fontD->fontEngine, d->state->matrix)
5419 : d->engine->type() != QPaintEngine::CoreGraphics && !d->state->matrix.isAffine();
5421 for (
int i=0; i<count; ++i) {
5422 QPointF processedPosition = position + glyphPositions[i];
5423 if (engineRequiresPretransformedGlyphPositions)
5424 processedPosition = d->state->transform().map(processedPosition);
5425 fixedPointPositions[i] = QFixedPoint::fromPointF(processedPosition);
5428 d->drawGlyphs(engineRequiresPretransformedGlyphPositions
5429 ? d->state->transform().map(position)
5432 fixedPointPositions.data(),
5435 glyphRun.overline(),
5436 glyphRun.underline(),
5437 glyphRun.strikeOut());
5441 const quint32 *glyphArray,
5453 if (extended !=
nullptr && state->matrix.isAffine()) {
5454 QStaticTextItem staticTextItem;
5455 staticTextItem.color = state->pen.color();
5456 staticTextItem.font = state->font;
5457 staticTextItem.setFontEngine(fontEngine);
5458 staticTextItem.numGlyphs = glyphCount;
5459 staticTextItem.glyphs =
reinterpret_cast<glyph_t *>(
const_cast<glyph_t *>(glyphArray));
5460 staticTextItem.glyphPositions = positions;
5462 staticTextItem.usesRawFont =
true;
5464 extended->drawStaticTextItem(&staticTextItem);
5467 textItem.fontEngine = fontEngine;
5469 QVarLengthArray<QFixed, 128> advances(glyphCount);
5470 QVarLengthArray<QGlyphJustification, 128> glyphJustifications(glyphCount);
5471 QVarLengthArray<QGlyphAttributes, 128> glyphAttributes(glyphCount);
5472 memset(glyphAttributes.data(), 0, glyphAttributes.size() *
sizeof(QGlyphAttributes));
5473 memset(
static_cast<
void *>(advances.data()), 0, advances.size() *
sizeof(QFixed));
5474 memset(
static_cast<
void *>(glyphJustifications.data()), 0, glyphJustifications.size() *
sizeof(QGlyphJustification));
5476 textItem.glyphs.numGlyphs = glyphCount;
5477 textItem.glyphs.glyphs =
const_cast<glyph_t *>(glyphArray);
5478 textItem.glyphs.offsets = positions;
5479 textItem.glyphs.advances = advances.data();
5480 textItem.glyphs.justifications = glyphJustifications.data();
5481 textItem.glyphs.attributes = glyphAttributes.data();
5483 engine->drawTextItem(QPointF(0, 0), textItem);
5486 qt_draw_decoration_for_glyphs(q,
5565void QPainter::drawStaticText(
const QPointF &topLeftPosition,
const QStaticText &staticText)
5568 if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen)
5571 QStaticTextPrivate *staticText_d =
5572 const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(&staticText));
5574 QFontPrivate *fp = QFontPrivate::get(font());
5575 QFontPrivate *stfp = QFontPrivate::get(staticText_d->font);
5576 if (font() != staticText_d->font || fp ==
nullptr || stfp ==
nullptr || fp->dpi != stfp->dpi) {
5577 staticText_d->font = font();
5578 staticText_d->needsRelayout =
true;
5579 }
else if (stfp->engineData ==
nullptr || stfp->engineData->fontCacheId != QFontCache::instance()->id()) {
5580 staticText_d->needsRelayout =
true;
5583 QFontEngine *fe = staticText_d->font.d->engineForScript(QChar::Script_Common);
5584 if (fe->type() == QFontEngine::Multi)
5585 fe =
static_cast<QFontEngineMulti *>(fe)->engine(0);
5590 if (d->extended ==
nullptr
5591 || !d->state->matrix.isAffine()
5592 || !fe->supportsTransformation(d->state->matrix)) {
5593 staticText_d->paintText(topLeftPosition,
this, pen().color());
5597 bool engineRequiresPretransform = d->extended->requiresPretransformedGlyphPositions(fe, d->state->matrix);
5598 if (staticText_d->untransformedCoordinates && engineRequiresPretransform) {
5601 staticText_d->untransformedCoordinates =
false;
5602 staticText_d->needsRelayout =
true;
5603 }
else if (!staticText_d->untransformedCoordinates && !engineRequiresPretransform) {
5606 staticText_d->untransformedCoordinates =
true;
5607 staticText_d->needsRelayout =
true;
5612 QPointF transformedPosition = topLeftPosition;
5613 if (!staticText_d->untransformedCoordinates)
5614 transformedPosition = transformedPosition * d->state->matrix;
5615 QTransform oldMatrix;
5619 if (d->state->matrix.isTranslating() && !staticText_d->untransformedCoordinates) {
5620 qreal m11 = d->state->matrix.m11();
5621 qreal m12 = d->state->matrix.m12();
5622 qreal m13 = d->state->matrix.m13();
5623 qreal m21 = d->state->matrix.m21();
5624 qreal m22 = d->state->matrix.m22();
5625 qreal m23 = d->state->matrix.m23();
5626 qreal m33 = d->state->matrix.m33();
5628 oldMatrix = d->state->matrix;
5629 d->state->matrix.setMatrix(m11, m12, m13,
5636 bool staticTextNeedsReinit = staticText_d->needsRelayout;
5637 if (!staticText_d->untransformedCoordinates && staticText_d->matrix != d->state->matrix) {
5638 staticText_d->matrix = d->state->matrix;
5639 staticTextNeedsReinit =
true;
5643 if (staticTextNeedsReinit)
5644 staticText_d->init();
5646 if (transformedPosition != staticText_d->position) {
5647 QFixed fx = QFixed::fromReal(transformedPosition.x());
5648 QFixed fy = QFixed::fromReal(transformedPosition.y());
5649 QFixed oldX = QFixed::fromReal(staticText_d->position.x());
5650 QFixed oldY = QFixed::fromReal(staticText_d->position.y());
5651 for (
int item=0; item<staticText_d->itemCount;++item) {
5652 QStaticTextItem *textItem = staticText_d->items + item;
5653 for (
int i=0; i<textItem->numGlyphs; ++i) {
5654 textItem->glyphPositions[i].x += fx - oldX;
5655 textItem->glyphPositions[i].y += fy - oldY;
5657 textItem->userDataNeedsUpdate =
true;
5660 staticText_d->position = transformedPosition;
5663 QPen oldPen = d->state->pen;
5664 QColor currentColor = oldPen.color();
5665 static const QColor bodyIndicator(0, 0, 0, 0);
5666 for (
int i=0; i<staticText_d->itemCount; ++i) {
5667 QStaticTextItem *item = staticText_d->items + i;
5668 if (item->color.isValid() && currentColor != item->color
5669 && item->color != bodyIndicator) {
5670 setPen(item->color);
5671 currentColor = item->color;
5672 }
else if (item->color == bodyIndicator) {
5674 currentColor = oldPen.color();
5676 d->extended->drawStaticTextItem(item);
5678 qt_draw_decoration_for_glyphs(
this,
5681 item->glyphPositions,
5684 staticText_d->font.underline(),
5685 staticText_d->font.overline(),
5686 staticText_d->font.strikeOut());
5688 if (currentColor != oldPen.color())
5691 if (!staticText_d->untransformedCoordinates && oldMatrix.isTranslating())
5692 d->state->matrix = oldMatrix;
5698void QPainter::drawText(
const QPointF &p,
const QString &str,
int tf,
int justificationPadding)
5701 if constexpr (qt_show_painter_debug_output)
5702 printf(
"QPainter::drawText(), pos=[%.2f,%.2f], str='%s'\n", p.x(), p.y(), str.toLatin1().constData());
5707 if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
5710 Q_DECL_UNINITIALIZED QStackTextEngine engine(str, d->state->font);
5711 engine.option.setTextDirection(d->state->layoutDirection);
5712 if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
5713 engine.ignoreBidi =
true;
5714 engine.option.setTextDirection((tf & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
5718 line.length = str.size();
5719 engine.shapeLine(line);
5721 int nItems = engine.layoutData->items.size();
5722 QVarLengthArray<
int> visualOrder(nItems);
5723 QVarLengthArray<uchar> levels(nItems);
5724 for (
int i = 0; i < nItems; ++i)
5725 levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
5726 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
5728 if (justificationPadding > 0) {
5729 engine.option.setAlignment(Qt::AlignJustify);
5730 engine.forceJustification =
true;
5732 line.width = justificationPadding;
5733 engine.justify(line);
5735 QFixed x = QFixed::fromReal(p.x());
5737 for (
int i = 0; i < nItems; ++i) {
5738 int item = visualOrder[i];
5739 const QScriptItem &si = engine.layoutData->items.at(item);
5740 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
5744 QFont f = engine.font(si);
5745 QTextItemInt gf(si, &f);
5746 gf.glyphs = engine.shapedGlyphs(&si);
5747 gf.chars = engine.layoutData->string.unicode() + si.position;
5748 gf.num_chars = engine.length(item);
5749 if (engine.forceJustification) {
5750 for (
int j=0; j<gf.glyphs.numGlyphs; ++j)
5751 gf.width += gf.glyphs.effectiveAdvance(j);
5753 gf.width = si.width;
5755 gf.logClusters = engine.logClusters(&si);
5757 drawTextItem(QPointF(x.toReal(), p.y()), gf);
6025 const qreal radiusBase = qMax(qreal(1), maxRadius);
6027 QString key =
"WaveUnderline-"_L1
6028 % pen.color().name()
6029 % HexString<qreal>(radiusBase)
6030 % HexString<qreal>(pen.widthF());
6033 if (QPixmapCache::find(key, &pixmap))
6036 const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399));
6037 const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod);
6038 const qreal radius = qFloor(radiusBase * 2) / 2.;
6045 while (xs < width) {
6048 path.quadTo(xs - halfPeriod / 2, ys, xs, 0);
6051 pixmap = QPixmap(width, radius * 2);
6052 pixmap.fill(Qt::transparent);
6055 wavePen.setCapStyle(Qt::SquareCap);
6059 const qreal maxPenWidth = .8 * radius;
6060 if (wavePen.widthF() > maxPenWidth)
6061 wavePen.setWidthF(maxPenWidth);
6064 imgPainter.setPen(wavePen);
6065 imgPainter.setRenderHint(QPainter::Antialiasing);
6066 imgPainter.translate(0, radius);
6067 imgPainter.drawPath(path);
6070 QPixmapCache::insert(key, pixmap);
6076 QTextCharFormat::UnderlineStyle underlineStyle,
6077 QTextItem::RenderFlags flags, qreal width,
6078 const QTextCharFormat &charFormat)
6080 if (underlineStyle == QTextCharFormat::NoUnderline
6081 && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
6084 const QPen oldPen = painter->pen();
6085 const QBrush oldBrush = painter->brush();
6086 painter->setBrush(Qt::NoBrush);
6088 pen.setStyle(Qt::SolidLine);
6089 pen.setWidthF(fe->lineThickness().toReal());
6090 pen.setCapStyle(Qt::FlatCap);
6092 QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y());
6094 const qreal underlineOffset = fe->underlinePosition().toReal();
6096 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
6097 QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
6099 underlineStyle = QTextCharFormat::UnderlineStyle(theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt());
6100 if (underlineStyle == QTextCharFormat::SpellCheckUnderline)
6101 underlineStyle = QTextCharFormat::WaveUnderline;
6104 if (underlineStyle == QTextCharFormat::WaveUnderline) {
6106 painter->translate(0, pos.y() + 1);
6107 qreal maxHeight = fe->descent().toReal() - qreal(1);
6109 QColor uc = charFormat.underlineColor();
6114 const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()), maxHeight / qreal(2.)), pen);
6115 const int descent = qFloor(maxHeight);
6117 painter->setBrushOrigin(painter->brushOrigin().x(), 0);
6118 painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
6120 }
else if (underlineStyle != QTextCharFormat::NoUnderline) {
6121 const bool isAntialiasing = painter->renderHints().testFlag(QPainter::Antialiasing);
6122 if (!isAntialiasing)
6123 pen.setWidthF(qMax(fe->lineThickness().round(), QFixed(1)).toReal());
6124 const qreal lineThicknessOffset = pen.widthF() / 2.0;
6128 qreal adjustedUnderlineOffset =
std::ceil(underlineOffset) + lineThicknessOffset;
6129 if (underlineOffset <= fe->descent().toReal())
6130 adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, fe->descent().toReal() - lineThicknessOffset);
6131 const qreal underlinePos = pos.y() + adjustedUnderlineOffset;
6132 QColor uc = charFormat.underlineColor();
6136 pen.setStyle((Qt::PenStyle)(underlineStyle));
6137 painter->setPen(pen);
6138 QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos);
6140 textEngine->addUnderline(painter, underline);
6142 painter->drawLine(underline);
6144 if (!isAntialiasing)
6145 pen.setWidthF(fe->lineThickness().toReal());
6148 pen.setStyle(Qt::SolidLine);
6149 pen.setColor(oldPen.color());
6151 if (flags & QTextItem::StrikeOut) {
6152 QLineF strikeOutLine = line;
6153 strikeOutLine.translate(0., - fe->ascent().toReal() / 3.);
6154 QColor uc = charFormat.underlineColor();
6157 painter->setPen(pen);
6159 textEngine->addStrikeOut(painter, strikeOutLine);
6161 painter->drawLine(strikeOutLine);
6164 if (flags & QTextItem::Overline) {
6165 QLineF overline = line;
6166 overline.translate(0., - fe->ascent().toReal());
6167 QColor uc = charFormat.underlineColor();
6170 painter->setPen(pen);
6172 textEngine->addOverline(painter, overline);
6174 painter->drawLine(overline);
6177 painter->setPen(oldPen);
6178 painter->setBrush(oldBrush);
6231 if constexpr (qt_show_painter_debug_output)
6232 printf(
"QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
6233 p.x(), p.y(), qPrintable(_ti.text()));
6241 QTextItemInt &ti =
const_cast<QTextItemInt &>(
static_cast<
const QTextItemInt &>(_ti));
6243 if (!extended && state->bgMode == Qt::OpaqueMode) {
6244 QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent).toReal());
6245 q->fillRect(rect, state->bgBrush);
6248 if (q->pen().style() == Qt::NoPen)
6251 const QPainter::RenderHints oldRenderHints = state->renderHints;
6252 if (!(state->renderHints & QPainter::Antialiasing) && state->matrix.type() >= QTransform::TxScale) {
6257 const QTransform &m = state->matrix;
6258 if (state->matrix.type() < QTransform::TxShear) {
6259 bool isPlain90DegreeRotation =
6260 (qFuzzyIsNull(m.m11())
6261 && qFuzzyIsNull(m.m12() - qreal(1))
6262 && qFuzzyIsNull(m.m21() + qreal(1))
6263 && qFuzzyIsNull(m.m22())
6266 (qFuzzyIsNull(m.m11() + qreal(1))
6267 && qFuzzyIsNull(m.m12())
6268 && qFuzzyIsNull(m.m21())
6269 && qFuzzyIsNull(m.m22() + qreal(1))
6272 (qFuzzyIsNull(m.m11())
6273 && qFuzzyIsNull(m.m12() + qreal(1))
6274 && qFuzzyIsNull(m.m21() - qreal(1))
6275 && qFuzzyIsNull(m.m22())
6278 aa = !isPlain90DegreeRotation;
6281 q->setRenderHint(QPainter::Antialiasing,
true);
6287 if (!ti.glyphs.numGlyphs) {
6288 drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
6289 ti.flags, ti.width.toReal(), ti.charFormat);
6290 }
else if (ti.fontEngine->type() == QFontEngine::Multi) {
6291 QFontEngineMulti *multi =
static_cast<QFontEngineMulti *>(ti.fontEngine);
6294 int which = glyphs.glyphs[0] >> 24;
6299 bool rtl = ti.flags & QTextItem::RightToLeft;
6301 x += ti.width.toReal();
6305 for (end = 0; end < ti.glyphs.numGlyphs; ++end) {
6306 const int e = glyphs.glyphs[end] >> 24;
6311 multi->ensureEngineAt(which);
6312 QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
6315 for (i = start; i < end; ++i) {
6316 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6317 ti2.width += ti.glyphs.effectiveAdvance(i);
6321 x -= ti2.width.toReal();
6326 engine->drawTextItem(QPointF(x, y), ti2);
6327 drawTextItemDecoration(q,
QPointF(x, y), ti2.fontEngine, textEngine, ti2.underlineStyle,
6328 ti2.flags, ti2.width.toReal(), ti2.charFormat);
6331 x += ti2.width.toReal();
6334 const int hi = which << 24;
6335 for (i = start; i < end; ++i) {
6336 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6344 multi->ensureEngineAt(which);
6345 QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
6348 for (i = start; i < end; ++i) {
6349 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6350 ti2.width += ti.glyphs.effectiveAdvance(i);
6354 x -= ti2.width.toReal();
6359 engine->drawTextItem(QPointF(x,y), ti2);
6360 drawTextItemDecoration(q,
QPointF(x, y), ti2.fontEngine, textEngine, ti2.underlineStyle,
6361 ti2.flags, ti2.width.toReal(), ti2.charFormat);
6364 const int hi = which << 24;
6365 for (i = start; i < end; ++i)
6366 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6372 engine->drawTextItem(p, ti);
6373 drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
6374 ti.flags, ti.width.toReal(), ti.charFormat);
6377 if (state->renderHints != oldRenderHints) {
6378 state->renderHints = oldRenderHints;
6382 state->dirtyFlags |= QPaintEngine::DirtyHints;
6510void QPainter::drawTiledPixmap(
const QRectF &r,
const QPixmap &pixmap,
const QPointF &sp)
6513 if constexpr (qt_show_painter_debug_output)
6514 printf(
"QPainter::drawTiledPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], offset=[%.2f,%.2f]\n",
6515 r.x(), r.y(), r.width(), r.height(),
6516 pixmap.width(), pixmap.height(),
6521 if (!d->engine || pixmap.isNull() || r.isEmpty())
6525 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawTiledPixmap()");
6528 const qreal sw = pixmap.width() / pixmap.devicePixelRatio();
6529 const qreal sh = pixmap.height() / pixmap.devicePixelRatio();
6533 sx = qRound(sw) - qRound(-sx) % qRound(sw);
6535 sx = qRound(sx) % qRound(sw);
6537 sy = qRound(sh) - -qRound(sy) % qRound(sh);
6539 sy = qRound(sy) % qRound(sh);
6543 d->extended->drawTiledPixmap(r, pixmap, QPointF(sx, sy));
6547 if (d->state->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
6548 fillRect(r, d->state->bgBrush);
6550 d->updateState(d->state);
6551 if ((d->state->matrix.type() > QTransform::TxTranslate
6552 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
6553 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
6556 setBackgroundMode(Qt::TransparentMode);
6557 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
6558 setBrush(QBrush(d->state->pen.color(), pixmap));
6563 if (d->state->matrix.type() <= QTransform::TxScale) {
6564 const QPointF p = roundInDeviceCoordinates(r.topLeft(), d->state->matrix);
6566 if (d->state->matrix.type() <= QTransform::TxTranslate) {
6571 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6572 drawRect(QRectF(p, r.size()));
6574 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6583 if (d->state->matrix.type() == QTransform::TxTranslate
6584 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
6585 x += d->state->matrix.dx();
6586 y += d->state->matrix.dy();
6589 d->engine->drawTiledPixmap(QRectF(x, y, r.width(), r.height()), pixmap, QPointF(sx, sy));
7214 const QTextOption *option,
7222 Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=
nullptr) );
7224 if (_r.isEmpty() && !(tf & Qt::TextDontClip)) {
7228 tf |= Qt::TextDontPrint;
7232 alignment |= option->alignment();
7233 if (option->wrapMode() != QTextOption::NoWrap)
7234 tf |= Qt::TextWordWrap;
7236 if (option->flags() & QTextOption::IncludeTrailingSpaces)
7237 tf |= Qt::TextIncludeTrailingSpaces;
7239 if (option->tabStopDistance() >= 0 || !option->tabArray().isEmpty())
7240 tf |= Qt::TextExpandTabs;
7246 bool dontclip = (tf & Qt::TextDontClip);
7247 bool wordwrap = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
7248 bool singleline = (tf & Qt::TextSingleLine);
7249 bool showmnemonic = (tf & Qt::TextShowMnemonic);
7250 bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
7252 Qt::LayoutDirection layout_direction;
7253 if (tf & Qt::TextForceLeftToRight)
7254 layout_direction = Qt::LeftToRight;
7255 else if (tf & Qt::TextForceRightToLeft)
7256 layout_direction = Qt::RightToLeft;
7258 layout_direction = option->textDirection();
7260 layout_direction = painter->layoutDirection();
7262 layout_direction = Qt::LeftToRight;
7264 alignment = QGuiApplicationPrivate::visualAlignment(layout_direction, QFlag(alignment));
7266 bool isRightToLeft = layout_direction == Qt::RightToLeft;
7267 bool expandtabs = ((tf & Qt::TextExpandTabs) &&
7268 (((alignment & Qt::AlignLeft) && !isRightToLeft) ||
7269 ((alignment & Qt::AlignRight) && isRightToLeft)));
7272 tf |= Qt::TextDontPrint;
7274 uint maxUnderlines = 0;
7276 QFontMetricsF fm(fnt);
7280 bool hasMoreLengthVariants =
false;
7283 int old_offset = offset;
7284 for (; offset < text.size(); offset++) {
7285 QChar chr = text.at(offset);
7286 if (chr == u'\r' || (singleline && chr == u'\n')) {
7287 text[offset] = u' ';
7288 }
else if (chr == u'\n') {
7289 text[offset] = QChar::LineSeparator;
7290 }
else if (chr == u'&') {
7292 }
else if (chr == u'\t') {
7294 text[offset] = u' ';
7295 }
else if (!tabarraylen && !tabstops) {
7296 tabstops = qRound(fm.horizontalAdvance(u'x')*8);
7298 }
else if (chr == u'\x9c') {
7300 hasMoreLengthVariants =
true;
7305 QList<QTextLayout::FormatRange> underlineFormats;
7306 int length = offset - old_offset;
7307 if ((hidemnmemonic || showmnemonic) && maxUnderlines > 0) {
7308 QChar *cout = text.data() + old_offset;
7309 QChar *cout0 = cout;
7319 if (*cin != u'&' && !hidemnmemonic && !(tf & Qt::TextDontPrint)) {
7320 QTextLayout::FormatRange range;
7321 range.start = cout - cout0;
7323 range.format.setFontUnderline(
true);
7324 underlineFormats.append(range);
7327 }
else if (hidemnmemonic && *cin == u'(' && l >= 4 &&
7328 cin[1] == u'&' && cin[2] != u'&' &&
7331 while ((cout - n) > cout0 && (cout - n - 1)->isSpace())
7350 QString finalText = text.mid(old_offset, length);
7351 Q_DECL_UNINITIALIZED QStackTextEngine engine(finalText, fnt);
7353 engine.option = *option;
7356 if (engine.option.tabStopDistance() < 0 && tabstops > 0)
7357 engine.option.setTabStopDistance(tabstops);
7359 if (engine.option.tabs().isEmpty() && ta) {
7361 tabs.reserve(tabarraylen);
7362 for (
int i = 0; i < tabarraylen; i++)
7363 tabs.append(qreal(ta[i]));
7364 engine.option.setTabArray(tabs);
7367 engine.option.setTextDirection(layout_direction);
7368 if (alignment & Qt::AlignJustify)
7369 engine.option.setAlignment(Qt::AlignJustify);
7371 engine.option.setAlignment(Qt::AlignLeft);
7373 if (!option && (tf & Qt::TextWrapAnywhere))
7374 engine.option.setWrapMode(QTextOption::WrapAnywhere);
7376 if (tf & Qt::TextJustificationForced)
7377 engine.forceJustification =
true;
7379 textLayout.setCacheEnabled(
true);
7380 textLayout.setFormats(underlineFormats);
7382 if (finalText.isEmpty()) {
7383 height = fm.height();
7385 tf |= Qt::TextDontPrint;
7387 qreal lineWidth = 0x01000000;
7388 if (wordwrap || (tf & Qt::TextJustificationForced))
7389 lineWidth = qMax<qreal>(0, r.width());
7391 tf |= Qt::TextIncludeTrailingSpaces;
7392 textLayout.beginLayout();
7394 qreal leading = fm.leading();
7398 QTextLine l = textLayout.createLine();
7402 l.setLineWidth(lineWidth);
7406 height = qCeil(height);
7408 if (alignment & Qt::AlignBaseline && l.lineNumber() == 0)
7409 height -= l.ascent();
7411 l.setPosition(
QPointF(0., height));
7412 height += textLayout.engine()->lines[l.lineNumber()].height().toReal();
7413 width = qMax(width, l.naturalTextWidth());
7414 if (!dontclip && !brect && height >= r.height())
7417 textLayout.endLayout();
7422 if (alignment & Qt::AlignBottom)
7423 yoff = r.height() - height;
7424 else if (alignment & Qt::AlignVCenter)
7425 yoff = (r.height() - height)/2;
7427 if (alignment & Qt::AlignRight)
7428 xoff = r.width() - width;
7429 else if (alignment & Qt::AlignHCenter)
7430 xoff = (r.width() - width)/2;
7432 QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
7434 if (hasMoreLengthVariants && !(tf & Qt::TextLongestVariant) && !r.contains(bounds)) {
7436 goto start_lengthVariant;
7441 if (!(tf & Qt::TextDontPrint)) {
7442 bool restore =
false;
7443 if (!dontclip && !r.contains(bounds)) {
7446 painter->setClipRect(r, Qt::IntersectClip);
7449 for (
int i = 0; i < textLayout.lineCount(); i++) {
7450 QTextLine line = textLayout.lineAt(i);
7452 eng->enableDelayDecorations();
7454 qreal advance = line.horizontalAdvance();
7456 if (alignment & Qt::AlignRight) {
7457 xoff = r.width() - advance -
7458 eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
7459 }
else if (alignment & Qt::AlignHCenter) {
7460 xoff = (r.width() - advance) / 2;
7463 line.draw(painter,
QPointF(r.x() + xoff, r.y() + yoff));
7464 eng->drawDecorations(painter);
7542QPainterState::QPainterState(
const QPainterState *s)
7543 : brushOrigin(s->brushOrigin), font(s->font), deviceFont(s->deviceFont),
7544 pen(s->pen), brush(s->brush), bgBrush(s->bgBrush),
7545 clipRegion(s->clipRegion), clipPath(s->clipPath),
7546 clipOperation(s->clipOperation),
7547 renderHints(s->renderHints), clipInfo(s->clipInfo),
7548 worldMatrix(s->worldMatrix), matrix(s->matrix), redirectionMatrix(s->redirectionMatrix),
7549 wx(s->wx), wy(s->wy), ww(s->ww), wh(s->wh),
7550 vx(s->vx), vy(s->vy), vw(s->vw), vh(s->vh),
7551 opacity(s->opacity), WxF(s->WxF), VxF(s->VxF),
7552 clipEnabled(s->clipEnabled), bgMode(s->bgMode), painter(s->painter),
7553 layoutDirection(s->layoutDirection),
7554 composition_mode(s->composition_mode),
7555 emulationSpecifier(s->emulationSpecifier), changeFlags(0)
7557 dirtyFlags = s->dirtyFlags;
8178void QPainter::drawPixmapFragments(
const PixmapFragment *fragments,
int fragmentCount,
8179 const QPixmap &pixmap, PixmapFragmentHints hints)
8183 if (!d->engine || pixmap.isNull())
8187 for (
int i = 0; i < fragmentCount; ++i) {
8188 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8189 fragments[i].width, fragments[i].height);
8190 if (!(QRectF(pixmap.rect()).contains(sourceRect)))
8191 qWarning(
"QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle");
8195 if (d->engine->isExtended()) {
8196 d->extended->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
8198 qreal oldOpacity = opacity();
8199 QTransform oldTransform = transform();
8201 for (
int i = 0; i < fragmentCount; ++i) {
8202 QTransform transform = oldTransform;
8205 if (fragments[i].rotation == 0) {
8206 xOffset = fragments[i].x;
8207 yOffset = fragments[i].y;
8209 transform.translate(fragments[i].x, fragments[i].y);
8210 transform.rotate(fragments[i].rotation);
8212 setOpacity(oldOpacity * fragments[i].opacity);
8213 setTransform(transform);
8215 qreal w = fragments[i].scaleX * fragments[i].width;
8216 qreal h = fragments[i].scaleY * fragments[i].height;
8217 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8218 fragments[i].width, fragments[i].height);
8219 drawPixmap(QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pixmap, sourceRect);
8222 setOpacity(oldOpacity);
8223 setTransform(oldTransform);