void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr, Qt::ImageConversionFlags) { #ifdef QT_DEBUG_DRAW qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth(); #endif QTime startTime = QTime::currentTime(); callCount++; qDebug() << QTime::currentTime().toString("ss, zzz") << " - QRasterPaintEngine::drawImage(), call count : " << callCount << ", total duration : " << totalMsecs << ", r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth() << ", alpha : " << img.hasAlphaChannel() << ", pixel ratio : " << img.devicePixelRatio() << ", size in bytes : " << img.sizeInBytes() << ", colour count : " << img.colorCount() << ", bitPlaneCount : " << img.bitPlaneCount(); qDebug() << "total duration by return point : "; qDebug() << "1: count : " << callCount1 << ", duration : " << totalMsecs1; qDebug() << "2: count : " << callCount2 << ", duration : " << totalMsecs2; qDebug() << "3: count : " << callCount3 << ", duration : " << totalMsecs3; qDebug() << "4: count : " << callCount4 << ", duration : " << totalMsecs4; qDebug() << "5: count : " << callCount5 << ", duration : " << totalMsecs5; qDebug() << "6: count : " << callCount6 << ", duration : " << totalMsecs6; qDebug() << "7: count : " << callCount7 << ", duration : " << totalMsecs7; qDebug() << "8: count : " << callCount8 << ", duration : " << totalMsecs8; qDebug() << "9: count : " << callCount9 << ", duration : " << totalMsecs9; qDebug() << "10: count : " << callCount10 << ", duration : " << totalMsecs10; qDebug() << "11: count : " << callCount11 << ", duration : " << totalMsecs11; qDebug() << "12: count : " << callCount12 << ", duration : " << totalMsecs12; qDebug() << "13: count : " << callCount13 << ", duration : " << totalMsecs13; qDebug() << "14: count : " << callCount14 << ", duration : " << totalMsecs14; qDebug() << "15: count : " << callCount15 << ", duration : " << totalMsecs15; if (r.isEmpty()) return; Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); Q_ASSERT(s); int sr_l = qFloor(sr.left()); int sr_r = qCeil(sr.right()) - 1; int sr_t = qFloor(sr.top()); int sr_b = qCeil(sr.bottom()) - 1; if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) { // as fillRect will apply the aliased coordinate delta we need to // subtract it here as we don't use it for image drawing QTransform old = s->matrix; // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied. QRgb color = img.pixel(sr_l, sr_t); switch (img.format()) { case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_ARGB6666_Premultiplied: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_ARGB4444_Premultiplied: case QImage::Format_RGBA8888_Premultiplied: case QImage::Format_A2BGR30_Premultiplied: case QImage::Format_A2RGB30_Premultiplied: // Combine premultiplied color with the opacity set on the painter. d->solid_color_filler.solidColor = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity); break; default: d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity)); break; } if (d->solid_color_filler.solidColor.isTransparent() && s->composition_mode == QPainter::CompositionMode_SourceOver) return; d->solid_color_filler.clip = d->clip(); d->solid_color_filler.adjustSpanMethods(); fillRect(r, &d->solid_color_filler); s->matrix = old; QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount1++; totalMsecs1 += duration; qDebug() << "return 1 (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 1 count : " << callCount1 << ", return 1 duration : " << totalMsecs1; return; } bool stretch_sr = r.width() != sr.width() || r.height() != sr.height(); const QClipData *clip = d->clip(); if (s->matrix.type() == QTransform::TxRotate && !stretch_sr && (!clip || clip->hasRectClip) && s->intOpacity == 256 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)) { RotationType rotationType = qRotationType(s->matrix); const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp; if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) { QRectF transformedTargetRect = s->matrix.mapRect(r); if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, transformedTargetRect.topRight(), sr)) { QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect); if (clippedTransformedTargetRect.isNull()) return; QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect)); QRect clippedSourceRect = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(), clippedTargetRect.width(), clippedTargetRect.height()).toRect(); clippedSourceRect = clippedSourceRect.intersected(img.rect()); const qsizetype dbpl = d->rasterBuffer->bytesPerLine(); const qsizetype sbpl = img.bytesPerLine(); uchar *dst = d->rasterBuffer->buffer(); uint bpp = img.depth() >> 3; const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp; uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp; uint cw = clippedSourceRect.width(); uint ch = clippedSourceRect.height(); qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount2++; totalMsecs2 += duration; qDebug() << "return 2 (s->matrix.type() == QTransform::TxRotate && !stretch_sr && (!clip || clip->hasRectClip) && s->intOpacity == 256 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 2 count : " << callCount2 << ", return 2 duration : " << totalMsecs2; return; } } } if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) { QRectF targetBounds = s->matrix.mapRect(r); bool exceedsPrecision = r.width() > 0x7fff || r.height() > 0x7fff || targetBounds.left() < -0x7fff || targetBounds.top() < -0x7fff || targetBounds.right() > 0x7fff || targetBounds.bottom() > 0x7fff || targetBounds.width() > 0x7fff || targetBounds.height() > 0x7fff || s->matrix.m11() >= 512 || s->matrix.m22() >= 512; if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) { if (s->matrix.type() > QTransform::TxScale) { SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()]; // The fast transform methods doesn't really work on small targets, see QTBUG-93475 // And it can't antialias the edges if (func && (!clip || clip->hasRectClip) && !s->flags.antialiased && targetBounds.width() >= 16 && targetBounds.height() >= 16) { func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(), img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect, s->matrix, s->intOpacity); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount3++; totalMsecs3 += duration; qDebug() << "return 3 (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img))"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 3 count : " << callCount3 << ", return 3 duration : " << totalMsecs3; return; } } else { // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.) bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height(); bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2)); if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) { SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()]; if (func) { QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy()); if (!clip) { d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect()); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount4++; totalMsecs4 += duration; qDebug() << "return 4 else { // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 4 count : " << callCount4 << ", return 4 duration : " << totalMsecs4; return; } else if (clip->hasRectClip) { d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect()); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount5++; totalMsecs5 += duration; qDebug() << "return 5 else if (clip->hasRectClip)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 5 count : " << callCount5 << ", return 5 duration : " << totalMsecs5; return; } } } SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()]; if (func && (!clip || clip->hasRectClip)) { func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(), img.bytesPerLine(), img.height(), qt_mapRect_non_normalizing(r, s->matrix), sr, !clip ? d->deviceRect : clip->clipRect, s->intOpacity); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount6++; totalMsecs6 += duration; qDebug() << "return 6 (func && (!clip || clip->hasRectClip))"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 6 count : " << callCount6 << ", return 6 duration : " << totalMsecs6; return; } } } QTransform copy = s->matrix; copy.translate(r.x(), r.y()); if (stretch_sr) copy.scale(r.width() / sr.width(), r.height() / sr.height()); copy.translate(-sr.x(), -sr.y()); d->image_filler_xform.clip = clip; d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr)); if (!d->image_filler_xform.blend) { QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount7++; totalMsecs7 += duration; qDebug() << "return 7 (!d->image_filler_xform.blend)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 7 count : " << callCount7 << ", return 7 duration : " << totalMsecs7; return; } d->image_filler_xform.setupMatrix(copy, s->flags.bilinear); if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) { QRectF rr = s->matrix.mapRect(r); const int x1 = qRound(rr.x()); const int y1 = qRound(rr.y()); const int x2 = qRound(rr.right()); const int y2 = qRound(rr.bottom()); fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount8++; totalMsecs8 += duration; qDebug() << "return 8 (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 8 count : " << callCount8 << ", return 8 duration : " << totalMsecs8; return; } #ifdef QT_FAST_SPANS ensureRasterState(); if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) { d->initializeRasterizer(&d->image_filler_xform); d->rasterizer->setAntialiased(s->flags.antialiased); const QRectF &rect = r.normalized(); const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f); const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f); if (s->flags.tx_noshear) d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width()); else d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width()))); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount9++; totalMsecs9 += duration; qDebug() << "return 9 (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 9 count : " << callCount9 << ", return 9 duration : " << totalMsecs9; return; } #endif QPainterPath path; path.addRect(r); QTransform m = s->matrix; s->matrix = QTransform(m.m11(), m.m12(), m.m13(), m.m21(), m.m22(), m.m23(), m.m31(), m.m32(), m.m33()); fillPath(path, &d->image_filler_xform); s->matrix = m; } else { QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy()); if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr)) { if (!clip) { d->blitImage(pt, img, d->deviceRect, sr.toRect()); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount10++; totalMsecs10 += duration; qDebug() << "return 10 (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr))"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 10 count : " << callCount10 << ", return 10 duration : " << totalMsecs10; return; } else if (clip->hasRectClip) { d->blitImage(pt, img, clip->clipRect, sr.toRect()); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount11++; totalMsecs11 += duration; qDebug() << "return 11 else if (clip->hasRectClip)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 11 count : " << callCount11 << ", return 11 duration : " << totalMsecs11; return; } } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) { SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()]; if (func) { if (!clip) { d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect()); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount12++; totalMsecs12 += duration; qDebug() << "return 12 (!clip)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 12 count : " << callCount12 << ", return 12 duration : " << totalMsecs12; return; } else if (clip->hasRectClip) { d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect()); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount13++; totalMsecs13 += duration; qDebug() << "return 13 else if (clip->hasRectClip)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 13 count : " << callCount13 << ", return 13 duration : " << totalMsecs13; return; } } } d->image_filler.clip = clip; d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr)); if (!d->image_filler.blend) { QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount14++; totalMsecs14 += duration; qDebug() << "return 14 (!d->image_filler.blend)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 14 count : " << callCount14 << ", return 14 duration : " << totalMsecs14; return; } d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x(); d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y(); QRectF rr = r; rr.translate(s->matrix.dx(), s->matrix.dy()); const int x1 = qRound(rr.x()); const int y1 = qRound(rr.y()); const int x2 = qRound(rr.right()); const int y2 = qRound(rr.bottom()); fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d); QTime endTime = QTime::currentTime(); int duration = startTime.msecsTo( endTime ); totalMsecs += duration; callCount15++; totalMsecs15 += duration; qDebug() << "return 15 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d)"; qDebug() << "this duration : " << duration << ", total duration : " << totalMsecs << "return 15 count : " << callCount15 << ", return 15 duration : " << totalMsecs15; } }