1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
57 // #include <private/qdatabuffer_p.h>
58 // #include <private/qpainter_p.h>
59 #include <private/qmath_p.h>
60 #include <private/qtextengine_p.h>
61 #include <private/qfontengine_p.h>
62 #include <private/qpixmap_raster_p.h>
63 // #include <private/qpolygonclipper_p.h>
64 // #include <private/qrasterizer_p.h>
65 #include <private/qimage_p.h>
66 #include <private/qstatictext_p.h>
67 #include <private/qcosmeticstroker_p.h>
68 #include "qmemrotate_p.h"
70 #include "qpaintengine_raster_p.h"
71 // #include "qbezier_p.h"
72 #include "qoutlinemapper_p.h"
77 # include <qvarlengtharray.h>
78 # include <private/qfontengine_p.h>
79 # include <qt_windows.h>
87 class QRectVectorPath : public QVectorPath {
89 inline void set(const QRect &r) {
91 qreal right = r.x() + r.width();
93 qreal bottom = r.y() + r.height();
104 inline void set(const QRectF &r) {
106 qreal right = r.x() + r.width();
108 qreal bottom = r.y() + r.height();
118 inline QRectVectorPath(const QRect &r)
119 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
123 inline QRectVectorPath(const QRectF &r)
124 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
128 inline QRectVectorPath()
129 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
135 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
137 #define qreal_to_fixed_26_6(f) (int(f * 64))
138 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
139 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
141 // #define QT_DEBUG_DRAW
143 void dumpClip(int width, int height, const QClipData *clip);
146 #define QT_FAST_SPANS
149 // A little helper macro to get a better approximation of dimensions.
150 // If we have a rect that starting at 0.5 of width 3.5 it should span
152 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
154 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
158 static inline bool winClearTypeFontsEnabled()
161 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
162 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
163 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
165 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
166 return result == FE_FONTSMOOTHINGCLEARTYPE;
172 bool QRasterPaintEngine::clearTypeFontsEnabled()
174 static const bool result = winClearTypeFontsEnabled();
182 /********************************************************************************
185 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
186 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
187 static void qt_span_clip(int count, const QSpan *spans, void *userData);
193 Qt::ClipOperation operation;
199 LineDrawIncludeLastPixel
202 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
203 ProcessSpans pen_func, ProcessSpans brush_func,
204 QSpanData *pen_data, QSpanData *brush_data);
206 struct QRasterFloatPoint {
212 static const QRectF boundingRect(const QPointF *points, int pointCount)
214 const QPointF *e = points;
215 const QPointF *last = points + pointCount;
216 qreal minx, maxx, miny, maxy;
217 minx = maxx = e->x();
218 miny = maxy = e->y();
222 else if (e->x() > maxx)
226 else if (e->y() > maxy)
229 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
233 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
234 return (elementCount == 5 // 5-point polygon, check for closed rect
235 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
236 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
237 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
238 && pts[0] < pts[4] && pts[1] < pts[5]
240 (elementCount == 4 // 4-point polygon, check for unclosed rect
241 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
242 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
243 && pts[0] < pts[4] && pts[1] < pts[5]
248 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
250 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
253 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
255 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
258 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
259 qfixed c2x, qfixed c2y,
260 qfixed ex, qfixed ey,
263 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
264 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
265 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
269 #if !defined(QT_NO_DEBUG) && 0
270 static void qt_debug_path(const QPainterPath &path)
272 const char *names[] = {
279 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
280 for (int i=0; i<path.elementCount(); ++i) {
281 const QPainterPath::Element &e = path.elementAt(i);
282 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
283 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
288 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
289 QPaintEngineExPrivate(),
296 \class QRasterPaintEngine
301 \brief The QRasterPaintEngine class enables hardware acceleration
302 of painting operations in Qt for Embedded Linux.
304 Note that this functionality is only available in
305 \l{Qt for Embedded Linux}.
307 In \l{Qt for Embedded Linux}, painting is a pure software
308 implementation. But starting with Qt 4.2, it is
309 possible to add an accelerated graphics driver to take advantage
310 of available hardware resources.
312 Hardware acceleration is accomplished by creating a custom screen
313 driver, accelerating the copying from memory to the screen, and
314 implementing a custom paint engine accelerating the various
315 painting operations. Then a custom paint device (derived from the
316 QCustomRasterPaintDevice class) and a custom window surface
317 (derived from QWSWindowSurface) must be implemented to make
318 \l{Qt for Embedded Linux} aware of the accelerated driver.
320 \note The QRasterPaintEngine class does not support 8-bit images.
321 Instead, they need to be converted to a supported format, such as
322 QImage::Format_ARGB32_Premultiplied.
324 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
325 documentation for details.
327 \sa QCustomRasterPaintDevice, QPaintEngine
331 \fn Type QRasterPaintEngine::type() const
337 \relates QRasterPaintEngine
339 A struct equivalent to QT_FT_Span, containing a position (x,
340 y), the span's length in pixels and its color/coverage (a value
341 ranging from 0 to 255).
347 Creates a raster based paint engine for operating on the given
348 \a device, with the complete set of \l
349 {QPaintEngine::PaintEngineFeature}{paint engine features and
352 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
353 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
355 d_func()->device = device;
362 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
365 d_func()->device = device;
369 void QRasterPaintEngine::init()
371 Q_D(QRasterPaintEngine);
378 // The antialiasing raster.
379 d->grayRaster.reset(new QT_FT_Raster);
380 Q_CHECK_PTR(d->grayRaster.data());
381 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
382 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
385 d->rasterizer.reset(new QRasterizer);
386 d->rasterBuffer.reset(new QRasterBuffer());
387 d->outlineMapper.reset(new QOutlineMapper);
388 d->outlinemapper_xform_dirty = true;
390 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
391 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
392 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
394 d->baseClip.reset(new QClipData(d->device->height()));
395 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
397 d->image_filler.init(d->rasterBuffer.data(), this);
398 d->image_filler.type = QSpanData::Texture;
400 d->image_filler_xform.init(d->rasterBuffer.data(), this);
401 d->image_filler_xform.type = QSpanData::Texture;
403 d->solid_color_filler.init(d->rasterBuffer.data(), this);
404 d->solid_color_filler.type = QSpanData::Solid;
406 d->deviceDepth = d->device->depth();
408 d->mono_surface = false;
409 gccaps &= ~PorterDuff;
411 QImage::Format format = QImage::Format_Invalid;
413 switch (d->device->devType()) {
414 case QInternal::Pixmap:
415 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
417 case QInternal::Image:
418 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
421 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
427 case QImage::Format_MonoLSB:
428 case QImage::Format_Mono:
429 d->mono_surface = true;
431 case QImage::Format_ARGB8565_Premultiplied:
432 case QImage::Format_ARGB8555_Premultiplied:
433 case QImage::Format_ARGB6666_Premultiplied:
434 case QImage::Format_ARGB4444_Premultiplied:
435 case QImage::Format_ARGB32_Premultiplied:
436 case QImage::Format_ARGB32:
437 gccaps |= PorterDuff;
439 case QImage::Format_RGB32:
440 case QImage::Format_RGB444:
441 case QImage::Format_RGB555:
442 case QImage::Format_RGB666:
443 case QImage::Format_RGB888:
444 case QImage::Format_RGB16:
455 Destroys this paint engine.
457 QRasterPaintEngine::~QRasterPaintEngine()
459 Q_D(QRasterPaintEngine);
461 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
467 bool QRasterPaintEngine::begin(QPaintDevice *device)
469 Q_D(QRasterPaintEngine);
471 if (device->devType() == QInternal::Pixmap) {
472 QPixmap *pixmap = static_cast<QPixmap *>(device);
473 QPlatformPixmap *pd = pixmap->handle();
474 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
475 d->device = pd->buffer();
480 // Make sure QPaintEngine::paintDevice() returns the proper device.
483 Q_ASSERT(d->device->devType() == QInternal::Image
484 || d->device->devType() == QInternal::CustomRaster);
486 d->systemStateChanged();
488 QRasterPaintEngineState *s = state();
489 ensureOutlineMapper();
490 d->outlineMapper->m_clip_rect = d->deviceRect;
492 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
493 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
494 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
495 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
497 d->rasterizer->setClipRect(d->deviceRect);
499 s->penData.init(d->rasterBuffer.data(), this);
500 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
501 s->stroker = &d->basicStroker;
502 d->basicStroker.setClipRect(d->deviceRect);
504 s->brushData.init(d->rasterBuffer.data(), this);
505 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
507 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
509 setDirty(DirtyBrushOrigin);
512 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
513 << ") devType:" << device->devType()
514 << "devRect:" << d->deviceRect;
516 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
521 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
522 #if defined(Q_OS_WIN)
523 else if (clearTypeFontsEnabled())
528 QImage::Format format = static_cast<QImage *>(d->device)->format();
529 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
530 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
532 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
534 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
543 bool QRasterPaintEngine::end()
546 Q_D(QRasterPaintEngine);
547 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
549 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
559 void QRasterPaintEngine::releaseBuffer()
561 Q_D(QRasterPaintEngine);
562 d->rasterBuffer.reset(new QRasterBuffer);
568 QSize QRasterPaintEngine::size() const
570 Q_D(const QRasterPaintEngine);
571 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
578 void QRasterPaintEngine::saveBuffer(const QString &s) const
580 Q_D(const QRasterPaintEngine);
581 d->rasterBuffer->bufferImage().save(s, "PNG");
588 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
590 QRasterPaintEngineState *s = state();
591 // FALCON: get rid of this line, see drawImage call below.
593 QTransform::TransformationType txop = s->matrix.type();
597 case QTransform::TxNone:
598 s->flags.int_xform = true;
601 case QTransform::TxTranslate:
602 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
603 && qreal(int(s->matrix.dy())) == s->matrix.dy();
606 case QTransform::TxScale:
607 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
608 && qreal(int(s->matrix.dy())) == s->matrix.dy()
609 && qreal(int(s->matrix.m11())) == s->matrix.m11()
610 && qreal(int(s->matrix.m22())) == s->matrix.m22();
613 default: // shear / perspective...
614 s->flags.int_xform = false;
618 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
620 ensureOutlineMapper();
625 QRasterPaintEngineState::~QRasterPaintEngineState()
627 if (flags.has_clip_ownership)
632 QRasterPaintEngineState::QRasterPaintEngineState()
644 flags.fast_pen = true;
645 flags.antialiased = false;
646 flags.bilinear = false;
647 flags.legacy_rounding = false;
648 flags.fast_text = true;
649 flags.int_xform = true;
650 flags.tx_noshear = true;
651 flags.fast_images = true;
654 flags.has_clip_ownership = false;
659 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
664 , strokeFlags(s.strokeFlags)
665 , lastBrush(s.lastBrush)
666 , brushData(s.brushData)
667 , fillFlags(s.fillFlags)
668 , pixmapFlags(s.pixmapFlags)
669 , intOpacity(s.intOpacity)
673 , flag_bits(s.flag_bits)
675 brushData.tempImage = 0;
676 penData.tempImage = 0;
677 flags.has_clip_ownership = false;
683 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
685 QRasterPaintEngineState *s;
687 s = new QRasterPaintEngineState();
689 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
697 void QRasterPaintEngine::setState(QPainterState *s)
699 Q_D(QRasterPaintEngine);
700 QPaintEngineEx::setState(s);
701 d->rasterBuffer->compositionMode = s->composition_mode;
705 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
710 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
717 void QRasterPaintEngine::penChanged()
720 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
722 QRasterPaintEngineState *s = state();
723 s->strokeFlags |= DirtyPen;
724 s->dirty |= DirtyPen;
730 void QRasterPaintEngine::updatePen(const QPen &pen)
732 Q_D(QRasterPaintEngine);
733 QRasterPaintEngineState *s = state();
735 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
738 Qt::PenStyle pen_style = qpen_style(pen);
743 s->penData.clip = d->clip();
744 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
746 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
747 || pen.brush().transform().type() >= QTransform::TxNone) {
748 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
751 // Slightly ugly handling of an uncommon case... We need to change
752 // the pen because it is reused in draw_midpoint to decide dashed
754 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
755 pen_style = Qt::SolidLine;
756 s->lastPen.setStyle(Qt::SolidLine);
759 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
760 d->basicStroker.setCapStyle(qpen_capStyle(pen));
761 d->basicStroker.setMiterLimit(pen.miterLimit());
763 qreal penWidth = qpen_widthf(pen);
765 d->basicStroker.setStrokeWidth(1);
767 d->basicStroker.setStrokeWidth(penWidth);
769 if(pen_style == Qt::SolidLine) {
770 s->stroker = &d->basicStroker;
771 } else if (pen_style != Qt::NoPen) {
773 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
774 if (qt_pen_is_cosmetic(pen, s->renderHints)) {
775 d->dashStroker->setClipRect(d->deviceRect);
777 // ### I've seen this inverted devrect multiple places now...
778 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
779 d->dashStroker->setClipRect(clipRect);
781 d->dashStroker->setDashPattern(pen.dashPattern());
782 d->dashStroker->setDashOffset(pen.dashOffset());
783 s->stroker = d->dashStroker.data();
788 ensureRasterState(); // needed because of tx_noshear...
789 bool cosmetic = qt_pen_is_cosmetic(pen, s->renderHints);
790 s->flags.fast_pen = pen_style > Qt::NoPen
792 && ((cosmetic && penWidth <= 1)
793 || (!cosmetic && s->flags.tx_noshear && penWidth * s->txscale <= 1));
795 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
805 void QRasterPaintEngine::brushOriginChanged()
807 QRasterPaintEngineState *s = state();
809 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
812 s->fillFlags |= DirtyBrushOrigin;
819 void QRasterPaintEngine::brushChanged()
821 QRasterPaintEngineState *s = state();
823 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
825 s->fillFlags |= DirtyBrush;
834 void QRasterPaintEngine::updateBrush(const QBrush &brush)
837 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
839 Q_D(QRasterPaintEngine);
840 QRasterPaintEngineState *s = state();
841 // must set clip prior to setup, as setup uses it...
842 s->brushData.clip = d->clip();
843 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
844 if (s->fillFlags & DirtyTransform
845 || brush.transform().type() >= QTransform::TxNone)
846 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
847 s->lastBrush = brush;
851 void QRasterPaintEngine::updateOutlineMapper()
853 Q_D(QRasterPaintEngine);
854 d->outlineMapper->setMatrix(state()->matrix);
857 void QRasterPaintEngine::updateRasterState()
859 QRasterPaintEngineState *s = state();
861 if (s->dirty & DirtyTransform)
862 updateMatrix(s->matrix);
864 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
865 const QPainter::CompositionMode mode = s->composition_mode;
866 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
867 && s->intOpacity == 256
868 && (mode == QPainter::CompositionMode_Source
869 || (mode == QPainter::CompositionMode_SourceOver
870 && qAlpha(s->penData.solid.color) == 255));
880 void QRasterPaintEngine::opacityChanged()
882 QRasterPaintEngineState *s = state();
885 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
888 s->fillFlags |= DirtyOpacity;
889 s->strokeFlags |= DirtyOpacity;
890 s->pixmapFlags |= DirtyOpacity;
891 s->dirty |= DirtyOpacity;
892 s->intOpacity = (int) (s->opacity * 256);
898 void QRasterPaintEngine::compositionModeChanged()
900 Q_D(QRasterPaintEngine);
901 QRasterPaintEngineState *s = state();
904 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
907 s->fillFlags |= DirtyCompositionMode;
908 s->dirty |= DirtyCompositionMode;
910 s->strokeFlags |= DirtyCompositionMode;
911 d->rasterBuffer->compositionMode = s->composition_mode;
913 d->recalculateFastImages();
919 void QRasterPaintEngine::renderHintsChanged()
921 QRasterPaintEngineState *s = state();
924 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
927 bool was_aa = s->flags.antialiased;
928 bool was_bilinear = s->flags.bilinear;
930 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
931 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
932 s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);
934 if (was_aa != s->flags.antialiased)
935 s->strokeFlags |= DirtyHints;
937 if (was_bilinear != s->flags.bilinear) {
938 s->strokeFlags |= DirtyPen;
939 s->fillFlags |= DirtyBrush;
942 Q_D(QRasterPaintEngine);
943 d->recalculateFastImages();
949 void QRasterPaintEngine::transformChanged()
951 QRasterPaintEngineState *s = state();
954 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
957 s->fillFlags |= DirtyTransform;
958 s->strokeFlags |= DirtyTransform;
960 s->dirty |= DirtyTransform;
962 Q_D(QRasterPaintEngine);
963 d->recalculateFastImages();
969 void QRasterPaintEngine::clipEnabledChanged()
971 QRasterPaintEngineState *s = state();
974 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
978 s->clip->enabled = s->clipEnabled;
979 s->fillFlags |= DirtyClipEnabled;
980 s->strokeFlags |= DirtyClipEnabled;
981 s->pixmapFlags |= DirtyClipEnabled;
985 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
987 SrcOverBlendFunc func,
992 if (alpha == 0 || !clip.isValid())
995 Q_ASSERT(img.depth() >= 8);
997 int srcBPL = img.bytesPerLine();
998 const uchar *srcBits = img.bits();
999 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1000 int iw = img.width();
1001 int ih = img.height();
1003 if (!sr.isEmpty()) {
1006 // Adjust the image according to the source offset...
1007 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1010 // adapt the x parameters
1011 int x = qRound(pt.x());
1013 int cx2 = clip.x() + clip.width();
1016 srcBits += srcSize * d;
1021 int d = x + iw - cx2;
1027 // adapt the y paremeters...
1029 int cy2 = clip.y() + clip.height();
1030 int y = qRound(pt.y());
1033 srcBits += srcBPL * d;
1038 int d = y + ih - cy2;
1044 // call the blend function...
1045 int dstSize = rasterBuffer->bytesPerPixel();
1046 int dstBPL = rasterBuffer->bytesPerLine();
1047 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1054 void QRasterPaintEnginePrivate::systemStateChanged()
1056 QRect clipRect(0, 0,
1057 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1058 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1060 if (!systemClip.isEmpty()) {
1061 QRegion clippedDeviceRgn = systemClip & clipRect;
1062 deviceRect = clippedDeviceRgn.boundingRect();
1063 baseClip->setClipRegion(clippedDeviceRgn);
1065 deviceRect = clipRect;
1066 baseClip->setClipRect(deviceRect);
1068 #ifdef QT_DEBUG_DRAW
1069 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1072 exDeviceRect = deviceRect;
1074 Q_Q(QRasterPaintEngine);
1076 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1077 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1078 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1082 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1084 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1087 Q_Q(QRasterPaintEngine);
1088 bool bilinear = q->state()->flags.bilinear;
1090 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1091 spanData->setupMatrix(b.transform() * m, bilinear);
1093 if (m.type() <= QTransform::TxTranslate) {
1094 // specialize setupMatrix for translation matrices
1095 // to avoid needless matrix inversion
1103 spanData->dx = -m.dx();
1104 spanData->dy = -m.dy();
1105 spanData->txop = m.type();
1106 spanData->bilinear = bilinear;
1107 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1108 spanData->adjustSpanMethods();
1110 spanData->setupMatrix(m, bilinear);
1115 // #define QT_CLIPPING_RATIOS
1117 #ifdef QT_CLIPPING_RATIOS
1122 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1124 if (d->clip()->hasRectClip)
1126 if (d->clip()->hasRegionClip)
1130 if ((totalClips % 5000) == 0) {
1131 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1132 rectClips * 100.0 / (qreal) totalClips,
1133 regionClips * 100.0 / (qreal) totalClips,
1134 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1143 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1145 if (s->flags.has_clip_ownership)
1148 s->flags.has_clip_ownership = false;
1151 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1153 s->fillFlags |= QPaintEngine::DirtyClipPath;
1154 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1155 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1157 d->solid_color_filler.clip = d->clip();
1158 d->solid_color_filler.adjustSpanMethods();
1160 #ifdef QT_DEBUG_DRAW
1161 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1170 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1172 #ifdef QT_DEBUG_DRAW
1173 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1175 if (path.elements()) {
1176 for (int i=0; i<path.elementCount(); ++i) {
1177 qDebug() << " - " << path.elements()[i]
1178 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1181 for (int i=0; i<path.elementCount(); ++i) {
1182 qDebug() << " ---- "
1183 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1188 Q_D(QRasterPaintEngine);
1189 QRasterPaintEngineState *s = state();
1191 const qreal *points = path.points();
1192 const QPainterPath::ElementType *types = path.elements();
1194 // There are some cases that are not supported by clip(QRect)
1195 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1196 if (s->matrix.type() <= QTransform::TxScale
1197 && ((path.shape() == QVectorPath::RectangleHint)
1198 || (isRect(points, path.elementCount())
1199 && (!types || (types[0] == QPainterPath::MoveToElement
1200 && types[1] == QPainterPath::LineToElement
1201 && types[2] == QPainterPath::LineToElement
1202 && types[3] == QPainterPath::LineToElement))))) {
1203 #ifdef QT_DEBUG_DRAW
1204 qDebug() << " --- optimizing vector clip to rect clip...";
1207 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1208 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1213 if (op == Qt::NoClip) {
1214 qrasterpaintengine_state_setNoClip(s);
1217 QClipData *base = d->baseClip.data();
1219 // Intersect with current clip when available...
1220 if (op == Qt::IntersectClip && s->clip)
1223 // We always intersect, except when there is nothing to
1224 // intersect with, in which case we simplify the operation to
1226 Qt::ClipOperation isectOp = Qt::IntersectClip;
1228 isectOp = Qt::ReplaceClip;
1230 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1231 newClip->initialize();
1232 ClipData clipData = { base, newClip, isectOp };
1233 ensureOutlineMapper();
1234 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1238 if (s->flags.has_clip_ownership)
1242 s->flags.has_clip_ownership = true;
1244 qrasterpaintengine_dirty_clip(d, s);
1252 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1254 #ifdef QT_DEBUG_DRAW
1255 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1258 QRasterPaintEngineState *s = state();
1260 if (op == Qt::NoClip) {
1261 qrasterpaintengine_state_setNoClip(s);
1263 } else if (s->matrix.type() > QTransform::TxScale) {
1264 QPaintEngineEx::clip(rect, op);
1267 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1268 QPaintEngineEx::clip(rect, op);
1274 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1276 Q_D(QRasterPaintEngine);
1277 QRect clipRect = r & d->deviceRect;
1278 QRasterPaintEngineState *s = state();
1280 if (op == Qt::ReplaceClip || s->clip == 0) {
1282 // No current clip, hence we intersect with sysclip and be
1284 QRegion clipRegion = systemClip();
1285 QClipData *clip = new QClipData(d->rasterBuffer->height());
1287 if (clipRegion.isEmpty())
1288 clip->setClipRect(clipRect);
1290 clip->setClipRegion(clipRegion & clipRect);
1292 if (s->flags.has_clip_ownership)
1296 s->clip->enabled = true;
1297 s->flags.has_clip_ownership = true;
1299 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1300 QClipData *base = s->clip;
1303 if (base->hasRectClip || base->hasRegionClip) {
1304 if (!s->flags.has_clip_ownership) {
1305 s->clip = new QClipData(d->rasterBuffer->height());
1306 s->flags.has_clip_ownership = true;
1308 if (base->hasRectClip)
1309 s->clip->setClipRect(base->clipRect & clipRect);
1311 s->clip->setClipRegion(base->clipRegion & clipRect);
1312 s->clip->enabled = true;
1320 qrasterpaintengine_dirty_clip(d, s);
1328 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1330 #ifdef QT_DEBUG_DRAW
1331 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1334 Q_D(QRasterPaintEngine);
1336 if (region.rectCount() == 1) {
1337 clip(region.boundingRect(), op);
1341 QRasterPaintEngineState *s = state();
1342 const QClipData *clip = d->clip();
1343 const QClipData *baseClip = d->baseClip.data();
1345 if (op == Qt::NoClip) {
1346 qrasterpaintengine_state_setNoClip(s);
1347 } else if (s->matrix.type() > QTransform::TxScale
1348 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1349 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1350 QPaintEngineEx::clip(region, op);
1352 const QClipData *curClip;
1355 if (op == Qt::IntersectClip)
1360 if (s->flags.has_clip_ownership) {
1364 newClip = new QClipData(d->rasterBuffer->height());
1366 s->flags.has_clip_ownership = true;
1369 QRegion r = s->matrix.map(region);
1370 if (curClip->hasRectClip)
1371 newClip->setClipRegion(r & curClip->clipRect);
1372 else if (curClip->hasRegionClip)
1373 newClip->setClipRegion(r & curClip->clipRegion);
1375 qrasterpaintengine_dirty_clip(d, s);
1380 \fn const QClipData *QRasterPaintEngine::clipData() const
1389 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1391 #ifdef QT_DEBUG_DRAW
1392 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1395 if (!fillData->blend)
1398 Q_D(QRasterPaintEngine);
1400 const QRectF controlPointRect = path.controlPointRect();
1402 QRasterPaintEngineState *s = state();
1403 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1404 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1405 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1406 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1407 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1408 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1410 if (!s->flags.antialiased && !do_clip) {
1411 d->initializeRasterizer(fillData);
1412 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1416 ensureOutlineMapper();
1417 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1420 static void fillRect_normalized(const QRect &r, QSpanData *data,
1421 QRasterPaintEnginePrivate *pe)
1425 bool rectClipped = true;
1428 x1 = qMax(r.x(), data->clip->xmin);
1429 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1430 y1 = qMax(r.y(), data->clip->ymin);
1431 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1432 rectClipped = data->clip->hasRectClip;
1435 x1 = qMax(r.x(), pe->deviceRect.x());
1436 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1437 y1 = qMax(r.y(), pe->deviceRect.y());
1438 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1440 x1 = qMax(r.x(), 0);
1441 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1442 y1 = qMax(r.y(), 0);
1443 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1446 if (x2 <= x1 || y2 <= y1)
1449 const int width = x2 - x1;
1450 const int height = y2 - y1;
1452 bool isUnclipped = rectClipped
1453 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1455 if (pe && isUnclipped) {
1456 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1458 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1459 || (mode == QPainter::CompositionMode_SourceOver
1460 && qAlpha(data->solid.color) == 255)))
1462 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1468 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1470 const int nspans = 256;
1471 QT_FT_Span spans[nspans];
1473 Q_ASSERT(data->blend);
1476 int n = qMin(nspans, y2 - y);
1480 spans[i].len = width;
1482 spans[i].coverage = 255;
1486 blend(n, spans, data);
1494 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1496 #ifdef QT_DEBUG_DRAW
1497 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1499 Q_D(QRasterPaintEngine);
1500 ensureRasterState();
1501 QRasterPaintEngineState *s = state();
1505 if (s->brushData.blend) {
1506 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1507 const QRect *r = rects;
1508 const QRect *lastRect = rects + rectCount;
1510 int offset_x = int(s->matrix.dx());
1511 int offset_y = int(s->matrix.dy());
1512 while (r < lastRect) {
1513 QRect rect = r->normalized();
1514 QRect rr = rect.translated(offset_x, offset_y);
1515 fillRect_normalized(rr, &s->brushData, d);
1519 QRectVectorPath path;
1520 for (int i=0; i<rectCount; ++i) {
1522 fill(path, s->brush);
1528 if (s->penData.blend) {
1529 QRectVectorPath path;
1530 if (s->flags.fast_pen) {
1531 QCosmeticStroker stroker(s, d->deviceRect);
1532 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1533 for (int i = 0; i < rectCount; ++i) {
1535 stroker.drawPath(path);
1538 for (int i = 0; i < rectCount; ++i) {
1540 stroke(path, s->pen);
1549 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1551 #ifdef QT_DEBUG_DRAW
1552 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1554 #ifdef QT_FAST_SPANS
1555 Q_D(QRasterPaintEngine);
1556 ensureRasterState();
1557 QRasterPaintEngineState *s = state();
1560 if (s->flags.tx_noshear) {
1562 if (s->brushData.blend) {
1563 d->initializeRasterizer(&s->brushData);
1564 for (int i = 0; i < rectCount; ++i) {
1565 const QRectF &rect = rects[i].normalized();
1568 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1569 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1570 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1575 if (s->penData.blend) {
1576 QRectVectorPath path;
1577 if (s->flags.fast_pen) {
1578 QCosmeticStroker stroker(s, d->deviceRect);
1579 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1580 for (int i = 0; i < rectCount; ++i) {
1582 stroker.drawPath(path);
1585 for (int i = 0; i < rectCount; ++i) {
1587 QPaintEngineEx::stroke(path, s->lastPen);
1594 #endif // QT_FAST_SPANS
1595 QPaintEngineEx::drawRects(rects, rectCount);
1602 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1604 Q_D(QRasterPaintEngine);
1605 QRasterPaintEngineState *s = state();
1608 if (!s->penData.blend)
1611 if (s->flags.fast_pen) {
1612 QCosmeticStroker stroker(s, d->deviceRect);
1613 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1614 stroker.drawPath(path);
1615 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1616 qreal width = qt_pen_is_cosmetic(s->lastPen, s->renderHints)
1617 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1618 : qpen_widthf(s->lastPen) * s->txscale;
1620 qreal dashOffset = s->lastPen.dashOffset();
1622 qreal patternLength = 0;
1623 const QVector<qreal> pattern = s->lastPen.dashPattern();
1624 for (int i = 0; i < pattern.size(); ++i)
1625 patternLength += pattern.at(i);
1627 if (patternLength > 0) {
1628 int n = qFloor(dashOffset / patternLength);
1629 dashOffset -= n * patternLength;
1630 while (dashOffset >= pattern.at(dashIndex)) {
1631 dashOffset -= pattern.at(dashIndex);
1632 if (++dashIndex >= pattern.size())
1638 Q_D(QRasterPaintEngine);
1639 d->initializeRasterizer(&s->penData);
1640 int lineCount = path.elementCount() / 2;
1641 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1643 for (int i = 0; i < lineCount; ++i) {
1644 if (lines[i].p1() == lines[i].p2()) {
1645 if (s->lastPen.capStyle() != Qt::FlatCap) {
1646 QPointF p = lines[i].p1();
1647 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1648 QPointF(p.x() + width*0.5, p.y())));
1649 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1654 const QLineF line = s->matrix.map(lines[i]);
1655 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1656 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1657 width / line.length(),
1658 s->lastPen.capStyle() == Qt::SquareCap);
1660 d->rasterizeLine_dashed(line, width,
1661 &dashIndex, &dashOffset, &inDash);
1666 QPaintEngineEx::stroke(path, pen);
1669 QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1671 QRasterPaintEngineState *s = state();
1673 qreal delta = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
1675 int x1 = qRound(rect.x() + delta);
1676 int y1 = qRound(rect.y() + delta);
1677 int x2 = qRound(rect.right() + delta);
1678 int y2 = qRound(rect.bottom() + delta);
1685 return QRect(x1, y1, x2 - x1, y2 - y1);
1691 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1695 #ifdef QT_DEBUG_DRAW
1696 QRectF rf = path.controlPointRect();
1697 qDebug() << "QRasterPaintEngine::fill(): "
1698 << "size=" << path.elementCount()
1699 << ", hints=" << hex << path.hints()
1703 Q_D(QRasterPaintEngine);
1704 QRasterPaintEngineState *s = state();
1707 if (!s->brushData.blend)
1710 if (path.shape() == QVectorPath::RectangleHint) {
1711 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1712 const qreal *p = path.points();
1713 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1714 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1715 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1718 ensureRasterState();
1719 if (s->flags.tx_noshear) {
1720 d->initializeRasterizer(&s->brushData);
1721 // ### Is normalizing really necessary here?
1722 const qreal *p = path.points();
1723 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1725 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1726 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1727 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1733 // ### Optimize for non transformed ellipses and rectangles...
1734 QRectF cpRect = path.controlPointRect();
1735 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1736 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1739 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1740 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1741 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1742 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1744 // ### Falonc: implement....
1745 // if (!s->flags.antialiased && !do_clip) {
1746 // d->initializeRasterizer(&s->brushData);
1747 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1751 ensureOutlineMapper();
1752 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1755 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1757 Q_D(QRasterPaintEngine);
1758 QRasterPaintEngineState *s = state();
1760 if (!s->flags.antialiased) {
1761 uint txop = s->matrix.type();
1762 if (txop == QTransform::TxNone) {
1763 fillRect_normalized(toNormalizedFillRect(r), data, d);
1765 } else if (txop == QTransform::TxTranslate) {
1766 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1767 fillRect_normalized(rr, data, d);
1769 } else if (txop == QTransform::TxScale) {
1770 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1771 fillRect_normalized(rr, data, d);
1775 ensureRasterState();
1776 if (s->flags.tx_noshear) {
1777 d->initializeRasterizer(data);
1778 QRectF nr = r.normalized();
1779 if (!nr.isEmpty()) {
1780 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1781 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1782 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1789 ensureOutlineMapper();
1790 fillPath(path, data);
1796 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1798 #ifdef QT_DEBUG_DRAW
1799 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1801 QRasterPaintEngineState *s = state();
1804 if (!s->brushData.blend)
1807 fillRect(r, &s->brushData);
1813 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1815 #ifdef QT_DEBUG_DRAW
1816 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1818 Q_D(QRasterPaintEngine);
1819 QRasterPaintEngineState *s = state();
1821 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1822 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1823 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1826 d->solid_color_filler.clip = d->clip();
1827 d->solid_color_filler.adjustSpanMethods();
1828 fillRect(r, &d->solid_color_filler);
1831 static inline bool isAbove(const QPointF *a, const QPointF *b)
1833 return a->y() < b->y();
1836 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1841 Q_ASSERT(pointCount >= 2);
1843 QVector<const QPointF *> sorted;
1844 sorted.reserve(pointCount);
1846 upper->reserve(pointCount * 3 / 4);
1847 lower->reserve(pointCount * 3 / 4);
1849 for (int i = 0; i < pointCount; ++i)
1850 sorted << points + i;
1852 qSort(sorted.begin(), sorted.end(), isAbove);
1854 qreal splitY = sorted.at(sorted.size() / 2)->y();
1856 const QPointF *end = points + pointCount;
1857 const QPointF *last = end - 1;
1859 QVector<QPointF> *bin[2] = { upper, lower };
1861 for (const QPointF *p = points; p < end; ++p) {
1862 int side = p->y() < splitY;
1863 int lastSide = last->y() < splitY;
1865 if (side != lastSide) {
1866 if (qFuzzyCompare(p->y(), splitY)) {
1867 bin[!side]->append(*p);
1868 } else if (qFuzzyCompare(last->y(), splitY)) {
1869 bin[side]->append(*last);
1871 QPointF delta = *p - *last;
1872 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1874 bin[0]->append(intersection);
1875 bin[1]->append(intersection);
1879 bin[side]->append(*p);
1884 // give up if we couldn't reduce the point count
1885 return upper->size() < pointCount && lower->size() < pointCount;
1891 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1893 Q_D(QRasterPaintEngine);
1894 QRasterPaintEngineState *s = state();
1896 const int maxPoints = 0xffff;
1898 // max amount of points that raster engine can reliably handle
1899 if (pointCount > maxPoints) {
1900 QVector<QPointF> upper, lower;
1902 if (splitPolygon(points, pointCount, &upper, &lower)) {
1903 fillPolygon(upper.constData(), upper.size(), mode);
1904 fillPolygon(lower.constData(), lower.size(), mode);
1906 qWarning("Polygon too complex for filling.");
1911 // Compose polygon fill..,
1912 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1913 ensureOutlineMapper();
1914 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1917 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1919 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1925 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1927 Q_D(QRasterPaintEngine);
1928 QRasterPaintEngineState *s = state();
1930 #ifdef QT_DEBUG_DRAW
1931 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1932 for (int i=0; i<pointCount; ++i)
1933 qDebug() << " - " << points[i];
1935 Q_ASSERT(pointCount >= 2);
1937 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1938 QRectF r(points[0], points[2]);
1944 if (mode != PolylineMode) {
1947 if (s->brushData.blend)
1948 fillPolygon(points, pointCount, mode);
1951 // Do the outline...
1952 if (s->penData.blend) {
1953 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1954 if (s->flags.fast_pen) {
1955 QCosmeticStroker stroker(s, d->deviceRect);
1956 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1957 stroker.drawPath(vp);
1959 QPaintEngineEx::stroke(vp, s->lastPen);
1967 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1969 Q_D(QRasterPaintEngine);
1970 QRasterPaintEngineState *s = state();
1972 #ifdef QT_DEBUG_DRAW
1973 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1974 for (int i=0; i<pointCount; ++i)
1975 qDebug() << " - " << points[i];
1977 Q_ASSERT(pointCount >= 2);
1978 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1979 QRect r(points[0].x(),
1981 points[2].x() - points[0].x(),
1982 points[2].y() - points[0].y());
1990 if (mode != PolylineMode) {
1992 if (s->brushData.blend) {
1993 // Compose polygon fill..,
1994 ensureOutlineMapper();
1995 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1996 d->outlineMapper->moveTo(*points);
1997 const QPoint *p = points;
1998 const QPoint *ep = points + pointCount - 1;
2000 d->outlineMapper->lineTo(*(++p));
2002 d->outlineMapper->endOutline();
2005 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2007 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2011 // Do the outline...
2012 if (s->penData.blend) {
2013 int count = pointCount * 2;
2014 QVarLengthArray<qreal> fpoints(count);
2015 for (int i=0; i<count; ++i)
2016 fpoints[i] = ((int *) points)[i];
2017 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2019 if (s->flags.fast_pen) {
2020 QCosmeticStroker stroker(s, d->deviceRect);
2021 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2022 stroker.drawPath(vp);
2024 QPaintEngineEx::stroke(vp, s->lastPen);
2032 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2034 #ifdef QT_DEBUG_DRAW
2035 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2038 QPlatformPixmap *pd = pixmap.handle();
2039 if (pd->classId() == QPlatformPixmap::RasterClass) {
2040 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2041 if (image.depth() == 1) {
2042 Q_D(QRasterPaintEngine);
2043 QRasterPaintEngineState *s = state();
2044 if (s->matrix.type() <= QTransform::TxTranslate) {
2046 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2048 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2051 QRasterPaintEngine::drawImage(pos, image);
2054 const QImage image = pixmap.toImage();
2055 if (pixmap.depth() == 1) {
2056 Q_D(QRasterPaintEngine);
2057 QRasterPaintEngineState *s = state();
2058 if (s->matrix.type() <= QTransform::TxTranslate) {
2060 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2062 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2065 QRasterPaintEngine::drawImage(pos, image);
2073 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2075 #ifdef QT_DEBUG_DRAW
2076 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2079 QPlatformPixmap* pd = pixmap.handle();
2080 if (pd->classId() == QPlatformPixmap::RasterClass) {
2081 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2082 if (image.depth() == 1) {
2083 Q_D(QRasterPaintEngine);
2084 QRasterPaintEngineState *s = state();
2085 if (s->matrix.type() <= QTransform::TxTranslate
2086 && r.size() == sr.size()
2087 && r.size() == pixmap.size()) {
2089 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2092 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2095 drawImage(r, image, sr);
2098 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2099 const QImage image = pd->toImage(clippedSource);
2100 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2101 if (image.depth() == 1) {
2102 Q_D(QRasterPaintEngine);
2103 QRasterPaintEngineState *s = state();
2104 if (s->matrix.type() <= QTransform::TxTranslate
2105 && r.size() == sr.size()
2106 && r.size() == pixmap.size()) {
2108 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2111 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2114 drawImage(r, image, translatedSource);
2119 static inline int fast_ceil_positive(const qreal &v)
2121 const int iv = int(v);
2128 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2130 const int xmin = int(rect.x());
2131 const int xmax = int(fast_ceil_positive(rect.right()));
2132 const int ymin = int(rect.y());
2133 const int ymax = int(fast_ceil_positive(rect.bottom()));
2134 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2140 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2142 #ifdef QT_DEBUG_DRAW
2143 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2146 Q_D(QRasterPaintEngine);
2147 QRasterPaintEngineState *s = state();
2148 qreal scale = img.devicePixelRatio();
2150 if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) {
2151 drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
2153 QRectF(0, 0, img.width(), img.height()));
2156 const QClipData *clip = d->clip();
2157 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2159 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2160 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2163 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2165 } else if (clip->hasRectClip) {
2166 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2174 d->image_filler.clip = clip;
2175 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2176 if (!d->image_filler.blend)
2178 d->image_filler.dx = -pt.x();
2179 d->image_filler.dy = -pt.y();
2180 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2182 fillRect_normalized(rr, &d->image_filler, d);
2187 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2189 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2200 inline RotationType qRotationType(const QTransform &transform)
2202 QTransform::TransformationType type = transform.type();
2204 if (type > QTransform::TxRotate)
2207 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2208 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2211 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2212 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2215 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2216 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2222 inline bool isPixelAligned(const QRectF &rect) {
2223 return QRectF(rect.toRect()) == rect;
2230 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2231 Qt::ImageConversionFlags)
2233 #ifdef QT_DEBUG_DRAW
2234 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2240 Q_D(QRasterPaintEngine);
2241 QRasterPaintEngineState *s = state();
2242 int sr_l = qFloor(sr.left());
2243 int sr_r = qCeil(sr.right()) - 1;
2244 int sr_t = qFloor(sr.top());
2245 int sr_b = qCeil(sr.bottom()) - 1;
2247 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2248 // as fillRect will apply the aliased coordinate delta we need to
2249 // subtract it here as we don't use it for image drawing
2250 QTransform old = s->matrix;
2252 if (s->flags.legacy_rounding)
2253 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2255 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2256 QRgb color = img.pixel(sr_l, sr_t);
2257 switch (img.format()) {
2258 case QImage::Format_ARGB32_Premultiplied:
2259 case QImage::Format_ARGB8565_Premultiplied:
2260 case QImage::Format_ARGB6666_Premultiplied:
2261 case QImage::Format_ARGB8555_Premultiplied:
2262 case QImage::Format_ARGB4444_Premultiplied:
2263 // Combine premultiplied color with the opacity set on the painter.
2264 d->solid_color_filler.solid.color =
2265 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2266 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2269 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2273 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2274 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2278 d->solid_color_filler.clip = d->clip();
2279 d->solid_color_filler.adjustSpanMethods();
2280 fillRect(r, &d->solid_color_filler);
2286 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2288 const QClipData *clip = d->clip();
2290 if (s->matrix.type() > QTransform::TxTranslate
2292 && (!clip || clip->hasRectClip)
2293 && s->intOpacity == 256
2294 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2295 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2296 && d->rasterBuffer->format == img.format()
2297 && (d->rasterBuffer->format == QImage::Format_RGB16
2298 || d->rasterBuffer->format == QImage::Format_RGB32
2299 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2300 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2302 RotationType rotationType = qRotationType(s->matrix);
2304 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2305 QRectF transformedTargetRect = s->matrix.mapRect(r);
2307 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2308 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2310 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2311 if (clippedTransformedTargetRect.isNull())
2314 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2316 QRect clippedSourceRect
2317 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2318 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2320 uint dbpl = d->rasterBuffer->bytesPerLine();
2321 uint sbpl = img.bytesPerLine();
2323 uchar *dst = d->rasterBuffer->buffer();
2324 uint bpp = img.depth() >> 3;
2326 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2327 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2329 uint cw = clippedSourceRect.width();
2330 uint ch = clippedSourceRect.height();
2332 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2339 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2341 QRectF targetBounds = s->matrix.mapRect(r);
2342 bool exceedsPrecision = targetBounds.width() > 0xffff
2343 || targetBounds.height() > 0xffff;
2345 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2346 if (s->matrix.type() > QTransform::TxScale) {
2347 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2348 if (func && (!clip || clip->hasRectClip)) {
2349 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2350 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2351 s->matrix, s->intOpacity);
2355 // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
2356 bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
2357 bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
2358 if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
2359 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2361 QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
2363 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2365 } else if (clip->hasRectClip) {
2366 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2371 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2372 if (func && (!clip || clip->hasRectClip)) {
2373 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2374 img.bits(), img.bytesPerLine(),
2375 qt_mapRect_non_normalizing(r, s->matrix), sr,
2376 !clip ? d->deviceRect : clip->clipRect,
2383 QTransform copy = s->matrix;
2384 copy.translate(r.x(), r.y());
2386 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2387 copy.translate(-sr.x(), -sr.y());
2389 d->image_filler_xform.clip = clip;
2390 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2391 if (!d->image_filler_xform.blend)
2393 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2395 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2396 QRectF rr = s->matrix.mapRect(r);
2398 const int x1 = qRound(rr.x());
2399 const int y1 = qRound(rr.y());
2400 const int x2 = qRound(rr.right());
2401 const int y2 = qRound(rr.bottom());
2403 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2407 #ifdef QT_FAST_SPANS
2408 ensureRasterState();
2409 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2410 d->initializeRasterizer(&d->image_filler_xform);
2411 d->rasterizer->setAntialiased(s->flags.antialiased);
2412 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2414 const QPointF offs = s->flags.legacy_rounding ? QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta) : QPointF();
2416 const QRectF &rect = r.normalized();
2417 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2418 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2420 if (s->flags.tx_noshear)
2421 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2423 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2427 const qreal offs = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
2430 QTransform m = s->matrix;
2431 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2432 m.m21(), m.m22(), m.m23(),
2433 m.m31() - offs, m.m32() - offs, m.m33());
2434 fillPath(path, &d->image_filler_xform);
2437 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2438 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2440 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2442 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2444 } else if (clip->hasRectClip) {
2445 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2451 d->image_filler.clip = clip;
2452 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2453 if (!d->image_filler.blend)
2455 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2456 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2459 rr.translate(s->matrix.dx(), s->matrix.dy());
2461 const int x1 = qRound(rr.x());
2462 const int y1 = qRound(rr.y());
2463 const int x2 = qRound(rr.right());
2464 const int y2 = qRound(rr.bottom());
2466 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2473 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2475 #ifdef QT_DEBUG_DRAW
2476 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2478 Q_D(QRasterPaintEngine);
2479 QRasterPaintEngineState *s = state();
2483 QPlatformPixmap *pd = pixmap.handle();
2484 if (pd->classId() == QPlatformPixmap::RasterClass) {
2485 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2487 image = pixmap.toImage();
2490 if (image.depth() == 1)
2491 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2493 if (s->matrix.type() > QTransform::TxTranslate) {
2494 QTransform copy = s->matrix;
2495 copy.translate(r.x(), r.y());
2496 copy.translate(-sr.x(), -sr.y());
2497 d->image_filler_xform.clip = d->clip();
2498 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2499 if (!d->image_filler_xform.blend)
2501 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2503 #ifdef QT_FAST_SPANS
2504 ensureRasterState();
2505 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2506 d->initializeRasterizer(&d->image_filler_xform);
2507 d->rasterizer->setAntialiased(s->flags.antialiased);
2508 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2510 const QRectF &rect = r.normalized();
2511 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2512 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2513 if (s->flags.tx_noshear)
2514 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2516 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2522 fillPath(path, &d->image_filler_xform);
2524 d->image_filler.clip = d->clip();
2526 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2527 if (!d->image_filler.blend)
2529 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2530 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2533 rr.translate(s->matrix.dx(), s->matrix.dy());
2534 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2540 static inline bool monoVal(const uchar* s, int x)
2542 return (s[x>>3] << (x&7)) & 0x80;
2548 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2550 Q_D(QRasterPaintEngine);
2551 return d->rasterBuffer.data();
2557 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2559 Q_D(QRasterPaintEngine);
2560 QRasterPaintEngineState *s = state();
2562 if (!s->penData.blend)
2565 QRasterBuffer *rb = d->rasterBuffer.data();
2567 const QRect rect(rx, ry, w, h);
2568 const QClipData *clip = d->clip();
2569 bool unclipped = false;
2571 // inlined QRect::intersects
2572 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2573 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2575 if (clip->hasRectClip) {
2576 unclipped = rx > clip->xmin
2577 && rx + w < clip->xmax
2579 && ry + h < clip->ymax;
2585 // inlined QRect::intersects
2586 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2587 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2591 // inlined QRect::contains
2592 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2593 && rect.top() >= 0 && rect.bottom() < rb->height();
2595 unclipped = contains && d->isUnclipped_normalized(rect);
2598 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2599 const uchar * scanline = static_cast<const uchar *>(src);
2601 if (s->flags.fast_text) {
2604 if (s->penData.bitmapBlit) {
2605 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2606 scanline, w, h, bpl);
2609 } else if (depth == 8) {
2610 if (s->penData.alphamapBlit) {
2611 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2612 scanline, w, h, bpl, 0);
2615 } else if (depth == 32) {
2616 // (A)RGB Alpha mask where the alpha component is not used.
2617 if (s->penData.alphaRGBBlit) {
2618 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2619 (const uint *) scanline, w, h, bpl / 4, 0);
2623 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2624 // (A)RGB Alpha mask where the alpha component is not used.
2626 int nx = qMax(0, rx);
2627 int ny = qMax(0, ry);
2629 // Move scanline pointer to compensate for moved x and y
2630 int xdiff = nx - rx;
2631 int ydiff = ny - ry;
2632 scanline += ydiff * bpl;
2633 scanline += xdiff * (depth == 32 ? 4 : 1);
2638 if (nx + w > d->rasterBuffer->width())
2639 w = d->rasterBuffer->width() - nx;
2640 if (ny + h > d->rasterBuffer->height())
2641 h = d->rasterBuffer->height() - ny;
2646 if (depth == 8 && s->penData.alphamapBlit) {
2647 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2648 scanline, w, h, bpl, clip);
2649 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2650 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2651 (const uint *) scanline, w, h, bpl / 4, clip);
2666 scanline += bpl * y0;
2670 w = qMin(w, rb->width() - qMax(0, rx));
2671 h = qMin(h, rb->height() - qMax(0, ry));
2673 if (w <= 0 || h <= 0)
2676 const int NSPANS = 256;
2677 QSpan spans[NSPANS];
2680 const int x1 = x0 + w;
2681 const int y1 = y0 + h;
2684 for (int y = y0; y < y1; ++y) {
2685 for (int x = x0; x < x1; ) {
2686 if (!monoVal(scanline, x)) {
2691 if (current == NSPANS) {
2692 blend(current, spans, &s->penData);
2695 spans[current].x = x + rx;
2696 spans[current].y = y + ry;
2697 spans[current].coverage = 255;
2700 // extend span until we find a different one.
2701 while (x < x1 && monoVal(scanline, x)) {
2705 spans[current].len = len;
2710 } else if (depth == 8) {
2711 for (int y = y0; y < y1; ++y) {
2712 for (int x = x0; x < x1; ) {
2713 // Skip those with 0 coverage
2714 if (scanline[x] == 0) {
2719 if (current == NSPANS) {
2720 blend(current, spans, &s->penData);
2723 int coverage = scanline[x];
2724 spans[current].x = x + rx;
2725 spans[current].y = y + ry;
2726 spans[current].coverage = coverage;
2730 // extend span until we find a different one.
2731 while (x < x1 && scanline[x] == coverage) {
2735 spans[current].len = len;
2740 } else { // 32-bit alpha...
2741 uint *sl = (uint *) src;
2742 for (int y = y0; y < y1; ++y) {
2743 for (int x = x0; x < x1; ) {
2744 // Skip those with 0 coverage
2745 if ((sl[x] & 0x00ffffff) == 0) {
2750 if (current == NSPANS) {
2751 blend(current, spans, &s->penData);
2754 uint rgbCoverage = sl[x];
2755 int coverage = qGreen(rgbCoverage);
2756 spans[current].x = x + rx;
2757 spans[current].y = y + ry;
2758 spans[current].coverage = coverage;
2762 // extend span until we find a different one.
2763 while (x < x1 && sl[x] == rgbCoverage) {
2767 spans[current].len = len;
2770 sl += bpl / sizeof(uint);
2773 // qDebug() << "alphaPenBlt: num spans=" << current
2774 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2775 // Call span func for current set of spans.
2777 blend(current, spans, &s->penData);
2783 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2784 const QFixedPoint *positions, QFontEngine *fontEngine)
2786 Q_D(QRasterPaintEngine);
2787 QRasterPaintEngineState *s = state();
2789 if (fontEngine->hasInternalCaching()) {
2790 QFontEngine::GlyphFormat neededFormat =
2791 painter()->device()->devType() == QInternal::Widget
2792 ? QFontEngine::Format_None
2793 : QFontEngine::Format_A8;
2795 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2796 neededFormat = QFontEngine::Format_Mono;
2798 for (int i = 0; i < numGlyphs; i++) {
2799 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2802 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2804 if (alphaMap == 0 || alphaMap->isNull())
2807 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2808 qFloor(positions[i].x) + offset.x(),
2809 qRound(positions[i].y) + offset.y(),
2810 alphaMap->width(), alphaMap->height());
2812 fontEngine->unlockAlphaMapForGlyph();
2816 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2818 QImageTextureGlyphCache *cache =
2819 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2821 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2822 fontEngine->setGlyphCache(0, cache);
2825 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2826 cache->fillInPendingGlyphs();
2828 const QImage &image = cache->image();
2829 int bpl = image.bytesPerLine();
2831 int depth = image.depth();
2835 leftShift = 2; // multiply by 4
2836 else if (depth == 1)
2837 rightShift = 3; // divide by 8
2839 int margin = fontEngine->glyphMargin(glyphType);
2840 const uchar *bits = image.bits();
2841 for (int i=0; i<numGlyphs; ++i) {
2843 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2844 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2845 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2849 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2850 int y = qRound(positions[i].y) - c.baseLineY - margin;
2852 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2855 // c.baseLineX, c.baseLineY,
2858 // positions[i].x.toInt(), positions[i].y.toInt());
2860 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2868 * Returns true if the rectangle is completely within the current clip
2869 * state of the paint engine.
2871 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2873 const QClipData *cl = clip();
2875 // inline contains() for performance (we know the rects are normalized)
2876 const QRect &r1 = deviceRect;
2877 return (r.left() >= r1.left() && r.right() <= r1.right()
2878 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2882 if (cl->hasRectClip) {
2883 // currently all painting functions clips to deviceRect internally
2884 if (cl->clipRect == deviceRect)
2887 // inline contains() for performance (we know the rects are normalized)
2888 const QRect &r1 = cl->clipRect;
2889 return (r.left() >= r1.left() && r.right() <= r1.right()
2890 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2892 return qt_region_strictContains(cl->clipRegion, r);
2896 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2899 Q_Q(const QRasterPaintEngine);
2900 const QRasterPaintEngineState *s = q->state();
2901 const QClipData *cl = clip();
2903 QRect r = rect.normalized();
2904 // inline contains() for performance (we know the rects are normalized)
2905 const QRect &r1 = deviceRect;
2906 return (r.left() >= r1.left() && r.right() <= r1.right()
2907 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2911 // currently all painting functions that call this function clip to deviceRect internally
2912 if (cl->hasRectClip && cl->clipRect == deviceRect)
2915 if (s->flags.antialiased)
2918 QRect r = rect.normalized();
2920 r.setX(r.x() - penWidth);
2921 r.setY(r.y() - penWidth);
2922 r.setWidth(r.width() + 2 * penWidth);
2923 r.setHeight(r.height() + 2 * penWidth);
2926 if (cl->hasRectClip) {
2927 // inline contains() for performance (we know the rects are normalized)
2928 const QRect &r1 = cl->clipRect;
2929 return (r.left() >= r1.left() && r.right() <= r1.right()
2930 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2932 return qt_region_strictContains(cl->clipRegion, r);
2936 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2939 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2943 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2944 const QSpanData *data) const
2946 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2950 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2951 const QSpanData *data) const
2953 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2957 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2958 const QSpanData *data) const
2960 Q_Q(const QRasterPaintEngine);
2961 const QRasterPaintEngineState *s = q->state();
2963 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2965 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2966 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2969 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2970 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2972 QFixed clipLeft = QFixed::fromReal(clip.left());
2973 QFixed clipRight = QFixed::fromReal(clip.right());
2974 QFixed clipTop = QFixed::fromReal(clip.top());
2975 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2978 while (first < numGlyphs) {
2979 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2980 QFixed left = metrics.x + positions[first].x;
2981 QFixed top = metrics.y + positions[first].y;
2982 QFixed right = left + metrics.width;
2983 QFixed bottom = top + metrics.height;
2984 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2988 int last = numGlyphs - 1;
2989 while (last > first) {
2990 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2991 QFixed left = metrics.x + positions[last].x;
2992 QFixed top = metrics.y + positions[last].y;
2993 QFixed right = left + metrics.width;
2994 QFixed bottom = top + metrics.height;
2995 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2999 return QPair<int, int>(first, last + 1);
3005 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3007 if (textItem->numGlyphs == 0)
3011 ensureRasterState();
3013 QFontEngine *fontEngine = textItem->fontEngine();
3014 if (!supportsTransformations(fontEngine)) {
3015 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3017 } else if (state()->matrix.type() < QTransform::TxProject) {
3019 QTransform invMat = state()->matrix.inverted(&invertible);
3023 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3024 textItem->fontEngine(), textItem->glyphs,
3025 textItem->glyphPositions, textItem->numGlyphs);
3026 QStaticTextItem copy = *textItem;
3027 copy.glyphs += range.first;
3028 copy.glyphPositions += range.first;
3029 copy.numGlyphs = range.second - range.first;
3030 QPaintEngineEx::drawStaticTextItem(©);
3032 QPaintEngineEx::drawStaticTextItem(textItem);
3039 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3041 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3043 #ifdef QT_DEBUG_DRAW
3044 Q_D(QRasterPaintEngine);
3045 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3046 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3050 if (ti.glyphs.numGlyphs == 0)
3053 ensureRasterState();
3055 QRasterPaintEngineState *s = state();
3056 QTransform matrix = s->matrix;
3058 if (!supportsTransformations(ti.fontEngine)) {
3059 QVarLengthArray<QFixedPoint> positions;
3060 QVarLengthArray<glyph_t> glyphs;
3062 matrix.translate(p.x(), p.y());
3063 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3065 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3066 } else if (matrix.type() < QTransform::TxProject) {
3068 QTransform invMat = matrix.inverted(&invertible);
3072 QVarLengthArray<QFixedPoint> positions;
3073 QVarLengthArray<glyph_t> glyphs;
3075 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3076 ti.flags, glyphs, positions);
3077 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3078 ti.fontEngine, glyphs.data(), positions.data(),
3081 if (range.first >= range.second)
3084 QStaticTextItem staticTextItem;
3085 staticTextItem.color = s->pen.color();
3086 staticTextItem.font = s->font;
3087 staticTextItem.setFontEngine(ti.fontEngine);
3088 staticTextItem.numGlyphs = range.second - range.first;
3089 staticTextItem.glyphs = glyphs.data() + range.first;
3090 staticTextItem.glyphPositions = positions.data() + range.first;
3091 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3093 QPaintEngineEx::drawTextItem(p, ti);
3100 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3102 Q_D(QRasterPaintEngine);
3103 QRasterPaintEngineState *s = state();
3106 if (!s->penData.blend)
3109 if (!s->flags.fast_pen) {
3110 QPaintEngineEx::drawPoints(points, pointCount);
3114 QCosmeticStroker stroker(s, d->deviceRect);
3115 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3116 stroker.drawPoints(points, pointCount);
3120 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3122 Q_D(QRasterPaintEngine);
3123 QRasterPaintEngineState *s = state();
3126 if (!s->penData.blend)
3129 if (!s->flags.fast_pen) {
3130 QPaintEngineEx::drawPoints(points, pointCount);
3134 QCosmeticStroker stroker(s, d->deviceRect);
3135 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3136 stroker.drawPoints(points, pointCount);
3142 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3144 #ifdef QT_DEBUG_DRAW
3145 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3147 Q_D(QRasterPaintEngine);
3148 QRasterPaintEngineState *s = state();
3151 if (!s->penData.blend)
3154 if (s->flags.fast_pen) {
3155 QCosmeticStroker stroker(s, d->deviceRect);
3156 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3157 for (int i=0; i<lineCount; ++i) {
3158 const QLine &l = lines[i];
3159 stroker.drawLine(l.p1(), l.p2());
3162 QPaintEngineEx::drawLines(lines, lineCount);
3166 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3172 Q_Q(QRasterPaintEngine);
3173 QRasterPaintEngineState *s = q->state();
3175 const QPen &pen = s->lastPen;
3176 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3177 const QVector<qreal> pattern = pen.dashPattern();
3179 qreal patternLength = 0;
3180 for (int i = 0; i < pattern.size(); ++i)
3181 patternLength += pattern.at(i);
3183 if (patternLength <= 0)
3186 qreal length = line.length();
3187 Q_ASSERT(length > 0);
3188 while (length > 0) {
3189 const bool rasterize = *inDash;
3190 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3193 if (dash >= length) {
3195 *dashOffset += dash / width;
3199 *inDash = !(*inDash);
3200 if (++*dashIndex >= pattern.size())
3207 if (rasterize && dash > 0)
3208 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3215 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3217 #ifdef QT_DEBUG_DRAW
3218 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3220 Q_D(QRasterPaintEngine);
3221 QRasterPaintEngineState *s = state();
3224 if (!s->penData.blend)
3226 if (s->flags.fast_pen) {
3227 QCosmeticStroker stroker(s, d->deviceRect);
3228 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3229 for (int i=0; i<lineCount; ++i) {
3230 QLineF line = lines[i];
3231 stroker.drawLine(line.p1(), line.p2());
3234 QPaintEngineEx::drawLines(lines, lineCount);
3242 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3244 Q_D(QRasterPaintEngine);
3245 QRasterPaintEngineState *s = state();
3248 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3249 || (qpen_style(s->lastPen) == Qt::NoPen))
3250 && !s->flags.antialiased
3251 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3253 && s->matrix.type() <= QTransform::TxScale) // no shear
3256 const QRectF r = s->matrix.mapRect(rect);
3257 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3258 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3259 const QRect brect = QRect(int(r.x()), int(r.y()),
3260 int_dim(r.x(), r.width()),
3261 int_dim(r.y(), r.height()));
3263 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3264 &s->penData, &s->brushData);
3268 QPaintEngineEx::drawEllipse(rect);
3276 void QRasterPaintEngine::setDC(HDC hdc) {
3277 Q_D(QRasterPaintEngine);
3284 HDC QRasterPaintEngine::getDC() const
3286 Q_D(const QRasterPaintEngine);
3293 void QRasterPaintEngine::releaseDC(HDC) const
3302 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3304 const QTransform &m = state()->matrix;
3305 return supportsTransformations(fontEngine, m);
3311 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3313 if (fontEngine->supportsTransformations(m))
3316 return !shouldDrawCachedGlyphs(fontEngine, m);
3322 QPoint QRasterPaintEngine::coordinateOffset() const
3324 return QPoint(0, 0);
3327 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3332 Q_D(QRasterPaintEngine);
3334 Q_ASSERT(image.depth() == 1);
3336 const int spanCount = 256;
3337 QT_FT_Span spans[spanCount];
3341 int w = image.width();
3342 int h = image.height();
3343 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3344 int ymin = qMax(qRound(pos.y()), 0);
3345 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3346 int xmin = qMax(qRound(pos.x()), 0);
3348 int x_offset = xmin - qRound(pos.x());
3350 QImage::Format format = image.format();
3351 for (int y = ymin; y < ymax; ++y) {
3352 const uchar *src = image.scanLine(y - qRound(pos.y()));
3353 if (format == QImage::Format_MonoLSB) {
3354 for (int x = 0; x < xmax - xmin; ++x) {
3355 int src_x = x + x_offset;
3356 uchar pixel = src[src_x >> 3];
3361 if (pixel & (0x1 << (src_x & 7))) {
3362 spans[n].x = xmin + x;
3364 spans[n].coverage = 255;
3366 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3370 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3373 if (n == spanCount) {
3374 fg->blend(n, spans, fg);
3380 for (int x = 0; x < xmax - xmin; ++x) {
3381 int src_x = x + x_offset;
3382 uchar pixel = src[src_x >> 3];
3387 if (pixel & (0x80 >> (x & 7))) {
3388 spans[n].x = xmin + x;
3390 spans[n].coverage = 255;
3392 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3396 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3399 if (n == spanCount) {
3400 fg->blend(n, spans, fg);
3408 fg->blend(n, spans, fg);
3414 \enum QRasterPaintEngine::ClipType
3417 \value RectClip Indicates that the currently set clip is a single rectangle.
3418 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3423 Returns the type of the clip currently set.
3425 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3427 Q_D(const QRasterPaintEngine);
3429 const QClipData *clip = d->clip();
3430 if (!clip || clip->hasRectClip)
3438 Returns the bounding rect of the currently set clip.
3440 QRect QRasterPaintEngine::clipBoundingRect() const
3442 Q_D(const QRasterPaintEngine);
3444 const QClipData *clip = d->clip();
3447 return d->deviceRect;
3449 if (clip->hasRectClip)
3450 return clip->clipRect;
3452 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3455 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3457 Q_Q(QRasterPaintEngine);
3458 QRasterPaintEngineState *s = q->state();
3460 rasterizer->setAntialiased(s->flags.antialiased);
3461 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3463 QRect clipRect(deviceRect);
3465 // ### get from optimized rectbased QClipData
3467 const QClipData *c = clip();
3469 const QRect r(QPoint(c->xmin, c->ymin),
3470 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3471 clipRect = clipRect.intersected(r);
3472 blend = data->blend;
3474 blend = data->unclipped_blend;
3477 rasterizer->setClipRect(clipRect);
3478 rasterizer->initialize(blend, data);
3481 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3482 ProcessSpans callback,
3483 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3485 if (!callback || !outline)
3488 Q_Q(QRasterPaintEngine);
3489 QRasterPaintEngineState *s = q->state();
3491 if (!s->flags.antialiased) {
3492 initializeRasterizer(spanData);
3494 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3498 rasterizer->rasterize(outline, fillRule);
3502 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3506 int q_gray_rendered_spans(QT_FT_Raster raster);
3509 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3511 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3514 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3515 ProcessSpans callback,
3516 void *userData, QRasterBuffer *)
3518 if (!callback || !outline)
3521 Q_Q(QRasterPaintEngine);
3522 QRasterPaintEngineState *s = q->state();
3524 if (!s->flags.antialiased) {
3525 rasterizer->setAntialiased(s->flags.antialiased);
3526 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3527 rasterizer->setClipRect(deviceRect);
3528 rasterizer->initialize(callback, userData);
3530 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3534 rasterizer->rasterize(outline, fillRule);
3538 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3539 // minimize memory reallocations. However if initial size for
3540 // raster pool is changed for lower value, reallocations will
3542 int rasterPoolSize = MINIMUM_POOL_SIZE;
3543 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3544 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3545 uchar *rasterPoolOnHeap = 0;
3547 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3549 void *data = userData;
3551 QT_FT_BBox clip_box = { deviceRect.x(),
3553 deviceRect.x() + deviceRect.width(),
3554 deviceRect.y() + deviceRect.height() };
3556 QT_FT_Raster_Params rasterParams;
3557 rasterParams.target = 0;
3558 rasterParams.source = outline;
3559 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3560 rasterParams.gray_spans = 0;
3561 rasterParams.black_spans = 0;
3562 rasterParams.bit_test = 0;
3563 rasterParams.bit_set = 0;
3564 rasterParams.user = data;
3565 rasterParams.clip_box = clip_box;
3570 int rendered_spans = 0;
3574 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3575 rasterParams.gray_spans = callback;
3576 rasterParams.skip_spans = rendered_spans;
3577 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3579 // Out of memory, reallocate some more and try again...
3580 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3581 rasterPoolSize *= 2;
3582 if (rasterPoolSize > 1024 * 1024) {
3583 qWarning("QPainter: Rasterization of primitive failed");
3587 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3589 free(rasterPoolOnHeap);
3590 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3592 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3594 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3596 qt_ft_grays_raster.raster_done(*grayRaster.data());
3597 qt_ft_grays_raster.raster_new(grayRaster.data());
3598 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3604 free(rasterPoolOnHeap);
3607 void QRasterPaintEnginePrivate::recalculateFastImages()
3609 Q_Q(QRasterPaintEngine);
3610 QRasterPaintEngineState *s = q->state();
3612 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3613 && s->matrix.type() <= QTransform::TxShear;
3616 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3618 Q_Q(const QRasterPaintEngine);
3619 const QRasterPaintEngineState *s = q->state();
3621 return s->flags.fast_images
3622 && (mode == QPainter::CompositionMode_SourceOver
3623 || (mode == QPainter::CompositionMode_Source
3624 && !image.hasAlphaChannel()));
3627 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3629 Q_ASSERT(image.depth() == 1);
3631 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3632 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3634 QRgb fg = PREMUL(color.rgba());
3637 int height = sourceImage.height();
3638 int width = sourceImage.width();
3639 for (int y=0; y<height; ++y) {
3640 uchar *source = sourceImage.scanLine(y);
3641 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3642 if (!source || !target)
3643 QT_THROW(std::bad_alloc()); // we must have run out of memory
3644 for (int x=0; x < width; ++x)
3645 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3650 QRasterBuffer::~QRasterBuffer()
3654 void QRasterBuffer::init()
3656 compositionMode = QPainter::CompositionMode_SourceOver;
3657 monoDestinationWithClut = false;
3662 QImage::Format QRasterBuffer::prepare(QImage *image)
3664 m_buffer = (uchar *)image->bits();
3665 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3666 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3667 bytes_per_pixel = image->depth()/8;
3668 bytes_per_line = image->bytesPerLine();
3670 format = image->format();
3671 drawHelper = qDrawHelper + format;
3672 if (image->depth() == 1 && image->colorTable().size() == 2) {
3673 monoDestinationWithClut = true;
3674 destColor0 = PREMUL(image->colorTable()[0]);
3675 destColor1 = PREMUL(image->colorTable()[1]);
3681 void QRasterBuffer::resetBuffer(int val)
3683 memset(m_buffer, val, m_height*bytes_per_line);
3686 QClipData::QClipData(int height)
3688 clipSpanHeight = height;
3693 xmin = xmax = ymin = ymax = 0;
3697 hasRectClip = hasRegionClip = false;
3700 QClipData::~QClipData()
3708 void QClipData::initialize()
3714 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3716 Q_CHECK_PTR(m_clipLines);
3718 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3719 allocated = clipSpanHeight;
3720 Q_CHECK_PTR(m_spans);
3726 m_clipLines[y].spans = 0;
3727 m_clipLines[y].count = 0;
3731 const int len = clipRect.width();
3734 QSpan *span = m_spans + count;
3738 span->coverage = 255;
3741 m_clipLines[y].spans = span;
3742 m_clipLines[y].count = 1;
3746 while (y < clipSpanHeight) {
3747 m_clipLines[y].spans = 0;
3748 m_clipLines[y].count = 0;
3751 } else if (hasRegionClip) {
3753 const QVector<QRect> rects = clipRegion.rects();
3754 const int numRects = rects.size();
3757 const int maxSpans = (ymax - ymin) * numRects;
3758 if (maxSpans > allocated) {
3759 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3760 allocated = maxSpans;
3765 int firstInBand = 0;
3767 while (firstInBand < numRects) {
3768 const int currMinY = rects.at(firstInBand).y();
3769 const int currMaxY = currMinY + rects.at(firstInBand).height();
3771 while (y < currMinY) {
3772 m_clipLines[y].spans = 0;
3773 m_clipLines[y].count = 0;
3777 int lastInBand = firstInBand;
3778 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3781 while (y < currMaxY) {
3783 m_clipLines[y].spans = m_spans + count;
3784 m_clipLines[y].count = lastInBand - firstInBand + 1;
3786 for (int r = firstInBand; r <= lastInBand; ++r) {
3787 const QRect &currRect = rects.at(r);
3788 QSpan *span = m_spans + count;
3789 span->x = currRect.x();
3790 span->len = currRect.width();
3792 span->coverage = 255;
3798 firstInBand = lastInBand + 1;
3801 Q_ASSERT(count <= allocated);
3803 while (y < clipSpanHeight) {
3804 m_clipLines[y].spans = 0;
3805 m_clipLines[y].count = 0;
3811 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3816 free(m_clipLines); // same for clipLines
3822 void QClipData::fixup()
3827 ymin = ymax = xmin = xmax = 0;
3832 ymin = m_spans[0].y;
3833 ymax = m_spans[count-1].y + 1;
3837 const int firstLeft = m_spans[0].x;
3838 const int firstRight = m_spans[0].x + m_spans[0].len;
3841 for (int i = 0; i < count; ++i) {
3842 QT_FT_Span_& span = m_spans[i];
3845 if (span.y != y + 1 && y != -1)
3848 m_clipLines[y].spans = &span;
3849 m_clipLines[y].count = 1;
3851 ++m_clipLines[y].count;
3853 const int spanLeft = span.x;
3854 const int spanRight = spanLeft + span.len;
3856 if (spanLeft < xmin)
3859 if (spanRight > xmax)
3862 if (spanLeft != firstLeft || spanRight != firstRight)
3868 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3873 Convert \a rect to clip spans.
3875 void QClipData::setClipRect(const QRect &rect)
3877 if (hasRectClip && rect == clipRect)
3880 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3882 hasRegionClip = false;
3886 xmax = rect.x() + rect.width();
3887 ymin = qMin(rect.y(), clipSpanHeight);
3888 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3895 // qDebug() << xmin << xmax << ymin << ymax;
3899 Convert \a region to clip spans.
3901 void QClipData::setClipRegion(const QRegion ®ion)
3903 if (region.rectCount() == 1) {
3904 setClipRect(region.rects().at(0));
3908 hasRegionClip = true;
3909 hasRectClip = false;
3910 clipRegion = region;
3912 { // set bounding rect
3913 const QRect rect = region.boundingRect();
3915 xmax = rect.x() + rect.width();
3917 ymax = rect.y() + rect.height();
3929 spans must be sorted on y
3931 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3932 const QSpan *spans, const QSpan *end,
3933 QSpan **outSpans, int available)
3935 const_cast<QClipData *>(clip)->initialize();
3937 QSpan *out = *outSpans;
3939 const QSpan *clipSpans = clip->m_spans + *currentClip;
3940 const QSpan *clipEnd = clip->m_spans + clip->count;
3942 while (available && spans < end ) {
3943 if (clipSpans >= clipEnd) {
3947 if (clipSpans->y > spans->y) {
3951 if (spans->y != clipSpans->y) {
3952 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3953 clipSpans = clip->m_clipLines[spans->y].spans;
3958 Q_ASSERT(spans->y == clipSpans->y);
3961 int sx2 = sx1 + spans->len;
3962 int cx1 = clipSpans->x;
3963 int cx2 = cx1 + clipSpans->len;
3965 if (cx1 < sx1 && cx2 < sx1) {
3968 } else if (sx1 < cx1 && sx2 < cx1) {
3972 int x = qMax(sx1, cx1);
3973 int len = qMin(sx2, cx2) - x;
3975 out->x = qMax(sx1, cx1);
3976 out->len = qMin(sx2, cx2) - out->x;
3978 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3990 *currentClip = clipSpans - clip->m_spans;
3994 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3996 // qDebug() << "qt_span_fill_clipped" << spanCount;
3997 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3999 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4001 const int NSPANS = 256;
4002 QSpan cspans[NSPANS];
4003 int currentClip = 0;
4004 const QSpan *end = spans + spanCount;
4005 while (spans < end) {
4006 QSpan *clipped = cspans;
4007 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4008 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4009 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4011 if (clipped - cspans)
4012 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4018 Clip spans to \a{clip}-rectangle.
4019 Returns number of unclipped spans
4021 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4024 const short minx = clip.left();
4025 const short miny = clip.top();
4026 const short maxx = clip.right();
4027 const short maxy = clip.bottom();
4030 for (int i = 0; i < numSpans; ++i) {
4031 if (spans[i].y > maxy)
4033 if (spans[i].y < miny
4034 || spans[i].x > maxx
4035 || spans[i].x + spans[i].len <= minx) {
4038 if (spans[i].x < minx) {
4039 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4042 spans[n].x = spans[i].x;
4043 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4045 if (spans[n].len == 0)
4047 spans[n].y = spans[i].y;
4048 spans[n].coverage = spans[i].coverage;
4055 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4058 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4059 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4061 Q_ASSERT(fillData->clip);
4062 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4064 // hw: check if this const_cast<> is safe!!!
4065 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4066 fillData->clip->clipRect);
4068 fillData->unclipped_blend(count, spans, fillData);
4071 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4073 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4075 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4076 // for (int i = 0; i < qMin(count, 10); ++i) {
4077 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4080 switch (clipData->operation) {
4082 case Qt::IntersectClip:
4084 QClipData *newClip = clipData->newClip;
4085 newClip->initialize();
4087 int currentClip = 0;
4088 const QSpan *end = spans + count;
4089 while (spans < end) {
4090 QSpan *newspans = newClip->m_spans + newClip->count;
4091 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4092 &newspans, newClip->allocated - newClip->count);
4093 newClip->count = newspans - newClip->m_spans;
4095 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4096 newClip->allocated *= 2;
4102 case Qt::ReplaceClip:
4103 clipData->newClip->appendSpans(spans, count);
4111 QImage QRasterBuffer::bufferImage() const
4113 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4115 for (int y = 0; y < m_height; ++y) {
4116 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4118 for (int x=0; x<m_width; ++x) {
4119 uint argb = span[x];
4120 image.setPixel(x, y, argb);
4128 void QRasterBuffer::flushToARGBImage(QImage *target) const
4130 int w = qMin(m_width, target->width());
4131 int h = qMin(m_height, target->height());
4133 for (int y=0; y<h; ++y) {
4134 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4135 QRgb *dest = (QRgb *) target->scanLine(y);
4136 for (int x=0; x<w; ++x) {
4137 QRgb pixel = sourceLine[x];
4138 int alpha = qAlpha(pixel);
4142 dest[x] = (alpha << 24)
4143 | ((255*qRed(pixel)/alpha) << 16)
4144 | ((255*qGreen(pixel)/alpha) << 8)
4145 | ((255*qBlue(pixel)/alpha) << 0);
4152 class QGradientCache
4156 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4157 stops(s), opacity(op), interpolationMode(mode) {}
4158 uint buffer[GRADIENT_STOPTABLE_SIZE];
4159 QGradientStops stops;
4161 QGradient::InterpolationMode interpolationMode;
4164 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4167 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4168 quint64 hash_val = 0;
4170 QGradientStops stops = gradient.stops();
4171 for (int i = 0; i < stops.size() && i <= 2; i++)
4172 hash_val += stops[i].second.rgba();
4174 QMutexLocker lock(&mutex);
4175 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4177 if (it == cache.constEnd())
4178 return addCacheElement(hash_val, gradient, opacity);
4181 const CacheInfo &cache_info = it.value();
4182 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4183 return cache_info.buffer;
4185 } while (it != cache.constEnd() && it.key() == hash_val);
4186 // an exact match for these stops and opacity was not found, create new cache
4187 return addCacheElement(hash_val, gradient, opacity);
4191 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4193 inline int maxCacheSize() const { return 60; }
4194 inline void generateGradientColorTable(const QGradient& g,
4196 int size, int opacity) const;
4197 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4198 if (cache.size() == maxCacheSize()) {
4199 // may remove more than 1, but OK
4200 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4202 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4203 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4204 return cache.insert(hash_val, cache_entry).value().buffer;
4207 QGradientColorTableHash cache;
4211 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4213 QGradientStops stops = gradient.stops();
4214 int stopCount = stops.count();
4215 Q_ASSERT(stopCount > 0);
4217 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4219 if (stopCount == 2) {
4220 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4221 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4223 qreal first_stop = stops[0].first;
4224 qreal second_stop = stops[1].first;
4226 if (second_stop < first_stop) {
4227 qSwap(first_color, second_color);
4228 qSwap(first_stop, second_stop);
4231 if (colorInterpolation) {
4232 first_color = PREMUL(first_color);
4233 second_color = PREMUL(second_color);
4236 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4237 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4239 uint red_first = qRed(first_color) << 16;
4240 uint green_first = qGreen(first_color) << 16;
4241 uint blue_first = qBlue(first_color) << 16;
4242 uint alpha_first = qAlpha(first_color) << 16;
4244 uint red_second = qRed(second_color) << 16;
4245 uint green_second = qGreen(second_color) << 16;
4246 uint blue_second = qBlue(second_color) << 16;
4247 uint alpha_second = qAlpha(second_color) << 16;
4250 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4251 if (colorInterpolation)
4252 colorTable[i] = first_color;
4254 colorTable[i] = PREMUL(first_color);
4257 if (i < second_index) {
4258 qreal reciprocal = qreal(1) / (second_index - first_index);
4260 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4261 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4262 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4263 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4266 red_first += 1 << 15;
4267 green_first += 1 << 15;
4268 blue_first += 1 << 15;
4269 alpha_first += 1 << 15;
4271 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4272 red_first += red_delta;
4273 green_first += green_delta;
4274 blue_first += blue_delta;
4275 alpha_first += alpha_delta;
4277 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4278 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4280 if (colorInterpolation)
4281 colorTable[i] = color;
4283 colorTable[i] = PREMUL(color);
4287 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4288 if (colorInterpolation)
4289 colorTable[i] = second_color;
4291 colorTable[i] = PREMUL(second_color);
4297 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4298 if (stopCount == 1) {
4299 current_color = PREMUL(current_color);
4300 for (int i = 0; i < size; ++i)
4301 colorTable[i] = current_color;
4305 // The position where the gradient begins and ends
4306 qreal begin_pos = stops[0].first;
4307 qreal end_pos = stops[stopCount-1].first;
4309 int pos = 0; // The position in the color table.
4312 qreal incr = 1 / qreal(size); // the double increment.
4313 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4315 // Up to first point
4316 colorTable[pos++] = PREMUL(current_color);
4317 while (dpos <= begin_pos) {
4318 colorTable[pos] = colorTable[pos - 1];
4323 int current_stop = 0; // We always interpolate between current and current + 1.
4325 qreal t; // position between current left and right stops
4326 qreal t_delta; // the t increment per entry in the color table
4328 if (dpos < end_pos) {
4330 while (dpos > stops[current_stop+1].first)
4333 if (current_stop != 0)
4334 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4335 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4337 if (colorInterpolation) {
4338 current_color = PREMUL(current_color);
4339 next_color = PREMUL(next_color);
4342 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4343 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4344 t = (dpos - stops[current_stop].first) * c;
4348 Q_ASSERT(current_stop < stopCount);
4350 int dist = qRound(t);
4351 int idist = 256 - dist;
4353 if (colorInterpolation)
4354 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4356 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4361 if (dpos >= end_pos)
4367 while (dpos > stops[current_stop+skip+1].first)
4371 current_stop += skip;
4373 current_color = next_color;
4375 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4376 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4378 if (colorInterpolation) {
4380 current_color = PREMUL(current_color);
4381 next_color = PREMUL(next_color);
4384 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4385 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4386 t = (dpos - stops[current_stop].first) * c;
4393 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4394 while (pos < size - 1) {
4395 colorTable[pos] = current_color;
4399 // Make sure the last color stop is represented at the end of the table
4400 colorTable[size - 1] = current_color;
4403 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4406 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4412 m11 = m22 = m33 = 1.;
4413 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4414 clip = pe ? pe->d_func()->clip() : 0;
4417 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4419 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4421 Qt::BrushStyle brushStyle = qbrush_style(brush);
4422 switch (brushStyle) {
4423 case Qt::SolidPattern: {
4425 QColor c = qbrush_color(brush);
4426 QRgb rgba = c.rgba();
4427 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4428 if ((solid.color & 0xff000000) == 0
4429 && compositionMode == QPainter::CompositionMode_SourceOver) {
4435 case Qt::LinearGradientPattern:
4437 type = LinearGradient;
4438 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4439 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4440 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4441 gradient.spread = g->spread();
4443 QLinearGradientData &linearData = gradient.linear;
4445 linearData.origin.x = g->start().x();
4446 linearData.origin.y = g->start().y();
4447 linearData.end.x = g->finalStop().x();
4448 linearData.end.y = g->finalStop().y();
4452 case Qt::RadialGradientPattern:
4454 type = RadialGradient;
4455 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4456 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4457 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4458 gradient.spread = g->spread();
4460 QRadialGradientData &radialData = gradient.radial;
4462 QPointF center = g->center();
4463 radialData.center.x = center.x();
4464 radialData.center.y = center.y();
4465 radialData.center.radius = g->centerRadius();
4466 QPointF focal = g->focalPoint();
4467 radialData.focal.x = focal.x();
4468 radialData.focal.y = focal.y();
4469 radialData.focal.radius = g->focalRadius();
4473 case Qt::ConicalGradientPattern:
4475 type = ConicalGradient;
4476 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4477 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4478 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4479 gradient.spread = QGradient::RepeatSpread;
4481 QConicalGradientData &conicalData = gradient.conical;
4483 QPointF center = g->center();
4484 conicalData.center.x = center.x();
4485 conicalData.center.y = center.y();
4486 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4490 case Qt::Dense1Pattern:
4491 case Qt::Dense2Pattern:
4492 case Qt::Dense3Pattern:
4493 case Qt::Dense4Pattern:
4494 case Qt::Dense5Pattern:
4495 case Qt::Dense6Pattern:
4496 case Qt::Dense7Pattern:
4497 case Qt::HorPattern:
4498 case Qt::VerPattern:
4499 case Qt::CrossPattern:
4500 case Qt::BDiagPattern:
4501 case Qt::FDiagPattern:
4502 case Qt::DiagCrossPattern:
4505 tempImage = new QImage();
4506 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4507 initTexture(tempImage, alpha, QTextureData::Tiled);
4509 case Qt::TexturePattern:
4512 tempImage = new QImage();
4514 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4515 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4517 *tempImage = brush.textureImage();
4518 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4526 adjustSpanMethods();
4529 void QSpanData::adjustSpanMethods()
4539 unclipped_blend = 0;
4542 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4543 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4544 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4545 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4546 fillRect = rasterBuffer->drawHelper->fillRect;
4548 case LinearGradient:
4549 case RadialGradient:
4550 case ConicalGradient:
4551 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4554 unclipped_blend = qBlendTexture;
4555 if (!texture.imageData)
4556 unclipped_blend = 0;
4561 if (!unclipped_blend) {
4564 blend = unclipped_blend;
4565 } else if (clip->hasRectClip) {
4566 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4568 blend = qt_span_fill_clipped;
4572 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4575 // make sure we round off correctly in qdrawhelper.cpp
4576 delta.translate(1.0 / 65536, 1.0 / 65536);
4578 QTransform inv = (delta * matrix).inverted();
4591 const bool affine = inv.isAffine();
4592 fast_matrix = affine
4593 && m11 * m11 + m21 * m21 < 1e4
4594 && m12 * m12 + m22 * m22 < 1e4
4598 adjustSpanMethods();
4601 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4603 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4604 if (!d || d->height == 0) {
4605 texture.imageData = 0;
4612 texture.bytesPerLine = 0;
4613 texture.format = QImage::Format_Invalid;
4614 texture.colorTable = 0;
4615 texture.hasAlpha = alpha != 256;
4617 texture.imageData = d->data;
4618 texture.width = d->width;
4619 texture.height = d->height;
4621 if (sourceRect.isNull()) {
4624 texture.x2 = texture.width;
4625 texture.y2 = texture.height;
4627 texture.x1 = sourceRect.x();
4628 texture.y1 = sourceRect.y();
4629 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4630 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4633 texture.bytesPerLine = d->bytes_per_line;
4635 texture.format = d->format;
4636 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4637 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4639 texture.const_alpha = alpha;
4640 texture.type = _type;
4642 adjustSpanMethods();
4647 \a x and \a y is relative to the midpoint of \a rect.
4649 static inline void drawEllipsePoints(int x, int y, int length,
4652 ProcessSpans pen_func, ProcessSpans brush_func,
4653 QSpanData *pen_data, QSpanData *brush_data)
4658 QT_FT_Span outline[4];
4659 const int midx = rect.x() + (rect.width() + 1) / 2;
4660 const int midy = rect.y() + (rect.height() + 1) / 2;
4666 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4667 outline[0].len = qMin(length, x - outline[0].x);
4669 outline[0].coverage = 255;
4673 outline[1].len = length;
4675 outline[1].coverage = 255;
4678 outline[2].x = outline[0].x;
4679 outline[2].len = outline[0].len;
4680 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4681 outline[2].coverage = 255;
4685 outline[3].len = length;
4686 outline[3].y = outline[2].y;
4687 outline[3].coverage = 255;
4689 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4693 fill[0].x = outline[0].x + outline[0].len - 1;
4694 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4695 fill[0].y = outline[1].y;
4696 fill[0].coverage = 255;
4699 fill[1].x = outline[2].x + outline[2].len - 1;
4700 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4701 fill[1].y = outline[3].y;
4702 fill[1].coverage = 255;
4704 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4705 n = qt_intersect_spans(fill, n, clip);
4707 brush_func(n, fill, brush_data);
4710 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4711 n = qt_intersect_spans(outline, n, clip);
4713 pen_func(n, outline, pen_data);
4719 Draws an ellipse using the integer point midpoint algorithm.
4721 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4722 ProcessSpans pen_func, ProcessSpans brush_func,
4723 QSpanData *pen_data, QSpanData *brush_data)
4725 const qreal a = qreal(rect.width()) / 2;
4726 const qreal b = qreal(rect.height()) / 2;
4727 qreal d = b*b - (a*a*b) + 0.25*a*a;
4730 int y = (rect.height() + 1) / 2;
4734 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4735 if (d < 0) { // select E
4738 } else { // select SE
4739 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4740 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4741 pen_func, brush_func, pen_data, brush_data);
4746 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4747 pen_func, brush_func, pen_data, brush_data);
4750 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4751 const int miny = rect.height() & 0x1;
4753 if (d < 0) { // select SE
4754 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4756 } else { // select S
4757 d += a*a*(-2*y + 3);
4760 drawEllipsePoints(x, y, 1, rect, clip,
4761 pen_func, brush_func, pen_data, brush_data);
4766 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4772 #ifdef QT_DEBUG_DRAW
4773 void dumpClip(int width, int height, const QClipData *clip)
4775 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4776 clipImg.fill(0xffff0000);
4783 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4785 for (int i = 0; i < clip->count; ++i) {
4786 const QSpan *span = ((QClipData *) clip)->spans() + i;
4787 for (int j = 0; j < span->len; ++j)
4788 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4789 x0 = qMin(x0, int(span->x));
4790 x1 = qMax(x1, int(span->x + span->len - 1));
4792 y0 = qMin(y0, int(span->y));
4793 y1 = qMax(y1, int(span->y));
4796 static int counter = 0;
4803 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4804 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));