1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Copyright (C) 2016 Ivailo Monev
6 ** This file is part of the QtGui module of the Katie Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser
12 ** General Public License version 2.1 as published by the Free Software
13 ** Foundation and appearing in the file LICENSE.LGPL included in the
14 ** packaging of this file. Please review the following information to
15 ** ensure the GNU Lesser General Public License version 2.1 requirements
16 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
20 ****************************************************************************/
23 #include "qrasterdefs_p.h"
24 #include "qpainterpath.h"
30 #include "qpixmap_raster_p.h"
32 #include "qpaintengine_raster_p.h"
33 #include "qoutlinemapper_p.h"
34 #include "qguicommon_p.h"
35 #include "qcorecommon_p.h"
41 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
43 // #define QT_DEBUG_DRAW
45 void dumpClip(int width, int height, const QClipData *clip);
48 /********************************************************************************
51 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
52 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
53 static void qt_span_clip(int count, const QSpan *spans, void *userData);
54 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
60 Qt::ClipOperation operation;
64 static const QRectF boundingRect(const QPointF *points, int pointCount)
66 const QPointF *e = points;
67 const QPointF *last = points + pointCount;
68 qreal minx, maxx, miny, maxy;
74 else if (e->x() > maxx)
78 else if (e->y() > maxy)
81 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
85 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
86 return (elementCount == 5 // 5-point polygon, check for closed rect
87 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
88 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
89 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
90 && pts[0] < pts[4] && pts[1] < pts[5]
92 (elementCount == 4 // 4-point polygon, check for unclosed rect
93 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
94 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
95 && pts[0] < pts[4] && pts[1] < pts[5]
100 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
102 ((QOutlineMapper *) data)->moveTo(QPointF(x, y));
105 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
107 ((QOutlineMapper *) data)->lineTo(QPointF(x, y));
110 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
111 qfixed c2x, qfixed c2y,
112 qfixed ex, qfixed ey,
115 ((QOutlineMapper *) data)->curveTo(QPointF(c1x, c1y),
122 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
123 QPaintEngineExPrivate()
128 \class QRasterPaintEngine
132 \brief The QRasterPaintEngine class enables hardware acceleration
133 of painting operations in Qt for Embedded Linux.
135 Note that this functionality is only available in
136 \l{Qt for Embedded Linux}.
138 \note The QRasterPaintEngine class does not support 8-bit images.
139 Instead, they need to be converted to a supported format, such as
140 QImage::Format_ARGB32_Premultiplied.
142 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
143 documentation for details.
149 \fn Type QRasterPaintEngine::type() const
155 \relates QRasterPaintEngine
157 A struct equivalent to QT_FT_Span, containing a position (x,
158 y), the span's length in pixels and its color/coverage (a value
159 ranging from 0 to 255).
165 Creates a raster based paint engine for operating on the given
166 \a device, with the complete set of \l
167 {QPaintEngine::PaintEngineFeature}{paint engine features and
170 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
171 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
173 d_func()->device = device;
180 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
183 d_func()->device = device;
187 void QRasterPaintEngine::init()
189 Q_D(QRasterPaintEngine);
191 d->rasterizer.reset(new QRasterizer);
192 d->rasterBuffer.reset(new QRasterBuffer());
193 d->outlineMapper.reset(new QOutlineMapper);
195 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
196 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
197 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
199 d->baseClip.reset(new QClipData(d->device->height()));
200 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
202 d->image_filler.init(d->rasterBuffer.data(), this);
203 d->image_filler.type = QSpanData::Texture;
205 d->image_filler_xform.init(d->rasterBuffer.data(), this);
206 d->image_filler_xform.type = QSpanData::Texture;
208 d->solid_color_filler.init(d->rasterBuffer.data(), this);
209 d->solid_color_filler.type = QSpanData::Solid;
211 d->deviceDepth = d->device->depth();
213 gccaps &= ~PorterDuff;
215 if (Q_UNLIKELY(d->device->devType() != QInternal::Image)) {
216 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
221 QImage::Format format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
223 case QImage::Format_ARGB32_Premultiplied:
224 case QImage::Format_ARGB32:
225 gccaps |= PorterDuff;
235 bool QRasterPaintEngine::begin(QPaintDevice *device)
237 Q_D(QRasterPaintEngine);
239 if (device->devType() == QInternal::Pixmap) {
240 QPixmap *pixmap = static_cast<QPixmap *>(device);
241 QPixmapData *pd = pixmap->pixmapData();
242 if (pd->classId() == QPixmapData::RasterClass)
243 d->device = pd->buffer();
248 // Make sure QPaintEngine::paintDevice() returns the proper device.
251 Q_ASSERT(d->device->devType() == QInternal::Image);
253 d->systemStateChanged();
255 QRasterPaintEngineState *s = state();
256 updateOutlineMapper();
257 d->outlineMapper->m_clip_rect = d->deviceRect;
259 if (d->outlineMapper->m_clip_rect.width() > RASTER_COORD_LIMIT)
260 d->outlineMapper->m_clip_rect.setWidth(RASTER_COORD_LIMIT);
261 if (d->outlineMapper->m_clip_rect.height() > RASTER_COORD_LIMIT)
262 d->outlineMapper->m_clip_rect.setHeight(RASTER_COORD_LIMIT);
264 d->rasterizer->setClipRect(d->deviceRect);
266 s->penData.init(d->rasterBuffer.data(), this);
267 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
268 s->stroker = &d->basicStroker;
269 d->basicStroker.setClipRect(d->deviceRect);
271 s->brushData.init(d->rasterBuffer.data(), this);
272 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
274 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
276 setDirty(DirtyBrushOrigin);
279 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
280 << ") devType:" << device->devType()
281 << "devRect:" << d->deviceRect;
283 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
294 bool QRasterPaintEngine::end()
297 Q_D(QRasterPaintEngine);
298 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
300 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
311 void QRasterPaintEngine::saveBuffer(const QString &s) const
313 Q_D(const QRasterPaintEngine);
314 d->rasterBuffer->bufferImage().save(s, "PNG");
321 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
323 QRasterPaintEngineState *s = state();
324 // FALCON: get rid of this line, see drawImage call below.
326 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
328 updateOutlineMapper();
333 QRasterPaintEngineState::~QRasterPaintEngineState()
335 if (flags.has_clip_ownership)
340 QRasterPaintEngineState::QRasterPaintEngineState()
351 flags.antialiased = false;
352 flags.bilinear = false;
353 flags.tx_noshear = true;
356 flags.has_clip_ownership = false;
361 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
366 , strokeFlags(s.strokeFlags)
367 , lastBrush(s.lastBrush)
368 , brushData(s.brushData)
369 , fillFlags(s.fillFlags)
370 , intOpacity(s.intOpacity)
376 brushData.tempImage = 0;
377 penData.tempImage = 0;
378 flags.has_clip_ownership = false;
384 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
387 return new QRasterPaintEngineState();
388 return new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
394 void QRasterPaintEngine::setState(QPainterState *s)
396 Q_D(QRasterPaintEngine);
397 QPaintEngineEx::setState(s);
398 d->rasterBuffer->compositionMode = s->composition_mode;
402 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
407 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
414 void QRasterPaintEngine::penChanged()
417 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
419 QRasterPaintEngineState *s = state();
420 s->strokeFlags |= DirtyPen;
421 s->dirty |= DirtyPen;
427 void QRasterPaintEngine::updatePen(const QPen &pen)
429 Q_D(QRasterPaintEngine);
430 QRasterPaintEngineState *s = state();
432 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
435 Qt::PenStyle pen_style = pen.style();
440 s->penData.clip = d->clip();
441 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
443 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
444 || pen.brush().transform().type() >= QTransform::TxNone) {
445 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
448 // Slightly ugly handling of an uncommon case... We need to change
449 // the pen because it is reused in draw_midpoint to decide dashed
451 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
452 pen_style = Qt::SolidLine;
453 s->lastPen.setStyle(Qt::SolidLine);
456 d->basicStroker.setJoinStyle(pen.joinStyle());
457 d->basicStroker.setCapStyle(pen.capStyle());
458 d->basicStroker.setMiterLimit(pen.miterLimit());
460 const qreal penWidth = pen.widthF();
462 d->basicStroker.setStrokeWidth(1);
464 d->basicStroker.setStrokeWidth(penWidth);
466 if(pen_style == Qt::SolidLine) {
467 s->stroker = &d->basicStroker;
468 } else if (pen_style != Qt::NoPen) {
470 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
471 if (pen.isCosmetic()) {
472 d->dashStroker->setClipRect(d->deviceRect);
474 // ### I've seen this inverted devrect multiple places now...
475 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
476 d->dashStroker->setClipRect(clipRect);
478 d->dashStroker->setDashPattern(pen.dashPattern());
479 d->dashStroker->setDashOffset(pen.dashOffset());
480 s->stroker = d->dashStroker.data();
485 ensureRasterState(); // needed because of tx_noshear...
487 s->flags.non_complex_pen = s->lastPen.capStyle() <= Qt::SquareCap && s->flags.tx_noshear;
497 void QRasterPaintEngine::brushOriginChanged()
499 QRasterPaintEngineState *s = state();
501 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
504 s->fillFlags |= DirtyBrushOrigin;
511 void QRasterPaintEngine::brushChanged()
513 QRasterPaintEngineState *s = state();
515 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
517 s->fillFlags |= DirtyBrush;
526 void QRasterPaintEngine::updateBrush(const QBrush &brush)
529 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
531 Q_D(QRasterPaintEngine);
532 QRasterPaintEngineState *s = state();
533 // must set clip prior to setup, as setup uses it...
534 s->brushData.clip = d->clip();
535 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
536 if (s->fillFlags & DirtyTransform
537 || brush.transform().type() >= QTransform::TxNone)
538 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
539 s->lastBrush = brush;
543 void QRasterPaintEngine::updateOutlineMapper()
545 Q_D(QRasterPaintEngine);
546 d->outlineMapper->setMatrix(state()->matrix);
549 void QRasterPaintEngine::updateRasterState()
551 QRasterPaintEngineState *s = state();
553 if (s->dirty & DirtyTransform) {
554 updateMatrix(s->matrix);
564 void QRasterPaintEngine::opacityChanged()
566 QRasterPaintEngineState *s = state();
569 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
572 s->fillFlags |= DirtyOpacity;
573 s->strokeFlags |= DirtyOpacity;
574 s->dirty |= DirtyOpacity;
575 s->intOpacity = (int) (s->opacity * 256);
581 void QRasterPaintEngine::compositionModeChanged()
583 Q_D(QRasterPaintEngine);
584 QRasterPaintEngineState *s = state();
587 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
590 s->fillFlags |= DirtyCompositionMode;
591 s->dirty |= DirtyCompositionMode;
593 s->strokeFlags |= DirtyCompositionMode;
594 d->rasterBuffer->compositionMode = s->composition_mode;
600 void QRasterPaintEngine::renderHintsChanged()
602 QRasterPaintEngineState *s = state();
605 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
608 bool was_aa = s->flags.antialiased;
609 bool was_bilinear = s->flags.bilinear;
611 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
612 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
614 if (was_aa != s->flags.antialiased)
615 s->strokeFlags |= DirtyHints;
617 if (was_bilinear != s->flags.bilinear) {
618 s->strokeFlags |= DirtyPen;
619 s->fillFlags |= DirtyBrush;
626 void QRasterPaintEngine::transformChanged()
628 QRasterPaintEngineState *s = state();
631 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
634 s->fillFlags |= DirtyTransform;
635 s->strokeFlags |= DirtyTransform;
637 s->dirty |= DirtyTransform;
643 void QRasterPaintEngine::clipEnabledChanged()
645 QRasterPaintEngineState *s = state();
648 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
652 s->clip->enabled = s->clipEnabled;
653 s->fillFlags |= DirtyClipEnabled;
654 s->strokeFlags |= DirtyClipEnabled;
658 void QRasterPaintEnginePrivate::systemStateChanged()
660 const QRect deviceRectUnclipped = QRect(0, 0,
661 qMin(RASTER_COORD_LIMIT, device->width()),
662 qMin(RASTER_COORD_LIMIT, device->height()));
664 if (!systemClip.isEmpty()) {
665 QRegion clippedDeviceRgn = systemClip & deviceRectUnclipped;
666 deviceRect = clippedDeviceRgn.boundingRect();
667 baseClip->setClipRegion(clippedDeviceRgn);
669 deviceRect = deviceRectUnclipped;
670 baseClip->setClipRect(deviceRect);
673 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << deviceRectUnclipped << systemClip;
676 exDeviceRect = deviceRect;
678 Q_Q(QRasterPaintEngine);
679 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
680 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
683 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
685 const Qt::BrushStyle bstyle(b.style());
686 if (bstyle == Qt::NoBrush || bstyle == Qt::SolidPattern)
689 Q_Q(QRasterPaintEngine);
690 bool bilinear = q->state()->flags.bilinear;
692 const QTransform btransform(b.transform());
693 if (btransform.type() > QTransform::TxNone) { // FALCON: optimize
694 spanData->setupMatrix(btransform * m, bilinear);
695 } else if (m.type() <= QTransform::TxTranslate) {
696 // specialize setupMatrix for translation matrices
697 // to avoid needless matrix inversion
705 spanData->dx = -m.dx();
706 spanData->dy = -m.dy();
707 spanData->txop = m.type();
708 spanData->bilinear = bilinear;
709 spanData->adjustSpanMethods();
711 spanData->setupMatrix(m, bilinear);
715 // #define QT_CLIPPING_RATIOS
717 #ifdef QT_CLIPPING_RATIOS
722 static void checkClipRatios(QRasterPaintEnginePrivate *d)
724 if (d->clip()->hasRectClip)
726 if (d->clip()->hasRegionClip)
730 if ((totalClips % 5000) == 0) {
731 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
732 rectClips * 100.0 / (qreal) totalClips,
733 regionClips * 100.0 / (qreal) totalClips,
734 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
743 static inline void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
745 if (s->flags.has_clip_ownership)
748 s->flags.has_clip_ownership = false;
751 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
753 s->fillFlags |= QPaintEngine::DirtyClipPath;
754 s->strokeFlags |= QPaintEngine::DirtyClipPath;
756 d->solid_color_filler.clip = d->clip();
757 d->solid_color_filler.adjustSpanMethods();
760 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
769 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
772 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
774 if (path.elements()) {
775 for (int i=0; i<path.elementCount(); ++i) {
776 qDebug() << " - " << path.elements()[i]
777 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
780 for (int i=0; i<path.elementCount(); ++i) {
782 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
787 Q_D(QRasterPaintEngine);
788 QRasterPaintEngineState *s = state();
790 const qreal *points = path.points();
791 const QPainterPath::ElementType *types = path.elements();
793 // There are some cases that are not supported by clip(QRect)
794 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
795 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
796 if (s->matrix.type() <= QTransform::TxScale
797 && ((path.shape() == QVectorPath::RectangleHint)
798 || (isRect(points, path.elementCount())
799 && (!types || (types[0] == QPainterPath::MoveToElement
800 && types[1] == QPainterPath::LineToElement
801 && types[2] == QPainterPath::LineToElement
802 && types[3] == QPainterPath::LineToElement))))) {
804 qDebug() << " --- optimizing vector clip to rect clip...";
807 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
808 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
813 if (op == Qt::NoClip) {
814 qrasterpaintengine_state_setNoClip(s);
817 QClipData *base = d->baseClip.data();
819 // Intersect with current clip when available...
820 if (op == Qt::IntersectClip && s->clip)
823 // We always intersect, except when there is nothing to
824 // intersect with, in which case we simplify the operation to
826 Qt::ClipOperation isectOp = Qt::IntersectClip;
828 isectOp = Qt::ReplaceClip;
830 QClipData *newClip = new QClipData(d->rasterBuffer->height());
831 newClip->initialize();
832 ClipData clipData = { base, newClip, isectOp };
833 updateOutlineMapper();
834 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData);
838 if (op == Qt::UniteClip) {
840 QClipData *result = new QClipData(d->rasterBuffer->height());
841 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
842 qt_merge_clip(current, newClip, result);
850 if (s->flags.has_clip_ownership)
854 s->flags.has_clip_ownership = true;
856 qrasterpaintengine_dirty_clip(d, s);
864 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
867 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
870 QRasterPaintEngineState *s = state();
872 if (op == Qt::NoClip) {
873 qrasterpaintengine_state_setNoClip(s);
875 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale
876 || !setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
877 QPaintEngineEx::clip(rect, op);
882 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
884 Q_D(QRasterPaintEngine);
885 const QRect clipRect = r & d->deviceRect;
886 QRasterPaintEngineState *s = state();
888 if (op == Qt::ReplaceClip || s->clip == 0) {
890 // No current clip, hence we intersect with sysclip and be
892 QRegion clipRegion = systemClip();
893 QClipData *clip = new QClipData(d->rasterBuffer->height());
895 if (clipRegion.isEmpty())
896 clip->setClipRect(clipRect);
898 clip->setClipRegion(clipRegion & clipRect);
900 if (s->flags.has_clip_ownership)
904 s->clip->enabled = true;
905 s->flags.has_clip_ownership = true;
907 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
908 QClipData *base = s->clip;
911 if (base->hasRectClip || base->hasRegionClip) {
912 if (!s->flags.has_clip_ownership) {
913 s->clip = new QClipData(d->rasterBuffer->height());
914 s->flags.has_clip_ownership = true;
916 if (base->hasRectClip)
917 s->clip->setClipRect(base->clipRect & clipRect);
919 s->clip->setClipRegion(base->clipRegion & clipRect);
920 s->clip->enabled = true;
928 qrasterpaintengine_dirty_clip(d, s);
936 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
939 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
942 Q_D(QRasterPaintEngine);
944 if (region.rectCount() == 1) {
945 clip(region.boundingRect(), op);
949 QRasterPaintEngineState *s = state();
950 const QClipData *clip = d->clip();
951 const QClipData *baseClip = d->baseClip.data();
953 if (op == Qt::NoClip) {
954 qrasterpaintengine_state_setNoClip(s);
955 } else if (s->matrix.type() > QTransform::TxScale
956 || op == Qt::UniteClip
957 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
958 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
959 QPaintEngineEx::clip(region, op);
961 const QClipData *curClip;
964 if (op == Qt::IntersectClip)
969 if (s->flags.has_clip_ownership) {
973 newClip = new QClipData(d->rasterBuffer->height());
975 s->flags.has_clip_ownership = true;
978 QRegion r = s->matrix.map(region);
979 if (curClip->hasRectClip)
980 newClip->setClipRegion(r & curClip->clipRect);
981 else if (curClip->hasRegionClip)
982 newClip->setClipRegion(r & curClip->clipRegion);
984 qrasterpaintengine_dirty_clip(d, s);
991 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
994 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
997 if (!fillData->blend)
1000 Q_D(QRasterPaintEngine);
1002 const QRectF controlPointRect = path.controlPointRect();
1004 QRasterPaintEngineState *s = state();
1005 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1006 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1007 const bool do_clip = (deviceRect.left() < -RASTER_COORD_LIMIT
1008 || deviceRect.right() > RASTER_COORD_LIMIT
1009 || deviceRect.top() < -RASTER_COORD_LIMIT
1010 || deviceRect.bottom() > RASTER_COORD_LIMIT);
1012 if (!s->flags.antialiased && !do_clip) {
1013 d->initializeRasterizer(fillData);
1014 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1018 updateOutlineMapper();
1019 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData);
1022 static void fillRect_normalized(const QRect &r, QSpanData *data,
1023 QRasterPaintEnginePrivate *pe)
1027 bool rectClipped = true;
1030 x1 = qMax(r.x(), data->clip->xmin);
1031 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1032 y1 = qMax(r.y(), data->clip->ymin);
1033 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1034 rectClipped = data->clip->hasRectClip;
1037 x1 = qMax(r.x(), pe->deviceRect.x());
1038 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1039 y1 = qMax(r.y(), pe->deviceRect.y());
1040 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1042 x1 = qMax(r.x(), 0);
1043 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1044 y1 = qMax(r.y(), 0);
1045 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1048 if (x2 <= x1 || y2 <= y1)
1051 const int width = x2 - x1;
1052 const int height = y2 - y1;
1054 bool isUnclipped = rectClipped
1055 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1057 if (pe && isUnclipped) {
1058 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1060 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1061 || (mode == QPainter::CompositionMode_SourceOver
1062 && qAlpha(data->solid.color) == 255)))
1064 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1070 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1072 const int nspans = 256;
1073 QSTACKARRAY(QT_FT_Span, spans, nspans);
1075 Q_ASSERT(data->blend);
1078 int n = qMin(nspans, y2 - y);
1082 spans[i].len = width;
1084 spans[i].coverage = 255;
1088 blend(n, spans, data);
1096 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1098 #ifdef QT_DEBUG_DRAW
1099 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1101 Q_D(QRasterPaintEngine);
1102 ensureRasterState();
1103 QRasterPaintEngineState *s = state();
1107 if (s->brushData.blend) {
1108 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1109 const QRect *r = rects;
1110 const QRect *lastRect = rects + rectCount;
1112 int offset_x = int(s->matrix.dx());
1113 int offset_y = int(s->matrix.dy());
1114 while (r < lastRect) {
1115 QRect rect = r->normalized();
1116 fillRect_normalized(rect.translated(offset_x, offset_y), &s->brushData, d);
1120 QRectVectorPath path;
1121 for (int i=0; i<rectCount; ++i) {
1123 fill(path, s->brush);
1129 if (s->penData.blend) {
1130 QRectVectorPath path;
1131 for (int i = 0; i < rectCount; ++i) {
1133 stroke(path, s->pen);
1142 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1144 QRasterPaintEngineState *s = state();
1147 if (!s->penData.blend)
1150 if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1151 const qreal lastwidthf = s->lastPen.widthF();
1152 const qreal width = s->lastPen.isCosmetic()
1153 ? (lastwidthf == 0 ? 1 : lastwidthf)
1154 : lastwidthf * s->txscale;
1156 qreal dashOffset = s->lastPen.dashOffset();
1158 qreal patternLength = 0;
1159 const QVector<qreal> pattern = s->lastPen.dashPattern();
1160 for (int i = 0; i < pattern.size(); ++i)
1161 patternLength += pattern.at(i);
1163 if (patternLength > 0) {
1164 int n = qFloor(dashOffset / patternLength);
1165 dashOffset -= n * patternLength;
1166 while (dashOffset >= pattern.at(dashIndex)) {
1167 dashOffset -= pattern.at(dashIndex);
1168 if (++dashIndex >= pattern.size())
1174 Q_D(QRasterPaintEngine);
1175 d->initializeRasterizer(&s->penData);
1176 int lineCount = path.elementCount() / 2;
1177 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1179 for (int i = 0; i < lineCount; ++i) {
1180 if (lines[i].p1() == lines[i].p2()) {
1181 if (s->lastPen.capStyle() != Qt::FlatCap) {
1182 QPointF p = lines[i].p1();
1183 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*qreal(0.5), p.y()),
1184 QPointF(p.x() + width*qreal(0.5), p.y())));
1185 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1190 const QLineF line = s->matrix.map(lines[i]);
1191 if (s->lastPen.style() == Qt::SolidLine) {
1192 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1193 width / line.length(),
1194 s->lastPen.capStyle() == Qt::SquareCap);
1196 d->rasterizeLine_dashed(line, width,
1197 &dashIndex, &dashOffset, &inDash);
1201 QPaintEngineEx::stroke(path, pen);
1205 static inline QRect toNormalizedFillRect(const QRectF &rect)
1207 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1208 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1209 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1210 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1217 return QRect(x1, y1, x2 - x1, y2 - y1);
1223 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1227 #ifdef QT_DEBUG_DRAW
1228 QRectF rf = path.controlPointRect();
1229 qDebug() << "QRasterPaintEngine::fill(): "
1230 << "size=" << path.elementCount()
1231 << ", hints=" << hex << path.hints()
1235 Q_D(QRasterPaintEngine);
1236 QRasterPaintEngineState *s = state();
1239 if (!s->brushData.blend)
1242 if (path.shape() == QVectorPath::RectangleHint) {
1243 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1244 const qreal *p = path.points();
1245 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1246 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1247 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1250 ensureRasterState();
1251 if (s->flags.tx_noshear) {
1252 d->initializeRasterizer(&s->brushData);
1253 // ### Is normalizing really necessary here?
1254 const qreal *p = path.points();
1255 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1257 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1258 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1259 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1265 // ### Optimize for non transformed ellipses and rectangles...
1266 QRectF cpRect = path.controlPointRect();
1267 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1268 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1271 // const bool do_clip = (deviceRect.left() < -RASTER_COORD_LIMIT
1272 // || deviceRect.right() > RASTER_COORD_LIMIT
1273 // || deviceRect.top() < -RASTER_COORD_LIMIT
1274 // || deviceRect.bottom() > RASTER_COORD_LIMIT);
1276 // ### Falonc: implement....
1277 // if (!s->flags.antialiased && !do_clip) {
1278 // d->initializeRasterizer(&s->brushData);
1279 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1283 updateOutlineMapper();
1284 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData);
1287 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1289 Q_D(QRasterPaintEngine);
1290 QRasterPaintEngineState *s = state();
1292 if (!s->flags.antialiased) {
1293 QTransform::TransformationType txop = s->matrix.type();
1294 if (txop == QTransform::TxNone) {
1295 fillRect_normalized(toNormalizedFillRect(r), data, d);
1297 } else if (txop == QTransform::TxTranslate) {
1298 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1299 fillRect_normalized(rr, data, d);
1301 } else if (txop == QTransform::TxScale) {
1302 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1303 fillRect_normalized(rr, data, d);
1307 ensureRasterState();
1308 if (s->flags.tx_noshear) {
1309 d->initializeRasterizer(data);
1310 QRectF nr = r.normalized();
1311 if (!nr.isEmpty()) {
1312 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1313 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1314 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1321 updateOutlineMapper();
1322 fillPath(path, data);
1328 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1330 #ifdef QT_DEBUG_DRAW
1331 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1333 QRasterPaintEngineState *s = state();
1336 if (!s->brushData.blend)
1339 fillRect(r, &s->brushData);
1345 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1347 #ifdef QT_DEBUG_DRAW
1348 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1350 Q_D(QRasterPaintEngine);
1351 QRasterPaintEngineState *s = state();
1353 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1354 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1355 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1358 d->solid_color_filler.clip = d->clip();
1359 d->solid_color_filler.adjustSpanMethods();
1360 fillRect(r, &d->solid_color_filler);
1366 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1368 Q_D(QRasterPaintEngine);
1369 QRasterPaintEngineState *s = state();
1371 // max amount of points that raster engine can reliably handle
1372 const int maxPoints = 0xffff;
1373 if (Q_UNLIKELY(pointCount > maxPoints)) {
1374 qWarning("Polygon too complex for filling.");
1378 // Compose polygon fill..,
1379 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1380 updateOutlineMapper();
1381 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1384 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1386 d->rasterize(outline, brushBlend, &s->brushData);
1392 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1394 QRasterPaintEngineState *s = state();
1396 #ifdef QT_DEBUG_DRAW
1397 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1398 for (int i=0; i<pointCount; ++i)
1399 qDebug() << " - " << points[i];
1401 Q_ASSERT(pointCount >= 2);
1403 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1404 QRectF r(points[0], points[2]);
1405 QPaintEngineEx::drawRects(&r, 1);
1410 if (mode != PolylineMode) {
1413 if (s->brushData.blend)
1414 fillPolygon(points, pointCount, mode);
1417 // Do the outline...
1418 if (s->penData.blend) {
1419 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1420 QPaintEngineEx::stroke(vp, s->lastPen);
1427 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1429 Q_D(QRasterPaintEngine);
1430 QRasterPaintEngineState *s = state();
1432 #ifdef QT_DEBUG_DRAW
1433 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1434 for (int i=0; i<pointCount; ++i)
1435 qDebug() << " - " << points[i];
1437 Q_ASSERT(pointCount >= 2);
1438 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1439 QRect r(points[0].x(),
1441 points[2].x() - points[0].x(),
1442 points[2].y() - points[0].y());
1450 if (mode != PolylineMode) {
1452 if (s->brushData.blend) {
1453 // Compose polygon fill..,
1454 updateOutlineMapper();
1455 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1456 d->outlineMapper->moveTo(*points);
1457 const QPoint *p = points;
1458 const QPoint *ep = points + pointCount - 1;
1460 d->outlineMapper->lineTo(*(++p));
1462 d->outlineMapper->endOutline();
1465 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1467 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData);
1471 // Do the outline...
1472 if (s->penData.blend) {
1473 const int count = pointCount * 2;
1474 QSTACKARRAY(qreal, fpoints, count);
1475 for (int i=0; i<count; ++i)
1476 fpoints[i] = ((int *) points)[i];
1477 QVectorPath vp((qreal *) fpoints, pointCount, 0, QVectorPath::polygonFlags(mode));
1479 QPaintEngineEx::stroke(vp, s->lastPen);
1486 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1488 #ifdef QT_DEBUG_DRAW
1489 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1492 QPixmapData *pd = pixmap.pixmapData();
1493 if (pd->classId() == QPixmapData::RasterClass) {
1494 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
1495 if (image.depth() == 1) {
1496 Q_D(QRasterPaintEngine);
1497 QRasterPaintEngineState *s = state();
1498 if (s->matrix.type() <= QTransform::TxTranslate) {
1500 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1502 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
1505 QRasterPaintEngine::drawImage(pos, image);
1508 const QImage image = pixmap.toImage();
1509 if (pixmap.depth() == 1) {
1510 Q_D(QRasterPaintEngine);
1511 QRasterPaintEngineState *s = state();
1512 if (s->matrix.type() <= QTransform::TxTranslate) {
1514 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1516 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
1519 QRasterPaintEngine::drawImage(pos, image);
1527 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
1529 #ifdef QT_DEBUG_DRAW
1530 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1533 QPixmapData* pd = pixmap.pixmapData();
1534 if (pd->classId() == QPixmapData::RasterClass) {
1535 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
1536 if (image.depth() == 1) {
1537 Q_D(QRasterPaintEngine);
1538 QRasterPaintEngineState *s = state();
1539 if (s->matrix.type() <= QTransform::TxTranslate
1540 && r.size() == sr.size()
1541 && r.size() == pixmap.size()) {
1543 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1546 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
1549 drawImage(r, image, sr);
1552 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
1553 const QImage image = pd->toImage(clippedSource);
1554 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
1555 if (image.depth() == 1) {
1556 Q_D(QRasterPaintEngine);
1557 QRasterPaintEngineState *s = state();
1558 if (s->matrix.type() <= QTransform::TxTranslate
1559 && r.size() == sr.size()
1560 && r.size() == pixmap.size()) {
1562 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1565 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
1568 drawImage(r, image, translatedSource);
1573 static inline int fast_ceil_positive(const qreal &v)
1575 const int iv = int(v);
1582 static inline const QRect toAlignedRect_positive(const QRectF &rect)
1584 const int xmin = int(rect.x());
1585 const int xmax = int(fast_ceil_positive(rect.right()));
1586 const int ymin = int(rect.y());
1587 const int ymax = int(fast_ceil_positive(rect.bottom()));
1588 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
1594 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
1596 #ifdef QT_DEBUG_DRAW
1597 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
1600 Q_D(QRasterPaintEngine);
1601 QRasterPaintEngineState *s = state();
1603 if (s->matrix.type() > QTransform::TxTranslate) {
1604 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
1606 QRectF(0, 0, img.width(), img.height()));
1609 const QClipData *clip = d->clip();
1610 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
1612 d->image_filler.clip = clip;
1613 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
1614 if (!d->image_filler.blend)
1616 d->image_filler.dx = -pt.x();
1617 d->image_filler.dy = -pt.y();
1618 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
1620 fillRect_normalized(rr, &d->image_filler, d);
1628 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
1629 Qt::ImageConversionFlags)
1631 #ifdef QT_DEBUG_DRAW
1632 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
1638 Q_D(QRasterPaintEngine);
1639 QRasterPaintEngineState *s = state();
1640 int sr_l = qFloor(sr.left());
1641 int sr_r = qCeil(sr.right()) - 1;
1642 int sr_t = qFloor(sr.top());
1643 int sr_b = qCeil(sr.bottom()) - 1;
1645 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
1646 // as fillRect will apply the aliased coordinate delta we need to
1647 // subtract it here as we don't use it for image drawing
1648 QTransform old = s->matrix;
1649 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
1651 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
1652 QRgb color = img.pixel(sr_l, sr_t);
1653 switch (img.format()) {
1654 case QImage::Format_ARGB32_Premultiplied:
1655 // Combine premultiplied color with the opacity set on the painter.
1656 d->solid_color_filler.solid.color =
1657 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
1658 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
1661 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
1665 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1666 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1670 d->solid_color_filler.clip = d->clip();
1671 d->solid_color_filler.adjustSpanMethods();
1672 fillRect(r, &d->solid_color_filler);
1678 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
1680 const QClipData *clip = d->clip();
1682 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
1683 QTransform copy = s->matrix;
1684 copy.translate(r.x(), r.y());
1686 copy.scale(r.width() / sr.width(), r.height() / sr.height());
1687 copy.translate(-sr.x(), -sr.y());
1689 d->image_filler_xform.clip = clip;
1690 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
1691 if (!d->image_filler_xform.blend)
1693 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
1695 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
1696 QPointF rr_tl = s->matrix.map(r.topLeft());
1697 QPointF rr_br = s->matrix.map(r.bottomRight());
1699 int x1 = qRound(rr_tl.x());
1700 int y1 = qRound(rr_tl.y());
1701 int x2 = qRound(rr_br.x());
1702 int y2 = qRound(rr_br.y());
1709 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
1713 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
1716 QTransform m = s->matrix;
1717 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
1718 m.m21(), m.m22(), m.m23(),
1719 m.m31() - offs, m.m32() - offs, m.m33());
1720 fillPath(path, &d->image_filler_xform);
1723 d->image_filler.clip = clip;
1724 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
1725 if (!d->image_filler.blend)
1727 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
1728 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
1730 QRectF rr = r.translated(s->matrix.dx(), s->matrix.dy());
1732 const int x1 = qRound(rr.x());
1733 const int y1 = qRound(rr.y());
1734 const int x2 = qRound(rr.right());
1735 const int y2 = qRound(rr.bottom());
1737 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
1744 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
1746 #ifdef QT_DEBUG_DRAW
1747 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
1749 Q_D(QRasterPaintEngine);
1750 QRasterPaintEngineState *s = state();
1754 QPixmapData *pd = pixmap.pixmapData();
1755 if (pd->classId() == QPixmapData::RasterClass) {
1756 image = static_cast<QRasterPixmapData *>(pd)->image;
1758 image = pixmap.toImage();
1761 if (image.depth() == 1)
1762 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
1764 if (s->matrix.type() > QTransform::TxTranslate) {
1765 QTransform copy = s->matrix;
1766 copy.translate(r.x(), r.y());
1767 copy.translate(-sr.x(), -sr.y());
1768 d->image_filler_xform.clip = d->clip();
1769 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
1770 if (!d->image_filler_xform.blend)
1772 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
1776 fillPath(path, &d->image_filler_xform);
1778 d->image_filler.clip = d->clip();
1780 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
1781 if (!d->image_filler.blend)
1783 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
1784 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
1786 QRectF rr = r.translated(s->matrix.dx(), s->matrix.dy());
1787 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
1792 * Returns true if the rectangle is completely within the current clip
1793 * state of the paint engine.
1795 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
1797 const QClipData *cl = clip();
1799 // inline contains() for performance (we know the rects are normalized)
1800 const QRect &r1 = deviceRect;
1801 return (r.left() >= r1.left() && r.right() <= r1.right()
1802 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
1806 if (cl->hasRectClip) {
1807 // currently all painting functions clips to deviceRect internally
1808 if (cl->clipRect == deviceRect)
1811 // inline contains() for performance (we know the rects are normalized)
1812 const QRect &r1 = cl->clipRect;
1813 return (r.left() >= r1.left() && r.right() <= r1.right()
1814 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
1816 return qt_region_strictContains(cl->clipRegion, r);
1820 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect) const
1822 Q_Q(const QRasterPaintEngine);
1823 const QRasterPaintEngineState *s = q->state();
1824 const QClipData *cl = clip();
1826 QRect r = rect.normalized();
1827 // inline contains() for performance (we know the rects are normalized)
1828 const QRect &r1 = deviceRect;
1829 return (r.left() >= r1.left() && r.right() <= r1.right()
1830 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
1834 // currently all painting functions that call this function clip to deviceRect internally
1835 if (cl->hasRectClip && cl->clipRect == deviceRect)
1838 QRect r = rect.normalized();
1839 if (s->flags.antialiased) {
1842 r.setWidth(r.width() + 2);
1843 r.setHeight(r.height() + 2);
1846 if (cl->hasRectClip) {
1847 // inline contains() for performance (we know the rects are normalized)
1848 const QRect &r1 = cl->clipRect;
1849 return (r.left() >= r1.left() && r.right() <= r1.right()
1850 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
1852 return qt_region_strictContains(cl->clipRegion, r);
1856 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect) const
1858 return isUnclipped(rect.normalized().toAlignedRect());
1862 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
1863 const QSpanData *data) const
1865 return isUnclipped(rect) ? data->unclipped_blend : data->blend;
1869 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
1870 const QSpanData *data) const
1872 return isUnclipped(rect) ? data->unclipped_blend : data->blend;
1878 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1880 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1882 #ifdef QT_DEBUG_DRAW
1883 Q_D(QRasterPaintEngine);
1884 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s\n",
1885 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data());
1889 ensureRasterState();
1891 QPaintEngineEx::drawTextItem(p, ti);
1897 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
1899 QRasterPaintEngineState *s = state();
1902 if (!s->penData.blend)
1905 QPaintEngineEx::drawPoints(points, pointCount);
1909 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
1911 QRasterPaintEngineState *s = state();
1914 if (!s->penData.blend)
1917 QPaintEngineEx::drawPoints(points, pointCount);
1923 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
1925 #ifdef QT_DEBUG_DRAW
1926 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
1928 QRasterPaintEngineState *s = state();
1931 if (!s->penData.blend)
1934 QPaintEngineEx::drawLines(lines, lineCount);
1937 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
1943 Q_Q(QRasterPaintEngine);
1944 QRasterPaintEngineState *s = q->state();
1946 const bool squareCap = (s->lastPen.capStyle() == Qt::SquareCap);
1947 const QVector<qreal> pattern = s->lastPen.dashPattern();
1949 qreal patternLength = 0;
1950 for (int i = 0; i < pattern.size(); ++i)
1951 patternLength += pattern.at(i);
1953 if (patternLength <= 0)
1956 qreal length = line.length();
1957 Q_ASSERT(length > 0);
1958 while (length > 0) {
1959 const bool rasterize = *inDash;
1960 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
1963 if (dash >= length) {
1965 *dashOffset += dash / width;
1969 *inDash = !(*inDash);
1970 if (++*dashIndex >= pattern.size())
1977 if (rasterize && dash > 0)
1978 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
1985 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
1987 #ifdef QT_DEBUG_DRAW
1988 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
1990 QRasterPaintEngineState *s = state();
1993 if (!s->penData.blend)
1996 QPaintEngineEx::drawLines(lines, lineCount);
2003 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
2007 QPaintEngineEx::drawEllipse(rect);
2010 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
2015 Q_D(QRasterPaintEngine);
2017 Q_ASSERT(image.depth() == 1);
2019 const int spanCount = 256;
2020 QSTACKARRAY(QT_FT_Span, spans, spanCount);
2024 int w = image.width();
2025 int h = image.height();
2026 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
2027 int ymin = qMax(qRound(pos.y()), 0);
2028 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
2029 int xmin = qMax(qRound(pos.x()), 0);
2031 int x_offset = xmin - qRound(pos.x());
2033 QImage::Format format = image.format();
2034 for (int y = ymin; y < ymax; ++y) {
2035 const uchar *src = image.constScanLine(y - qRound(pos.y()));
2036 if (format == QImage::Format_MonoLSB) {
2037 for (int x = 0; x < xmax - xmin; ++x) {
2038 int src_x = x + x_offset;
2039 uchar pixel = src[src_x >> 3];
2044 if (pixel & (0x1 << (src_x & 7))) {
2045 spans[n].x = xmin + x;
2047 spans[n].coverage = 255;
2049 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
2053 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
2056 if (n == spanCount) {
2057 fg->blend(n, spans, fg);
2063 for (int x = 0; x < xmax - xmin; ++x) {
2064 int src_x = x + x_offset;
2065 uchar pixel = src[src_x >> 3];
2070 if (pixel & (0x80 >> (x & 7))) {
2071 spans[n].x = xmin + x;
2073 spans[n].coverage = 255;
2075 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
2079 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
2082 if (n == spanCount) {
2083 fg->blend(n, spans, fg);
2091 fg->blend(n, spans, fg);
2095 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
2097 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
2099 QVarLengthArray<short, 4096> buffer;
2101 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
2102 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
2103 result->initialize();
2105 for (int y = 0; y < c1->clipSpanHeight; ++y) {
2106 const QSpan *c1_spans = c1ClipLines[y].spans;
2107 int c1_count = c1ClipLines[y].count;
2108 const QSpan *c2_spans = c2ClipLines[y].spans;
2109 int c2_count = c2ClipLines[y].count;
2111 if (c1_count == 0 && c2_count == 0)
2113 if (c1_count == 0) {
2114 result->appendSpans(c2_spans, c2_count);
2116 } else if (c2_count == 0) {
2117 result->appendSpans(c1_spans, c1_count);
2121 // we need to merge the two
2123 // find required length
2124 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
2125 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
2127 memset(buffer.data(), 0, buffer.size() * sizeof(short));
2129 // Fill with old spans.
2130 for (int i = 0; i < c1_count; ++i) {
2131 const QSpan *cs = c1_spans + i;
2132 for (int j=cs->x; j<cs->x + cs->len; ++j)
2133 buffer[j] = cs->coverage;
2136 // Fill with new spans
2137 for (int i = 0; i < c2_count; ++i) {
2138 const QSpan *cs = c2_spans + i;
2139 for (int j = cs->x; j < cs->x + cs->len; ++j) {
2140 buffer[j] += cs->coverage;
2141 if (buffer[j] > 255)
2149 // Skip to next span
2150 while (x < max && buffer[x] == 0) ++x;
2151 if (x >= max) break;
2154 int coverage = buffer[x];
2156 // Find length of span
2157 while (x < max && buffer[x] == coverage)
2160 result->appendSpan(sx, x - sx, y, coverage);
2165 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
2167 Q_Q(QRasterPaintEngine);
2168 QRasterPaintEngineState *s = q->state();
2170 rasterizer->setAntialiased(s->flags.antialiased);
2172 QRect clipRect(deviceRect);
2174 // ### get from optimized rectbased QClipData
2176 const QClipData *c = clip();
2178 const QRect r(QPoint(c->xmin, c->ymin),
2179 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
2180 clipRect = clipRect.intersected(r);
2181 blend = data->blend;
2183 blend = data->unclipped_blend;
2186 rasterizer->setClipRect(clipRect);
2187 rasterizer->initialize(blend, data);
2190 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
2191 ProcessSpans callback,
2194 if (!callback || !outline)
2197 Q_Q(QRasterPaintEngine);
2198 QRasterPaintEngineState *s = q->state();
2200 if (!s->flags.antialiased) {
2201 rasterizer->setAntialiased(s->flags.antialiased);
2202 rasterizer->setClipRect(deviceRect);
2203 rasterizer->initialize(callback, userData);
2205 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
2209 rasterizer->rasterize(outline, fillRule);
2213 gray_raster_reset();
2215 QT_FT_BBox clip_box = { deviceRect.x(),
2217 deviceRect.x() + deviceRect.width(),
2218 deviceRect.y() + deviceRect.height() };
2220 QT_FT_Raster_Params rasterParams;
2221 rasterParams.source = outline;
2222 rasterParams.user = userData;
2223 rasterParams.clip_box = clip_box;
2224 rasterParams.gray_spans = callback;
2225 gray_raster_render(&rasterParams);
2228 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
2230 Q_ASSERT(image.depth() == 1);
2232 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
2233 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
2235 QRgb fg = PREMUL(color.rgba());
2238 int height = sourceImage.height();
2239 int width = sourceImage.width();
2240 int bpl = dest.bytesPerLine();
2241 uchar *data = dest.bits();
2242 for (int y=0; y<height; ++y) {
2243 const uchar *source = sourceImage.constScanLine(y);
2244 QRgb *target = reinterpret_cast<QRgb *>(QFAST_SCAN_LINE(data, bpl, y));
2245 for (int x=0; x < width; ++x)
2246 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
2251 QRasterBuffer::~QRasterBuffer()
2255 QRasterBuffer::QRasterBuffer()
2256 : monoDestinationWithClut(false),
2259 compositionMode(QPainter::CompositionMode_SourceOver),
2266 QImage::Format QRasterBuffer::prepare(QImage *image)
2268 m_buffer = image->bits();
2269 m_width = qMin(RASTER_COORD_LIMIT, image->width());
2270 m_height = qMin(RASTER_COORD_LIMIT, image->height());
2271 bytes_per_pixel = image->depth()/8;
2272 bytes_per_line = image->bytesPerLine();
2274 format = image->format();
2275 drawHelper = qDrawHelper + format;
2276 if (image->depth() == 1) {
2277 monoDestinationWithClut = true;
2278 destColor0 = PREMUL(image->colorTable()[0]);
2279 destColor1 = PREMUL(image->colorTable()[1]);
2285 QClipData::QClipData(int height)
2287 clipSpanHeight = height;
2292 xmin = xmax = ymin = ymax = 0;
2296 hasRectClip = hasRegionClip = false;
2299 QClipData::~QClipData()
2307 void QClipData::initialize()
2313 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
2315 Q_CHECK_PTR(m_clipLines);
2316 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
2317 allocated = clipSpanHeight;
2318 Q_CHECK_PTR(m_spans);
2323 m_clipLines[y].spans = 0;
2324 m_clipLines[y].count = 0;
2328 const int len = clipRect.width();
2331 QSpan *span = m_spans + count;
2335 span->coverage = 255;
2338 m_clipLines[y].spans = span;
2339 m_clipLines[y].count = 1;
2343 while (y < clipSpanHeight) {
2344 m_clipLines[y].spans = 0;
2345 m_clipLines[y].count = 0;
2348 } else if (hasRegionClip) {
2350 const QVector<QRect> rects = clipRegion.rects();
2351 const int numRects = rects.size();
2354 const int maxSpans = (ymax - ymin) * numRects;
2355 if (maxSpans > allocated) {
2356 m_spans = (QSpan *)::realloc(m_spans, maxSpans * sizeof(QSpan));
2357 Q_CHECK_PTR(m_spans);
2358 allocated = maxSpans;
2363 int firstInBand = 0;
2365 while (firstInBand < numRects) {
2366 const int currMinY = rects.at(firstInBand).y();
2367 const int currMaxY = currMinY + rects.at(firstInBand).height();
2369 while (y < currMinY) {
2370 m_clipLines[y].spans = 0;
2371 m_clipLines[y].count = 0;
2375 int lastInBand = firstInBand;
2376 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
2379 while (y < currMaxY) {
2381 m_clipLines[y].spans = m_spans + count;
2382 m_clipLines[y].count = lastInBand - firstInBand + 1;
2384 for (int r = firstInBand; r <= lastInBand; ++r) {
2385 const QRect &currRect = rects.at(r);
2386 QSpan *span = m_spans + count;
2387 span->x = currRect.x();
2388 span->len = currRect.width();
2390 span->coverage = 255;
2396 firstInBand = lastInBand + 1;
2399 Q_ASSERT(count <= allocated);
2401 while (y < clipSpanHeight) {
2402 m_clipLines[y].spans = 0;
2403 m_clipLines[y].count = 0;
2410 void QClipData::fixup()
2415 ymin = ymax = xmin = xmax = 0;
2420 ymin = m_spans[0].y;
2421 ymax = m_spans[count-1].y + 1;
2425 const int firstLeft = m_spans[0].x;
2426 const int firstRight = m_spans[0].x + m_spans[0].len;
2429 for (int i = 0; i < count; ++i) {
2430 QT_FT_Span_& span = m_spans[i];
2433 if (span.y != y + 1 && y != -1)
2436 m_clipLines[y].spans = &span;
2437 m_clipLines[y].count = 1;
2439 ++m_clipLines[y].count;
2441 const int spanLeft = span.x;
2442 const int spanRight = spanLeft + span.len;
2444 if (spanLeft < xmin)
2447 if (spanRight > xmax)
2450 if (spanLeft != firstLeft || spanRight != firstRight)
2456 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
2461 Convert \a rect to clip spans.
2463 void QClipData::setClipRect(const QRect &rect)
2465 if (hasRectClip && rect == clipRect)
2468 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
2470 hasRegionClip = false;
2474 xmax = rect.x() + rect.width();
2475 ymin = qMin(rect.y(), clipSpanHeight);
2476 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
2483 // qDebug() << xmin << xmax << ymin << ymax;
2487 Convert \a region to clip spans.
2489 void QClipData::setClipRegion(const QRegion ®ion)
2491 if (region.rectCount() == 1) {
2492 setClipRect(region.rects().at(0));
2496 hasRegionClip = true;
2497 hasRectClip = false;
2498 clipRegion = region;
2500 { // set bounding rect
2501 const QRect rect = region.boundingRect();
2503 xmax = rect.x() + rect.width();
2505 ymax = rect.y() + rect.height();
2517 spans must be sorted on y
2519 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
2520 const QSpan *spans, const QSpan *end,
2521 QSpan **outSpans, int available)
2523 const_cast<QClipData *>(clip)->initialize();
2525 QSpan *out = *outSpans;
2527 const QSpan *clipSpans = clip->m_spans + *currentClip;
2528 const QSpan *clipEnd = clip->m_spans + clip->count;
2530 while (available && spans < end ) {
2531 if (clipSpans >= clipEnd) {
2535 if (clipSpans->y > spans->y) {
2539 if (spans->y != clipSpans->y) {
2540 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
2541 clipSpans = clip->m_clipLines[spans->y].spans;
2546 Q_ASSERT(spans->y == clipSpans->y);
2549 int sx2 = sx1 + spans->len;
2550 int cx1 = clipSpans->x;
2551 int cx2 = cx1 + clipSpans->len;
2553 if (cx1 < sx1 && cx2 < sx1) {
2556 } else if (sx1 < cx1 && sx2 < cx1) {
2560 int x = qMax(sx1, cx1);
2561 int len = qMin(sx2, cx2) - x;
2563 out->x = qMax(sx1, cx1);
2564 out->len = qMin(sx2, cx2) - out->x;
2566 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
2578 *currentClip = clipSpans - clip->m_spans;
2582 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
2584 // qDebug() << "qt_span_fill_clipped" << spanCount;
2585 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
2587 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
2589 const int NSPANS = 256;
2590 QSpan cspans[NSPANS];
2591 int currentClip = 0;
2592 const QSpan *end = spans + spanCount;
2593 while (spans < end) {
2594 QSpan *clipped = cspans;
2595 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
2596 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
2597 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
2599 if (clipped - cspans)
2600 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
2606 Clip spans to \a{clip}-rectangle.
2607 Returns number of unclipped spans
2609 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
2612 const short minx = clip.left();
2613 const short miny = clip.top();
2614 const short maxx = clip.right();
2615 const short maxy = clip.bottom();
2618 for (int i = 0; i < numSpans; ++i) {
2619 if (spans[i].y > maxy)
2621 if (spans[i].y < miny
2622 || spans[i].x > maxx
2623 || spans[i].x + spans[i].len <= minx) {
2626 if (spans[i].x < minx) {
2627 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
2630 spans[n].x = spans[i].x;
2631 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
2633 if (spans[n].len == 0)
2635 spans[n].y = spans[i].y;
2636 spans[n].coverage = spans[i].coverage;
2643 static void qt_span_fill_clipRect(int count, const QSpan *spans,
2646 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
2647 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
2649 Q_ASSERT(fillData->clip);
2650 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
2652 // hw: check if this const_cast<> is safe!!!
2653 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
2654 fillData->clip->clipRect);
2656 fillData->unclipped_blend(count, spans, fillData);
2659 static void qt_span_clip(int count, const QSpan *spans, void *userData)
2661 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
2663 // qDebug() << " qt_span_clip: " << count << clipData->operation;
2664 // for (int i = 0; i < qMin(count, 10); ++i) {
2665 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
2668 switch (clipData->operation) {
2670 case Qt::IntersectClip:
2672 QClipData *newClip = clipData->newClip;
2673 newClip->initialize();
2675 int currentClip = 0;
2676 const QSpan *end = spans + count;
2677 while (spans < end) {
2678 QSpan *newspans = newClip->m_spans + newClip->count;
2679 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
2680 &newspans, newClip->allocated - newClip->count);
2681 newClip->count = newspans - newClip->m_spans;
2683 newClip->m_spans = (QSpan *)::realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan));
2684 Q_CHECK_PTR(newClip->m_spans);
2685 newClip->allocated *= 2;
2692 case Qt::ReplaceClip:
2693 clipData->newClip->appendSpans(spans, count);
2701 QImage QRasterBuffer::bufferImage() const
2703 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
2705 for (int y = 0; y < m_height; ++y) {
2706 const uint *span = (const uint *)this->scanLine(y);
2708 for (int x=0; x<m_width; ++x) {
2709 image.setPixel(x, y, span[x]);
2716 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
2720 txop = QTransform::TxNone;
2722 m11 = m22 = m33 = 1.;
2723 m12 = m13 = m21 = m23 = dx = dy = 0.0;
2724 clip = pe ? pe->d_func()->clip() : 0;
2727 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle);
2729 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
2731 Qt::BrushStyle brushStyle = brush.style();
2732 switch (brushStyle) {
2733 case Qt::SolidPattern: {
2735 const QColor c = brush.color();
2736 const QRgb rgba = c.rgba();
2737 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
2738 if ((solid.color & 0xff000000) == 0
2739 && compositionMode == QPainter::CompositionMode_SourceOver) {
2745 case Qt::LinearGradientPattern: {
2746 type = LinearGradient;
2747 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
2748 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
2749 gradient.generateGradientColorTable(*g, alpha);
2750 gradient.spread = g->spread();
2752 QLinearGradientData &linearData = gradient.linear;
2754 linearData.origin.x = g->start().x();
2755 linearData.origin.y = g->start().y();
2756 linearData.end.x = g->finalStop().x();
2757 linearData.end.y = g->finalStop().y();
2761 case Qt::RadialGradientPattern: {
2762 type = RadialGradient;
2763 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
2764 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
2765 gradient.generateGradientColorTable(*g, alpha);
2766 gradient.spread = g->spread();
2768 QRadialGradientData &radialData = gradient.radial;
2770 QPointF center = g->center();
2771 radialData.center.x = center.x();
2772 radialData.center.y = center.y();
2773 radialData.center.radius = g->centerRadius();
2774 QPointF focal = g->focalPoint();
2775 radialData.focal.x = focal.x();
2776 radialData.focal.y = focal.y();
2777 radialData.focal.radius = g->focalRadius();
2781 case Qt::Dense1Pattern:
2782 case Qt::Dense2Pattern:
2783 case Qt::Dense3Pattern:
2784 case Qt::Dense4Pattern:
2785 case Qt::Dense5Pattern:
2786 case Qt::Dense6Pattern:
2787 case Qt::Dense7Pattern:
2788 case Qt::HorPattern:
2789 case Qt::VerPattern:
2790 case Qt::CrossPattern:
2791 case Qt::BDiagPattern:
2792 case Qt::FDiagPattern:
2793 case Qt::DiagCrossPattern: {
2796 tempImage = new QImage();
2797 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle), brush.color());
2798 initTexture(tempImage, alpha, QTextureData::Tiled);
2802 case Qt::TexturePattern: {
2805 tempImage = new QImage();
2807 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
2808 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
2810 *tempImage = brush.textureImage();
2811 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
2820 adjustSpanMethods();
2823 void QSpanData::adjustSpanMethods()
2829 unclipped_blend = 0;
2832 unclipped_blend = rasterBuffer->drawHelper->blendColor;
2833 fillRect = rasterBuffer->drawHelper->fillRect;
2835 case LinearGradient:
2836 case RadialGradient:
2837 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
2840 unclipped_blend = qBlendTexture;
2841 if (!texture.imageData)
2842 unclipped_blend = 0;
2847 if (!unclipped_blend) {
2850 blend = unclipped_blend;
2851 } else if (clip->hasRectClip) {
2852 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
2854 blend = qt_span_fill_clipped;
2858 void QSpanData::setupMatrix(const QTransform &matrix, bool bilin)
2860 // make sure we round off correctly in qdrawhelper.cpp
2861 static const qreal delta = 1.0 / 65536;
2862 static const QTransform deltam = QTransform::fromTranslate(delta, delta);
2864 QTransform inv = (deltam * matrix).inverted();
2877 adjustSpanMethods();
2880 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
2882 const QImageData *d = image->d;
2883 if (!d || d->height == 0) {
2884 texture.imageData = 0;
2891 texture.bytesPerLine = 0;
2892 texture.format = QImage::Format_Invalid;
2895 texture.hasAlpha = alpha != 256;
2897 texture.imageData = d->data;
2898 texture.width = d->width;
2899 texture.height = d->height;
2901 if (sourceRect.isNull()) {
2904 texture.x2 = texture.width;
2905 texture.y2 = texture.height;
2907 texture.x1 = sourceRect.x();
2908 texture.y1 = sourceRect.y();
2909 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
2910 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
2913 texture.bytesPerLine = d->bytes_per_line;
2915 texture.format = d->format;
2916 if (d->depth == 1) {
2917 texture.mono0 = d->mono0;
2918 texture.mono1 = d->mono1;
2923 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
2925 texture.const_alpha = alpha;
2926 texture.type = _type;
2928 adjustSpanMethods();
2932 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
2935 Draws the first \a pointCount points in the buffer \a points
2937 The default implementation converts the first \a pointCount QPoints in \a points
2938 to QPointFs and calls the floating point version of drawPoints.
2942 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
2945 Reimplement this function to draw the largest ellipse that can be
2946 contained within rectangle \a rect.
2949 #ifdef QT_DEBUG_DRAW
2950 void dumpClip(int width, int height, const QClipData *clip)
2952 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
2953 clipImg.fill(0xffff0000);
2960 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
2962 for (int i = 0; i < clip->count; ++i) {
2963 const QSpan *span = ((QClipData *) clip)->spans() + i;
2964 for (int j = 0; j < span->len; ++j)
2965 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
2966 x0 = qMin(x0, int(span->x));
2967 x1 = qMax(x1, int(span->x + span->len - 1));
2969 y0 = qMin(y0, int(span->y));
2970 y1 = qMax(y1, int(span->y));
2973 static int counter = 0;
2980 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
2981 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));