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;
1588void QPainter::restore()
1591 if constexpr (qt_show_painter_debug_output)
1592 printf(
"QPainter::restore()\n");
1595 if (d->savedStates.empty()) {
1596 qWarning(
"QPainter::restore: Unbalanced save/restore");
1598 }
else if (!d->engine) {
1599 qWarning(
"QPainter::restore: Painter not active");
1603 const auto tmp = std::exchange(d->state, std::move(d->savedStates.top()));
1604 d->savedStates.pop();
1608 d->checkEmulation();
1609 d->extended->setState(d->state.get());
1615 if (!d->state->clipInfo.isEmpty()
1616 && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) {
1618 tmp->dirtyFlags = QPaintEngine::DirtyClipPath;
1619 tmp->clipOperation = Qt::NoClip;
1620 tmp->clipPath = QPainterPath();
1621 d->engine->updateState(*tmp);
1623 for (
const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
1624 tmp->matrix = info.matrix;
1625 tmp->clipOperation = info.operation;
1626 if (info.clipType == QPainterClipInfo::RectClip) {
1627 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1628 tmp->clipRegion = info.rect;
1629 }
else if (info.clipType == QPainterClipInfo::RegionClip) {
1630 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1631 tmp->clipRegion = info.region;
1633 tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform;
1634 tmp->clipPath = info.path;
1636 d->engine->updateState(*tmp);
1641 d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1642 tmp->changeFlags &= ~uint(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1643 tmp->changeFlags |= QPaintEngine::DirtyTransform;
1646 d->updateState(d->state.get());
1685bool QPainter::begin(QPaintDevice *pd)
1689 if (pd->painters > 0) {
1690 qWarning(
"QPainter::begin: A paint device can only be painted by one painter at a time.");
1694 if (d_ptr->engine) {
1695 qWarning(
"QPainter::begin: Painter already active");
1699 if (QPainterPrivate::attachPainterPrivate(
this, pd))
1704 d->helper_device = pd;
1705 d->original_device = pd;
1707 QPoint redirectionOffset;
1708 QPaintDevice *rpd = pd->redirected(&redirectionOffset);
1713 if constexpr (qt_show_painter_debug_output)
1714 printf(
"QPainter::begin(), device=%p, type=%d\n", pd, pd->devType());
1717 if (pd->devType() == QInternal::Pixmap)
1718 static_cast<QPixmap *>(pd)->detach();
1719 else if (pd->devType() == QInternal::Image)
1720 static_cast<QImage *>(pd)->detach();
1722 d->engine.reset(pd->paintEngine());
1725 qWarning(
"QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());
1731 d->extended = d->engine->isExtended() ?
static_cast<QPaintEngineEx *>(d->engine.get()) :
nullptr;
1732 if (d->emulationEngine)
1733 d->emulationEngine->real_engine = d->extended;
1736 Q_ASSERT(!d->state);
1737 d->state.reset(d->extended ? d->extended->createState(
nullptr) :
new QPainterState);
1738 d->state->painter =
this;
1740 d->state->redirectionMatrix.translate(-redirectionOffset.x(), -redirectionOffset.y());
1741 d->state->brushOrigin = QPointF();
1745 d->extended->setState(d->state.get());
1747 d->engine->state = d->state.get();
1749 switch (pd->devType()) {
1750 case QInternal::Pixmap:
1752 QPixmap *pm =
static_cast<QPixmap *>(pd);
1755 qWarning(
"QPainter::begin: Cannot paint on a null pixmap");
1756 qt_cleanup_painter_state(d);
1760 if (pm->depth() == 1) {
1761 d->state->pen = QPen(Qt::color1);
1762 d->state->brush = QBrush(Qt::color0);
1766 case QInternal::Image:
1768 QImage *img =
static_cast<QImage *>(pd);
1770 if (img->isNull()) {
1771 qWarning(
"QPainter::begin: Cannot paint on a null image");
1772 qt_cleanup_painter_state(d);
1774 }
else if (img->format() == QImage::Format_Indexed8 ||
1775 img->format() == QImage::Format_CMYK8888) {
1777 qWarning() <<
"QPainter::begin: Cannot paint on an image with the"
1780 qt_cleanup_painter_state(d);
1783 if (img->depth() == 1) {
1784 d->state->pen = QPen(Qt::color1);
1785 d->state->brush = QBrush(Qt::color0);
1792 if (d->state->ww == 0)
1793 d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024;
1795 d->engine->setPaintDevice(pd);
1797 bool begun = d->engine->begin(pd);
1799 qWarning(
"QPainter::begin(): Returned false");
1800 if (d->engine->isActive()) {
1803 qt_cleanup_painter_state(d);
1807 d->engine->setActive(begun);
1810 switch (d->original_device->devType()) {
1811 case QInternal::Widget:
1812 d->initFrom(d->original_device);
1816 d->state->layoutDirection = Qt::LayoutDirectionAuto;
1818 d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device());
1822 QRect systemRect = d->engine->systemRect();
1823 if (!systemRect.isEmpty()) {
1824 d->state->ww = d->state->vw = systemRect.width();
1825 d->state->wh = d->state->vh = systemRect.height();
1827 d->state->ww = d->state->vw = pd->metric(QPaintDevice::PdmWidth);
1828 d->state->wh = d->state->vh = pd->metric(QPaintDevice::PdmHeight);
1831 const QPoint coordinateOffset = d->engine->coordinateOffset();
1832 d->state->redirectionMatrix.translate(-coordinateOffset.x(), -coordinateOffset.y());
1834 Q_ASSERT(d->engine->isActive());
1836 if (!d->state->redirectionMatrix.isIdentity() || !qFuzzyCompare(d->effectiveDevicePixelRatio(), qreal(1.0)))
1839 Q_ASSERT(d->engine->isActive());
1840 d->state->renderHints = QPainter::TextAntialiasing;
1841 ++d->device->painters;
1843 d->state->emulationSpecifier = 0;
1845 switch (d->original_device->devType()) {
1846 case QInternal::Widget:
1850 d->initFrom(d->original_device);
2508QRegion QPainter::clipRegion()
const
2510 Q_D(
const QPainter);
2512 qWarning(
"QPainter::clipRegion: Painter not active");
2517 bool lastWasNothing =
true;
2520 const_cast<QPainter *>(
this)->d_ptr->updateInvMatrix();
2523 for (
const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
2524 switch (info.clipType) {
2526 case QPainterClipInfo::RegionClip: {
2527 QTransform matrix = (info.matrix * d->invMatrix);
2528 if (lastWasNothing) {
2529 region = info.region * matrix;
2530 lastWasNothing =
false;
2533 if (info.operation == Qt::IntersectClip)
2534 region &= info.region * matrix;
2535 else if (info.operation == Qt::NoClip) {
2536 lastWasNothing =
true;
2539 region = info.region * matrix;
2543 case QPainterClipInfo::PathClip: {
2544 QTransform matrix = (info.matrix * d->invMatrix);
2545 if (lastWasNothing) {
2546 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2547 info.path.fillRule());
2548 lastWasNothing =
false;
2551 if (info.operation == Qt::IntersectClip) {
2552 region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2553 info.path.fillRule());
2554 }
else if (info.operation == Qt::NoClip) {
2555 lastWasNothing =
true;
2558 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2559 info.path.fillRule());
2564 case QPainterClipInfo::RectClip: {
2565 QTransform matrix = (info.matrix * d->invMatrix);
2566 if (lastWasNothing) {
2567 region = QRegion(info.rect) * matrix;
2568 lastWasNothing =
false;
2571 if (info.operation == Qt::IntersectClip) {
2573 if (matrix.type() <= QTransform::TxScale)
2574 region &= matrix.mapRect(info.rect);
2576 region &= matrix.map(QRegion(info.rect));
2577 }
else if (info.operation == Qt::NoClip) {
2578 lastWasNothing =
true;
2581 region = QRegion(info.rect) * matrix;
2586 case QPainterClipInfo::RectFClip: {
2587 QTransform matrix = (info.matrix * d->invMatrix);
2588 if (lastWasNothing) {
2589 region = QRegion(info.rectf.toRect()) * matrix;
2590 lastWasNothing =
false;
2593 if (info.operation == Qt::IntersectClip) {
2595 if (matrix.type() <= QTransform::TxScale)
2596 region &= matrix.mapRect(info.rectf.toRect());
2598 region &= matrix.map(QRegion(info.rectf.toRect()));
2599 }
else if (info.operation == Qt::NoClip) {
2600 lastWasNothing =
true;
2603 region = QRegion(info.rectf.toRect()) * matrix;
2732void QPainter::setClipRect(
const QRectF &rect, Qt::ClipOperation op)
2738 qWarning(
"QPainter::setClipRect: Painter not active");
2741 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2742 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2743 op = Qt::ReplaceClip;
2745 qreal right = rect.x() + rect.width();
2746 qreal bottom = rect.y() + rect.height();
2747 qreal pts[] = { rect.x(), rect.y(),
2751 QVectorPath vp(pts, 4,
nullptr, QVectorPath::RectangleHint);
2752 d->state->clipEnabled =
true;
2753 d->extended->clip(vp, op);
2754 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2755 d->state->clipInfo.clear();
2756 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2757 d->state->clipOperation = op;
2761 if (qreal(
int(rect.top())) == rect.top()
2762 && qreal(
int(rect.bottom())) == rect.bottom()
2763 && qreal(
int(rect.left())) == rect.left()
2764 && qreal(
int(rect.right())) == rect.right())
2766 setClipRect(rect.toRect(), op);
2770 if (rect.isEmpty()) {
2771 setClipRegion(QRegion(), op);
2777 setClipPath(path, op);
2787void QPainter::setClipRect(
const QRect &rect, Qt::ClipOperation op)
2792 qWarning(
"QPainter::setClipRect: Painter not active");
2795 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2797 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2798 op = Qt::ReplaceClip;
2801 d->state->clipEnabled =
true;
2802 d->extended->clip(rect, op);
2803 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2804 d->state->clipInfo.clear();
2805 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2806 d->state->clipOperation = op;
2810 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2811 op = Qt::ReplaceClip;
2813 d->state->clipRegion = rect;
2814 d->state->clipOperation = op;
2815 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2816 d->state->clipInfo.clear();
2817 d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix));
2818 d->state->clipEnabled =
true;
2819 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2820 d->updateState(d->state);
2841void QPainter::setClipRegion(
const QRegion &r, Qt::ClipOperation op)
2845 QRect rect = r.boundingRect();
2846 if constexpr (qt_show_painter_debug_output)
2847 printf(
"QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
2848 r.rectCount(), rect.x(), rect.y(), rect.width(), rect.height());
2851 qWarning(
"QPainter::setClipRegion: Painter not active");
2854 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2856 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2857 op = Qt::ReplaceClip;
2860 d->state->clipEnabled =
true;
2861 d->extended->clip(r, op);
2862 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2863 d->state->clipInfo.clear();
2864 d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix));
2865 d->state->clipOperation = op;
2869 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2870 op = Qt::ReplaceClip;
2872 d->state->clipRegion = r;
2873 d->state->clipOperation = 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->clipEnabled =
true;
2878 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2879 d->updateState(d->state);
3053void QPainter::setClipPath(
const QPainterPath &path, Qt::ClipOperation op)
3056 if constexpr (qt_show_painter_debug_output) {
3057 QRectF b = path.boundingRect();
3058 printf(
"QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n",
3059 path.elementCount(), op, b.x(), b.y(), b.width(), b.height());
3065 qWarning(
"QPainter::setClipPath: Painter not active");
3069 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
3070 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
3071 op = Qt::ReplaceClip;
3074 d->state->clipEnabled =
true;
3075 d->extended->clip(path, op);
3076 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3077 d->state->clipInfo.clear();
3078 d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix));
3079 d->state->clipOperation = op;
3083 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
3084 op = Qt::ReplaceClip;
3086 d->state->clipPath = path;
3087 d->state->clipOperation = 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->clipEnabled =
true;
3092 d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled;
3093 d->updateState(d->state);
3302void QPainter::drawRects(
const QRectF *rects,
int rectCount)
3305 if constexpr (qt_show_painter_debug_output)
3306 printf(
"QPainter::drawRects(), count=%d\n", rectCount);
3311 qWarning(
"QPainter::drawRects: Painter not active");
3319 d->extended->drawRects(rects, rectCount);
3323 d->updateState(d->state);
3325 if (!d->state->emulationSpecifier) {
3326 d->engine->drawRects(rects, rectCount);
3330 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3331 && d->state->matrix.type() == QTransform::TxTranslate) {
3332 for (
int i=0; i<rectCount; ++i) {
3333 QRectF r(rects[i].x() + d->state->matrix.dx(),
3334 rects[i].y() + d->state->matrix.dy(),
3337 d->engine->drawRects(&r, 1);
3340 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3341 for (
int i=0; i<rectCount; ++i) {
3342 QPainterPath rectPath;
3343 rectPath.addRect(rects[i]);
3344 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3347 QPainterPath rectPath;
3348 for (
int i=0; i<rectCount; ++i)
3349 rectPath.addRect(rects[i]);
3350 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3362void QPainter::drawRects(
const QRect *rects,
int rectCount)
3365 if constexpr (qt_show_painter_debug_output)
3366 printf(
"QPainter::drawRects(), count=%d\n", rectCount);
3371 qWarning(
"QPainter::drawRects: Painter not active");
3379 d->extended->drawRects(rects, rectCount);
3383 d->updateState(d->state);
3385 if (!d->state->emulationSpecifier) {
3386 d->engine->drawRects(rects, rectCount);
3390 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3391 && d->state->matrix.type() == QTransform::TxTranslate) {
3392 for (
int i=0; i<rectCount; ++i) {
3393 QRectF r(rects[i].x() + d->state->matrix.dx(),
3394 rects[i].y() + d->state->matrix.dy(),
3398 d->engine->drawRects(&r, 1);
3401 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3402 for (
int i=0; i<rectCount; ++i) {
3403 QPainterPath rectPath;
3404 rectPath.addRect(rects[i]);
3405 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3408 QPainterPath rectPath;
3409 for (
int i=0; i<rectCount; ++i)
3410 rectPath.addRect(rects[i]);
3412 d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
3462void QPainter::drawPoints(
const QPointF *points,
int pointCount)
3465 if constexpr (qt_show_painter_debug_output)
3466 printf(
"QPainter::drawPoints(), count=%d\n", pointCount);
3471 qWarning(
"QPainter::drawPoints: Painter not active");
3475 if (pointCount <= 0)
3479 d->extended->drawPoints(points, pointCount);
3483 d->updateState(d->state);
3485 if (!d->state->emulationSpecifier) {
3486 d->engine->drawPoints(points, pointCount);
3490 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3491 && d->state->matrix.type() == QTransform::TxTranslate) {
3493 for (
int i=0; i<pointCount; ++i) {
3494 QPointF pt(points[i].x() + d->state->matrix.dx(),
3495 points[i].y() + d->state->matrix.dy());
3496 d->engine->drawPoints(&pt, 1);
3499 QPen pen = d->state->pen;
3500 bool flat_pen = pen.capStyle() == Qt::FlatCap;
3503 pen.setCapStyle(Qt::SquareCap);
3507 for (
int i=0; i<pointCount; ++i) {
3508 path.moveTo(points[i].x(), points[i].y());
3509 path.lineTo(points[i].x() + 0.0001, points[i].y());
3511 d->draw_helper(path, QPainterPrivate::StrokeDraw);
3524void QPainter::drawPoints(
const QPoint *points,
int pointCount)
3527 if constexpr (qt_show_painter_debug_output)
3528 printf(
"QPainter::drawPoints(), count=%d\n", pointCount);
3533 qWarning(
"QPainter::drawPoints: Painter not active");
3537 if (pointCount <= 0)
3541 d->extended->drawPoints(points, pointCount);
3545 d->updateState(d->state);
3547 if (!d->state->emulationSpecifier) {
3548 d->engine->drawPoints(points, pointCount);
3552 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3553 && d->state->matrix.type() == QTransform::TxTranslate) {
3555 for (
int i=0; i<pointCount; ++i) {
3556 QPointF pt(points[i].x() + d->state->matrix.dx(),
3557 points[i].y() + d->state->matrix.dy());
3558 d->engine->drawPoints(&pt, 1);
3561 QPen pen = d->state->pen;
3562 bool flat_pen = (pen.capStyle() == Qt::FlatCap);
3565 pen.setCapStyle(Qt::SquareCap);
3569 for (
int i=0; i<pointCount; ++i) {
3570 path.moveTo(points[i].x(), points[i].y());
3571 path.lineTo(points[i].x() + 0.0001, points[i].y());
3573 d->draw_helper(path, QPainterPrivate::StrokeDraw);
4885void QPainter::drawPixmap(
const QPointF &p,
const QPixmap &pm)
4887#if defined QT_DEBUG_DRAW
4888 if constexpr (qt_show_painter_debug_output)
4889 printf(
"QPainter::drawPixmap(), p=[%.2f,%.2f], pix=[%d,%d]\n",
4891 pm.width(), pm.height());
4896 if (!d->engine || pm.isNull())
4900 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawPixmap()");
4904 d->extended->drawPixmap(p, pm);
4912 int h = pm.height();
4918 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap()) {
4919 fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
4922 d->updateState(d->state);
4924 if ((d->state->matrix.type() > QTransform::TxTranslate
4925 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
4926 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
4927 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
4932 if (d->state->matrix.type() <= QTransform::TxScale) {
4933 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
4938 setBackgroundMode(Qt::TransparentMode);
4939 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
4940 QBrush brush(d->state->pen.color(), pm);
4943 setBrushOrigin(QPointF(0, 0));
4945 drawRect(pm.rect());
4948 if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
4949 x += d->state->matrix.dx();
4950 y += d->state->matrix.dy();
4952 qreal scale = pm.devicePixelRatio();
4953 d->engine->drawPixmap(QRectF(x, y, w / scale, h / scale), pm, QRectF(0, 0, w, h));
4957void QPainter::drawPixmap(
const QRectF &r,
const QPixmap &pm,
const QRectF &sr)
4959#if defined QT_DEBUG_DRAW
4960 if constexpr (qt_show_painter_debug_output)
4961 printf(
"QPainter::drawPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], source=[%.2f,%.2f,%.2f,%.2f]\n",
4962 r.x(), r.y(), r.width(), r.height(),
4963 pm.width(), pm.height(),
4964 sr.x(), sr.y(), sr.width(), sr.height());
4968 if (!d->engine || pm.isNull())
4971 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawPixmap()");
4976 qreal w = r.width();
4977 qreal h = r.height();
4980 qreal sw = sr.width();
4981 qreal sh = sr.height();
4986 const qreal pmscale = pm.devicePixelRatio();
4990 sw = pm.width() - sx;
4993 sh = pm.height() - sy;
5001 qreal w_ratio = sx * w/sw;
5009 qreal h_ratio = sy * h/sh;
5016 if (sw + sx > pm.width()) {
5017 qreal delta = sw - (pm.width() - sx);
5018 qreal w_ratio = delta * w/sw;
5023 if (sh + sy > pm.height()) {
5024 qreal delta = sh - (pm.height() - sy);
5025 qreal h_ratio = delta * h/sh;
5030 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5034 d->extended->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
5039 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap())
5040 fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
5042 d->updateState(d->state);
5044 if ((d->state->matrix.type() > QTransform::TxTranslate
5045 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5046 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5047 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity))
5048 || ((sw != w || sh != h) && !d->engine->hasFeature(QPaintEngine::PixmapTransform)))
5053 if (d->state->matrix.type() <= QTransform::TxScale) {
5054 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5059 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5067 scale(w / sw, h / sh);
5068 setBackgroundMode(Qt::TransparentMode);
5069 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5072 if (sw == pm.width() && sh == pm.height())
5073 brush = QBrush(d->state->pen.color(), pm);
5075 brush = QBrush(d->state->pen.color(), pm.copy(sx, sy, sw, sh));
5080 drawRect(QRectF(0, 0, sw, sh));
5083 if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5084 x += d->state->matrix.dx();
5085 y += d->state->matrix.dy();
5087 d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
5194void QPainter::drawImage(
const QPointF &p,
const QImage &image)
5198 if (!d->engine || image.isNull())
5202 d->extended->drawImage(p, image);
5209 int w = image.width();
5210 int h = image.height();
5211 qreal scale = image.devicePixelRatio();
5213 d->updateState(d->state);
5215 if (((d->state->matrix.type() > QTransform::TxTranslate)
5216 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5217 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5218 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
5223 if (d->state->matrix.type() <= QTransform::TxScale) {
5224 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5229 setBackgroundMode(Qt::TransparentMode);
5230 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5231 QBrush brush(image);
5234 setBrushOrigin(QPointF(0, 0));
5235 drawRect(QRect(QPoint(0, 0), image.size() / scale));
5240 if (d->state->matrix.type() == QTransform::TxTranslate
5241 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5242 x += d->state->matrix.dx();
5243 y += d->state->matrix.dy();
5246 d->engine->drawImage(QRectF(x, y, w / scale, h / scale), image, QRectF(0, 0, w, h), Qt::AutoColor);
5249void QPainter::drawImage(
const QRectF &targetRect,
const QImage &image,
const QRectF &sourceRect,
5250 Qt::ImageConversionFlags flags)
5254 if (!d->engine || image.isNull())
5257 qreal x = targetRect.x();
5258 qreal y = targetRect.y();
5259 qreal w = targetRect.width();
5260 qreal h = targetRect.height();
5261 qreal sx = sourceRect.x();
5262 qreal sy = sourceRect.y();
5263 qreal sw = sourceRect.width();
5264 qreal sh = sourceRect.height();
5265 qreal imageScale = image.devicePixelRatio();
5269 sw = image.width() - sx;
5272 sh = image.height() - sy;
5275 w = sw / imageScale;
5277 h = sh / imageScale;
5280 qreal w_ratio = sx * w/sw;
5288 qreal h_ratio = sy * h/sh;
5295 if (sw + sx > image.width()) {
5296 qreal delta = sw - (image.width() - sx);
5297 qreal w_ratio = delta * w/sw;
5302 if (sh + sy > image.height()) {
5303 qreal delta = sh - (image.height() - sy);
5304 qreal h_ratio = delta * h/sh;
5309 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5313 d->extended->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
5317 d->updateState(d->state);
5319 if (((d->state->matrix.type() > QTransform::TxTranslate || (sw != w || sh != h))
5320 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
5321 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
5322 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
5327 if (d->state->matrix.type() <= QTransform::TxScale) {
5328 const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
5333 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5340 scale(w / sw, h / sh);
5341 setBackgroundMode(Qt::TransparentMode);
5342 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
5343 QBrush brush(image);
5346 setBrushOrigin(QPointF(-sx, -sy));
5348 drawRect(QRectF(0, 0, sw, sh));
5353 if (d->state->matrix.type() == QTransform::TxTranslate
5354 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
5355 x += d->state->matrix.dx();
5356 y += d->state->matrix.dy();
5359 d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
5374void QPainter::drawGlyphRun(
const QPointF &position,
const QGlyphRun &glyphRun)
5379 qWarning(
"QPainter::drawGlyphRun: Painter not active");
5383 QRawFont font = glyphRun.rawFont();
5384 if (!font.isValid())
5387 QGlyphRunPrivate *glyphRun_d = QGlyphRunPrivate::get(glyphRun);
5389 const quint32 *glyphIndexes = glyphRun_d->glyphIndexData;
5390 const QPointF *glyphPositions = glyphRun_d->glyphPositionData;
5392 int count = qMin(glyphRun_d->glyphIndexDataSize, glyphRun_d->glyphPositionDataSize);
5393 QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count);
5395 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
5396 bool engineRequiresPretransformedGlyphPositions = d->extended
5397 ? d->extended->requiresPretransformedGlyphPositions(fontD->fontEngine, d->state->matrix)
5398 : d->engine->type() != QPaintEngine::CoreGraphics && !d->state->matrix.isAffine();
5400 for (
int i=0; i<count; ++i) {
5401 QPointF processedPosition = position + glyphPositions[i];
5402 if (engineRequiresPretransformedGlyphPositions)
5403 processedPosition = d->state->transform().map(processedPosition);
5404 fixedPointPositions[i] = QFixedPoint::fromPointF(processedPosition);
5407 d->drawGlyphs(engineRequiresPretransformedGlyphPositions
5408 ? d->state->transform().map(position)
5411 fixedPointPositions.data(),
5414 glyphRun.overline(),
5415 glyphRun.underline(),
5416 glyphRun.strikeOut());
5420 const quint32 *glyphArray,
5432 if (extended !=
nullptr && state->matrix.isAffine()) {
5433 QStaticTextItem staticTextItem;
5434 staticTextItem.color = state->pen.color();
5435 staticTextItem.font = state->font;
5436 staticTextItem.setFontEngine(fontEngine);
5437 staticTextItem.numGlyphs = glyphCount;
5438 staticTextItem.glyphs =
reinterpret_cast<glyph_t *>(
const_cast<glyph_t *>(glyphArray));
5439 staticTextItem.glyphPositions = positions;
5441 staticTextItem.usesRawFont =
true;
5443 extended->drawStaticTextItem(&staticTextItem);
5446 textItem.fontEngine = fontEngine;
5448 QVarLengthArray<QFixed, 128> advances(glyphCount);
5449 QVarLengthArray<QGlyphJustification, 128> glyphJustifications(glyphCount);
5450 QVarLengthArray<QGlyphAttributes, 128> glyphAttributes(glyphCount);
5451 memset(glyphAttributes.data(), 0, glyphAttributes.size() *
sizeof(QGlyphAttributes));
5452 memset(
static_cast<
void *>(advances.data()), 0, advances.size() *
sizeof(QFixed));
5453 memset(
static_cast<
void *>(glyphJustifications.data()), 0, glyphJustifications.size() *
sizeof(QGlyphJustification));
5455 textItem.glyphs.numGlyphs = glyphCount;
5456 textItem.glyphs.glyphs =
const_cast<glyph_t *>(glyphArray);
5457 textItem.glyphs.offsets = positions;
5458 textItem.glyphs.advances = advances.data();
5459 textItem.glyphs.justifications = glyphJustifications.data();
5460 textItem.glyphs.attributes = glyphAttributes.data();
5462 engine->drawTextItem(QPointF(0, 0), textItem);
5465 qt_draw_decoration_for_glyphs(q,
5544void QPainter::drawStaticText(
const QPointF &topLeftPosition,
const QStaticText &staticText)
5547 if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen)
5550 QStaticTextPrivate *staticText_d =
5551 const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(&staticText));
5553 QFontPrivate *fp = QFontPrivate::get(font());
5554 QFontPrivate *stfp = QFontPrivate::get(staticText_d->font);
5555 if (font() != staticText_d->font || fp ==
nullptr || stfp ==
nullptr || fp->dpi != stfp->dpi) {
5556 staticText_d->font = font();
5557 staticText_d->needsRelayout =
true;
5558 }
else if (stfp->engineData ==
nullptr || stfp->engineData->fontCacheId != QFontCache::instance()->id()) {
5559 staticText_d->needsRelayout =
true;
5562 QFontEngine *fe = staticText_d->font.d->engineForScript(QChar::Script_Common);
5563 if (fe->type() == QFontEngine::Multi)
5564 fe =
static_cast<QFontEngineMulti *>(fe)->engine(0);
5569 if (d->extended ==
nullptr
5570 || !d->state->matrix.isAffine()
5571 || !fe->supportsTransformation(d->state->matrix)) {
5572 staticText_d->paintText(topLeftPosition,
this, pen().color());
5576 bool engineRequiresPretransform = d->extended->requiresPretransformedGlyphPositions(fe, d->state->matrix);
5577 if (staticText_d->untransformedCoordinates && engineRequiresPretransform) {
5580 staticText_d->untransformedCoordinates =
false;
5581 staticText_d->needsRelayout =
true;
5582 }
else if (!staticText_d->untransformedCoordinates && !engineRequiresPretransform) {
5585 staticText_d->untransformedCoordinates =
true;
5586 staticText_d->needsRelayout =
true;
5591 QPointF transformedPosition = topLeftPosition;
5592 if (!staticText_d->untransformedCoordinates)
5593 transformedPosition = transformedPosition * d->state->matrix;
5594 QTransform oldMatrix;
5598 if (d->state->matrix.isTranslating() && !staticText_d->untransformedCoordinates) {
5599 qreal m11 = d->state->matrix.m11();
5600 qreal m12 = d->state->matrix.m12();
5601 qreal m13 = d->state->matrix.m13();
5602 qreal m21 = d->state->matrix.m21();
5603 qreal m22 = d->state->matrix.m22();
5604 qreal m23 = d->state->matrix.m23();
5605 qreal m33 = d->state->matrix.m33();
5607 oldMatrix = d->state->matrix;
5608 d->state->matrix.setMatrix(m11, m12, m13,
5615 bool staticTextNeedsReinit = staticText_d->needsRelayout;
5616 if (!staticText_d->untransformedCoordinates && staticText_d->matrix != d->state->matrix) {
5617 staticText_d->matrix = d->state->matrix;
5618 staticTextNeedsReinit =
true;
5622 if (staticTextNeedsReinit)
5623 staticText_d->init();
5625 if (transformedPosition != staticText_d->position) {
5626 QFixed fx = QFixed::fromReal(transformedPosition.x());
5627 QFixed fy = QFixed::fromReal(transformedPosition.y());
5628 QFixed oldX = QFixed::fromReal(staticText_d->position.x());
5629 QFixed oldY = QFixed::fromReal(staticText_d->position.y());
5630 for (
int item=0; item<staticText_d->itemCount;++item) {
5631 QStaticTextItem *textItem = staticText_d->items + item;
5632 for (
int i=0; i<textItem->numGlyphs; ++i) {
5633 textItem->glyphPositions[i].x += fx - oldX;
5634 textItem->glyphPositions[i].y += fy - oldY;
5636 textItem->userDataNeedsUpdate =
true;
5639 staticText_d->position = transformedPosition;
5642 QPen oldPen = d->state->pen;
5643 QColor currentColor = oldPen.color();
5644 static const QColor bodyIndicator(0, 0, 0, 0);
5645 for (
int i=0; i<staticText_d->itemCount; ++i) {
5646 QStaticTextItem *item = staticText_d->items + i;
5647 if (item->color.isValid() && currentColor != item->color
5648 && item->color != bodyIndicator) {
5649 setPen(item->color);
5650 currentColor = item->color;
5651 }
else if (item->color == bodyIndicator) {
5653 currentColor = oldPen.color();
5655 d->extended->drawStaticTextItem(item);
5657 qt_draw_decoration_for_glyphs(
this,
5660 item->glyphPositions,
5663 staticText_d->font.underline(),
5664 staticText_d->font.overline(),
5665 staticText_d->font.strikeOut());
5667 if (currentColor != oldPen.color())
5670 if (!staticText_d->untransformedCoordinates && oldMatrix.isTranslating())
5671 d->state->matrix = oldMatrix;
5677void QPainter::drawText(
const QPointF &p,
const QString &str,
int tf,
int justificationPadding)
5680 if constexpr (qt_show_painter_debug_output)
5681 printf(
"QPainter::drawText(), pos=[%.2f,%.2f], str='%s'\n", p.x(), p.y(), str.toLatin1().constData());
5686 if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
5689 Q_DECL_UNINITIALIZED QStackTextEngine engine(str, d->state->font);
5690 engine.option.setTextDirection(d->state->layoutDirection);
5691 if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
5692 engine.ignoreBidi =
true;
5693 engine.option.setTextDirection((tf & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
5697 line.length = str.size();
5698 engine.shapeLine(line);
5700 int nItems = engine.layoutData->items.size();
5701 QVarLengthArray<
int> visualOrder(nItems);
5702 QVarLengthArray<uchar> levels(nItems);
5703 for (
int i = 0; i < nItems; ++i)
5704 levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
5705 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
5707 if (justificationPadding > 0) {
5708 engine.option.setAlignment(Qt::AlignJustify);
5709 engine.forceJustification =
true;
5711 line.width = justificationPadding;
5712 engine.justify(line);
5714 QFixed x = QFixed::fromReal(p.x());
5716 for (
int i = 0; i < nItems; ++i) {
5717 int item = visualOrder[i];
5718 const QScriptItem &si = engine.layoutData->items.at(item);
5719 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
5723 QFont f = engine.font(si);
5724 QTextItemInt gf(si, &f);
5725 gf.glyphs = engine.shapedGlyphs(&si);
5726 gf.chars = engine.layoutData->string.unicode() + si.position;
5727 gf.num_chars = engine.length(item);
5728 if (engine.forceJustification) {
5729 for (
int j=0; j<gf.glyphs.numGlyphs; ++j)
5730 gf.width += gf.glyphs.effectiveAdvance(j);
5732 gf.width = si.width;
5734 gf.logClusters = engine.logClusters(&si);
5736 drawTextItem(QPointF(x.toReal(), p.y()), gf);
6004 const qreal radiusBase = qMax(qreal(1), maxRadius);
6006 QString key =
"WaveUnderline-"_L1
6007 % pen.color().name()
6008 % HexString<qreal>(radiusBase)
6009 % HexString<qreal>(pen.widthF());
6012 if (QPixmapCache::find(key, &pixmap))
6015 const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399));
6016 const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod);
6017 const qreal radius = qFloor(radiusBase * 2) / 2.;
6024 while (xs < width) {
6027 path.quadTo(xs - halfPeriod / 2, ys, xs, 0);
6030 pixmap = QPixmap(width, radius * 2);
6031 pixmap.fill(Qt::transparent);
6034 wavePen.setCapStyle(Qt::SquareCap);
6038 const qreal maxPenWidth = .8 * radius;
6039 if (wavePen.widthF() > maxPenWidth)
6040 wavePen.setWidthF(maxPenWidth);
6043 imgPainter.setPen(wavePen);
6044 imgPainter.setRenderHint(QPainter::Antialiasing);
6045 imgPainter.translate(0, radius);
6046 imgPainter.drawPath(path);
6049 QPixmapCache::insert(key, pixmap);
6055 QTextCharFormat::UnderlineStyle underlineStyle,
6056 QTextItem::RenderFlags flags, qreal width,
6057 const QTextCharFormat &charFormat)
6059 if (underlineStyle == QTextCharFormat::NoUnderline
6060 && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
6063 const QPen oldPen = painter->pen();
6064 const QBrush oldBrush = painter->brush();
6065 painter->setBrush(Qt::NoBrush);
6067 pen.setStyle(Qt::SolidLine);
6068 pen.setWidthF(fe->lineThickness().toReal());
6069 pen.setCapStyle(Qt::FlatCap);
6071 QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y());
6073 const qreal underlineOffset = fe->underlinePosition().toReal();
6075 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
6076 QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
6078 underlineStyle = QTextCharFormat::UnderlineStyle(theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt());
6079 if (underlineStyle == QTextCharFormat::SpellCheckUnderline)
6080 underlineStyle = QTextCharFormat::WaveUnderline;
6083 if (underlineStyle == QTextCharFormat::WaveUnderline) {
6085 painter->translate(0, pos.y() + 1);
6086 qreal maxHeight = fe->descent().toReal() - qreal(1);
6088 QColor uc = charFormat.underlineColor();
6093 const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()), maxHeight / qreal(2.)), pen);
6094 const int descent = qFloor(maxHeight);
6096 painter->setBrushOrigin(painter->brushOrigin().x(), 0);
6097 painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
6099 }
else if (underlineStyle != QTextCharFormat::NoUnderline) {
6100 const bool isAntialiasing = painter->renderHints().testFlag(QPainter::Antialiasing);
6101 if (!isAntialiasing)
6102 pen.setWidthF(qMax(fe->lineThickness().round(), QFixed(1)).toReal());
6103 const qreal lineThicknessOffset = pen.widthF() / 2.0;
6107 qreal adjustedUnderlineOffset =
std::ceil(underlineOffset) + lineThicknessOffset;
6108 if (underlineOffset <= fe->descent().toReal())
6109 adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, fe->descent().toReal() - lineThicknessOffset);
6110 const qreal underlinePos = pos.y() + adjustedUnderlineOffset;
6111 QColor uc = charFormat.underlineColor();
6115 pen.setStyle((Qt::PenStyle)(underlineStyle));
6116 painter->setPen(pen);
6117 QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos);
6119 textEngine->addUnderline(painter, underline);
6121 painter->drawLine(underline);
6123 if (!isAntialiasing)
6124 pen.setWidthF(fe->lineThickness().toReal());
6127 pen.setStyle(Qt::SolidLine);
6128 pen.setColor(oldPen.color());
6130 if (flags & QTextItem::StrikeOut) {
6131 QLineF strikeOutLine = line;
6132 strikeOutLine.translate(0., - fe->ascent().toReal() / 3.);
6133 QColor uc = charFormat.underlineColor();
6136 painter->setPen(pen);
6138 textEngine->addStrikeOut(painter, strikeOutLine);
6140 painter->drawLine(strikeOutLine);
6143 if (flags & QTextItem::Overline) {
6144 QLineF overline = line;
6145 overline.translate(0., - fe->ascent().toReal());
6146 QColor uc = charFormat.underlineColor();
6149 painter->setPen(pen);
6151 textEngine->addOverline(painter, overline);
6153 painter->drawLine(overline);
6156 painter->setPen(oldPen);
6157 painter->setBrush(oldBrush);
6210 if constexpr (qt_show_painter_debug_output)
6211 printf(
"QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
6212 p.x(), p.y(), qPrintable(_ti.text()));
6220 QTextItemInt &ti =
const_cast<QTextItemInt &>(
static_cast<
const QTextItemInt &>(_ti));
6222 if (!extended && state->bgMode == Qt::OpaqueMode) {
6223 QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent).toReal());
6224 q->fillRect(rect, state->bgBrush);
6227 if (q->pen().style() == Qt::NoPen)
6230 const QPainter::RenderHints oldRenderHints = state->renderHints;
6231 if (!(state->renderHints & QPainter::Antialiasing) && state->matrix.type() >= QTransform::TxScale) {
6236 const QTransform &m = state->matrix;
6237 if (state->matrix.type() < QTransform::TxShear) {
6238 bool isPlain90DegreeRotation =
6239 (qFuzzyIsNull(m.m11())
6240 && qFuzzyIsNull(m.m12() - qreal(1))
6241 && qFuzzyIsNull(m.m21() + qreal(1))
6242 && qFuzzyIsNull(m.m22())
6245 (qFuzzyIsNull(m.m11() + qreal(1))
6246 && qFuzzyIsNull(m.m12())
6247 && qFuzzyIsNull(m.m21())
6248 && qFuzzyIsNull(m.m22() + qreal(1))
6251 (qFuzzyIsNull(m.m11())
6252 && qFuzzyIsNull(m.m12() + qreal(1))
6253 && qFuzzyIsNull(m.m21() - qreal(1))
6254 && qFuzzyIsNull(m.m22())
6257 aa = !isPlain90DegreeRotation;
6260 q->setRenderHint(QPainter::Antialiasing,
true);
6266 if (!ti.glyphs.numGlyphs) {
6267 drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
6268 ti.flags, ti.width.toReal(), ti.charFormat);
6269 }
else if (ti.fontEngine->type() == QFontEngine::Multi) {
6270 QFontEngineMulti *multi =
static_cast<QFontEngineMulti *>(ti.fontEngine);
6273 int which = glyphs.glyphs[0] >> 24;
6278 bool rtl = ti.flags & QTextItem::RightToLeft;
6280 x += ti.width.toReal();
6284 for (end = 0; end < ti.glyphs.numGlyphs; ++end) {
6285 const int e = glyphs.glyphs[end] >> 24;
6290 multi->ensureEngineAt(which);
6291 QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
6294 for (i = start; i < end; ++i) {
6295 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6296 ti2.width += ti.glyphs.effectiveAdvance(i);
6300 x -= ti2.width.toReal();
6305 engine->drawTextItem(QPointF(x, y), ti2);
6306 drawTextItemDecoration(q,
QPointF(x, y), ti2.fontEngine, textEngine, ti2.underlineStyle,
6307 ti2.flags, ti2.width.toReal(), ti2.charFormat);
6310 x += ti2.width.toReal();
6313 const int hi = which << 24;
6314 for (i = start; i < end; ++i) {
6315 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6323 multi->ensureEngineAt(which);
6324 QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
6327 for (i = start; i < end; ++i) {
6328 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6329 ti2.width += ti.glyphs.effectiveAdvance(i);
6333 x -= ti2.width.toReal();
6338 engine->drawTextItem(QPointF(x,y), ti2);
6339 drawTextItemDecoration(q,
QPointF(x, y), ti2.fontEngine, textEngine, ti2.underlineStyle,
6340 ti2.flags, ti2.width.toReal(), ti2.charFormat);
6343 const int hi = which << 24;
6344 for (i = start; i < end; ++i)
6345 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6351 engine->drawTextItem(p, ti);
6352 drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
6353 ti.flags, ti.width.toReal(), ti.charFormat);
6356 if (state->renderHints != oldRenderHints) {
6357 state->renderHints = oldRenderHints;
6361 state->dirtyFlags |= QPaintEngine::DirtyHints;
6489void QPainter::drawTiledPixmap(
const QRectF &r,
const QPixmap &pixmap,
const QPointF &sp)
6492 if constexpr (qt_show_painter_debug_output)
6493 printf(
"QPainter::drawTiledPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], offset=[%.2f,%.2f]\n",
6494 r.x(), r.y(), r.width(), r.height(),
6495 pixmap.width(), pixmap.height(),
6500 if (!d->engine || pixmap.isNull() || r.isEmpty())
6504 qt_painter_thread_test(d->device->devType(), d->engine->type(),
"drawTiledPixmap()");
6507 const qreal sw = pixmap.width() / pixmap.devicePixelRatio();
6508 const qreal sh = pixmap.height() / pixmap.devicePixelRatio();
6512 sx = qRound(sw) - qRound(-sx) % qRound(sw);
6514 sx = qRound(sx) % qRound(sw);
6516 sy = qRound(sh) - -qRound(sy) % qRound(sh);
6518 sy = qRound(sy) % qRound(sh);
6522 d->extended->drawTiledPixmap(r, pixmap, QPointF(sx, sy));
6526 if (d->state->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
6527 fillRect(r, d->state->bgBrush);
6529 d->updateState(d->state);
6530 if ((d->state->matrix.type() > QTransform::TxTranslate
6531 && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
6532 || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
6535 setBackgroundMode(Qt::TransparentMode);
6536 setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
6537 setBrush(QBrush(d->state->pen.color(), pixmap));
6542 if (d->state->matrix.type() <= QTransform::TxScale) {
6543 const QPointF p = roundInDeviceCoordinates(r.topLeft(), d->state->matrix);
6545 if (d->state->matrix.type() <= QTransform::TxTranslate) {
6550 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6551 drawRect(QRectF(p, r.size()));
6553 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6562 if (d->state->matrix.type() == QTransform::TxTranslate
6563 && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
6564 x += d->state->matrix.dx();
6565 y += d->state->matrix.dy();
6568 d->engine->drawTiledPixmap(QRectF(x, y, r.width(), r.height()), pixmap, QPointF(sx, sy));
7193 const QTextOption *option,
7201 Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=
nullptr) );
7203 if (_r.isEmpty() && !(tf & Qt::TextDontClip)) {
7207 tf |= Qt::TextDontPrint;
7211 alignment |= option->alignment();
7212 if (option->wrapMode() != QTextOption::NoWrap)
7213 tf |= Qt::TextWordWrap;
7215 if (option->flags() & QTextOption::IncludeTrailingSpaces)
7216 tf |= Qt::TextIncludeTrailingSpaces;
7218 if (option->tabStopDistance() >= 0 || !option->tabArray().isEmpty())
7219 tf |= Qt::TextExpandTabs;
7225 bool dontclip = (tf & Qt::TextDontClip);
7226 bool wordwrap = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
7227 bool singleline = (tf & Qt::TextSingleLine);
7228 bool showmnemonic = (tf & Qt::TextShowMnemonic);
7229 bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
7231 Qt::LayoutDirection layout_direction;
7232 if (tf & Qt::TextForceLeftToRight)
7233 layout_direction = Qt::LeftToRight;
7234 else if (tf & Qt::TextForceRightToLeft)
7235 layout_direction = Qt::RightToLeft;
7237 layout_direction = option->textDirection();
7239 layout_direction = painter->layoutDirection();
7241 layout_direction = Qt::LeftToRight;
7243 alignment = QGuiApplicationPrivate::visualAlignment(layout_direction, QFlag(alignment));
7245 bool isRightToLeft = layout_direction == Qt::RightToLeft;
7246 bool expandtabs = ((tf & Qt::TextExpandTabs) &&
7247 (((alignment & Qt::AlignLeft) && !isRightToLeft) ||
7248 ((alignment & Qt::AlignRight) && isRightToLeft)));
7251 tf |= Qt::TextDontPrint;
7253 uint maxUnderlines = 0;
7255 QFontMetricsF fm(fnt);
7259 bool hasMoreLengthVariants =
false;
7262 int old_offset = offset;
7263 for (; offset < text.size(); offset++) {
7264 QChar chr = text.at(offset);
7265 if (chr == u'\r' || (singleline && chr == u'\n')) {
7266 text[offset] = u' ';
7267 }
else if (chr == u'\n') {
7268 text[offset] = QChar::LineSeparator;
7269 }
else if (chr == u'&') {
7271 }
else if (chr == u'\t') {
7273 text[offset] = u' ';
7274 }
else if (!tabarraylen && !tabstops) {
7275 tabstops = qRound(fm.horizontalAdvance(u'x')*8);
7277 }
else if (chr == u'\x9c') {
7279 hasMoreLengthVariants =
true;
7284 QList<QTextLayout::FormatRange> underlineFormats;
7285 int length = offset - old_offset;
7286 if ((hidemnmemonic || showmnemonic) && maxUnderlines > 0) {
7287 QChar *cout = text.data() + old_offset;
7288 QChar *cout0 = cout;
7298 if (*cin != u'&' && !hidemnmemonic && !(tf & Qt::TextDontPrint)) {
7299 QTextLayout::FormatRange range;
7300 range.start = cout - cout0;
7302 range.format.setFontUnderline(
true);
7303 underlineFormats.append(range);
7306 }
else if (hidemnmemonic && *cin == u'(' && l >= 4 &&
7307 cin[1] == u'&' && cin[2] != u'&' &&
7310 while ((cout - n) > cout0 && (cout - n - 1)->isSpace())
7329 QString finalText = text.mid(old_offset, length);
7330 Q_DECL_UNINITIALIZED QStackTextEngine engine(finalText, fnt);
7332 engine.option = *option;
7335 if (engine.option.tabStopDistance() < 0 && tabstops > 0)
7336 engine.option.setTabStopDistance(tabstops);
7338 if (engine.option.tabs().isEmpty() && ta) {
7340 tabs.reserve(tabarraylen);
7341 for (
int i = 0; i < tabarraylen; i++)
7342 tabs.append(qreal(ta[i]));
7343 engine.option.setTabArray(tabs);
7346 engine.option.setTextDirection(layout_direction);
7347 if (alignment & Qt::AlignJustify)
7348 engine.option.setAlignment(Qt::AlignJustify);
7350 engine.option.setAlignment(Qt::AlignLeft);
7352 if (!option && (tf & Qt::TextWrapAnywhere))
7353 engine.option.setWrapMode(QTextOption::WrapAnywhere);
7355 if (tf & Qt::TextJustificationForced)
7356 engine.forceJustification =
true;
7358 textLayout.setCacheEnabled(
true);
7359 textLayout.setFormats(underlineFormats);
7361 if (finalText.isEmpty()) {
7362 height = fm.height();
7364 tf |= Qt::TextDontPrint;
7366 qreal lineWidth = 0x01000000;
7367 if (wordwrap || (tf & Qt::TextJustificationForced))
7368 lineWidth = qMax<qreal>(0, r.width());
7370 tf |= Qt::TextIncludeTrailingSpaces;
7371 textLayout.beginLayout();
7373 qreal leading = fm.leading();
7377 QTextLine l = textLayout.createLine();
7381 l.setLineWidth(lineWidth);
7385 height = qCeil(height);
7387 if (alignment & Qt::AlignBaseline && l.lineNumber() == 0)
7388 height -= l.ascent();
7390 l.setPosition(
QPointF(0., height));
7391 height += textLayout.engine()->lines[l.lineNumber()].height().toReal();
7392 width = qMax(width, l.naturalTextWidth());
7393 if (!dontclip && !brect && height >= r.height())
7396 textLayout.endLayout();
7401 if (alignment & Qt::AlignBottom)
7402 yoff = r.height() - height;
7403 else if (alignment & Qt::AlignVCenter)
7404 yoff = (r.height() - height)/2;
7406 if (alignment & Qt::AlignRight)
7407 xoff = r.width() - width;
7408 else if (alignment & Qt::AlignHCenter)
7409 xoff = (r.width() - width)/2;
7411 QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
7413 if (hasMoreLengthVariants && !(tf & Qt::TextLongestVariant) && !r.contains(bounds)) {
7415 goto start_lengthVariant;
7420 if (!(tf & Qt::TextDontPrint)) {
7421 bool restore =
false;
7422 if (!dontclip && !r.contains(bounds)) {
7425 painter->setClipRect(r, Qt::IntersectClip);
7428 for (
int i = 0; i < textLayout.lineCount(); i++) {
7429 QTextLine line = textLayout.lineAt(i);
7431 eng->enableDelayDecorations();
7433 qreal advance = line.horizontalAdvance();
7435 if (alignment & Qt::AlignRight) {
7436 xoff = r.width() - advance -
7437 eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
7438 }
else if (alignment & Qt::AlignHCenter) {
7439 xoff = (r.width() - advance) / 2;
7442 line.draw(painter,
QPointF(r.x() + xoff, r.y() + yoff));
7443 eng->drawDecorations(painter);
7521QPainterState::QPainterState(
const QPainterState *s)
7522 : brushOrigin(s->brushOrigin), font(s->font), deviceFont(s->deviceFont),
7523 pen(s->pen), brush(s->brush), bgBrush(s->bgBrush),
7524 clipRegion(s->clipRegion), clipPath(s->clipPath),
7525 clipOperation(s->clipOperation),
7526 renderHints(s->renderHints), clipInfo(s->clipInfo),
7527 worldMatrix(s->worldMatrix), matrix(s->matrix), redirectionMatrix(s->redirectionMatrix),
7528 wx(s->wx), wy(s->wy), ww(s->ww), wh(s->wh),
7529 vx(s->vx), vy(s->vy), vw(s->vw), vh(s->vh),
7530 opacity(s->opacity), WxF(s->WxF), VxF(s->VxF),
7531 clipEnabled(s->clipEnabled), bgMode(s->bgMode), painter(s->painter),
7532 layoutDirection(s->layoutDirection),
7533 composition_mode(s->composition_mode),
7534 emulationSpecifier(s->emulationSpecifier), changeFlags(0)
7536 dirtyFlags = s->dirtyFlags;
8157void QPainter::drawPixmapFragments(
const PixmapFragment *fragments,
int fragmentCount,
8158 const QPixmap &pixmap, PixmapFragmentHints hints)
8162 if (!d->engine || pixmap.isNull())
8166 for (
int i = 0; i < fragmentCount; ++i) {
8167 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8168 fragments[i].width, fragments[i].height);
8169 if (!(QRectF(pixmap.rect()).contains(sourceRect)))
8170 qWarning(
"QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle");
8174 if (d->engine->isExtended()) {
8175 d->extended->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
8177 qreal oldOpacity = opacity();
8178 QTransform oldTransform = transform();
8180 for (
int i = 0; i < fragmentCount; ++i) {
8181 QTransform transform = oldTransform;
8184 if (fragments[i].rotation == 0) {
8185 xOffset = fragments[i].x;
8186 yOffset = fragments[i].y;
8188 transform.translate(fragments[i].x, fragments[i].y);
8189 transform.rotate(fragments[i].rotation);
8191 setOpacity(oldOpacity * fragments[i].opacity);
8192 setTransform(transform);
8194 qreal w = fragments[i].scaleX * fragments[i].width;
8195 qreal h = fragments[i].scaleY * fragments[i].height;
8196 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8197 fragments[i].width, fragments[i].height);
8198 drawPixmap(QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pixmap, sourceRect);
8201 setOpacity(oldOpacity);
8202 setTransform(oldTransform);