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 (pen.isCosmetic()) {
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 s->flags.fast_pen = pen_style > Qt::NoPen
791 && ((pen.isCosmetic() && penWidth <= 1)
792 || (!pen.isCosmetic() && s->flags.tx_noshear && penWidth * s->txscale <= 1));
794 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
804 void QRasterPaintEngine::brushOriginChanged()
806 QRasterPaintEngineState *s = state();
808 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
811 s->fillFlags |= DirtyBrushOrigin;
818 void QRasterPaintEngine::brushChanged()
820 QRasterPaintEngineState *s = state();
822 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
824 s->fillFlags |= DirtyBrush;
833 void QRasterPaintEngine::updateBrush(const QBrush &brush)
836 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
838 Q_D(QRasterPaintEngine);
839 QRasterPaintEngineState *s = state();
840 // must set clip prior to setup, as setup uses it...
841 s->brushData.clip = d->clip();
842 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
843 if (s->fillFlags & DirtyTransform
844 || brush.transform().type() >= QTransform::TxNone)
845 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
846 s->lastBrush = brush;
850 void QRasterPaintEngine::updateOutlineMapper()
852 Q_D(QRasterPaintEngine);
853 d->outlineMapper->setMatrix(state()->matrix);
856 void QRasterPaintEngine::updateRasterState()
858 QRasterPaintEngineState *s = state();
860 if (s->dirty & DirtyTransform)
861 updateMatrix(s->matrix);
863 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
864 const QPainter::CompositionMode mode = s->composition_mode;
865 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
866 && s->intOpacity == 256
867 && (mode == QPainter::CompositionMode_Source
868 || (mode == QPainter::CompositionMode_SourceOver
869 && qAlpha(s->penData.solid.color) == 255));
879 void QRasterPaintEngine::opacityChanged()
881 QRasterPaintEngineState *s = state();
884 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
887 s->fillFlags |= DirtyOpacity;
888 s->strokeFlags |= DirtyOpacity;
889 s->pixmapFlags |= DirtyOpacity;
890 s->dirty |= DirtyOpacity;
891 s->intOpacity = (int) (s->opacity * 256);
897 void QRasterPaintEngine::compositionModeChanged()
899 Q_D(QRasterPaintEngine);
900 QRasterPaintEngineState *s = state();
903 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
906 s->fillFlags |= DirtyCompositionMode;
907 s->dirty |= DirtyCompositionMode;
909 s->strokeFlags |= DirtyCompositionMode;
910 d->rasterBuffer->compositionMode = s->composition_mode;
912 d->recalculateFastImages();
918 void QRasterPaintEngine::renderHintsChanged()
920 QRasterPaintEngineState *s = state();
923 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
926 bool was_aa = s->flags.antialiased;
927 bool was_bilinear = s->flags.bilinear;
929 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
930 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
931 s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);
933 if (was_aa != s->flags.antialiased)
934 s->strokeFlags |= DirtyHints;
936 if (was_bilinear != s->flags.bilinear) {
937 s->strokeFlags |= DirtyPen;
938 s->fillFlags |= DirtyBrush;
941 Q_D(QRasterPaintEngine);
942 d->recalculateFastImages();
948 void QRasterPaintEngine::transformChanged()
950 QRasterPaintEngineState *s = state();
953 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
956 s->fillFlags |= DirtyTransform;
957 s->strokeFlags |= DirtyTransform;
959 s->dirty |= DirtyTransform;
961 Q_D(QRasterPaintEngine);
962 d->recalculateFastImages();
968 void QRasterPaintEngine::clipEnabledChanged()
970 QRasterPaintEngineState *s = state();
973 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
977 s->clip->enabled = s->clipEnabled;
978 s->fillFlags |= DirtyClipEnabled;
979 s->strokeFlags |= DirtyClipEnabled;
980 s->pixmapFlags |= DirtyClipEnabled;
984 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
986 SrcOverBlendFunc func,
991 if (alpha == 0 || !clip.isValid())
994 Q_ASSERT(img.depth() >= 8);
996 int srcBPL = img.bytesPerLine();
997 const uchar *srcBits = img.bits();
998 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
999 int iw = img.width();
1000 int ih = img.height();
1002 if (!sr.isEmpty()) {
1005 // Adjust the image according to the source offset...
1006 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1009 // adapt the x parameters
1010 int x = qRound(pt.x());
1012 int cx2 = clip.x() + clip.width();
1015 srcBits += srcSize * d;
1020 int d = x + iw - cx2;
1026 // adapt the y paremeters...
1028 int cy2 = clip.y() + clip.height();
1029 int y = qRound(pt.y());
1032 srcBits += srcBPL * d;
1037 int d = y + ih - cy2;
1043 // call the blend function...
1044 int dstSize = rasterBuffer->bytesPerPixel();
1045 int dstBPL = rasterBuffer->bytesPerLine();
1046 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1053 void QRasterPaintEnginePrivate::systemStateChanged()
1055 QRect clipRect(0, 0,
1056 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1057 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1059 if (!systemClip.isEmpty()) {
1060 QRegion clippedDeviceRgn = systemClip & clipRect;
1061 deviceRect = clippedDeviceRgn.boundingRect();
1062 baseClip->setClipRegion(clippedDeviceRgn);
1064 deviceRect = clipRect;
1065 baseClip->setClipRect(deviceRect);
1067 #ifdef QT_DEBUG_DRAW
1068 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1071 exDeviceRect = deviceRect;
1073 Q_Q(QRasterPaintEngine);
1074 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1075 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1076 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1079 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1081 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1084 Q_Q(QRasterPaintEngine);
1085 bool bilinear = q->state()->flags.bilinear;
1087 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1088 spanData->setupMatrix(b.transform() * m, bilinear);
1090 if (m.type() <= QTransform::TxTranslate) {
1091 // specialize setupMatrix for translation matrices
1092 // to avoid needless matrix inversion
1100 spanData->dx = -m.dx();
1101 spanData->dy = -m.dy();
1102 spanData->txop = m.type();
1103 spanData->bilinear = bilinear;
1104 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1105 spanData->adjustSpanMethods();
1107 spanData->setupMatrix(m, bilinear);
1112 // #define QT_CLIPPING_RATIOS
1114 #ifdef QT_CLIPPING_RATIOS
1119 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1121 if (d->clip()->hasRectClip)
1123 if (d->clip()->hasRegionClip)
1127 if ((totalClips % 5000) == 0) {
1128 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1129 rectClips * 100.0 / (qreal) totalClips,
1130 regionClips * 100.0 / (qreal) totalClips,
1131 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1140 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1142 if (s->flags.has_clip_ownership)
1145 s->flags.has_clip_ownership = false;
1148 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1150 s->fillFlags |= QPaintEngine::DirtyClipPath;
1151 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1152 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1154 d->solid_color_filler.clip = d->clip();
1155 d->solid_color_filler.adjustSpanMethods();
1157 #ifdef QT_DEBUG_DRAW
1158 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1167 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1169 #ifdef QT_DEBUG_DRAW
1170 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1172 if (path.elements()) {
1173 for (int i=0; i<path.elementCount(); ++i) {
1174 qDebug() << " - " << path.elements()[i]
1175 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1178 for (int i=0; i<path.elementCount(); ++i) {
1179 qDebug() << " ---- "
1180 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1185 Q_D(QRasterPaintEngine);
1186 QRasterPaintEngineState *s = state();
1188 const qreal *points = path.points();
1189 const QPainterPath::ElementType *types = path.elements();
1191 // There are some cases that are not supported by clip(QRect)
1192 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1193 if (s->matrix.type() <= QTransform::TxScale
1194 && ((path.shape() == QVectorPath::RectangleHint)
1195 || (isRect(points, path.elementCount())
1196 && (!types || (types[0] == QPainterPath::MoveToElement
1197 && types[1] == QPainterPath::LineToElement
1198 && types[2] == QPainterPath::LineToElement
1199 && types[3] == QPainterPath::LineToElement))))) {
1200 #ifdef QT_DEBUG_DRAW
1201 qDebug() << " --- optimizing vector clip to rect clip...";
1204 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1205 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1210 if (op == Qt::NoClip) {
1211 qrasterpaintengine_state_setNoClip(s);
1214 QClipData *base = d->baseClip.data();
1216 // Intersect with current clip when available...
1217 if (op == Qt::IntersectClip && s->clip)
1220 // We always intersect, except when there is nothing to
1221 // intersect with, in which case we simplify the operation to
1223 Qt::ClipOperation isectOp = Qt::IntersectClip;
1225 isectOp = Qt::ReplaceClip;
1227 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1228 newClip->initialize();
1229 ClipData clipData = { base, newClip, isectOp };
1230 ensureOutlineMapper();
1231 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1235 if (s->flags.has_clip_ownership)
1239 s->flags.has_clip_ownership = true;
1241 qrasterpaintengine_dirty_clip(d, s);
1249 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1251 #ifdef QT_DEBUG_DRAW
1252 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1255 QRasterPaintEngineState *s = state();
1257 if (op == Qt::NoClip) {
1258 qrasterpaintengine_state_setNoClip(s);
1260 } else if (s->matrix.type() > QTransform::TxScale) {
1261 QPaintEngineEx::clip(rect, op);
1264 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1265 QPaintEngineEx::clip(rect, op);
1271 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1273 Q_D(QRasterPaintEngine);
1274 QRect clipRect = r & d->deviceRect;
1275 QRasterPaintEngineState *s = state();
1277 if (op == Qt::ReplaceClip || s->clip == 0) {
1279 // No current clip, hence we intersect with sysclip and be
1281 QRegion clipRegion = systemClip();
1282 QClipData *clip = new QClipData(d->rasterBuffer->height());
1284 if (clipRegion.isEmpty())
1285 clip->setClipRect(clipRect);
1287 clip->setClipRegion(clipRegion & clipRect);
1289 if (s->flags.has_clip_ownership)
1293 s->clip->enabled = true;
1294 s->flags.has_clip_ownership = true;
1296 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1297 QClipData *base = s->clip;
1300 if (base->hasRectClip || base->hasRegionClip) {
1301 if (!s->flags.has_clip_ownership) {
1302 s->clip = new QClipData(d->rasterBuffer->height());
1303 s->flags.has_clip_ownership = true;
1305 if (base->hasRectClip)
1306 s->clip->setClipRect(base->clipRect & clipRect);
1308 s->clip->setClipRegion(base->clipRegion & clipRect);
1309 s->clip->enabled = true;
1317 qrasterpaintengine_dirty_clip(d, s);
1325 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1327 #ifdef QT_DEBUG_DRAW
1328 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1331 Q_D(QRasterPaintEngine);
1333 if (region.rectCount() == 1) {
1334 clip(region.boundingRect(), op);
1338 QRasterPaintEngineState *s = state();
1339 const QClipData *clip = d->clip();
1340 const QClipData *baseClip = d->baseClip.data();
1342 if (op == Qt::NoClip) {
1343 qrasterpaintengine_state_setNoClip(s);
1344 } else if (s->matrix.type() > QTransform::TxScale
1345 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1346 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1347 QPaintEngineEx::clip(region, op);
1349 const QClipData *curClip;
1352 if (op == Qt::IntersectClip)
1357 if (s->flags.has_clip_ownership) {
1361 newClip = new QClipData(d->rasterBuffer->height());
1363 s->flags.has_clip_ownership = true;
1366 QRegion r = s->matrix.map(region);
1367 if (curClip->hasRectClip)
1368 newClip->setClipRegion(r & curClip->clipRect);
1369 else if (curClip->hasRegionClip)
1370 newClip->setClipRegion(r & curClip->clipRegion);
1372 qrasterpaintengine_dirty_clip(d, s);
1377 \fn const QClipData *QRasterPaintEngine::clipData() const
1386 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1388 #ifdef QT_DEBUG_DRAW
1389 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1392 if (!fillData->blend)
1395 Q_D(QRasterPaintEngine);
1397 const QRectF controlPointRect = path.controlPointRect();
1399 QRasterPaintEngineState *s = state();
1400 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1401 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1402 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1403 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1404 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1405 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1407 if (!s->flags.antialiased && !do_clip) {
1408 d->initializeRasterizer(fillData);
1409 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1413 ensureOutlineMapper();
1414 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1417 static void fillRect_normalized(const QRect &r, QSpanData *data,
1418 QRasterPaintEnginePrivate *pe)
1422 bool rectClipped = true;
1425 x1 = qMax(r.x(), data->clip->xmin);
1426 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1427 y1 = qMax(r.y(), data->clip->ymin);
1428 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1429 rectClipped = data->clip->hasRectClip;
1432 x1 = qMax(r.x(), pe->deviceRect.x());
1433 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1434 y1 = qMax(r.y(), pe->deviceRect.y());
1435 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1437 x1 = qMax(r.x(), 0);
1438 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1439 y1 = qMax(r.y(), 0);
1440 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1443 if (x2 <= x1 || y2 <= y1)
1446 const int width = x2 - x1;
1447 const int height = y2 - y1;
1449 bool isUnclipped = rectClipped
1450 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1452 if (pe && isUnclipped) {
1453 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1455 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1456 || (mode == QPainter::CompositionMode_SourceOver
1457 && qAlpha(data->solid.color) == 255)))
1459 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1465 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1467 const int nspans = 256;
1468 QT_FT_Span spans[nspans];
1470 Q_ASSERT(data->blend);
1473 int n = qMin(nspans, y2 - y);
1477 spans[i].len = width;
1479 spans[i].coverage = 255;
1483 blend(n, spans, data);
1491 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1493 #ifdef QT_DEBUG_DRAW
1494 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1496 Q_D(QRasterPaintEngine);
1497 ensureRasterState();
1498 QRasterPaintEngineState *s = state();
1502 if (s->brushData.blend) {
1503 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1504 const QRect *r = rects;
1505 const QRect *lastRect = rects + rectCount;
1507 int offset_x = int(s->matrix.dx());
1508 int offset_y = int(s->matrix.dy());
1509 while (r < lastRect) {
1510 QRect rect = r->normalized();
1511 QRect rr = rect.translated(offset_x, offset_y);
1512 fillRect_normalized(rr, &s->brushData, d);
1516 QRectVectorPath path;
1517 for (int i=0; i<rectCount; ++i) {
1519 fill(path, s->brush);
1525 if (s->penData.blend) {
1526 QRectVectorPath path;
1527 if (s->flags.fast_pen) {
1528 QCosmeticStroker stroker(s, d->deviceRect);
1529 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1530 for (int i = 0; i < rectCount; ++i) {
1532 stroker.drawPath(path);
1535 for (int i = 0; i < rectCount; ++i) {
1537 stroke(path, s->pen);
1546 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1548 #ifdef QT_DEBUG_DRAW
1549 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1551 #ifdef QT_FAST_SPANS
1552 Q_D(QRasterPaintEngine);
1553 ensureRasterState();
1554 QRasterPaintEngineState *s = state();
1557 if (s->flags.tx_noshear) {
1559 if (s->brushData.blend) {
1560 d->initializeRasterizer(&s->brushData);
1561 for (int i = 0; i < rectCount; ++i) {
1562 const QRectF &rect = rects[i].normalized();
1565 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1566 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1567 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1572 if (s->penData.blend) {
1573 QRectVectorPath path;
1574 if (s->flags.fast_pen) {
1575 QCosmeticStroker stroker(s, d->deviceRect);
1576 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1577 for (int i = 0; i < rectCount; ++i) {
1579 stroker.drawPath(path);
1582 for (int i = 0; i < rectCount; ++i) {
1584 QPaintEngineEx::stroke(path, s->lastPen);
1591 #endif // QT_FAST_SPANS
1592 QPaintEngineEx::drawRects(rects, rectCount);
1599 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1601 Q_D(QRasterPaintEngine);
1602 QRasterPaintEngineState *s = state();
1605 if (!s->penData.blend)
1608 if (s->flags.fast_pen) {
1609 QCosmeticStroker stroker(s, d->deviceRect);
1610 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1611 stroker.drawPath(path);
1612 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1613 qreal width = s->lastPen.isCosmetic()
1614 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1615 : qpen_widthf(s->lastPen) * s->txscale;
1617 qreal dashOffset = s->lastPen.dashOffset();
1619 qreal patternLength = 0;
1620 const QVector<qreal> pattern = s->lastPen.dashPattern();
1621 for (int i = 0; i < pattern.size(); ++i)
1622 patternLength += pattern.at(i);
1624 if (patternLength > 0) {
1625 int n = qFloor(dashOffset / patternLength);
1626 dashOffset -= n * patternLength;
1627 while (dashOffset >= pattern.at(dashIndex)) {
1628 dashOffset -= pattern.at(dashIndex);
1629 if (++dashIndex >= pattern.size())
1635 Q_D(QRasterPaintEngine);
1636 d->initializeRasterizer(&s->penData);
1637 int lineCount = path.elementCount() / 2;
1638 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1640 for (int i = 0; i < lineCount; ++i) {
1641 if (lines[i].p1() == lines[i].p2()) {
1642 if (s->lastPen.capStyle() != Qt::FlatCap) {
1643 QPointF p = lines[i].p1();
1644 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1645 QPointF(p.x() + width*0.5, p.y())));
1646 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1651 const QLineF line = s->matrix.map(lines[i]);
1652 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1653 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1654 width / line.length(),
1655 s->lastPen.capStyle() == Qt::SquareCap);
1657 d->rasterizeLine_dashed(line, width,
1658 &dashIndex, &dashOffset, &inDash);
1663 QPaintEngineEx::stroke(path, pen);
1666 QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1668 QRasterPaintEngineState *s = state();
1670 qreal delta = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
1672 int x1 = qRound(rect.x() + delta);
1673 int y1 = qRound(rect.y() + delta);
1674 int x2 = qRound(rect.right() + delta);
1675 int y2 = qRound(rect.bottom() + delta);
1682 return QRect(x1, y1, x2 - x1, y2 - y1);
1688 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1692 #ifdef QT_DEBUG_DRAW
1693 QRectF rf = path.controlPointRect();
1694 qDebug() << "QRasterPaintEngine::fill(): "
1695 << "size=" << path.elementCount()
1696 << ", hints=" << hex << path.hints()
1700 Q_D(QRasterPaintEngine);
1701 QRasterPaintEngineState *s = state();
1704 if (!s->brushData.blend)
1707 if (path.shape() == QVectorPath::RectangleHint) {
1708 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1709 const qreal *p = path.points();
1710 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1711 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1712 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1715 ensureRasterState();
1716 if (s->flags.tx_noshear) {
1717 d->initializeRasterizer(&s->brushData);
1718 // ### Is normalizing really necessary here?
1719 const qreal *p = path.points();
1720 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1722 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1723 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1724 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1730 // ### Optimize for non transformed ellipses and rectangles...
1731 QRectF cpRect = path.controlPointRect();
1732 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1733 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1736 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1737 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1738 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1739 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1741 // ### Falonc: implement....
1742 // if (!s->flags.antialiased && !do_clip) {
1743 // d->initializeRasterizer(&s->brushData);
1744 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1748 ensureOutlineMapper();
1749 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1752 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1754 Q_D(QRasterPaintEngine);
1755 QRasterPaintEngineState *s = state();
1757 if (!s->flags.antialiased) {
1758 uint txop = s->matrix.type();
1759 if (txop == QTransform::TxNone) {
1760 fillRect_normalized(toNormalizedFillRect(r), data, d);
1762 } else if (txop == QTransform::TxTranslate) {
1763 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1764 fillRect_normalized(rr, data, d);
1766 } else if (txop == QTransform::TxScale) {
1767 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1768 fillRect_normalized(rr, data, d);
1772 ensureRasterState();
1773 if (s->flags.tx_noshear) {
1774 d->initializeRasterizer(data);
1775 QRectF nr = r.normalized();
1776 if (!nr.isEmpty()) {
1777 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1778 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1779 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1786 ensureOutlineMapper();
1787 fillPath(path, data);
1793 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1795 #ifdef QT_DEBUG_DRAW
1796 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1798 QRasterPaintEngineState *s = state();
1801 if (!s->brushData.blend)
1804 fillRect(r, &s->brushData);
1810 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1812 #ifdef QT_DEBUG_DRAW
1813 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1815 Q_D(QRasterPaintEngine);
1816 QRasterPaintEngineState *s = state();
1818 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1819 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1820 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1823 d->solid_color_filler.clip = d->clip();
1824 d->solid_color_filler.adjustSpanMethods();
1825 fillRect(r, &d->solid_color_filler);
1828 static inline bool isAbove(const QPointF *a, const QPointF *b)
1830 return a->y() < b->y();
1833 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1838 Q_ASSERT(pointCount >= 2);
1840 QVector<const QPointF *> sorted;
1841 sorted.reserve(pointCount);
1843 upper->reserve(pointCount * 3 / 4);
1844 lower->reserve(pointCount * 3 / 4);
1846 for (int i = 0; i < pointCount; ++i)
1847 sorted << points + i;
1849 qSort(sorted.begin(), sorted.end(), isAbove);
1851 qreal splitY = sorted.at(sorted.size() / 2)->y();
1853 const QPointF *end = points + pointCount;
1854 const QPointF *last = end - 1;
1856 QVector<QPointF> *bin[2] = { upper, lower };
1858 for (const QPointF *p = points; p < end; ++p) {
1859 int side = p->y() < splitY;
1860 int lastSide = last->y() < splitY;
1862 if (side != lastSide) {
1863 if (qFuzzyCompare(p->y(), splitY)) {
1864 bin[!side]->append(*p);
1865 } else if (qFuzzyCompare(last->y(), splitY)) {
1866 bin[side]->append(*last);
1868 QPointF delta = *p - *last;
1869 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1871 bin[0]->append(intersection);
1872 bin[1]->append(intersection);
1876 bin[side]->append(*p);
1881 // give up if we couldn't reduce the point count
1882 return upper->size() < pointCount && lower->size() < pointCount;
1888 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1890 Q_D(QRasterPaintEngine);
1891 QRasterPaintEngineState *s = state();
1893 const int maxPoints = 0xffff;
1895 // max amount of points that raster engine can reliably handle
1896 if (pointCount > maxPoints) {
1897 QVector<QPointF> upper, lower;
1899 if (splitPolygon(points, pointCount, &upper, &lower)) {
1900 fillPolygon(upper.constData(), upper.size(), mode);
1901 fillPolygon(lower.constData(), lower.size(), mode);
1903 qWarning("Polygon too complex for filling.");
1908 // Compose polygon fill..,
1909 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1910 ensureOutlineMapper();
1911 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1914 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1916 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1922 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1924 Q_D(QRasterPaintEngine);
1925 QRasterPaintEngineState *s = state();
1927 #ifdef QT_DEBUG_DRAW
1928 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1929 for (int i=0; i<pointCount; ++i)
1930 qDebug() << " - " << points[i];
1932 Q_ASSERT(pointCount >= 2);
1934 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1935 QRectF r(points[0], points[2]);
1941 if (mode != PolylineMode) {
1944 if (s->brushData.blend)
1945 fillPolygon(points, pointCount, mode);
1948 // Do the outline...
1949 if (s->penData.blend) {
1950 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1951 if (s->flags.fast_pen) {
1952 QCosmeticStroker stroker(s, d->deviceRect);
1953 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1954 stroker.drawPath(vp);
1956 QPaintEngineEx::stroke(vp, s->lastPen);
1964 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1966 Q_D(QRasterPaintEngine);
1967 QRasterPaintEngineState *s = state();
1969 #ifdef QT_DEBUG_DRAW
1970 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1971 for (int i=0; i<pointCount; ++i)
1972 qDebug() << " - " << points[i];
1974 Q_ASSERT(pointCount >= 2);
1975 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1976 QRect r(points[0].x(),
1978 points[2].x() - points[0].x(),
1979 points[2].y() - points[0].y());
1987 if (mode != PolylineMode) {
1989 if (s->brushData.blend) {
1990 // Compose polygon fill..,
1991 ensureOutlineMapper();
1992 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1993 d->outlineMapper->moveTo(*points);
1994 const QPoint *p = points;
1995 const QPoint *ep = points + pointCount - 1;
1997 d->outlineMapper->lineTo(*(++p));
1999 d->outlineMapper->endOutline();
2002 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2004 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2008 // Do the outline...
2009 if (s->penData.blend) {
2010 int count = pointCount * 2;
2011 QVarLengthArray<qreal> fpoints(count);
2012 for (int i=0; i<count; ++i)
2013 fpoints[i] = ((int *) points)[i];
2014 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2016 if (s->flags.fast_pen) {
2017 QCosmeticStroker stroker(s, d->deviceRect);
2018 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2019 stroker.drawPath(vp);
2021 QPaintEngineEx::stroke(vp, s->lastPen);
2029 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2031 #ifdef QT_DEBUG_DRAW
2032 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2035 QPlatformPixmap *pd = pixmap.handle();
2036 if (pd->classId() == QPlatformPixmap::RasterClass) {
2037 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2038 if (image.depth() == 1) {
2039 Q_D(QRasterPaintEngine);
2040 QRasterPaintEngineState *s = state();
2041 if (s->matrix.type() <= QTransform::TxTranslate) {
2043 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2045 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2048 QRasterPaintEngine::drawImage(pos, image);
2051 const QImage image = pixmap.toImage();
2052 if (pixmap.depth() == 1) {
2053 Q_D(QRasterPaintEngine);
2054 QRasterPaintEngineState *s = state();
2055 if (s->matrix.type() <= QTransform::TxTranslate) {
2057 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2059 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2062 QRasterPaintEngine::drawImage(pos, image);
2070 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2072 #ifdef QT_DEBUG_DRAW
2073 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2076 QPlatformPixmap* pd = pixmap.handle();
2077 if (pd->classId() == QPlatformPixmap::RasterClass) {
2078 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2079 if (image.depth() == 1) {
2080 Q_D(QRasterPaintEngine);
2081 QRasterPaintEngineState *s = state();
2082 if (s->matrix.type() <= QTransform::TxTranslate
2083 && r.size() == sr.size()
2084 && r.size() == pixmap.size()) {
2086 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2089 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2092 drawImage(r, image, sr);
2095 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2096 const QImage image = pd->toImage(clippedSource);
2097 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2098 if (image.depth() == 1) {
2099 Q_D(QRasterPaintEngine);
2100 QRasterPaintEngineState *s = state();
2101 if (s->matrix.type() <= QTransform::TxTranslate
2102 && r.size() == sr.size()
2103 && r.size() == pixmap.size()) {
2105 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2108 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2111 drawImage(r, image, translatedSource);
2116 static inline int fast_ceil_positive(const qreal &v)
2118 const int iv = int(v);
2125 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2127 const int xmin = int(rect.x());
2128 const int xmax = int(fast_ceil_positive(rect.right()));
2129 const int ymin = int(rect.y());
2130 const int ymax = int(fast_ceil_positive(rect.bottom()));
2131 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2137 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2139 #ifdef QT_DEBUG_DRAW
2140 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2143 Q_D(QRasterPaintEngine);
2144 QRasterPaintEngineState *s = state();
2146 if (s->matrix.type() > QTransform::TxTranslate) {
2147 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2149 QRectF(0, 0, img.width(), img.height()));
2152 const QClipData *clip = d->clip();
2153 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2155 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2156 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2159 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2161 } else if (clip->hasRectClip) {
2162 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2170 d->image_filler.clip = clip;
2171 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2172 if (!d->image_filler.blend)
2174 d->image_filler.dx = -pt.x();
2175 d->image_filler.dy = -pt.y();
2176 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2178 fillRect_normalized(rr, &d->image_filler, d);
2183 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2185 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2196 inline RotationType qRotationType(const QTransform &transform)
2198 QTransform::TransformationType type = transform.type();
2200 if (type > QTransform::TxRotate)
2203 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2204 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2207 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2208 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2211 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2212 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2218 inline bool isPixelAligned(const QRectF &rect) {
2219 return QRectF(rect.toRect()) == rect;
2226 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2227 Qt::ImageConversionFlags)
2229 #ifdef QT_DEBUG_DRAW
2230 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2236 Q_D(QRasterPaintEngine);
2237 QRasterPaintEngineState *s = state();
2238 int sr_l = qFloor(sr.left());
2239 int sr_r = qCeil(sr.right()) - 1;
2240 int sr_t = qFloor(sr.top());
2241 int sr_b = qCeil(sr.bottom()) - 1;
2243 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2244 // as fillRect will apply the aliased coordinate delta we need to
2245 // subtract it here as we don't use it for image drawing
2246 QTransform old = s->matrix;
2248 if (s->flags.legacy_rounding)
2249 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2251 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2252 QRgb color = img.pixel(sr_l, sr_t);
2253 switch (img.format()) {
2254 case QImage::Format_ARGB32_Premultiplied:
2255 case QImage::Format_ARGB8565_Premultiplied:
2256 case QImage::Format_ARGB6666_Premultiplied:
2257 case QImage::Format_ARGB8555_Premultiplied:
2258 case QImage::Format_ARGB4444_Premultiplied:
2259 // Combine premultiplied color with the opacity set on the painter.
2260 d->solid_color_filler.solid.color =
2261 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2262 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2265 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2269 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2270 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2274 d->solid_color_filler.clip = d->clip();
2275 d->solid_color_filler.adjustSpanMethods();
2276 fillRect(r, &d->solid_color_filler);
2282 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2284 const QClipData *clip = d->clip();
2286 if (s->matrix.type() > QTransform::TxTranslate
2288 && (!clip || clip->hasRectClip)
2289 && s->intOpacity == 256
2290 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2291 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2292 && d->rasterBuffer->format == img.format()
2293 && (d->rasterBuffer->format == QImage::Format_RGB16
2294 || d->rasterBuffer->format == QImage::Format_RGB32
2295 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2296 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2298 RotationType rotationType = qRotationType(s->matrix);
2300 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2301 QRectF transformedTargetRect = s->matrix.mapRect(r);
2303 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2304 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2306 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2307 if (clippedTransformedTargetRect.isNull())
2310 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2312 QRect clippedSourceRect
2313 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2314 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2316 uint dbpl = d->rasterBuffer->bytesPerLine();
2317 uint sbpl = img.bytesPerLine();
2319 uchar *dst = d->rasterBuffer->buffer();
2320 uint bpp = img.depth() >> 3;
2322 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2323 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2325 uint cw = clippedSourceRect.width();
2326 uint ch = clippedSourceRect.height();
2328 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2335 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2337 QRectF targetBounds = s->matrix.mapRect(r);
2338 bool exceedsPrecision = targetBounds.width() > 0xffff
2339 || targetBounds.height() > 0xffff;
2341 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2342 if (s->matrix.type() > QTransform::TxScale) {
2343 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2344 if (func && (!clip || clip->hasRectClip)) {
2345 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2346 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2347 s->matrix, s->intOpacity);
2351 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2352 if (func && (!clip || clip->hasRectClip)) {
2353 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2354 img.bits(), img.bytesPerLine(),
2355 qt_mapRect_non_normalizing(r, s->matrix), sr,
2356 !clip ? d->deviceRect : clip->clipRect,
2363 QTransform copy = s->matrix;
2364 copy.translate(r.x(), r.y());
2366 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2367 copy.translate(-sr.x(), -sr.y());
2369 d->image_filler_xform.clip = clip;
2370 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2371 if (!d->image_filler_xform.blend)
2373 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2375 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2376 QRectF rr = s->matrix.mapRect(r);
2378 const int x1 = qRound(rr.x());
2379 const int y1 = qRound(rr.y());
2380 const int x2 = qRound(rr.right());
2381 const int y2 = qRound(rr.bottom());
2383 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2387 #ifdef QT_FAST_SPANS
2388 ensureRasterState();
2389 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2390 d->initializeRasterizer(&d->image_filler_xform);
2391 d->rasterizer->setAntialiased(s->flags.antialiased);
2392 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2394 const QPointF offs = s->flags.legacy_rounding ? QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta) : QPointF();
2396 const QRectF &rect = r.normalized();
2397 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2398 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2400 if (s->flags.tx_noshear)
2401 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2403 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2407 const qreal offs = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
2410 QTransform m = s->matrix;
2411 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2412 m.m21(), m.m22(), m.m23(),
2413 m.m31() - offs, m.m32() - offs, m.m33());
2414 fillPath(path, &d->image_filler_xform);
2417 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2418 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2420 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2422 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2424 } else if (clip->hasRectClip) {
2425 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2431 d->image_filler.clip = clip;
2432 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2433 if (!d->image_filler.blend)
2435 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2436 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2439 rr.translate(s->matrix.dx(), s->matrix.dy());
2441 const int x1 = qRound(rr.x());
2442 const int y1 = qRound(rr.y());
2443 const int x2 = qRound(rr.right());
2444 const int y2 = qRound(rr.bottom());
2446 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2453 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2455 #ifdef QT_DEBUG_DRAW
2456 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2458 Q_D(QRasterPaintEngine);
2459 QRasterPaintEngineState *s = state();
2463 QPlatformPixmap *pd = pixmap.handle();
2464 if (pd->classId() == QPlatformPixmap::RasterClass) {
2465 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2467 image = pixmap.toImage();
2470 if (image.depth() == 1)
2471 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2473 if (s->matrix.type() > QTransform::TxTranslate) {
2474 QTransform copy = s->matrix;
2475 copy.translate(r.x(), r.y());
2476 copy.translate(-sr.x(), -sr.y());
2477 d->image_filler_xform.clip = d->clip();
2478 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2479 if (!d->image_filler_xform.blend)
2481 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2483 #ifdef QT_FAST_SPANS
2484 ensureRasterState();
2485 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2486 d->initializeRasterizer(&d->image_filler_xform);
2487 d->rasterizer->setAntialiased(s->flags.antialiased);
2488 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2490 const QRectF &rect = r.normalized();
2491 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2492 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2493 if (s->flags.tx_noshear)
2494 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2496 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2502 fillPath(path, &d->image_filler_xform);
2504 d->image_filler.clip = d->clip();
2506 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2507 if (!d->image_filler.blend)
2509 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2510 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2513 rr.translate(s->matrix.dx(), s->matrix.dy());
2514 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2520 static inline bool monoVal(const uchar* s, int x)
2522 return (s[x>>3] << (x&7)) & 0x80;
2528 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2530 Q_D(QRasterPaintEngine);
2531 return d->rasterBuffer.data();
2537 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2539 Q_D(QRasterPaintEngine);
2540 QRasterPaintEngineState *s = state();
2542 if (!s->penData.blend)
2545 QRasterBuffer *rb = d->rasterBuffer.data();
2547 const QRect rect(rx, ry, w, h);
2548 const QClipData *clip = d->clip();
2549 bool unclipped = false;
2551 // inlined QRect::intersects
2552 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2553 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2555 if (clip->hasRectClip) {
2556 unclipped = rx > clip->xmin
2557 && rx + w < clip->xmax
2559 && ry + h < clip->ymax;
2565 // inlined QRect::intersects
2566 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2567 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2571 // inlined QRect::contains
2572 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2573 && rect.top() >= 0 && rect.bottom() < rb->height();
2575 unclipped = contains && d->isUnclipped_normalized(rect);
2578 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2579 const uchar * scanline = static_cast<const uchar *>(src);
2581 if (s->flags.fast_text) {
2584 if (s->penData.bitmapBlit) {
2585 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2586 scanline, w, h, bpl);
2589 } else if (depth == 8) {
2590 if (s->penData.alphamapBlit) {
2591 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2592 scanline, w, h, bpl, 0);
2595 } else if (depth == 32) {
2596 // (A)RGB Alpha mask where the alpha component is not used.
2597 if (s->penData.alphaRGBBlit) {
2598 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2599 (const uint *) scanline, w, h, bpl / 4, 0);
2603 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2604 // (A)RGB Alpha mask where the alpha component is not used.
2606 int nx = qMax(0, rx);
2607 int ny = qMax(0, ry);
2609 // Move scanline pointer to compensate for moved x and y
2610 int xdiff = nx - rx;
2611 int ydiff = ny - ry;
2612 scanline += ydiff * bpl;
2613 scanline += xdiff * (depth == 32 ? 4 : 1);
2618 if (nx + w > d->rasterBuffer->width())
2619 w = d->rasterBuffer->width() - nx;
2620 if (ny + h > d->rasterBuffer->height())
2621 h = d->rasterBuffer->height() - ny;
2626 if (depth == 8 && s->penData.alphamapBlit) {
2627 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2628 scanline, w, h, bpl, clip);
2629 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2630 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2631 (const uint *) scanline, w, h, bpl / 4, clip);
2646 scanline += bpl * y0;
2650 w = qMin(w, rb->width() - qMax(0, rx));
2651 h = qMin(h, rb->height() - qMax(0, ry));
2653 if (w <= 0 || h <= 0)
2656 const int NSPANS = 256;
2657 QSpan spans[NSPANS];
2660 const int x1 = x0 + w;
2661 const int y1 = y0 + h;
2664 for (int y = y0; y < y1; ++y) {
2665 for (int x = x0; x < x1; ) {
2666 if (!monoVal(scanline, x)) {
2671 if (current == NSPANS) {
2672 blend(current, spans, &s->penData);
2675 spans[current].x = x + rx;
2676 spans[current].y = y + ry;
2677 spans[current].coverage = 255;
2680 // extend span until we find a different one.
2681 while (x < x1 && monoVal(scanline, x)) {
2685 spans[current].len = len;
2690 } else if (depth == 8) {
2691 for (int y = y0; y < y1; ++y) {
2692 for (int x = x0; x < x1; ) {
2693 // Skip those with 0 coverage
2694 if (scanline[x] == 0) {
2699 if (current == NSPANS) {
2700 blend(current, spans, &s->penData);
2703 int coverage = scanline[x];
2704 spans[current].x = x + rx;
2705 spans[current].y = y + ry;
2706 spans[current].coverage = coverage;
2710 // extend span until we find a different one.
2711 while (x < x1 && scanline[x] == coverage) {
2715 spans[current].len = len;
2720 } else { // 32-bit alpha...
2721 uint *sl = (uint *) src;
2722 for (int y = y0; y < y1; ++y) {
2723 for (int x = x0; x < x1; ) {
2724 // Skip those with 0 coverage
2725 if ((sl[x] & 0x00ffffff) == 0) {
2730 if (current == NSPANS) {
2731 blend(current, spans, &s->penData);
2734 uint rgbCoverage = sl[x];
2735 int coverage = qGreen(rgbCoverage);
2736 spans[current].x = x + rx;
2737 spans[current].y = y + ry;
2738 spans[current].coverage = coverage;
2742 // extend span until we find a different one.
2743 while (x < x1 && sl[x] == rgbCoverage) {
2747 spans[current].len = len;
2750 sl += bpl / sizeof(uint);
2753 // qDebug() << "alphaPenBlt: num spans=" << current
2754 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2755 // Call span func for current set of spans.
2757 blend(current, spans, &s->penData);
2763 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2764 const QFixedPoint *positions, QFontEngine *fontEngine)
2766 Q_D(QRasterPaintEngine);
2767 QRasterPaintEngineState *s = state();
2769 if (fontEngine->hasInternalCaching()) {
2770 QFontEngine::GlyphFormat neededFormat =
2771 painter()->device()->devType() == QInternal::Widget
2772 ? QFontEngine::Format_None
2773 : QFontEngine::Format_A8;
2775 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2776 neededFormat = QFontEngine::Format_Mono;
2778 for (int i = 0; i < numGlyphs; i++) {
2779 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2782 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2784 if (alphaMap == 0 || alphaMap->isNull())
2787 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2788 qFloor(positions[i].x) + offset.x(),
2789 qFloor(positions[i].y) + offset.y(),
2790 alphaMap->width(), alphaMap->height());
2792 fontEngine->unlockAlphaMapForGlyph();
2796 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2798 QImageTextureGlyphCache *cache =
2799 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2801 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2802 fontEngine->setGlyphCache(0, cache);
2805 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2806 cache->fillInPendingGlyphs();
2808 const QImage &image = cache->image();
2809 int bpl = image.bytesPerLine();
2811 int depth = image.depth();
2815 leftShift = 2; // multiply by 4
2816 else if (depth == 1)
2817 rightShift = 3; // divide by 8
2819 int margin = fontEngine->glyphMargin(glyphType);
2820 const QFixed offs = s->flags.legacy_rounding ? QFixed::fromReal(aliasedCoordinateDelta) : QFixed();
2821 const uchar *bits = image.bits();
2822 for (int i=0; i<numGlyphs; ++i) {
2824 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2825 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2826 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2830 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2831 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2833 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2836 // c.baseLineX, c.baseLineY,
2839 // positions[i].x.toInt(), positions[i].y.toInt());
2841 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2849 * Returns true if the rectangle is completely within the current clip
2850 * state of the paint engine.
2852 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2854 const QClipData *cl = clip();
2856 // inline contains() for performance (we know the rects are normalized)
2857 const QRect &r1 = deviceRect;
2858 return (r.left() >= r1.left() && r.right() <= r1.right()
2859 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2863 if (cl->hasRectClip) {
2864 // currently all painting functions clips to deviceRect internally
2865 if (cl->clipRect == deviceRect)
2868 // inline contains() for performance (we know the rects are normalized)
2869 const QRect &r1 = cl->clipRect;
2870 return (r.left() >= r1.left() && r.right() <= r1.right()
2871 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2873 return qt_region_strictContains(cl->clipRegion, r);
2877 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2880 Q_Q(const QRasterPaintEngine);
2881 const QRasterPaintEngineState *s = q->state();
2882 const QClipData *cl = clip();
2884 QRect r = rect.normalized();
2885 // inline contains() for performance (we know the rects are normalized)
2886 const QRect &r1 = deviceRect;
2887 return (r.left() >= r1.left() && r.right() <= r1.right()
2888 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2892 // currently all painting functions that call this function clip to deviceRect internally
2893 if (cl->hasRectClip && cl->clipRect == deviceRect)
2896 if (s->flags.antialiased)
2899 QRect r = rect.normalized();
2901 r.setX(r.x() - penWidth);
2902 r.setY(r.y() - penWidth);
2903 r.setWidth(r.width() + 2 * penWidth);
2904 r.setHeight(r.height() + 2 * penWidth);
2907 if (cl->hasRectClip) {
2908 // inline contains() for performance (we know the rects are normalized)
2909 const QRect &r1 = cl->clipRect;
2910 return (r.left() >= r1.left() && r.right() <= r1.right()
2911 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2913 return qt_region_strictContains(cl->clipRegion, r);
2917 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2920 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2924 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2925 const QSpanData *data) const
2927 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2931 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2932 const QSpanData *data) const
2934 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2938 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2939 const QSpanData *data) const
2941 Q_Q(const QRasterPaintEngine);
2942 const QRasterPaintEngineState *s = q->state();
2944 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2946 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2947 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2950 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2951 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2953 QFixed clipLeft = QFixed::fromReal(clip.left());
2954 QFixed clipRight = QFixed::fromReal(clip.right());
2955 QFixed clipTop = QFixed::fromReal(clip.top());
2956 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2959 while (first < numGlyphs) {
2960 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2961 QFixed left = metrics.x + positions[first].x;
2962 QFixed top = metrics.y + positions[first].y;
2963 QFixed right = left + metrics.width;
2964 QFixed bottom = top + metrics.height;
2965 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2969 int last = numGlyphs - 1;
2970 while (last > first) {
2971 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2972 QFixed left = metrics.x + positions[last].x;
2973 QFixed top = metrics.y + positions[last].y;
2974 QFixed right = left + metrics.width;
2975 QFixed bottom = top + metrics.height;
2976 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2980 return QPair<int, int>(first, last + 1);
2986 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2988 if (textItem->numGlyphs == 0)
2992 ensureRasterState();
2994 QFontEngine *fontEngine = textItem->fontEngine();
2995 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2996 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2998 } else if (state()->matrix.type() < QTransform::TxProject) {
3000 QTransform invMat = state()->matrix.inverted(&invertible);
3004 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3005 textItem->fontEngine(), textItem->glyphs,
3006 textItem->glyphPositions, textItem->numGlyphs);
3007 QStaticTextItem copy = *textItem;
3008 copy.glyphs += range.first;
3009 copy.glyphPositions += range.first;
3010 copy.numGlyphs = range.second - range.first;
3011 QPaintEngineEx::drawStaticTextItem(©);
3013 QPaintEngineEx::drawStaticTextItem(textItem);
3020 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3022 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3024 #ifdef QT_DEBUG_DRAW
3025 Q_D(QRasterPaintEngine);
3026 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3027 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3031 if (ti.glyphs.numGlyphs == 0)
3034 ensureRasterState();
3036 QRasterPaintEngineState *s = state();
3037 QTransform matrix = s->matrix;
3039 if (!supportsTransformations(ti.fontEngine)) {
3040 QVarLengthArray<QFixedPoint> positions;
3041 QVarLengthArray<glyph_t> glyphs;
3043 matrix.translate(p.x(), p.y());
3044 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3046 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3047 } else if (matrix.type() < QTransform::TxProject) {
3049 QTransform invMat = matrix.inverted(&invertible);
3053 QVarLengthArray<QFixedPoint> positions;
3054 QVarLengthArray<glyph_t> glyphs;
3056 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3057 ti.flags, glyphs, positions);
3058 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3059 ti.fontEngine, glyphs.data(), positions.data(),
3062 if (range.first >= range.second)
3065 QStaticTextItem staticTextItem;
3066 staticTextItem.color = s->pen.color();
3067 staticTextItem.font = s->font;
3068 staticTextItem.setFontEngine(ti.fontEngine);
3069 staticTextItem.numGlyphs = range.second - range.first;
3070 staticTextItem.glyphs = glyphs.data() + range.first;
3071 staticTextItem.glyphPositions = positions.data() + range.first;
3072 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3074 QPaintEngineEx::drawTextItem(p, ti);
3081 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3083 Q_D(QRasterPaintEngine);
3084 QRasterPaintEngineState *s = state();
3087 if (!s->penData.blend)
3090 if (!s->flags.fast_pen) {
3091 QPaintEngineEx::drawPoints(points, pointCount);
3095 QCosmeticStroker stroker(s, d->deviceRect);
3096 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3097 stroker.drawPoints(points, pointCount);
3101 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3103 Q_D(QRasterPaintEngine);
3104 QRasterPaintEngineState *s = state();
3107 if (!s->penData.blend)
3110 if (!s->flags.fast_pen) {
3111 QPaintEngineEx::drawPoints(points, pointCount);
3115 QCosmeticStroker stroker(s, d->deviceRect);
3116 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3117 stroker.drawPoints(points, pointCount);
3123 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3125 #ifdef QT_DEBUG_DRAW
3126 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3128 Q_D(QRasterPaintEngine);
3129 QRasterPaintEngineState *s = state();
3132 if (!s->penData.blend)
3135 if (s->flags.fast_pen) {
3136 QCosmeticStroker stroker(s, d->deviceRect);
3137 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3138 for (int i=0; i<lineCount; ++i) {
3139 const QLine &l = lines[i];
3140 stroker.drawLine(l.p1(), l.p2());
3143 QPaintEngineEx::drawLines(lines, lineCount);
3147 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3153 Q_Q(QRasterPaintEngine);
3154 QRasterPaintEngineState *s = q->state();
3156 const QPen &pen = s->lastPen;
3157 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3158 const QVector<qreal> pattern = pen.dashPattern();
3160 qreal patternLength = 0;
3161 for (int i = 0; i < pattern.size(); ++i)
3162 patternLength += pattern.at(i);
3164 if (patternLength <= 0)
3167 qreal length = line.length();
3168 Q_ASSERT(length > 0);
3169 while (length > 0) {
3170 const bool rasterize = *inDash;
3171 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3174 if (dash >= length) {
3176 *dashOffset += dash / width;
3180 *inDash = !(*inDash);
3181 if (++*dashIndex >= pattern.size())
3188 if (rasterize && dash > 0)
3189 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3196 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3198 #ifdef QT_DEBUG_DRAW
3199 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3201 Q_D(QRasterPaintEngine);
3202 QRasterPaintEngineState *s = state();
3205 if (!s->penData.blend)
3207 if (s->flags.fast_pen) {
3208 QCosmeticStroker stroker(s, d->deviceRect);
3209 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3210 for (int i=0; i<lineCount; ++i) {
3211 QLineF line = lines[i];
3212 stroker.drawLine(line.p1(), line.p2());
3215 QPaintEngineEx::drawLines(lines, lineCount);
3223 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3225 Q_D(QRasterPaintEngine);
3226 QRasterPaintEngineState *s = state();
3229 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3230 || (qpen_style(s->lastPen) == Qt::NoPen))
3231 && !s->flags.antialiased
3232 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3234 && s->matrix.type() <= QTransform::TxScale) // no shear
3237 const QRectF r = s->matrix.mapRect(rect);
3238 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3239 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3240 const QRect brect = QRect(int(r.x()), int(r.y()),
3241 int_dim(r.x(), r.width()),
3242 int_dim(r.y(), r.height()));
3244 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3245 &s->penData, &s->brushData);
3249 QPaintEngineEx::drawEllipse(rect);
3257 void QRasterPaintEngine::setDC(HDC hdc) {
3258 Q_D(QRasterPaintEngine);
3265 HDC QRasterPaintEngine::getDC() const
3267 Q_D(const QRasterPaintEngine);
3274 void QRasterPaintEngine::releaseDC(HDC) const
3283 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3285 const QTransform &m = state()->matrix;
3286 return supportsTransformations(fontEngine, m);
3292 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3294 if (m.type() >= QTransform::TxProject)
3297 return !shouldDrawCachedGlyphs(fontEngine, m);
3303 QPoint QRasterPaintEngine::coordinateOffset() const
3305 return QPoint(0, 0);
3308 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3313 Q_D(QRasterPaintEngine);
3315 Q_ASSERT(image.depth() == 1);
3317 const int spanCount = 256;
3318 QT_FT_Span spans[spanCount];
3322 int w = image.width();
3323 int h = image.height();
3324 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3325 int ymin = qMax(qRound(pos.y()), 0);
3326 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3327 int xmin = qMax(qRound(pos.x()), 0);
3329 int x_offset = xmin - qRound(pos.x());
3331 QImage::Format format = image.format();
3332 for (int y = ymin; y < ymax; ++y) {
3333 const uchar *src = image.scanLine(y - qRound(pos.y()));
3334 if (format == QImage::Format_MonoLSB) {
3335 for (int x = 0; x < xmax - xmin; ++x) {
3336 int src_x = x + x_offset;
3337 uchar pixel = src[src_x >> 3];
3342 if (pixel & (0x1 << (src_x & 7))) {
3343 spans[n].x = xmin + x;
3345 spans[n].coverage = 255;
3347 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3351 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3354 if (n == spanCount) {
3355 fg->blend(n, spans, fg);
3361 for (int x = 0; x < xmax - xmin; ++x) {
3362 int src_x = x + x_offset;
3363 uchar pixel = src[src_x >> 3];
3368 if (pixel & (0x80 >> (x & 7))) {
3369 spans[n].x = xmin + x;
3371 spans[n].coverage = 255;
3373 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3377 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3380 if (n == spanCount) {
3381 fg->blend(n, spans, fg);
3389 fg->blend(n, spans, fg);
3395 \enum QRasterPaintEngine::ClipType
3398 \value RectClip Indicates that the currently set clip is a single rectangle.
3399 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3404 Returns the type of the clip currently set.
3406 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3408 Q_D(const QRasterPaintEngine);
3410 const QClipData *clip = d->clip();
3411 if (!clip || clip->hasRectClip)
3419 Returns the bounding rect of the currently set clip.
3421 QRect QRasterPaintEngine::clipBoundingRect() const
3423 Q_D(const QRasterPaintEngine);
3425 const QClipData *clip = d->clip();
3428 return d->deviceRect;
3430 if (clip->hasRectClip)
3431 return clip->clipRect;
3433 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3436 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3438 Q_Q(QRasterPaintEngine);
3439 QRasterPaintEngineState *s = q->state();
3441 rasterizer->setAntialiased(s->flags.antialiased);
3442 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3444 QRect clipRect(deviceRect);
3446 // ### get from optimized rectbased QClipData
3448 const QClipData *c = clip();
3450 const QRect r(QPoint(c->xmin, c->ymin),
3451 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3452 clipRect = clipRect.intersected(r);
3453 blend = data->blend;
3455 blend = data->unclipped_blend;
3458 rasterizer->setClipRect(clipRect);
3459 rasterizer->initialize(blend, data);
3462 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3463 ProcessSpans callback,
3464 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3466 if (!callback || !outline)
3469 Q_Q(QRasterPaintEngine);
3470 QRasterPaintEngineState *s = q->state();
3472 if (!s->flags.antialiased) {
3473 initializeRasterizer(spanData);
3475 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3479 rasterizer->rasterize(outline, fillRule);
3483 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3487 int q_gray_rendered_spans(QT_FT_Raster raster);
3490 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3492 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3495 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3496 ProcessSpans callback,
3497 void *userData, QRasterBuffer *)
3499 if (!callback || !outline)
3502 Q_Q(QRasterPaintEngine);
3503 QRasterPaintEngineState *s = q->state();
3505 if (!s->flags.antialiased) {
3506 rasterizer->setAntialiased(s->flags.antialiased);
3507 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3508 rasterizer->setClipRect(deviceRect);
3509 rasterizer->initialize(callback, userData);
3511 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3515 rasterizer->rasterize(outline, fillRule);
3519 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3520 // minimize memory reallocations. However if initial size for
3521 // raster pool is changed for lower value, reallocations will
3523 int rasterPoolSize = MINIMUM_POOL_SIZE;
3524 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3525 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3526 uchar *rasterPoolOnHeap = 0;
3528 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3530 void *data = userData;
3532 QT_FT_BBox clip_box = { deviceRect.x(),
3534 deviceRect.x() + deviceRect.width(),
3535 deviceRect.y() + deviceRect.height() };
3537 QT_FT_Raster_Params rasterParams;
3538 rasterParams.target = 0;
3539 rasterParams.source = outline;
3540 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3541 rasterParams.gray_spans = 0;
3542 rasterParams.black_spans = 0;
3543 rasterParams.bit_test = 0;
3544 rasterParams.bit_set = 0;
3545 rasterParams.user = data;
3546 rasterParams.clip_box = clip_box;
3551 int rendered_spans = 0;
3555 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3556 rasterParams.gray_spans = callback;
3557 rasterParams.skip_spans = rendered_spans;
3558 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3560 // Out of memory, reallocate some more and try again...
3561 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3562 rasterPoolSize *= 2;
3563 if (rasterPoolSize > 1024 * 1024) {
3564 qWarning("QPainter: Rasterization of primitive failed");
3568 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3570 free(rasterPoolOnHeap);
3571 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3573 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3575 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3577 qt_ft_grays_raster.raster_done(*grayRaster.data());
3578 qt_ft_grays_raster.raster_new(grayRaster.data());
3579 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3585 free(rasterPoolOnHeap);
3588 void QRasterPaintEnginePrivate::recalculateFastImages()
3590 Q_Q(QRasterPaintEngine);
3591 QRasterPaintEngineState *s = q->state();
3593 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3594 && s->matrix.type() <= QTransform::TxShear;
3597 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3599 Q_Q(const QRasterPaintEngine);
3600 const QRasterPaintEngineState *s = q->state();
3602 return s->flags.fast_images
3603 && (mode == QPainter::CompositionMode_SourceOver
3604 || (mode == QPainter::CompositionMode_Source
3605 && !image.hasAlphaChannel()));
3608 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3610 Q_ASSERT(image.depth() == 1);
3612 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3613 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3615 QRgb fg = PREMUL(color.rgba());
3618 int height = sourceImage.height();
3619 int width = sourceImage.width();
3620 for (int y=0; y<height; ++y) {
3621 uchar *source = sourceImage.scanLine(y);
3622 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3623 if (!source || !target)
3624 QT_THROW(std::bad_alloc()); // we must have run out of memory
3625 for (int x=0; x < width; ++x)
3626 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3631 QRasterBuffer::~QRasterBuffer()
3635 void QRasterBuffer::init()
3637 compositionMode = QPainter::CompositionMode_SourceOver;
3638 monoDestinationWithClut = false;
3643 QImage::Format QRasterBuffer::prepare(QImage *image)
3645 m_buffer = (uchar *)image->bits();
3646 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3647 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3648 bytes_per_pixel = image->depth()/8;
3649 bytes_per_line = image->bytesPerLine();
3651 format = image->format();
3652 drawHelper = qDrawHelper + format;
3653 if (image->depth() == 1 && image->colorTable().size() == 2) {
3654 monoDestinationWithClut = true;
3655 destColor0 = PREMUL(image->colorTable()[0]);
3656 destColor1 = PREMUL(image->colorTable()[1]);
3662 void QRasterBuffer::resetBuffer(int val)
3664 memset(m_buffer, val, m_height*bytes_per_line);
3667 QClipData::QClipData(int height)
3669 clipSpanHeight = height;
3674 xmin = xmax = ymin = ymax = 0;
3678 hasRectClip = hasRegionClip = false;
3681 QClipData::~QClipData()
3689 void QClipData::initialize()
3695 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3697 Q_CHECK_PTR(m_clipLines);
3699 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3700 allocated = clipSpanHeight;
3701 Q_CHECK_PTR(m_spans);
3707 m_clipLines[y].spans = 0;
3708 m_clipLines[y].count = 0;
3712 const int len = clipRect.width();
3715 QSpan *span = m_spans + count;
3719 span->coverage = 255;
3722 m_clipLines[y].spans = span;
3723 m_clipLines[y].count = 1;
3727 while (y < clipSpanHeight) {
3728 m_clipLines[y].spans = 0;
3729 m_clipLines[y].count = 0;
3732 } else if (hasRegionClip) {
3734 const QVector<QRect> rects = clipRegion.rects();
3735 const int numRects = rects.size();
3738 const int maxSpans = (ymax - ymin) * numRects;
3739 if (maxSpans > allocated) {
3740 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3741 allocated = maxSpans;
3746 int firstInBand = 0;
3748 while (firstInBand < numRects) {
3749 const int currMinY = rects.at(firstInBand).y();
3750 const int currMaxY = currMinY + rects.at(firstInBand).height();
3752 while (y < currMinY) {
3753 m_clipLines[y].spans = 0;
3754 m_clipLines[y].count = 0;
3758 int lastInBand = firstInBand;
3759 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3762 while (y < currMaxY) {
3764 m_clipLines[y].spans = m_spans + count;
3765 m_clipLines[y].count = lastInBand - firstInBand + 1;
3767 for (int r = firstInBand; r <= lastInBand; ++r) {
3768 const QRect &currRect = rects.at(r);
3769 QSpan *span = m_spans + count;
3770 span->x = currRect.x();
3771 span->len = currRect.width();
3773 span->coverage = 255;
3779 firstInBand = lastInBand + 1;
3782 Q_ASSERT(count <= allocated);
3784 while (y < clipSpanHeight) {
3785 m_clipLines[y].spans = 0;
3786 m_clipLines[y].count = 0;
3792 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3797 free(m_clipLines); // same for clipLines
3803 void QClipData::fixup()
3808 ymin = ymax = xmin = xmax = 0;
3813 ymin = m_spans[0].y;
3814 ymax = m_spans[count-1].y + 1;
3818 const int firstLeft = m_spans[0].x;
3819 const int firstRight = m_spans[0].x + m_spans[0].len;
3822 for (int i = 0; i < count; ++i) {
3823 QT_FT_Span_& span = m_spans[i];
3826 if (span.y != y + 1 && y != -1)
3829 m_clipLines[y].spans = &span;
3830 m_clipLines[y].count = 1;
3832 ++m_clipLines[y].count;
3834 const int spanLeft = span.x;
3835 const int spanRight = spanLeft + span.len;
3837 if (spanLeft < xmin)
3840 if (spanRight > xmax)
3843 if (spanLeft != firstLeft || spanRight != firstRight)
3849 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3854 Convert \a rect to clip spans.
3856 void QClipData::setClipRect(const QRect &rect)
3858 if (hasRectClip && rect == clipRect)
3861 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3863 hasRegionClip = false;
3867 xmax = rect.x() + rect.width();
3868 ymin = qMin(rect.y(), clipSpanHeight);
3869 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3876 // qDebug() << xmin << xmax << ymin << ymax;
3880 Convert \a region to clip spans.
3882 void QClipData::setClipRegion(const QRegion ®ion)
3884 if (region.rectCount() == 1) {
3885 setClipRect(region.rects().at(0));
3889 hasRegionClip = true;
3890 hasRectClip = false;
3891 clipRegion = region;
3893 { // set bounding rect
3894 const QRect rect = region.boundingRect();
3896 xmax = rect.x() + rect.width();
3898 ymax = rect.y() + rect.height();
3910 spans must be sorted on y
3912 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3913 const QSpan *spans, const QSpan *end,
3914 QSpan **outSpans, int available)
3916 const_cast<QClipData *>(clip)->initialize();
3918 QSpan *out = *outSpans;
3920 const QSpan *clipSpans = clip->m_spans + *currentClip;
3921 const QSpan *clipEnd = clip->m_spans + clip->count;
3923 while (available && spans < end ) {
3924 if (clipSpans >= clipEnd) {
3928 if (clipSpans->y > spans->y) {
3932 if (spans->y != clipSpans->y) {
3933 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3934 clipSpans = clip->m_clipLines[spans->y].spans;
3939 Q_ASSERT(spans->y == clipSpans->y);
3942 int sx2 = sx1 + spans->len;
3943 int cx1 = clipSpans->x;
3944 int cx2 = cx1 + clipSpans->len;
3946 if (cx1 < sx1 && cx2 < sx1) {
3949 } else if (sx1 < cx1 && sx2 < cx1) {
3953 int x = qMax(sx1, cx1);
3954 int len = qMin(sx2, cx2) - x;
3956 out->x = qMax(sx1, cx1);
3957 out->len = qMin(sx2, cx2) - out->x;
3959 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3971 *currentClip = clipSpans - clip->m_spans;
3975 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3977 // qDebug() << "qt_span_fill_clipped" << spanCount;
3978 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3980 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3982 const int NSPANS = 256;
3983 QSpan cspans[NSPANS];
3984 int currentClip = 0;
3985 const QSpan *end = spans + spanCount;
3986 while (spans < end) {
3987 QSpan *clipped = cspans;
3988 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3989 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3990 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3992 if (clipped - cspans)
3993 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3999 Clip spans to \a{clip}-rectangle.
4000 Returns number of unclipped spans
4002 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4005 const short minx = clip.left();
4006 const short miny = clip.top();
4007 const short maxx = clip.right();
4008 const short maxy = clip.bottom();
4011 for (int i = 0; i < numSpans; ++i) {
4012 if (spans[i].y > maxy)
4014 if (spans[i].y < miny
4015 || spans[i].x > maxx
4016 || spans[i].x + spans[i].len <= minx) {
4019 if (spans[i].x < minx) {
4020 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4023 spans[n].x = spans[i].x;
4024 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4026 if (spans[n].len == 0)
4028 spans[n].y = spans[i].y;
4029 spans[n].coverage = spans[i].coverage;
4036 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4039 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4040 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4042 Q_ASSERT(fillData->clip);
4043 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4045 // hw: check if this const_cast<> is safe!!!
4046 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4047 fillData->clip->clipRect);
4049 fillData->unclipped_blend(count, spans, fillData);
4052 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4054 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4056 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4057 // for (int i = 0; i < qMin(count, 10); ++i) {
4058 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4061 switch (clipData->operation) {
4063 case Qt::IntersectClip:
4065 QClipData *newClip = clipData->newClip;
4066 newClip->initialize();
4068 int currentClip = 0;
4069 const QSpan *end = spans + count;
4070 while (spans < end) {
4071 QSpan *newspans = newClip->m_spans + newClip->count;
4072 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4073 &newspans, newClip->allocated - newClip->count);
4074 newClip->count = newspans - newClip->m_spans;
4076 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4077 newClip->allocated *= 2;
4083 case Qt::ReplaceClip:
4084 clipData->newClip->appendSpans(spans, count);
4092 QImage QRasterBuffer::bufferImage() const
4094 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4096 for (int y = 0; y < m_height; ++y) {
4097 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4099 for (int x=0; x<m_width; ++x) {
4100 uint argb = span[x];
4101 image.setPixel(x, y, argb);
4109 void QRasterBuffer::flushToARGBImage(QImage *target) const
4111 int w = qMin(m_width, target->width());
4112 int h = qMin(m_height, target->height());
4114 for (int y=0; y<h; ++y) {
4115 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4116 QRgb *dest = (QRgb *) target->scanLine(y);
4117 for (int x=0; x<w; ++x) {
4118 QRgb pixel = sourceLine[x];
4119 int alpha = qAlpha(pixel);
4123 dest[x] = (alpha << 24)
4124 | ((255*qRed(pixel)/alpha) << 16)
4125 | ((255*qGreen(pixel)/alpha) << 8)
4126 | ((255*qBlue(pixel)/alpha) << 0);
4133 class QGradientCache
4137 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4138 stops(s), opacity(op), interpolationMode(mode) {}
4139 uint buffer[GRADIENT_STOPTABLE_SIZE];
4140 QGradientStops stops;
4142 QGradient::InterpolationMode interpolationMode;
4145 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4148 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4149 quint64 hash_val = 0;
4151 QGradientStops stops = gradient.stops();
4152 for (int i = 0; i < stops.size() && i <= 2; i++)
4153 hash_val += stops[i].second.rgba();
4155 QMutexLocker lock(&mutex);
4156 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4158 if (it == cache.constEnd())
4159 return addCacheElement(hash_val, gradient, opacity);
4162 const CacheInfo &cache_info = it.value();
4163 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4164 return cache_info.buffer;
4166 } while (it != cache.constEnd() && it.key() == hash_val);
4167 // an exact match for these stops and opacity was not found, create new cache
4168 return addCacheElement(hash_val, gradient, opacity);
4172 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4174 inline int maxCacheSize() const { return 60; }
4175 inline void generateGradientColorTable(const QGradient& g,
4177 int size, int opacity) const;
4178 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4179 if (cache.size() == maxCacheSize()) {
4180 // may remove more than 1, but OK
4181 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4183 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4184 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4185 return cache.insert(hash_val, cache_entry).value().buffer;
4188 QGradientColorTableHash cache;
4192 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4194 QGradientStops stops = gradient.stops();
4195 int stopCount = stops.count();
4196 Q_ASSERT(stopCount > 0);
4198 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4200 if (stopCount == 2) {
4201 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4202 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4204 qreal first_stop = stops[0].first;
4205 qreal second_stop = stops[1].first;
4207 if (second_stop < first_stop) {
4208 qSwap(first_color, second_color);
4209 qSwap(first_stop, second_stop);
4212 if (colorInterpolation) {
4213 first_color = PREMUL(first_color);
4214 second_color = PREMUL(second_color);
4217 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4218 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4220 uint red_first = qRed(first_color) << 16;
4221 uint green_first = qGreen(first_color) << 16;
4222 uint blue_first = qBlue(first_color) << 16;
4223 uint alpha_first = qAlpha(first_color) << 16;
4225 uint red_second = qRed(second_color) << 16;
4226 uint green_second = qGreen(second_color) << 16;
4227 uint blue_second = qBlue(second_color) << 16;
4228 uint alpha_second = qAlpha(second_color) << 16;
4231 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4232 if (colorInterpolation)
4233 colorTable[i] = first_color;
4235 colorTable[i] = PREMUL(first_color);
4238 if (i < second_index) {
4239 qreal reciprocal = qreal(1) / (second_index - first_index);
4241 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4242 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4243 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4244 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4247 red_first += 1 << 15;
4248 green_first += 1 << 15;
4249 blue_first += 1 << 15;
4250 alpha_first += 1 << 15;
4252 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4253 red_first += red_delta;
4254 green_first += green_delta;
4255 blue_first += blue_delta;
4256 alpha_first += alpha_delta;
4258 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4259 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4261 if (colorInterpolation)
4262 colorTable[i] = color;
4264 colorTable[i] = PREMUL(color);
4268 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4269 if (colorInterpolation)
4270 colorTable[i] = second_color;
4272 colorTable[i] = PREMUL(second_color);
4278 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4279 if (stopCount == 1) {
4280 current_color = PREMUL(current_color);
4281 for (int i = 0; i < size; ++i)
4282 colorTable[i] = current_color;
4286 // The position where the gradient begins and ends
4287 qreal begin_pos = stops[0].first;
4288 qreal end_pos = stops[stopCount-1].first;
4290 int pos = 0; // The position in the color table.
4293 qreal incr = 1 / qreal(size); // the double increment.
4294 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4296 // Up to first point
4297 colorTable[pos++] = PREMUL(current_color);
4298 while (dpos <= begin_pos) {
4299 colorTable[pos] = colorTable[pos - 1];
4304 int current_stop = 0; // We always interpolate between current and current + 1.
4306 qreal t; // position between current left and right stops
4307 qreal t_delta; // the t increment per entry in the color table
4309 if (dpos < end_pos) {
4311 while (dpos > stops[current_stop+1].first)
4314 if (current_stop != 0)
4315 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4316 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4318 if (colorInterpolation) {
4319 current_color = PREMUL(current_color);
4320 next_color = PREMUL(next_color);
4323 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4324 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4325 t = (dpos - stops[current_stop].first) * c;
4329 Q_ASSERT(current_stop < stopCount);
4331 int dist = qRound(t);
4332 int idist = 256 - dist;
4334 if (colorInterpolation)
4335 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4337 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4342 if (dpos >= end_pos)
4348 while (dpos > stops[current_stop+skip+1].first)
4352 current_stop += skip;
4354 current_color = next_color;
4356 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4357 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4359 if (colorInterpolation) {
4361 current_color = PREMUL(current_color);
4362 next_color = PREMUL(next_color);
4365 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4366 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4367 t = (dpos - stops[current_stop].first) * c;
4374 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4375 while (pos < size - 1) {
4376 colorTable[pos] = current_color;
4380 // Make sure the last color stop is represented at the end of the table
4381 colorTable[size - 1] = current_color;
4384 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4387 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4393 m11 = m22 = m33 = 1.;
4394 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4395 clip = pe ? pe->d_func()->clip() : 0;
4398 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4400 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4402 Qt::BrushStyle brushStyle = qbrush_style(brush);
4403 switch (brushStyle) {
4404 case Qt::SolidPattern: {
4406 QColor c = qbrush_color(brush);
4407 QRgb rgba = c.rgba();
4408 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4409 if ((solid.color & 0xff000000) == 0
4410 && compositionMode == QPainter::CompositionMode_SourceOver) {
4416 case Qt::LinearGradientPattern:
4418 type = LinearGradient;
4419 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4420 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4421 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4422 gradient.spread = g->spread();
4424 QLinearGradientData &linearData = gradient.linear;
4426 linearData.origin.x = g->start().x();
4427 linearData.origin.y = g->start().y();
4428 linearData.end.x = g->finalStop().x();
4429 linearData.end.y = g->finalStop().y();
4433 case Qt::RadialGradientPattern:
4435 type = RadialGradient;
4436 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4437 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4438 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4439 gradient.spread = g->spread();
4441 QRadialGradientData &radialData = gradient.radial;
4443 QPointF center = g->center();
4444 radialData.center.x = center.x();
4445 radialData.center.y = center.y();
4446 radialData.center.radius = g->centerRadius();
4447 QPointF focal = g->focalPoint();
4448 radialData.focal.x = focal.x();
4449 radialData.focal.y = focal.y();
4450 radialData.focal.radius = g->focalRadius();
4454 case Qt::ConicalGradientPattern:
4456 type = ConicalGradient;
4457 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4458 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4459 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4460 gradient.spread = QGradient::RepeatSpread;
4462 QConicalGradientData &conicalData = gradient.conical;
4464 QPointF center = g->center();
4465 conicalData.center.x = center.x();
4466 conicalData.center.y = center.y();
4467 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4471 case Qt::Dense1Pattern:
4472 case Qt::Dense2Pattern:
4473 case Qt::Dense3Pattern:
4474 case Qt::Dense4Pattern:
4475 case Qt::Dense5Pattern:
4476 case Qt::Dense6Pattern:
4477 case Qt::Dense7Pattern:
4478 case Qt::HorPattern:
4479 case Qt::VerPattern:
4480 case Qt::CrossPattern:
4481 case Qt::BDiagPattern:
4482 case Qt::FDiagPattern:
4483 case Qt::DiagCrossPattern:
4486 tempImage = new QImage();
4487 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4488 initTexture(tempImage, alpha, QTextureData::Tiled);
4490 case Qt::TexturePattern:
4493 tempImage = new QImage();
4495 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4496 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4498 *tempImage = brush.textureImage();
4499 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4507 adjustSpanMethods();
4510 void QSpanData::adjustSpanMethods()
4520 unclipped_blend = 0;
4523 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4524 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4525 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4526 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4527 fillRect = rasterBuffer->drawHelper->fillRect;
4529 case LinearGradient:
4530 case RadialGradient:
4531 case ConicalGradient:
4532 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4535 unclipped_blend = qBlendTexture;
4536 if (!texture.imageData)
4537 unclipped_blend = 0;
4542 if (!unclipped_blend) {
4545 blend = unclipped_blend;
4546 } else if (clip->hasRectClip) {
4547 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4549 blend = qt_span_fill_clipped;
4553 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4556 // make sure we round off correctly in qdrawhelper.cpp
4557 delta.translate(1.0 / 65536, 1.0 / 65536);
4559 QTransform inv = (delta * matrix).inverted();
4572 const bool affine = !m13 && !m23;
4573 fast_matrix = affine
4574 && m11 * m11 + m21 * m21 < 1e4
4575 && m12 * m12 + m22 * m22 < 1e4
4579 adjustSpanMethods();
4582 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4584 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4585 if (!d || d->height == 0) {
4586 texture.imageData = 0;
4593 texture.bytesPerLine = 0;
4594 texture.format = QImage::Format_Invalid;
4595 texture.colorTable = 0;
4596 texture.hasAlpha = alpha != 256;
4598 texture.imageData = d->data;
4599 texture.width = d->width;
4600 texture.height = d->height;
4602 if (sourceRect.isNull()) {
4605 texture.x2 = texture.width;
4606 texture.y2 = texture.height;
4608 texture.x1 = sourceRect.x();
4609 texture.y1 = sourceRect.y();
4610 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4611 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4614 texture.bytesPerLine = d->bytes_per_line;
4616 texture.format = d->format;
4617 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4618 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4620 texture.const_alpha = alpha;
4621 texture.type = _type;
4623 adjustSpanMethods();
4628 \a x and \a y is relative to the midpoint of \a rect.
4630 static inline void drawEllipsePoints(int x, int y, int length,
4633 ProcessSpans pen_func, ProcessSpans brush_func,
4634 QSpanData *pen_data, QSpanData *brush_data)
4639 QT_FT_Span outline[4];
4640 const int midx = rect.x() + (rect.width() + 1) / 2;
4641 const int midy = rect.y() + (rect.height() + 1) / 2;
4647 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4648 outline[0].len = qMin(length, x - outline[0].x);
4650 outline[0].coverage = 255;
4654 outline[1].len = length;
4656 outline[1].coverage = 255;
4659 outline[2].x = outline[0].x;
4660 outline[2].len = outline[0].len;
4661 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4662 outline[2].coverage = 255;
4666 outline[3].len = length;
4667 outline[3].y = outline[2].y;
4668 outline[3].coverage = 255;
4670 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4674 fill[0].x = outline[0].x + outline[0].len - 1;
4675 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4676 fill[0].y = outline[1].y;
4677 fill[0].coverage = 255;
4680 fill[1].x = outline[2].x + outline[2].len - 1;
4681 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4682 fill[1].y = outline[3].y;
4683 fill[1].coverage = 255;
4685 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4686 n = qt_intersect_spans(fill, n, clip);
4688 brush_func(n, fill, brush_data);
4691 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4692 n = qt_intersect_spans(outline, n, clip);
4694 pen_func(n, outline, pen_data);
4700 Draws an ellipse using the integer point midpoint algorithm.
4702 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4703 ProcessSpans pen_func, ProcessSpans brush_func,
4704 QSpanData *pen_data, QSpanData *brush_data)
4706 const qreal a = qreal(rect.width()) / 2;
4707 const qreal b = qreal(rect.height()) / 2;
4708 qreal d = b*b - (a*a*b) + 0.25*a*a;
4711 int y = (rect.height() + 1) / 2;
4715 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4716 if (d < 0) { // select E
4719 } else { // select SE
4720 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4721 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4722 pen_func, brush_func, pen_data, brush_data);
4727 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4728 pen_func, brush_func, pen_data, brush_data);
4731 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4732 const int miny = rect.height() & 0x1;
4734 if (d < 0) { // select SE
4735 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4737 } else { // select S
4738 d += a*a*(-2*y + 3);
4741 drawEllipsePoints(x, y, 1, rect, clip,
4742 pen_func, brush_func, pen_data, brush_data);
4747 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4753 #ifdef QT_DEBUG_DRAW
4754 void dumpClip(int width, int height, const QClipData *clip)
4756 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4757 clipImg.fill(0xffff0000);
4764 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4766 for (int i = 0; i < clip->count; ++i) {
4767 const QSpan *span = ((QClipData *) clip)->spans() + i;
4768 for (int j = 0; j < span->len; ++j)
4769 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4770 x0 = qMin(x0, int(span->x));
4771 x1 = qMax(x1, int(span->x + span->len - 1));
4773 y0 = qMin(y0, int(span->y));
4774 y1 = qMax(y1, int(span->y));
4777 static int counter = 0;
4784 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4785 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));