1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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"
75 # include <qt_windows.h>
76 # include <qvarlengtharray.h>
77 # include <private/qfontengine_p.h>
78 # if defined(Q_OS_WINCE)
79 # include "qguifunctions_wince.h"
81 #elif defined(Q_WS_MAC)
82 # include <private/qt_mac_p.h>
83 # include <private/qpixmap_mac_p.h>
84 # include <private/qpaintengine_mac_p.h>
85 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
86 # include <private/qfontengine_s60_p.h>
89 #if defined(Q_OS_WIN64)
96 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
98 #define qreal_to_fixed_26_6(f) (int(f * 64))
99 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
100 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
102 // #define QT_DEBUG_DRAW
104 void dumpClip(int width, int height, const QClipData *clip);
107 #define QT_FAST_SPANS
110 // A little helper macro to get a better approximation of dimensions.
111 // If we have a rect that starting at 0.5 of width 3.5 it should span
113 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
115 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
119 static inline bool winClearTypeFontsEnabled()
122 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
123 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
124 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
126 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
127 return result == FE_FONTSMOOTHINGCLEARTYPE;
130 bool QRasterPaintEngine::clearTypeFontsEnabled()
132 static const bool result = winClearTypeFontsEnabled();
139 extern bool qt_applefontsmoothing_enabled;
143 /********************************************************************************
146 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
147 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
148 static void qt_span_clip(int count, const QSpan *spans, void *userData);
154 Qt::ClipOperation operation;
160 LineDrawIncludeLastPixel
163 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
164 ProcessSpans pen_func, ProcessSpans brush_func,
165 QSpanData *pen_data, QSpanData *brush_data);
167 struct QRasterFloatPoint {
173 static const QRectF boundingRect(const QPointF *points, int pointCount)
175 const QPointF *e = points;
176 const QPointF *last = points + pointCount;
177 qreal minx, maxx, miny, maxy;
178 minx = maxx = e->x();
179 miny = maxy = e->y();
183 else if (e->x() > maxx)
187 else if (e->y() > maxy)
190 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
194 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
195 return (elementCount == 5 // 5-point polygon, check for closed rect
196 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
197 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
198 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
199 && pts[0] < pts[4] && pts[1] < pts[5]
201 (elementCount == 4 // 4-point polygon, check for unclosed rect
202 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
203 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
204 && pts[0] < pts[4] && pts[1] < pts[5]
209 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
211 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
214 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
216 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
219 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
220 qfixed c2x, qfixed c2y,
221 qfixed ex, qfixed ey,
224 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
225 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
226 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
230 #if !defined(QT_NO_DEBUG) && 0
231 static void qt_debug_path(const QPainterPath &path)
233 const char *names[] = {
240 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
241 for (int i=0; i<path.elementCount(); ++i) {
242 const QPainterPath::Element &e = path.elementAt(i);
243 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
244 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
249 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
250 QPaintEngineExPrivate(),
257 \class QRasterPaintEngine
262 \brief The QRasterPaintEngine class enables hardware acceleration
263 of painting operations in Qt for Embedded Linux.
265 Note that this functionality is only available in
266 \l{Qt for Embedded Linux}.
268 In \l{Qt for Embedded Linux}, painting is a pure software
269 implementation. But starting with Qt 4.2, it is
270 possible to add an accelerated graphics driver to take advantage
271 of available hardware resources.
273 Hardware acceleration is accomplished by creating a custom screen
274 driver, accelerating the copying from memory to the screen, and
275 implementing a custom paint engine accelerating the various
276 painting operations. Then a custom paint device (derived from the
277 QCustomRasterPaintDevice class) and a custom window surface
278 (derived from QWSWindowSurface) must be implemented to make
279 \l{Qt for Embedded Linux} aware of the accelerated driver.
281 \note The QRasterPaintEngine class does not support 8-bit images.
282 Instead, they need to be converted to a supported format, such as
283 QImage::Format_ARGB32_Premultiplied.
285 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
286 documentation for details.
288 \sa QCustomRasterPaintDevice, QPaintEngine
292 \fn Type QRasterPaintEngine::type() const
298 \relates QRasterPaintEngine
300 A struct equivalent to QT_FT_Span, containing a position (x,
301 y), the span's length in pixels and its color/coverage (a value
302 ranging from 0 to 255).
308 Creates a raster based paint engine for operating on the given
309 \a device, with the complete set of \l
310 {QPaintEngine::PaintEngineFeature}{paint engine features and
313 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
314 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
316 d_func()->device = device;
323 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
326 d_func()->device = device;
330 void QRasterPaintEngine::init()
332 Q_D(QRasterPaintEngine);
339 // The antialiasing raster.
340 d->grayRaster.reset(new QT_FT_Raster);
341 Q_CHECK_PTR(d->grayRaster.data());
342 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
343 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
346 d->rasterizer.reset(new QRasterizer);
347 d->rasterBuffer.reset(new QRasterBuffer());
348 d->outlineMapper.reset(new QOutlineMapper);
349 d->outlinemapper_xform_dirty = true;
351 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
352 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
353 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
355 d->baseClip.reset(new QClipData(d->device->height()));
356 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
358 d->image_filler.init(d->rasterBuffer.data(), this);
359 d->image_filler.type = QSpanData::Texture;
361 d->image_filler_xform.init(d->rasterBuffer.data(), this);
362 d->image_filler_xform.type = QSpanData::Texture;
364 d->solid_color_filler.init(d->rasterBuffer.data(), this);
365 d->solid_color_filler.type = QSpanData::Solid;
367 d->deviceDepth = d->device->depth();
369 d->mono_surface = false;
370 gccaps &= ~PorterDuff;
372 QImage::Format format = QImage::Format_Invalid;
374 switch (d->device->devType()) {
375 case QInternal::Pixmap:
376 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
378 case QInternal::Image:
379 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
382 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
388 case QImage::Format_MonoLSB:
389 case QImage::Format_Mono:
390 d->mono_surface = true;
392 case QImage::Format_ARGB8565_Premultiplied:
393 case QImage::Format_ARGB8555_Premultiplied:
394 case QImage::Format_ARGB6666_Premultiplied:
395 case QImage::Format_ARGB4444_Premultiplied:
396 case QImage::Format_ARGB32_Premultiplied:
397 case QImage::Format_ARGB32:
398 gccaps |= PorterDuff;
400 case QImage::Format_RGB32:
401 case QImage::Format_RGB444:
402 case QImage::Format_RGB555:
403 case QImage::Format_RGB666:
404 case QImage::Format_RGB888:
405 case QImage::Format_RGB16:
416 Destroys this paint engine.
418 QRasterPaintEngine::~QRasterPaintEngine()
420 Q_D(QRasterPaintEngine);
422 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
428 bool QRasterPaintEngine::begin(QPaintDevice *device)
430 Q_D(QRasterPaintEngine);
432 if (device->devType() == QInternal::Pixmap) {
433 QPixmap *pixmap = static_cast<QPixmap *>(device);
434 QPlatformPixmap *pd = pixmap->handle();
435 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
436 d->device = pd->buffer();
441 // Make sure QPaintEngine::paintDevice() returns the proper device.
444 Q_ASSERT(d->device->devType() == QInternal::Image
445 || d->device->devType() == QInternal::CustomRaster);
447 d->systemStateChanged();
449 QRasterPaintEngineState *s = state();
450 ensureOutlineMapper();
451 d->outlineMapper->m_clip_rect = d->deviceRect;
453 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
454 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
455 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
456 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
458 d->rasterizer->setClipRect(d->deviceRect);
460 s->penData.init(d->rasterBuffer.data(), this);
461 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
462 s->stroker = &d->basicStroker;
463 d->basicStroker.setClipRect(d->deviceRect);
465 s->brushData.init(d->rasterBuffer.data(), this);
466 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
468 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
470 setDirty(DirtyBrushOrigin);
473 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
474 << ") devType:" << device->devType()
475 << "devRect:" << d->deviceRect;
477 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
482 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
483 #if defined(Q_OS_WIN)
484 else if (clearTypeFontsEnabled())
485 #elif defined (Q_WS_MAC)
486 else if (qt_applefontsmoothing_enabled)
491 QImage::Format format = static_cast<QImage *>(d->device)->format();
492 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
493 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
495 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
497 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
506 bool QRasterPaintEngine::end()
509 Q_D(QRasterPaintEngine);
510 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
512 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
522 void QRasterPaintEngine::releaseBuffer()
524 Q_D(QRasterPaintEngine);
525 d->rasterBuffer.reset(new QRasterBuffer);
531 QSize QRasterPaintEngine::size() const
533 Q_D(const QRasterPaintEngine);
534 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
541 void QRasterPaintEngine::saveBuffer(const QString &s) const
543 Q_D(const QRasterPaintEngine);
544 d->rasterBuffer->bufferImage().save(s, "PNG");
551 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
553 QRasterPaintEngineState *s = state();
554 // FALCON: get rid of this line, see drawImage call below.
556 QTransform::TransformationType txop = s->matrix.type();
560 case QTransform::TxNone:
561 s->flags.int_xform = true;
564 case QTransform::TxTranslate:
565 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
566 && qreal(int(s->matrix.dy())) == s->matrix.dy();
569 case QTransform::TxScale:
570 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
571 && qreal(int(s->matrix.dy())) == s->matrix.dy()
572 && qreal(int(s->matrix.m11())) == s->matrix.m11()
573 && qreal(int(s->matrix.m22())) == s->matrix.m22();
576 default: // shear / perspective...
577 s->flags.int_xform = false;
581 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
583 ensureOutlineMapper();
588 QRasterPaintEngineState::~QRasterPaintEngineState()
590 if (flags.has_clip_ownership)
595 QRasterPaintEngineState::QRasterPaintEngineState()
607 flags.fast_pen = true;
608 flags.antialiased = false;
609 flags.bilinear = false;
610 flags.fast_text = true;
611 flags.int_xform = true;
612 flags.tx_noshear = true;
613 flags.fast_images = true;
616 flags.has_clip_ownership = false;
621 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
626 , strokeFlags(s.strokeFlags)
627 , lastBrush(s.lastBrush)
628 , brushData(s.brushData)
629 , fillFlags(s.fillFlags)
630 , pixmapFlags(s.pixmapFlags)
631 , intOpacity(s.intOpacity)
635 , flag_bits(s.flag_bits)
637 brushData.tempImage = 0;
638 penData.tempImage = 0;
639 flags.has_clip_ownership = false;
645 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
647 QRasterPaintEngineState *s;
649 s = new QRasterPaintEngineState();
651 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
659 void QRasterPaintEngine::setState(QPainterState *s)
661 Q_D(QRasterPaintEngine);
662 QPaintEngineEx::setState(s);
663 d->rasterBuffer->compositionMode = s->composition_mode;
667 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
672 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
679 void QRasterPaintEngine::penChanged()
682 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
684 QRasterPaintEngineState *s = state();
685 s->strokeFlags |= DirtyPen;
686 s->dirty |= DirtyPen;
692 void QRasterPaintEngine::updatePen(const QPen &pen)
694 Q_D(QRasterPaintEngine);
695 QRasterPaintEngineState *s = state();
697 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
700 Qt::PenStyle pen_style = qpen_style(pen);
705 s->penData.clip = d->clip();
706 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
708 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
709 || pen.brush().transform().type() >= QTransform::TxNone) {
710 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
713 // Slightly ugly handling of an uncommon case... We need to change
714 // the pen because it is reused in draw_midpoint to decide dashed
716 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
717 pen_style = Qt::SolidLine;
718 s->lastPen.setStyle(Qt::SolidLine);
721 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
722 d->basicStroker.setCapStyle(qpen_capStyle(pen));
723 d->basicStroker.setMiterLimit(pen.miterLimit());
725 qreal penWidth = qpen_widthf(pen);
727 d->basicStroker.setStrokeWidth(1);
729 d->basicStroker.setStrokeWidth(penWidth);
731 if(pen_style == Qt::SolidLine) {
732 s->stroker = &d->basicStroker;
733 } else if (pen_style != Qt::NoPen) {
735 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
736 if (pen.isCosmetic()) {
737 d->dashStroker->setClipRect(d->deviceRect);
739 // ### I've seen this inverted devrect multiple places now...
740 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
741 d->dashStroker->setClipRect(clipRect);
743 d->dashStroker->setDashPattern(pen.dashPattern());
744 d->dashStroker->setDashOffset(pen.dashOffset());
745 s->stroker = d->dashStroker.data();
750 ensureState(); // needed because of tx_noshear...
751 s->flags.fast_pen = pen_style > Qt::NoPen
753 && ((pen.isCosmetic() && penWidth <= 1)
754 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
756 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
766 void QRasterPaintEngine::brushOriginChanged()
768 QRasterPaintEngineState *s = state();
770 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
773 s->fillFlags |= DirtyBrushOrigin;
780 void QRasterPaintEngine::brushChanged()
782 QRasterPaintEngineState *s = state();
784 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
786 s->fillFlags |= DirtyBrush;
795 void QRasterPaintEngine::updateBrush(const QBrush &brush)
798 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
800 Q_D(QRasterPaintEngine);
801 QRasterPaintEngineState *s = state();
802 // must set clip prior to setup, as setup uses it...
803 s->brushData.clip = d->clip();
804 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
805 if (s->fillFlags & DirtyTransform
806 || brush.transform().type() >= QTransform::TxNone)
807 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
808 s->lastBrush = brush;
812 void QRasterPaintEngine::updateOutlineMapper()
814 Q_D(QRasterPaintEngine);
815 d->outlineMapper->setMatrix(state()->matrix);
818 void QRasterPaintEngine::updateState()
820 QRasterPaintEngineState *s = state();
822 if (s->dirty & DirtyTransform)
823 updateMatrix(s->matrix);
825 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
826 const QPainter::CompositionMode mode = s->composition_mode;
827 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
828 && s->intOpacity == 256
829 && (mode == QPainter::CompositionMode_Source
830 || (mode == QPainter::CompositionMode_SourceOver
831 && qAlpha(s->penData.solid.color) == 255));
841 void QRasterPaintEngine::opacityChanged()
843 QRasterPaintEngineState *s = state();
846 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
849 s->fillFlags |= DirtyOpacity;
850 s->strokeFlags |= DirtyOpacity;
851 s->pixmapFlags |= DirtyOpacity;
852 s->dirty |= DirtyOpacity;
853 s->intOpacity = (int) (s->opacity * 256);
859 void QRasterPaintEngine::compositionModeChanged()
861 Q_D(QRasterPaintEngine);
862 QRasterPaintEngineState *s = state();
865 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
868 s->fillFlags |= DirtyCompositionMode;
869 s->dirty |= DirtyCompositionMode;
871 s->strokeFlags |= DirtyCompositionMode;
872 d->rasterBuffer->compositionMode = s->composition_mode;
874 d->recalculateFastImages();
880 void QRasterPaintEngine::renderHintsChanged()
882 QRasterPaintEngineState *s = state();
885 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
888 bool was_aa = s->flags.antialiased;
889 bool was_bilinear = s->flags.bilinear;
891 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
892 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
894 if (was_aa != s->flags.antialiased)
895 s->strokeFlags |= DirtyHints;
897 if (was_bilinear != s->flags.bilinear) {
898 s->strokeFlags |= DirtyPen;
899 s->fillFlags |= DirtyBrush;
902 Q_D(QRasterPaintEngine);
903 d->recalculateFastImages();
909 void QRasterPaintEngine::transformChanged()
911 QRasterPaintEngineState *s = state();
914 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
917 s->fillFlags |= DirtyTransform;
918 s->strokeFlags |= DirtyTransform;
920 s->dirty |= DirtyTransform;
922 Q_D(QRasterPaintEngine);
923 d->recalculateFastImages();
929 void QRasterPaintEngine::clipEnabledChanged()
931 QRasterPaintEngineState *s = state();
934 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
938 s->clip->enabled = s->clipEnabled;
939 s->fillFlags |= DirtyClipEnabled;
940 s->strokeFlags |= DirtyClipEnabled;
941 s->pixmapFlags |= DirtyClipEnabled;
945 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
947 SrcOverBlendFunc func,
952 if (alpha == 0 || !clip.isValid())
955 Q_ASSERT(img.depth() >= 8);
957 int srcBPL = img.bytesPerLine();
958 const uchar *srcBits = img.bits();
959 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
960 int iw = img.width();
961 int ih = img.height();
966 // Adjust the image according to the source offset...
967 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
970 // adapt the x parameters
971 int x = qRound(pt.x());
973 int cx2 = clip.x() + clip.width();
976 srcBits += srcSize * d;
981 int d = x + iw - cx2;
987 // adapt the y paremeters...
989 int cy2 = clip.y() + clip.height();
990 int y = qRound(pt.y());
993 srcBits += srcBPL * d;
998 int d = y + ih - cy2;
1004 // call the blend function...
1005 int dstSize = rasterBuffer->bytesPerPixel();
1006 int dstBPL = rasterBuffer->bytesPerLine();
1007 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1014 void QRasterPaintEnginePrivate::systemStateChanged()
1016 QRect clipRect(0, 0,
1017 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1018 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1020 if (!systemClip.isEmpty()) {
1021 QRegion clippedDeviceRgn = systemClip & clipRect;
1022 deviceRect = clippedDeviceRgn.boundingRect();
1023 baseClip->setClipRegion(clippedDeviceRgn);
1025 deviceRect = clipRect;
1026 baseClip->setClipRect(deviceRect);
1028 #ifdef QT_DEBUG_DRAW
1029 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1032 exDeviceRect = deviceRect;
1034 Q_Q(QRasterPaintEngine);
1035 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1036 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1037 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1040 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1042 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1045 Q_Q(QRasterPaintEngine);
1046 bool bilinear = q->state()->flags.bilinear;
1048 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1049 spanData->setupMatrix(b.transform() * m, bilinear);
1051 if (m.type() <= QTransform::TxTranslate) {
1052 // specialize setupMatrix for translation matrices
1053 // to avoid needless matrix inversion
1061 spanData->dx = -m.dx();
1062 spanData->dy = -m.dy();
1063 spanData->txop = m.type();
1064 spanData->bilinear = bilinear;
1065 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1066 spanData->adjustSpanMethods();
1068 spanData->setupMatrix(m, bilinear);
1073 // #define QT_CLIPPING_RATIOS
1075 #ifdef QT_CLIPPING_RATIOS
1080 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1082 if (d->clip()->hasRectClip)
1084 if (d->clip()->hasRegionClip)
1088 if ((totalClips % 5000) == 0) {
1089 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1090 rectClips * 100.0 / (qreal) totalClips,
1091 regionClips * 100.0 / (qreal) totalClips,
1092 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1101 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1103 if (s->flags.has_clip_ownership)
1106 s->flags.has_clip_ownership = false;
1109 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1111 s->fillFlags |= QPaintEngine::DirtyClipPath;
1112 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1113 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1115 d->solid_color_filler.clip = d->clip();
1116 d->solid_color_filler.adjustSpanMethods();
1118 #ifdef QT_DEBUG_DRAW
1119 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1128 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1130 #ifdef QT_DEBUG_DRAW
1131 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1133 if (path.elements()) {
1134 for (int i=0; i<path.elementCount(); ++i) {
1135 qDebug() << " - " << path.elements()[i]
1136 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1139 for (int i=0; i<path.elementCount(); ++i) {
1140 qDebug() << " ---- "
1141 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1146 Q_D(QRasterPaintEngine);
1147 QRasterPaintEngineState *s = state();
1149 const qreal *points = path.points();
1150 const QPainterPath::ElementType *types = path.elements();
1152 // There are some cases that are not supported by clip(QRect)
1153 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1154 if (s->matrix.type() <= QTransform::TxScale
1155 && ((path.shape() == QVectorPath::RectangleHint)
1156 || (isRect(points, path.elementCount())
1157 && (!types || (types[0] == QPainterPath::MoveToElement
1158 && types[1] == QPainterPath::LineToElement
1159 && types[2] == QPainterPath::LineToElement
1160 && types[3] == QPainterPath::LineToElement))))) {
1161 #ifdef QT_DEBUG_DRAW
1162 qDebug() << " --- optimizing vector clip to rect clip...";
1165 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1166 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1171 if (op == Qt::NoClip) {
1172 qrasterpaintengine_state_setNoClip(s);
1175 QClipData *base = d->baseClip.data();
1177 // Intersect with current clip when available...
1178 if (op == Qt::IntersectClip && s->clip)
1181 // We always intersect, except when there is nothing to
1182 // intersect with, in which case we simplify the operation to
1184 Qt::ClipOperation isectOp = Qt::IntersectClip;
1186 isectOp = Qt::ReplaceClip;
1188 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1189 newClip->initialize();
1190 ClipData clipData = { base, newClip, isectOp };
1191 ensureOutlineMapper();
1192 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1196 if (s->flags.has_clip_ownership)
1200 s->flags.has_clip_ownership = true;
1202 qrasterpaintengine_dirty_clip(d, s);
1210 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1212 #ifdef QT_DEBUG_DRAW
1213 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1216 QRasterPaintEngineState *s = state();
1218 if (op == Qt::NoClip) {
1219 qrasterpaintengine_state_setNoClip(s);
1221 } else if (s->matrix.type() > QTransform::TxScale) {
1222 QPaintEngineEx::clip(rect, op);
1225 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1226 QPaintEngineEx::clip(rect, op);
1232 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1234 Q_D(QRasterPaintEngine);
1235 QRect clipRect = r & d->deviceRect;
1236 QRasterPaintEngineState *s = state();
1238 if (op == Qt::ReplaceClip || s->clip == 0) {
1240 // No current clip, hence we intersect with sysclip and be
1242 QRegion clipRegion = systemClip();
1243 QClipData *clip = new QClipData(d->rasterBuffer->height());
1245 if (clipRegion.isEmpty())
1246 clip->setClipRect(clipRect);
1248 clip->setClipRegion(clipRegion & clipRect);
1250 if (s->flags.has_clip_ownership)
1254 s->clip->enabled = true;
1255 s->flags.has_clip_ownership = true;
1257 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1258 QClipData *base = s->clip;
1261 if (base->hasRectClip || base->hasRegionClip) {
1262 if (!s->flags.has_clip_ownership) {
1263 s->clip = new QClipData(d->rasterBuffer->height());
1264 s->flags.has_clip_ownership = true;
1266 if (base->hasRectClip)
1267 s->clip->setClipRect(base->clipRect & clipRect);
1269 s->clip->setClipRegion(base->clipRegion & clipRect);
1270 s->clip->enabled = true;
1278 qrasterpaintengine_dirty_clip(d, s);
1286 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1288 #ifdef QT_DEBUG_DRAW
1289 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1292 Q_D(QRasterPaintEngine);
1294 if (region.rectCount() == 1) {
1295 clip(region.boundingRect(), op);
1299 QRasterPaintEngineState *s = state();
1300 const QClipData *clip = d->clip();
1301 const QClipData *baseClip = d->baseClip.data();
1303 if (op == Qt::NoClip) {
1304 qrasterpaintengine_state_setNoClip(s);
1305 } else if (s->matrix.type() > QTransform::TxScale
1306 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1307 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1308 QPaintEngineEx::clip(region, op);
1310 const QClipData *curClip;
1313 if (op == Qt::IntersectClip)
1318 if (s->flags.has_clip_ownership) {
1322 newClip = new QClipData(d->rasterBuffer->height());
1324 s->flags.has_clip_ownership = true;
1327 QRegion r = s->matrix.map(region);
1328 if (curClip->hasRectClip)
1329 newClip->setClipRegion(r & curClip->clipRect);
1330 else if (curClip->hasRegionClip)
1331 newClip->setClipRegion(r & curClip->clipRegion);
1333 qrasterpaintengine_dirty_clip(d, s);
1340 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1342 #ifdef QT_DEBUG_DRAW
1343 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1346 if (!fillData->blend)
1349 Q_D(QRasterPaintEngine);
1351 const QRectF controlPointRect = path.controlPointRect();
1353 QRasterPaintEngineState *s = state();
1354 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1355 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1356 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1357 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1358 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1359 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1361 if (!s->flags.antialiased && !do_clip) {
1362 d->initializeRasterizer(fillData);
1363 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1367 ensureOutlineMapper();
1368 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1371 static void fillRect_normalized(const QRect &r, QSpanData *data,
1372 QRasterPaintEnginePrivate *pe)
1376 bool rectClipped = true;
1379 x1 = qMax(r.x(), data->clip->xmin);
1380 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1381 y1 = qMax(r.y(), data->clip->ymin);
1382 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1383 rectClipped = data->clip->hasRectClip;
1386 x1 = qMax(r.x(), pe->deviceRect.x());
1387 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1388 y1 = qMax(r.y(), pe->deviceRect.y());
1389 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1391 x1 = qMax(r.x(), 0);
1392 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1393 y1 = qMax(r.y(), 0);
1394 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1397 if (x2 <= x1 || y2 <= y1)
1400 const int width = x2 - x1;
1401 const int height = y2 - y1;
1403 bool isUnclipped = rectClipped
1404 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1406 if (pe && isUnclipped) {
1407 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1409 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1410 || (mode == QPainter::CompositionMode_SourceOver
1411 && qAlpha(data->solid.color) == 255)))
1413 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1419 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1421 const int nspans = 256;
1422 QT_FT_Span spans[nspans];
1424 Q_ASSERT(data->blend);
1427 int n = qMin(nspans, y2 - y);
1431 spans[i].len = width;
1433 spans[i].coverage = 255;
1437 blend(n, spans, data);
1445 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1447 #ifdef QT_DEBUG_DRAW
1448 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1450 Q_D(QRasterPaintEngine);
1452 QRasterPaintEngineState *s = state();
1456 if (s->brushData.blend) {
1457 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1458 const QRect *r = rects;
1459 const QRect *lastRect = rects + rectCount;
1461 int offset_x = int(s->matrix.dx());
1462 int offset_y = int(s->matrix.dy());
1463 while (r < lastRect) {
1464 QRect rect = r->normalized();
1465 QRect rr = rect.translated(offset_x, offset_y);
1466 fillRect_normalized(rr, &s->brushData, d);
1470 QRectVectorPath path;
1471 for (int i=0; i<rectCount; ++i) {
1473 fill(path, s->brush);
1479 if (s->penData.blend) {
1480 QRectVectorPath path;
1481 if (s->flags.fast_pen) {
1482 QCosmeticStroker stroker(s, d->deviceRect);
1483 for (int i = 0; i < rectCount; ++i) {
1485 stroker.drawPath(path);
1488 for (int i = 0; i < rectCount; ++i) {
1490 stroke(path, s->pen);
1499 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1501 #ifdef QT_DEBUG_DRAW
1502 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1504 #ifdef QT_FAST_SPANS
1505 Q_D(QRasterPaintEngine);
1507 QRasterPaintEngineState *s = state();
1510 if (s->flags.tx_noshear) {
1512 if (s->brushData.blend) {
1513 d->initializeRasterizer(&s->brushData);
1514 for (int i = 0; i < rectCount; ++i) {
1515 const QRectF &rect = rects[i].normalized();
1518 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1519 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1520 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1525 if (s->penData.blend) {
1526 QRectVectorPath path;
1527 if (s->flags.fast_pen) {
1528 QCosmeticStroker stroker(s, d->deviceRect);
1529 for (int i = 0; i < rectCount; ++i) {
1531 stroker.drawPath(path);
1534 for (int i = 0; i < rectCount; ++i) {
1536 QPaintEngineEx::stroke(path, s->lastPen);
1543 #endif // QT_FAST_SPANS
1544 QPaintEngineEx::drawRects(rects, rectCount);
1551 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1553 Q_D(QRasterPaintEngine);
1554 QRasterPaintEngineState *s = state();
1557 if (!s->penData.blend)
1560 if (s->flags.fast_pen) {
1561 QCosmeticStroker stroker(s, d->deviceRect);
1562 stroker.drawPath(path);
1563 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1564 qreal width = s->lastPen.isCosmetic()
1565 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1566 : qpen_widthf(s->lastPen) * s->txscale;
1568 qreal dashOffset = s->lastPen.dashOffset();
1570 qreal patternLength = 0;
1571 const QVector<qreal> pattern = s->lastPen.dashPattern();
1572 for (int i = 0; i < pattern.size(); ++i)
1573 patternLength += pattern.at(i);
1575 if (patternLength > 0) {
1576 int n = qFloor(dashOffset / patternLength);
1577 dashOffset -= n * patternLength;
1578 while (dashOffset >= pattern.at(dashIndex)) {
1579 dashOffset -= pattern.at(dashIndex);
1580 if (++dashIndex >= pattern.size())
1586 Q_D(QRasterPaintEngine);
1587 d->initializeRasterizer(&s->penData);
1588 int lineCount = path.elementCount() / 2;
1589 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1591 for (int i = 0; i < lineCount; ++i) {
1592 if (lines[i].p1() == lines[i].p2()) {
1593 if (s->lastPen.capStyle() != Qt::FlatCap) {
1594 QPointF p = lines[i].p1();
1595 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1596 QPointF(p.x() + width*0.5, p.y())));
1597 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1602 const QLineF line = s->matrix.map(lines[i]);
1603 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1604 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1605 width / line.length(),
1606 s->lastPen.capStyle() == Qt::SquareCap);
1608 d->rasterizeLine_dashed(line, width,
1609 &dashIndex, &dashOffset, &inDash);
1614 QPaintEngineEx::stroke(path, pen);
1617 static inline QRect toNormalizedFillRect(const QRectF &rect)
1619 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1620 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1621 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1622 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1629 return QRect(x1, y1, x2 - x1, y2 - y1);
1635 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1639 #ifdef QT_DEBUG_DRAW
1640 QRectF rf = path.controlPointRect();
1641 qDebug() << "QRasterPaintEngine::fill(): "
1642 << "size=" << path.elementCount()
1643 << ", hints=" << hex << path.hints()
1647 Q_D(QRasterPaintEngine);
1648 QRasterPaintEngineState *s = state();
1651 if (!s->brushData.blend)
1654 if (path.shape() == QVectorPath::RectangleHint) {
1655 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1656 const qreal *p = path.points();
1657 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1658 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1659 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1663 if (s->flags.tx_noshear) {
1664 d->initializeRasterizer(&s->brushData);
1665 // ### Is normalizing really necessary here?
1666 const qreal *p = path.points();
1667 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1669 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1670 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1671 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1677 // ### Optimize for non transformed ellipses and rectangles...
1678 QRectF cpRect = path.controlPointRect();
1679 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1680 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1683 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1684 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1685 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1686 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1688 // ### Falonc: implement....
1689 // if (!s->flags.antialiased && !do_clip) {
1690 // d->initializeRasterizer(&s->brushData);
1691 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1695 ensureOutlineMapper();
1696 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1699 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1701 Q_D(QRasterPaintEngine);
1702 QRasterPaintEngineState *s = state();
1704 if (!s->flags.antialiased) {
1705 uint txop = s->matrix.type();
1706 if (txop == QTransform::TxNone) {
1707 fillRect_normalized(toNormalizedFillRect(r), data, d);
1709 } else if (txop == QTransform::TxTranslate) {
1710 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1711 fillRect_normalized(rr, data, d);
1713 } else if (txop == QTransform::TxScale) {
1714 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1715 fillRect_normalized(rr, data, d);
1720 if (s->flags.tx_noshear) {
1721 d->initializeRasterizer(data);
1722 QRectF nr = r.normalized();
1723 if (!nr.isEmpty()) {
1724 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1725 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1726 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1733 ensureOutlineMapper();
1734 fillPath(path, data);
1740 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1742 #ifdef QT_DEBUG_DRAW
1743 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1745 QRasterPaintEngineState *s = state();
1748 if (!s->brushData.blend)
1751 fillRect(r, &s->brushData);
1757 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1759 #ifdef QT_DEBUG_DRAW
1760 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1762 Q_D(QRasterPaintEngine);
1763 QRasterPaintEngineState *s = state();
1765 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1766 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1767 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1770 d->solid_color_filler.clip = d->clip();
1771 d->solid_color_filler.adjustSpanMethods();
1772 fillRect(r, &d->solid_color_filler);
1775 static inline bool isAbove(const QPointF *a, const QPointF *b)
1777 return a->y() < b->y();
1780 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1785 Q_ASSERT(pointCount >= 2);
1787 QVector<const QPointF *> sorted;
1788 sorted.reserve(pointCount);
1790 upper->reserve(pointCount * 3 / 4);
1791 lower->reserve(pointCount * 3 / 4);
1793 for (int i = 0; i < pointCount; ++i)
1794 sorted << points + i;
1796 qSort(sorted.begin(), sorted.end(), isAbove);
1798 qreal splitY = sorted.at(sorted.size() / 2)->y();
1800 const QPointF *end = points + pointCount;
1801 const QPointF *last = end - 1;
1803 QVector<QPointF> *bin[2] = { upper, lower };
1805 for (const QPointF *p = points; p < end; ++p) {
1806 int side = p->y() < splitY;
1807 int lastSide = last->y() < splitY;
1809 if (side != lastSide) {
1810 if (qFuzzyCompare(p->y(), splitY)) {
1811 bin[!side]->append(*p);
1812 } else if (qFuzzyCompare(last->y(), splitY)) {
1813 bin[side]->append(*last);
1815 QPointF delta = *p - *last;
1816 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1818 bin[0]->append(intersection);
1819 bin[1]->append(intersection);
1823 bin[side]->append(*p);
1828 // give up if we couldn't reduce the point count
1829 return upper->size() < pointCount && lower->size() < pointCount;
1835 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1837 Q_D(QRasterPaintEngine);
1838 QRasterPaintEngineState *s = state();
1840 const int maxPoints = 0xffff;
1842 // max amount of points that raster engine can reliably handle
1843 if (pointCount > maxPoints) {
1844 QVector<QPointF> upper, lower;
1846 if (splitPolygon(points, pointCount, &upper, &lower)) {
1847 fillPolygon(upper.constData(), upper.size(), mode);
1848 fillPolygon(lower.constData(), lower.size(), mode);
1850 qWarning("Polygon too complex for filling.");
1855 // Compose polygon fill..,
1856 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1857 ensureOutlineMapper();
1858 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1861 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1863 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1869 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1871 Q_D(QRasterPaintEngine);
1872 QRasterPaintEngineState *s = state();
1874 #ifdef QT_DEBUG_DRAW
1875 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1876 for (int i=0; i<pointCount; ++i)
1877 qDebug() << " - " << points[i];
1879 Q_ASSERT(pointCount >= 2);
1881 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1882 QRectF r(points[0], points[2]);
1888 if (mode != PolylineMode) {
1891 if (s->brushData.blend) {
1892 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1893 fillPolygon(points, pointCount, mode);
1894 d->outlineMapper->setCoordinateRounding(false);
1898 // Do the outline...
1899 if (s->penData.blend) {
1900 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1901 if (s->flags.fast_pen) {
1902 QCosmeticStroker stroker(s, d->deviceRect);
1903 stroker.drawPath(vp);
1905 QPaintEngineEx::stroke(vp, s->lastPen);
1913 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1915 Q_D(QRasterPaintEngine);
1916 QRasterPaintEngineState *s = state();
1918 #ifdef QT_DEBUG_DRAW
1919 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1920 for (int i=0; i<pointCount; ++i)
1921 qDebug() << " - " << points[i];
1923 Q_ASSERT(pointCount >= 2);
1924 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1925 QRect r(points[0].x(),
1927 points[2].x() - points[0].x(),
1928 points[2].y() - points[0].y());
1936 if (mode != PolylineMode) {
1938 if (s->brushData.blend) {
1939 // Compose polygon fill..,
1940 ensureOutlineMapper();
1941 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1942 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1943 d->outlineMapper->moveTo(*points);
1944 const QPoint *p = points;
1945 const QPoint *ep = points + pointCount - 1;
1947 d->outlineMapper->lineTo(*(++p));
1949 d->outlineMapper->endOutline();
1952 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1954 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1955 d->outlineMapper->setCoordinateRounding(false);
1959 // Do the outline...
1960 if (s->penData.blend) {
1961 int count = pointCount * 2;
1962 QVarLengthArray<qreal> fpoints(count);
1963 for (int i=0; i<count; ++i)
1964 fpoints[i] = ((int *) points)[i];
1965 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1967 if (s->flags.fast_pen) {
1968 QCosmeticStroker stroker(s, d->deviceRect);
1969 stroker.drawPath(vp);
1971 QPaintEngineEx::stroke(vp, s->lastPen);
1979 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1981 #ifdef QT_DEBUG_DRAW
1982 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1985 QPlatformPixmap *pd = pixmap.handle();
1986 if (pd->classId() == QPlatformPixmap::RasterClass) {
1987 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
1988 if (image.depth() == 1) {
1989 Q_D(QRasterPaintEngine);
1990 QRasterPaintEngineState *s = state();
1991 if (s->matrix.type() <= QTransform::TxTranslate) {
1993 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1995 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
1998 QRasterPaintEngine::drawImage(pos, image);
2001 const QImage image = pixmap.toImage();
2002 if (pixmap.depth() == 1) {
2003 Q_D(QRasterPaintEngine);
2004 QRasterPaintEngineState *s = state();
2005 if (s->matrix.type() <= QTransform::TxTranslate) {
2007 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2009 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2012 QRasterPaintEngine::drawImage(pos, image);
2020 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2022 #ifdef QT_DEBUG_DRAW
2023 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2026 QPlatformPixmap* pd = pixmap.handle();
2027 if (pd->classId() == QPlatformPixmap::RasterClass) {
2028 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2029 if (image.depth() == 1) {
2030 Q_D(QRasterPaintEngine);
2031 QRasterPaintEngineState *s = state();
2032 if (s->matrix.type() <= QTransform::TxTranslate
2033 && r.size() == sr.size()
2034 && r.size() == pixmap.size()) {
2036 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2039 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2042 drawImage(r, image, sr);
2045 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2046 const QImage image = pd->toImage(clippedSource);
2047 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2048 if (image.depth() == 1) {
2049 Q_D(QRasterPaintEngine);
2050 QRasterPaintEngineState *s = state();
2051 if (s->matrix.type() <= QTransform::TxTranslate
2052 && r.size() == sr.size()
2053 && r.size() == pixmap.size()) {
2055 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2058 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2061 drawImage(r, image, translatedSource);
2066 // assumes that rect has positive width and height
2067 static inline const QRect toRect_normalized(const QRectF &rect)
2069 const int x = qRound(rect.x());
2070 const int y = qRound(rect.y());
2071 const int w = int(rect.width() + qreal(0.5));
2072 const int h = int(rect.height() + qreal(0.5));
2074 return QRect(x, y, w, h);
2077 static inline int fast_ceil_positive(const qreal &v)
2079 const int iv = int(v);
2086 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2088 const int xmin = int(rect.x());
2089 const int xmax = int(fast_ceil_positive(rect.right()));
2090 const int ymin = int(rect.y());
2091 const int ymax = int(fast_ceil_positive(rect.bottom()));
2092 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2098 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2100 #ifdef QT_DEBUG_DRAW
2101 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2104 Q_D(QRasterPaintEngine);
2105 QRasterPaintEngineState *s = state();
2107 if (s->matrix.type() > QTransform::TxTranslate) {
2108 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2110 QRectF(0, 0, img.width(), img.height()));
2113 const QClipData *clip = d->clip();
2114 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2116 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2117 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2120 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2122 } else if (clip->hasRectClip) {
2123 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2131 d->image_filler.clip = clip;
2132 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2133 if (!d->image_filler.blend)
2135 d->image_filler.dx = -pt.x();
2136 d->image_filler.dy = -pt.y();
2137 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2139 fillRect_normalized(rr, &d->image_filler, d);
2144 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2146 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2157 inline RotationType qRotationType(const QTransform &transform)
2159 QTransform::TransformationType type = transform.type();
2161 if (type > QTransform::TxRotate)
2164 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2165 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2168 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2169 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2172 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2173 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2179 inline bool isPixelAligned(const QRectF &rect) {
2180 return QRectF(rect.toRect()) == rect;
2187 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2188 Qt::ImageConversionFlags)
2190 #ifdef QT_DEBUG_DRAW
2191 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2197 Q_D(QRasterPaintEngine);
2198 QRasterPaintEngineState *s = state();
2199 int sr_l = qFloor(sr.left());
2200 int sr_r = qCeil(sr.right()) - 1;
2201 int sr_t = qFloor(sr.top());
2202 int sr_b = qCeil(sr.bottom()) - 1;
2204 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2205 // as fillRect will apply the aliased coordinate delta we need to
2206 // subtract it here as we don't use it for image drawing
2207 QTransform old = s->matrix;
2208 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2210 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2211 QRgb color = img.pixel(sr_l, sr_t);
2212 switch (img.format()) {
2213 case QImage::Format_ARGB32_Premultiplied:
2214 case QImage::Format_ARGB8565_Premultiplied:
2215 case QImage::Format_ARGB6666_Premultiplied:
2216 case QImage::Format_ARGB8555_Premultiplied:
2217 case QImage::Format_ARGB4444_Premultiplied:
2218 // Combine premultiplied color with the opacity set on the painter.
2219 d->solid_color_filler.solid.color =
2220 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2221 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2224 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2228 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2229 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2233 d->solid_color_filler.clip = d->clip();
2234 d->solid_color_filler.adjustSpanMethods();
2235 fillRect(r, &d->solid_color_filler);
2241 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2243 const QClipData *clip = d->clip();
2245 if (s->matrix.type() > QTransform::TxTranslate
2247 && (!clip || clip->hasRectClip)
2248 && s->intOpacity == 256
2249 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2250 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2251 && d->rasterBuffer->format == img.format()
2252 && (d->rasterBuffer->format == QImage::Format_RGB16
2253 || d->rasterBuffer->format == QImage::Format_RGB32
2254 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2255 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2257 RotationType rotationType = qRotationType(s->matrix);
2259 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2260 QRectF transformedTargetRect = s->matrix.mapRect(r);
2262 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2263 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2265 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2266 if (clippedTransformedTargetRect.isNull())
2269 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2271 QRect clippedSourceRect
2272 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2273 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2275 uint dbpl = d->rasterBuffer->bytesPerLine();
2276 uint sbpl = img.bytesPerLine();
2278 uchar *dst = d->rasterBuffer->buffer();
2279 uint bpp = img.depth() >> 3;
2281 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2282 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2284 uint cw = clippedSourceRect.width();
2285 uint ch = clippedSourceRect.height();
2287 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2294 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2296 QRectF targetBounds = s->matrix.mapRect(r);
2297 bool exceedsPrecision = targetBounds.width() > 0xffff
2298 || targetBounds.height() > 0xffff;
2300 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2301 if (s->matrix.type() > QTransform::TxScale) {
2302 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2303 if (func && (!clip || clip->hasRectClip)) {
2304 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2305 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2306 s->matrix, s->intOpacity);
2310 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2311 if (func && (!clip || clip->hasRectClip)) {
2312 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2313 img.bits(), img.bytesPerLine(),
2314 qt_mapRect_non_normalizing(r, s->matrix), sr,
2315 !clip ? d->deviceRect : clip->clipRect,
2322 QTransform copy = s->matrix;
2323 copy.translate(r.x(), r.y());
2325 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2326 copy.translate(-sr.x(), -sr.y());
2328 d->image_filler_xform.clip = clip;
2329 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2330 if (!d->image_filler_xform.blend)
2332 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2334 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2335 QRectF rr = s->matrix.mapRect(r);
2337 const int x1 = qRound(rr.x());
2338 const int y1 = qRound(rr.y());
2339 const int x2 = qRound(rr.right());
2340 const int y2 = qRound(rr.bottom());
2342 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2346 #ifdef QT_FAST_SPANS
2348 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2349 d->initializeRasterizer(&d->image_filler_xform);
2350 d->rasterizer->setAntialiased(s->flags.antialiased);
2352 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2354 const QRectF &rect = r.normalized();
2355 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2356 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2358 if (s->flags.tx_noshear)
2359 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2361 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2365 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2368 QTransform m = s->matrix;
2369 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2370 m.m21(), m.m22(), m.m23(),
2371 m.m31() - offs, m.m32() - offs, m.m33());
2372 fillPath(path, &d->image_filler_xform);
2375 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2376 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2378 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2380 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2382 } else if (clip->hasRectClip) {
2383 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2389 d->image_filler.clip = clip;
2390 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2391 if (!d->image_filler.blend)
2393 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2394 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2397 rr.translate(s->matrix.dx(), s->matrix.dy());
2399 const int x1 = qRound(rr.x());
2400 const int y1 = qRound(rr.y());
2401 const int x2 = qRound(rr.right());
2402 const int y2 = qRound(rr.bottom());
2404 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2411 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2413 #ifdef QT_DEBUG_DRAW
2414 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2416 Q_D(QRasterPaintEngine);
2417 QRasterPaintEngineState *s = state();
2421 QPlatformPixmap *pd = pixmap.handle();
2422 if (pd->classId() == QPlatformPixmap::RasterClass) {
2423 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2425 image = pixmap.toImage();
2428 if (image.depth() == 1)
2429 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2431 if (s->matrix.type() > QTransform::TxTranslate) {
2432 QTransform copy = s->matrix;
2433 copy.translate(r.x(), r.y());
2434 copy.translate(-sr.x(), -sr.y());
2435 d->image_filler_xform.clip = d->clip();
2436 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2437 if (!d->image_filler_xform.blend)
2439 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2441 #ifdef QT_FAST_SPANS
2443 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2444 d->initializeRasterizer(&d->image_filler_xform);
2445 d->rasterizer->setAntialiased(s->flags.antialiased);
2447 const QRectF &rect = r.normalized();
2448 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2449 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2450 if (s->flags.tx_noshear)
2451 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2453 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2459 fillPath(path, &d->image_filler_xform);
2461 d->image_filler.clip = d->clip();
2463 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2464 if (!d->image_filler.blend)
2466 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2467 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2470 rr.translate(s->matrix.dx(), s->matrix.dy());
2471 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2477 static inline bool monoVal(const uchar* s, int x)
2479 return (s[x>>3] << (x&7)) & 0x80;
2485 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2487 Q_D(QRasterPaintEngine);
2488 QRasterPaintEngineState *s = state();
2490 if (!s->penData.blend)
2493 QRasterBuffer *rb = d->rasterBuffer.data();
2495 const QRect rect(rx, ry, w, h);
2496 const QClipData *clip = d->clip();
2497 bool unclipped = false;
2499 // inlined QRect::intersects
2500 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2501 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2503 if (clip->hasRectClip) {
2504 unclipped = rx > clip->xmin
2505 && rx + w < clip->xmax
2507 && ry + h < clip->ymax;
2513 // inlined QRect::intersects
2514 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2515 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2519 // inlined QRect::contains
2520 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2521 && rect.top() >= 0 && rect.bottom() < rb->height();
2523 unclipped = contains && d->isUnclipped_normalized(rect);
2526 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2527 const uchar * scanline = static_cast<const uchar *>(src);
2529 if (s->flags.fast_text) {
2532 if (s->penData.bitmapBlit) {
2533 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2534 scanline, w, h, bpl);
2537 } else if (depth == 8) {
2538 if (s->penData.alphamapBlit) {
2539 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2540 scanline, w, h, bpl, 0);
2543 } else if (depth == 32) {
2544 // (A)RGB Alpha mask where the alpha component is not used.
2545 if (s->penData.alphaRGBBlit) {
2546 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2547 (const uint *) scanline, w, h, bpl / 4, 0);
2551 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2552 // (A)RGB Alpha mask where the alpha component is not used.
2554 int nx = qMax(0, rx);
2555 int ny = qMax(0, ry);
2557 // Move scanline pointer to compensate for moved x and y
2558 int xdiff = nx - rx;
2559 int ydiff = ny - ry;
2560 scanline += ydiff * bpl;
2561 scanline += xdiff * (depth == 32 ? 4 : 1);
2566 if (nx + w > d->rasterBuffer->width())
2567 w = d->rasterBuffer->width() - nx;
2568 if (ny + h > d->rasterBuffer->height())
2569 h = d->rasterBuffer->height() - ny;
2574 if (depth == 8 && s->penData.alphamapBlit) {
2575 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2576 scanline, w, h, bpl, clip);
2577 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2578 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2579 (const uint *) scanline, w, h, bpl / 4, clip);
2594 scanline += bpl * y0;
2598 w = qMin(w, rb->width() - qMax(0, rx));
2599 h = qMin(h, rb->height() - qMax(0, ry));
2601 if (w <= 0 || h <= 0)
2604 const int NSPANS = 256;
2605 QSpan spans[NSPANS];
2608 const int x1 = x0 + w;
2609 const int y1 = y0 + h;
2612 for (int y = y0; y < y1; ++y) {
2613 for (int x = x0; x < x1; ) {
2614 if (!monoVal(scanline, x)) {
2619 if (current == NSPANS) {
2620 blend(current, spans, &s->penData);
2623 spans[current].x = x + rx;
2624 spans[current].y = y + ry;
2625 spans[current].coverage = 255;
2628 // extend span until we find a different one.
2629 while (x < x1 && monoVal(scanline, x)) {
2633 spans[current].len = len;
2638 } else if (depth == 8) {
2639 for (int y = y0; y < y1; ++y) {
2640 for (int x = x0; x < x1; ) {
2641 // Skip those with 0 coverage
2642 if (scanline[x] == 0) {
2647 if (current == NSPANS) {
2648 blend(current, spans, &s->penData);
2651 int coverage = scanline[x];
2652 spans[current].x = x + rx;
2653 spans[current].y = y + ry;
2654 spans[current].coverage = coverage;
2658 // extend span until we find a different one.
2659 while (x < x1 && scanline[x] == coverage) {
2663 spans[current].len = len;
2668 } else { // 32-bit alpha...
2669 uint *sl = (uint *) src;
2670 for (int y = y0; y < y1; ++y) {
2671 for (int x = x0; x < x1; ) {
2672 // Skip those with 0 coverage
2673 if ((sl[x] & 0x00ffffff) == 0) {
2678 if (current == NSPANS) {
2679 blend(current, spans, &s->penData);
2682 uint rgbCoverage = sl[x];
2683 int coverage = qGreen(rgbCoverage);
2684 spans[current].x = x + rx;
2685 spans[current].y = y + ry;
2686 spans[current].coverage = coverage;
2690 // extend span until we find a different one.
2691 while (x < x1 && sl[x] == rgbCoverage) {
2695 spans[current].len = len;
2698 sl += bpl / sizeof(uint);
2701 // qDebug() << "alphaPenBlt: num spans=" << current
2702 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2703 // Call span func for current set of spans.
2705 blend(current, spans, &s->penData);
2708 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2709 const QFixedPoint *positions, QFontEngine *fontEngine)
2711 Q_D(QRasterPaintEngine);
2712 QRasterPaintEngineState *s = state();
2714 if (fontEngine->hasInternalCaching()) {
2715 QFontEngine::GlyphFormat neededFormat =
2716 painter()->device()->devType() == QInternal::Widget
2717 ? QFontEngine::Format_None
2718 : QFontEngine::Format_A8;
2720 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2721 neededFormat = QFontEngine::Format_Mono;
2723 for (int i = 0; i < numGlyphs; i++) {
2724 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2727 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2729 if (alphaMap == 0 || alphaMap->isNull())
2732 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2733 qFloor(positions[i].x) + offset.x(),
2734 qFloor(positions[i].y) + offset.y(),
2735 alphaMap->width(), alphaMap->height());
2737 fontEngine->unlockAlphaMapForGlyph();
2741 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2743 QImageTextureGlyphCache *cache =
2744 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2746 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2747 fontEngine->setGlyphCache(0, cache);
2750 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2751 cache->fillInPendingGlyphs();
2753 const QImage &image = cache->image();
2754 int bpl = image.bytesPerLine();
2756 int depth = image.depth();
2760 leftShift = 2; // multiply by 4
2761 else if (depth == 1)
2762 rightShift = 3; // divide by 8
2764 int margin = cache->glyphMargin();
2765 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2766 const uchar *bits = image.bits();
2767 for (int i=0; i<numGlyphs; ++i) {
2769 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2770 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2771 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2775 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2776 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2778 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2781 // c.baseLineX, c.baseLineY,
2784 // positions[i].x.toInt(), positions[i].y.toInt());
2786 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2792 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2793 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2795 Q_D(QRasterPaintEngine);
2796 QRasterPaintEngineState *s = state();
2798 QFontEngine *fontEngine = ti.fontEngine;
2799 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2800 QPaintEngineEx::drawTextItem(p, ti);
2804 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2806 QVarLengthArray<QFixedPoint> positions;
2807 QVarLengthArray<glyph_t> glyphs;
2808 QTransform matrix = s->matrix;
2809 matrix.translate(p.x(), p.y());
2810 if (matrix.type() == QTransform::TxScale)
2811 fe->setFontScale(matrix.m11());
2812 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2814 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2816 for (int i=0; i<glyphs.size(); ++i) {
2817 TOpenFontCharMetrics tmetrics;
2818 const TUint8 *glyphBitmapBytes;
2819 TSize glyphBitmapSize;
2820 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2821 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2822 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2823 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2826 if (matrix.type() == QTransform::TxScale)
2827 fe->setFontScale(1.0);
2831 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2834 * Returns true if the rectangle is completely within the current clip
2835 * state of the paint engine.
2837 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2839 const QClipData *cl = clip();
2841 // inline contains() for performance (we know the rects are normalized)
2842 const QRect &r1 = deviceRect;
2843 return (r.left() >= r1.left() && r.right() <= r1.right()
2844 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2848 if (cl->hasRectClip) {
2849 // currently all painting functions clips to deviceRect internally
2850 if (cl->clipRect == deviceRect)
2853 // inline contains() for performance (we know the rects are normalized)
2854 const QRect &r1 = cl->clipRect;
2855 return (r.left() >= r1.left() && r.right() <= r1.right()
2856 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2858 return qt_region_strictContains(cl->clipRegion, r);
2862 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2865 Q_Q(const QRasterPaintEngine);
2866 const QRasterPaintEngineState *s = q->state();
2867 const QClipData *cl = clip();
2869 QRect r = rect.normalized();
2870 // inline contains() for performance (we know the rects are normalized)
2871 const QRect &r1 = deviceRect;
2872 return (r.left() >= r1.left() && r.right() <= r1.right()
2873 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2877 // currently all painting functions that call this function clip to deviceRect internally
2878 if (cl->hasRectClip && cl->clipRect == deviceRect)
2881 if (s->flags.antialiased)
2884 QRect r = rect.normalized();
2886 r.setX(r.x() - penWidth);
2887 r.setY(r.y() - penWidth);
2888 r.setWidth(r.width() + 2 * penWidth);
2889 r.setHeight(r.height() + 2 * penWidth);
2892 if (cl->hasRectClip) {
2893 // inline contains() for performance (we know the rects are normalized)
2894 const QRect &r1 = cl->clipRect;
2895 return (r.left() >= r1.left() && r.right() <= r1.right()
2896 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2898 return qt_region_strictContains(cl->clipRegion, r);
2902 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2905 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2909 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2910 const QSpanData *data) const
2912 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2916 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2917 const QSpanData *data) const
2919 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2923 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2924 const QSpanData *data) const
2926 Q_Q(const QRasterPaintEngine);
2927 const QRasterPaintEngineState *s = q->state();
2929 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2931 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2932 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2938 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2943 QFontEngine *fontEngine = textItem->fontEngine();
2944 if (shouldDrawCachedGlyphs(fontEngine->fontDef.pixelSize, state()->matrix)) {
2945 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2948 QPaintEngineEx::drawStaticTextItem(textItem);
2955 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
2957 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
2958 QRasterPaintEngineState *s = state();
2960 #ifdef QT_DEBUG_DRAW
2961 Q_D(QRasterPaintEngine);
2962 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
2963 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
2970 #if defined (Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_WS_QPA)
2972 if (!supportsTransformations(ti.fontEngine)) {
2973 QVarLengthArray<QFixedPoint> positions;
2974 QVarLengthArray<glyph_t> glyphs;
2976 QTransform matrix = s->matrix;
2977 matrix.translate(p.x(), p.y());
2979 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2981 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
2985 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC || Q_WS_QPA
2986 if (s->matrix.type() <= QTransform::TxTranslate
2987 || (s->matrix.type() == QTransform::TxScale
2988 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
2989 drawGlyphsS60(p, ti);
2992 #else // Q_OS_WIN || Q_WS_MAC || Q_WS_QPA
2994 QFontEngine *fontEngine = ti.fontEngine;
2996 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2998 if (fontEngine->type() == QFontEngine::Freetype) {
2999 QTransform matrix = s->matrix;
3000 matrix.translate(p.x(), p.y());
3002 QVarLengthArray<QFixedPoint> positions;
3003 QVarLengthArray<glyph_t> glyphs;
3004 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3005 if (glyphs.size() == 0)
3008 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3009 QPaintEngine::drawTextItem(p, ti);
3016 QPaintEngineEx::drawTextItem(p, ti);
3022 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3024 Q_D(QRasterPaintEngine);
3025 QRasterPaintEngineState *s = state();
3028 if (!s->penData.blend)
3031 if (!s->flags.fast_pen) {
3032 QPaintEngineEx::drawPoints(points, pointCount);
3036 QCosmeticStroker stroker(s, d->deviceRect);
3037 stroker.drawPoints(points, pointCount);
3041 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3043 Q_D(QRasterPaintEngine);
3044 QRasterPaintEngineState *s = state();
3047 if (!s->penData.blend)
3050 if (!s->flags.fast_pen) {
3051 QPaintEngineEx::drawPoints(points, pointCount);
3055 QCosmeticStroker stroker(s, d->deviceRect);
3056 stroker.drawPoints(points, pointCount);
3062 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3064 #ifdef QT_DEBUG_DRAW
3065 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3067 Q_D(QRasterPaintEngine);
3068 QRasterPaintEngineState *s = state();
3071 if (!s->penData.blend)
3074 if (s->flags.fast_pen) {
3075 QCosmeticStroker stroker(s, d->deviceRect);
3076 for (int i=0; i<lineCount; ++i) {
3077 const QLine &l = lines[i];
3078 stroker.drawLine(l.p1(), l.p2());
3081 QPaintEngineEx::drawLines(lines, lineCount);
3085 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3091 Q_Q(QRasterPaintEngine);
3092 QRasterPaintEngineState *s = q->state();
3094 const QPen &pen = s->lastPen;
3095 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3096 const QVector<qreal> pattern = pen.dashPattern();
3098 qreal patternLength = 0;
3099 for (int i = 0; i < pattern.size(); ++i)
3100 patternLength += pattern.at(i);
3102 if (patternLength <= 0)
3105 qreal length = line.length();
3106 Q_ASSERT(length > 0);
3107 while (length > 0) {
3108 const bool rasterize = *inDash;
3109 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3112 if (dash >= length) {
3114 *dashOffset += dash / width;
3118 *inDash = !(*inDash);
3119 if (++*dashIndex >= pattern.size())
3126 if (rasterize && dash > 0)
3127 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3134 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3136 #ifdef QT_DEBUG_DRAW
3137 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3139 Q_D(QRasterPaintEngine);
3140 QRasterPaintEngineState *s = state();
3143 if (!s->penData.blend)
3145 if (s->flags.fast_pen) {
3146 QCosmeticStroker stroker(s, d->deviceRect);
3147 for (int i=0; i<lineCount; ++i) {
3148 QLineF line = lines[i];
3149 stroker.drawLine(line.p1(), line.p2());
3152 QPaintEngineEx::drawLines(lines, lineCount);
3160 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3162 Q_D(QRasterPaintEngine);
3163 QRasterPaintEngineState *s = state();
3166 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3167 || (qpen_style(s->lastPen) == Qt::NoPen))
3168 && !s->flags.antialiased
3169 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3171 && s->matrix.type() <= QTransform::TxScale) // no shear
3174 const QRectF r = s->matrix.mapRect(rect);
3175 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3176 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3177 const QRect brect = QRect(int(r.x()), int(r.y()),
3178 int_dim(r.x(), r.width()),
3179 int_dim(r.y(), r.height()));
3181 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3182 &s->penData, &s->brushData);
3186 QPaintEngineEx::drawEllipse(rect);
3193 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3195 Q_D(QRasterPaintEngine);
3202 CGContextRef QRasterPaintEngine::getCGContext() const
3204 Q_D(const QRasterPaintEngine);
3205 return d->cgContext;
3213 void QRasterPaintEngine::setDC(HDC hdc) {
3214 Q_D(QRasterPaintEngine);
3221 HDC QRasterPaintEngine::getDC() const
3223 Q_D(const QRasterPaintEngine);
3230 void QRasterPaintEngine::releaseDC(HDC) const
3236 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3238 const QTransform &m = state()->matrix;
3239 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3240 QFontEngine::Type fontEngineType = fontEngine->type();
3241 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3242 || (m.type() <= QTransform::TxTranslate
3243 && (fontEngineType == QFontEngine::TestFontEngine
3244 || fontEngineType == QFontEngine::Box))) {
3248 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3251 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3253 #if defined(Q_WS_MAC)
3254 // Mac font engines don't support scaling and rotation
3255 if (m.type() > QTransform::TxTranslate)
3257 if (m.type() >= QTransform::TxProject)
3261 return !shouldDrawCachedGlyphs(pixelSize, m);
3267 QPoint QRasterPaintEngine::coordinateOffset() const
3269 return QPoint(0, 0);
3272 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3277 Q_D(QRasterPaintEngine);
3279 Q_ASSERT(image.depth() == 1);
3281 const int spanCount = 256;
3282 QT_FT_Span spans[spanCount];
3286 int w = image.width();
3287 int h = image.height();
3288 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3289 int ymin = qMax(qRound(pos.y()), 0);
3290 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3291 int xmin = qMax(qRound(pos.x()), 0);
3293 int x_offset = xmin - qRound(pos.x());
3295 QImage::Format format = image.format();
3296 for (int y = ymin; y < ymax; ++y) {
3297 const uchar *src = image.scanLine(y - qRound(pos.y()));
3298 if (format == QImage::Format_MonoLSB) {
3299 for (int x = 0; x < xmax - xmin; ++x) {
3300 int src_x = x + x_offset;
3301 uchar pixel = src[src_x >> 3];
3306 if (pixel & (0x1 << (src_x & 7))) {
3307 spans[n].x = xmin + x;
3309 spans[n].coverage = 255;
3311 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3315 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3318 if (n == spanCount) {
3319 fg->blend(n, spans, fg);
3325 for (int x = 0; x < xmax - xmin; ++x) {
3326 int src_x = x + x_offset;
3327 uchar pixel = src[src_x >> 3];
3332 if (pixel & (0x80 >> (x & 7))) {
3333 spans[n].x = xmin + x;
3335 spans[n].coverage = 255;
3337 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3341 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3344 if (n == spanCount) {
3345 fg->blend(n, spans, fg);
3353 fg->blend(n, spans, fg);
3359 \enum QRasterPaintEngine::ClipType
3362 \value RectClip Indicates that the currently set clip is a single rectangle.
3363 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3368 Returns the type of the clip currently set.
3370 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3372 Q_D(const QRasterPaintEngine);
3374 const QClipData *clip = d->clip();
3375 if (!clip || clip->hasRectClip)
3383 Returns the bounding rect of the currently set clip.
3385 QRect QRasterPaintEngine::clipBoundingRect() const
3387 Q_D(const QRasterPaintEngine);
3389 const QClipData *clip = d->clip();
3392 return d->deviceRect;
3394 if (clip->hasRectClip)
3395 return clip->clipRect;
3397 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3400 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3402 Q_Q(QRasterPaintEngine);
3403 QRasterPaintEngineState *s = q->state();
3405 rasterizer->setAntialiased(s->flags.antialiased);
3407 QRect clipRect(deviceRect);
3409 // ### get from optimized rectbased QClipData
3411 const QClipData *c = clip();
3413 const QRect r(QPoint(c->xmin, c->ymin),
3414 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3415 clipRect = clipRect.intersected(r);
3416 blend = data->blend;
3418 blend = data->unclipped_blend;
3421 rasterizer->setClipRect(clipRect);
3422 rasterizer->initialize(blend, data);
3425 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3426 ProcessSpans callback,
3427 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3429 if (!callback || !outline)
3432 Q_Q(QRasterPaintEngine);
3433 QRasterPaintEngineState *s = q->state();
3435 if (!s->flags.antialiased) {
3436 initializeRasterizer(spanData);
3438 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3442 rasterizer->rasterize(outline, fillRule);
3446 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3450 int q_gray_rendered_spans(QT_FT_Raster raster);
3453 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3455 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3458 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3459 ProcessSpans callback,
3460 void *userData, QRasterBuffer *)
3462 if (!callback || !outline)
3465 Q_Q(QRasterPaintEngine);
3466 QRasterPaintEngineState *s = q->state();
3468 if (!s->flags.antialiased) {
3469 rasterizer->setAntialiased(s->flags.antialiased);
3470 rasterizer->setClipRect(deviceRect);
3471 rasterizer->initialize(callback, userData);
3473 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3477 rasterizer->rasterize(outline, fillRule);
3481 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3482 // minimize memory reallocations. However if initial size for
3483 // raster pool is changed for lower value, reallocations will
3485 int rasterPoolSize = MINIMUM_POOL_SIZE;
3486 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3487 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3488 uchar *rasterPoolOnHeap = 0;
3490 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3492 void *data = userData;
3494 QT_FT_BBox clip_box = { deviceRect.x(),
3496 deviceRect.x() + deviceRect.width(),
3497 deviceRect.y() + deviceRect.height() };
3499 QT_FT_Raster_Params rasterParams;
3500 rasterParams.target = 0;
3501 rasterParams.source = outline;
3502 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3503 rasterParams.gray_spans = 0;
3504 rasterParams.black_spans = 0;
3505 rasterParams.bit_test = 0;
3506 rasterParams.bit_set = 0;
3507 rasterParams.user = data;
3508 rasterParams.clip_box = clip_box;
3513 int rendered_spans = 0;
3517 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3518 rasterParams.gray_spans = callback;
3519 rasterParams.skip_spans = rendered_spans;
3520 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3522 // Out of memory, reallocate some more and try again...
3523 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3524 rasterPoolSize *= 2;
3525 if (rasterPoolSize > 1024 * 1024) {
3526 qWarning("QPainter: Rasterization of primitive failed");
3530 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3532 free(rasterPoolOnHeap);
3533 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3535 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3537 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3539 qt_ft_grays_raster.raster_done(*grayRaster.data());
3540 qt_ft_grays_raster.raster_new(grayRaster.data());
3541 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3547 free(rasterPoolOnHeap);
3550 void QRasterPaintEnginePrivate::recalculateFastImages()
3552 Q_Q(QRasterPaintEngine);
3553 QRasterPaintEngineState *s = q->state();
3555 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3556 && s->matrix.type() <= QTransform::TxShear;
3559 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3561 Q_Q(const QRasterPaintEngine);
3562 const QRasterPaintEngineState *s = q->state();
3564 return s->flags.fast_images
3565 && (mode == QPainter::CompositionMode_SourceOver
3566 || (mode == QPainter::CompositionMode_Source
3567 && !image.hasAlphaChannel()));
3570 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3572 Q_ASSERT(image.depth() == 1);
3574 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3575 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3577 QRgb fg = PREMUL(color.rgba());
3580 int height = sourceImage.height();
3581 int width = sourceImage.width();
3582 for (int y=0; y<height; ++y) {
3583 uchar *source = sourceImage.scanLine(y);
3584 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3585 if (!source || !target)
3586 QT_THROW(std::bad_alloc()); // we must have run out of memory
3587 for (int x=0; x < width; ++x)
3588 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3593 QRasterBuffer::~QRasterBuffer()
3597 void QRasterBuffer::init()
3599 compositionMode = QPainter::CompositionMode_SourceOver;
3600 monoDestinationWithClut = false;
3605 QImage::Format QRasterBuffer::prepare(QImage *image)
3607 m_buffer = (uchar *)image->bits();
3608 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3609 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3610 bytes_per_pixel = image->depth()/8;
3611 bytes_per_line = image->bytesPerLine();
3613 format = image->format();
3614 drawHelper = qDrawHelper + format;
3615 if (image->depth() == 1 && image->colorTable().size() == 2) {
3616 monoDestinationWithClut = true;
3617 destColor0 = PREMUL(image->colorTable()[0]);
3618 destColor1 = PREMUL(image->colorTable()[1]);
3624 void QRasterBuffer::resetBuffer(int val)
3626 memset(m_buffer, val, m_height*bytes_per_line);
3629 QClipData::QClipData(int height)
3631 clipSpanHeight = height;
3636 xmin = xmax = ymin = ymax = 0;
3640 hasRectClip = hasRegionClip = false;
3643 QClipData::~QClipData()
3651 void QClipData::initialize()
3657 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3659 Q_CHECK_PTR(m_clipLines);
3661 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3662 allocated = clipSpanHeight;
3663 Q_CHECK_PTR(m_spans);
3669 m_clipLines[y].spans = 0;
3670 m_clipLines[y].count = 0;
3674 const int len = clipRect.width();
3677 QSpan *span = m_spans + count;
3681 span->coverage = 255;
3684 m_clipLines[y].spans = span;
3685 m_clipLines[y].count = 1;
3689 while (y < clipSpanHeight) {
3690 m_clipLines[y].spans = 0;
3691 m_clipLines[y].count = 0;
3694 } else if (hasRegionClip) {
3696 const QVector<QRect> rects = clipRegion.rects();
3697 const int numRects = rects.size();
3700 const int maxSpans = (ymax - ymin) * numRects;
3701 if (maxSpans > allocated) {
3702 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3703 allocated = maxSpans;
3708 int firstInBand = 0;
3710 while (firstInBand < numRects) {
3711 const int currMinY = rects.at(firstInBand).y();
3712 const int currMaxY = currMinY + rects.at(firstInBand).height();
3714 while (y < currMinY) {
3715 m_clipLines[y].spans = 0;
3716 m_clipLines[y].count = 0;
3720 int lastInBand = firstInBand;
3721 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3724 while (y < currMaxY) {
3726 m_clipLines[y].spans = m_spans + count;
3727 m_clipLines[y].count = lastInBand - firstInBand + 1;
3729 for (int r = firstInBand; r <= lastInBand; ++r) {
3730 const QRect &currRect = rects.at(r);
3731 QSpan *span = m_spans + count;
3732 span->x = currRect.x();
3733 span->len = currRect.width();
3735 span->coverage = 255;
3741 firstInBand = lastInBand + 1;
3744 Q_ASSERT(count <= allocated);
3746 while (y < clipSpanHeight) {
3747 m_clipLines[y].spans = 0;
3748 m_clipLines[y].count = 0;
3754 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3759 free(m_clipLines); // same for clipLines
3765 void QClipData::fixup()
3770 ymin = ymax = xmin = xmax = 0;
3775 ymin = m_spans[0].y;
3776 ymax = m_spans[count-1].y + 1;
3780 const int firstLeft = m_spans[0].x;
3781 const int firstRight = m_spans[0].x + m_spans[0].len;
3784 for (int i = 0; i < count; ++i) {
3785 QT_FT_Span_& span = m_spans[i];
3788 if (span.y != y + 1 && y != -1)
3791 m_clipLines[y].spans = &span;
3792 m_clipLines[y].count = 1;
3794 ++m_clipLines[y].count;
3796 const int spanLeft = span.x;
3797 const int spanRight = spanLeft + span.len;
3799 if (spanLeft < xmin)
3802 if (spanRight > xmax)
3805 if (spanLeft != firstLeft || spanRight != firstRight)
3811 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3816 Convert \a rect to clip spans.
3818 void QClipData::setClipRect(const QRect &rect)
3820 if (hasRectClip && rect == clipRect)
3823 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3825 hasRegionClip = false;
3829 xmax = rect.x() + rect.width();
3830 ymin = qMin(rect.y(), clipSpanHeight);
3831 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3838 // qDebug() << xmin << xmax << ymin << ymax;
3842 Convert \a region to clip spans.
3844 void QClipData::setClipRegion(const QRegion ®ion)
3846 if (region.rectCount() == 1) {
3847 setClipRect(region.rects().at(0));
3851 hasRegionClip = true;
3852 hasRectClip = false;
3853 clipRegion = region;
3855 { // set bounding rect
3856 const QRect rect = region.boundingRect();
3858 xmax = rect.x() + rect.width();
3860 ymax = rect.y() + rect.height();
3872 spans must be sorted on y
3874 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3875 const QSpan *spans, const QSpan *end,
3876 QSpan **outSpans, int available)
3878 const_cast<QClipData *>(clip)->initialize();
3880 QSpan *out = *outSpans;
3882 const QSpan *clipSpans = clip->m_spans + *currentClip;
3883 const QSpan *clipEnd = clip->m_spans + clip->count;
3885 while (available && spans < end ) {
3886 if (clipSpans >= clipEnd) {
3890 if (clipSpans->y > spans->y) {
3894 if (spans->y != clipSpans->y) {
3895 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3896 clipSpans = clip->m_clipLines[spans->y].spans;
3901 Q_ASSERT(spans->y == clipSpans->y);
3904 int sx2 = sx1 + spans->len;
3905 int cx1 = clipSpans->x;
3906 int cx2 = cx1 + clipSpans->len;
3908 if (cx1 < sx1 && cx2 < sx1) {
3911 } else if (sx1 < cx1 && sx2 < cx1) {
3915 int x = qMax(sx1, cx1);
3916 int len = qMin(sx2, cx2) - x;
3918 out->x = qMax(sx1, cx1);
3919 out->len = qMin(sx2, cx2) - out->x;
3921 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3933 *currentClip = clipSpans - clip->m_spans;
3937 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3939 // qDebug() << "qt_span_fill_clipped" << spanCount;
3940 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3942 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3944 const int NSPANS = 256;
3945 QSpan cspans[NSPANS];
3946 int currentClip = 0;
3947 const QSpan *end = spans + spanCount;
3948 while (spans < end) {
3949 QSpan *clipped = cspans;
3950 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3951 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3952 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3954 if (clipped - cspans)
3955 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3961 Clip spans to \a{clip}-rectangle.
3962 Returns number of unclipped spans
3964 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3967 const short minx = clip.left();
3968 const short miny = clip.top();
3969 const short maxx = clip.right();
3970 const short maxy = clip.bottom();
3973 for (int i = 0; i < numSpans; ++i) {
3974 if (spans[i].y > maxy)
3976 if (spans[i].y < miny
3977 || spans[i].x > maxx
3978 || spans[i].x + spans[i].len <= minx) {
3981 if (spans[i].x < minx) {
3982 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
3985 spans[n].x = spans[i].x;
3986 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
3988 if (spans[n].len == 0)
3990 spans[n].y = spans[i].y;
3991 spans[n].coverage = spans[i].coverage;
3998 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4001 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4002 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4004 Q_ASSERT(fillData->clip);
4005 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4007 // hw: check if this const_cast<> is safe!!!
4008 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4009 fillData->clip->clipRect);
4011 fillData->unclipped_blend(count, spans, fillData);
4014 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4016 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4018 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4019 // for (int i = 0; i < qMin(count, 10); ++i) {
4020 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4023 switch (clipData->operation) {
4025 case Qt::IntersectClip:
4027 QClipData *newClip = clipData->newClip;
4028 newClip->initialize();
4030 int currentClip = 0;
4031 const QSpan *end = spans + count;
4032 while (spans < end) {
4033 QSpan *newspans = newClip->m_spans + newClip->count;
4034 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4035 &newspans, newClip->allocated - newClip->count);
4036 newClip->count = newspans - newClip->m_spans;
4038 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4039 newClip->allocated *= 2;
4045 case Qt::ReplaceClip:
4046 clipData->newClip->appendSpans(spans, count);
4054 QImage QRasterBuffer::bufferImage() const
4056 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4058 for (int y = 0; y < m_height; ++y) {
4059 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4061 for (int x=0; x<m_width; ++x) {
4062 uint argb = span[x];
4063 image.setPixel(x, y, argb);
4071 void QRasterBuffer::flushToARGBImage(QImage *target) const
4073 int w = qMin(m_width, target->width());
4074 int h = qMin(m_height, target->height());
4076 for (int y=0; y<h; ++y) {
4077 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4078 QRgb *dest = (QRgb *) target->scanLine(y);
4079 for (int x=0; x<w; ++x) {
4080 QRgb pixel = sourceLine[x];
4081 int alpha = qAlpha(pixel);
4085 dest[x] = (alpha << 24)
4086 | ((255*qRed(pixel)/alpha) << 16)
4087 | ((255*qGreen(pixel)/alpha) << 8)
4088 | ((255*qBlue(pixel)/alpha) << 0);
4095 class QGradientCache
4099 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4100 stops(s), opacity(op), interpolationMode(mode) {}
4101 uint buffer[GRADIENT_STOPTABLE_SIZE];
4102 QGradientStops stops;
4104 QGradient::InterpolationMode interpolationMode;
4107 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4110 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4111 quint64 hash_val = 0;
4113 QGradientStops stops = gradient.stops();
4114 for (int i = 0; i < stops.size() && i <= 2; i++)
4115 hash_val += stops[i].second.rgba();
4117 QMutexLocker lock(&mutex);
4118 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4120 if (it == cache.constEnd())
4121 return addCacheElement(hash_val, gradient, opacity);
4124 const CacheInfo &cache_info = it.value();
4125 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4126 return cache_info.buffer;
4128 } while (it != cache.constEnd() && it.key() == hash_val);
4129 // an exact match for these stops and opacity was not found, create new cache
4130 return addCacheElement(hash_val, gradient, opacity);
4134 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4136 inline int maxCacheSize() const { return 60; }
4137 inline void generateGradientColorTable(const QGradient& g,
4139 int size, int opacity) const;
4140 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4141 if (cache.size() == maxCacheSize()) {
4142 // may remove more than 1, but OK
4143 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4145 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4146 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4147 return cache.insert(hash_val, cache_entry).value().buffer;
4150 QGradientColorTableHash cache;
4154 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4156 QGradientStops stops = gradient.stops();
4157 int stopCount = stops.count();
4158 Q_ASSERT(stopCount > 0);
4160 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4162 if (stopCount == 2) {
4163 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4164 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4166 qreal first_stop = stops[0].first;
4167 qreal second_stop = stops[1].first;
4169 if (second_stop < first_stop) {
4170 qSwap(first_color, second_color);
4171 qSwap(first_stop, second_stop);
4174 if (colorInterpolation) {
4175 first_color = PREMUL(first_color);
4176 second_color = PREMUL(second_color);
4179 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4180 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4182 uint red_first = qRed(first_color) << 16;
4183 uint green_first = qGreen(first_color) << 16;
4184 uint blue_first = qBlue(first_color) << 16;
4185 uint alpha_first = qAlpha(first_color) << 16;
4187 uint red_second = qRed(second_color) << 16;
4188 uint green_second = qGreen(second_color) << 16;
4189 uint blue_second = qBlue(second_color) << 16;
4190 uint alpha_second = qAlpha(second_color) << 16;
4193 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4194 if (colorInterpolation)
4195 colorTable[i] = first_color;
4197 colorTable[i] = PREMUL(first_color);
4200 if (i < second_index) {
4201 qreal reciprocal = qreal(1) / (second_index - first_index);
4203 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4204 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4205 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4206 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4209 red_first += 1 << 15;
4210 green_first += 1 << 15;
4211 blue_first += 1 << 15;
4212 alpha_first += 1 << 15;
4214 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4215 red_first += red_delta;
4216 green_first += green_delta;
4217 blue_first += blue_delta;
4218 alpha_first += alpha_delta;
4220 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4221 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4223 if (colorInterpolation)
4224 colorTable[i] = color;
4226 colorTable[i] = PREMUL(color);
4230 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4231 if (colorInterpolation)
4232 colorTable[i] = second_color;
4234 colorTable[i] = PREMUL(second_color);
4240 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4241 if (stopCount == 1) {
4242 current_color = PREMUL(current_color);
4243 for (int i = 0; i < size; ++i)
4244 colorTable[i] = current_color;
4248 // The position where the gradient begins and ends
4249 qreal begin_pos = stops[0].first;
4250 qreal end_pos = stops[stopCount-1].first;
4252 int pos = 0; // The position in the color table.
4255 qreal incr = 1 / qreal(size); // the double increment.
4256 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4258 // Up to first point
4259 colorTable[pos++] = PREMUL(current_color);
4260 while (dpos <= begin_pos) {
4261 colorTable[pos] = colorTable[pos - 1];
4266 int current_stop = 0; // We always interpolate between current and current + 1.
4268 qreal t; // position between current left and right stops
4269 qreal t_delta; // the t increment per entry in the color table
4271 if (dpos < end_pos) {
4273 while (dpos > stops[current_stop+1].first)
4276 if (current_stop != 0)
4277 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4278 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4280 if (colorInterpolation) {
4281 current_color = PREMUL(current_color);
4282 next_color = PREMUL(next_color);
4285 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4286 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4287 t = (dpos - stops[current_stop].first) * c;
4291 Q_ASSERT(current_stop < stopCount);
4293 int dist = qRound(t);
4294 int idist = 256 - dist;
4296 if (colorInterpolation)
4297 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4299 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4304 if (dpos >= end_pos)
4310 while (dpos > stops[current_stop+skip+1].first)
4314 current_stop += skip;
4316 current_color = next_color;
4318 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4319 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4321 if (colorInterpolation) {
4323 current_color = PREMUL(current_color);
4324 next_color = PREMUL(next_color);
4327 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4328 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4329 t = (dpos - stops[current_stop].first) * c;
4336 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4337 while (pos < size - 1) {
4338 colorTable[pos] = current_color;
4342 // Make sure the last color stop is represented at the end of the table
4343 colorTable[size - 1] = current_color;
4346 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4349 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4355 m11 = m22 = m33 = 1.;
4356 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4357 clip = pe ? pe->d_func()->clip() : 0;
4360 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4362 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4364 Qt::BrushStyle brushStyle = qbrush_style(brush);
4365 switch (brushStyle) {
4366 case Qt::SolidPattern: {
4368 QColor c = qbrush_color(brush);
4369 QRgb rgba = c.rgba();
4370 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4371 if ((solid.color & 0xff000000) == 0
4372 && compositionMode == QPainter::CompositionMode_SourceOver) {
4378 case Qt::LinearGradientPattern:
4380 type = LinearGradient;
4381 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4382 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4383 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4384 gradient.spread = g->spread();
4386 QLinearGradientData &linearData = gradient.linear;
4388 linearData.origin.x = g->start().x();
4389 linearData.origin.y = g->start().y();
4390 linearData.end.x = g->finalStop().x();
4391 linearData.end.y = g->finalStop().y();
4395 case Qt::RadialGradientPattern:
4397 type = RadialGradient;
4398 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4399 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4400 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4401 gradient.spread = g->spread();
4403 QRadialGradientData &radialData = gradient.radial;
4405 QPointF center = g->center();
4406 radialData.center.x = center.x();
4407 radialData.center.y = center.y();
4408 radialData.center.radius = g->centerRadius();
4409 QPointF focal = g->focalPoint();
4410 radialData.focal.x = focal.x();
4411 radialData.focal.y = focal.y();
4412 radialData.focal.radius = g->focalRadius();
4416 case Qt::ConicalGradientPattern:
4418 type = ConicalGradient;
4419 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4420 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4421 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4422 gradient.spread = QGradient::RepeatSpread;
4424 QConicalGradientData &conicalData = gradient.conical;
4426 QPointF center = g->center();
4427 conicalData.center.x = center.x();
4428 conicalData.center.y = center.y();
4429 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4433 case Qt::Dense1Pattern:
4434 case Qt::Dense2Pattern:
4435 case Qt::Dense3Pattern:
4436 case Qt::Dense4Pattern:
4437 case Qt::Dense5Pattern:
4438 case Qt::Dense6Pattern:
4439 case Qt::Dense7Pattern:
4440 case Qt::HorPattern:
4441 case Qt::VerPattern:
4442 case Qt::CrossPattern:
4443 case Qt::BDiagPattern:
4444 case Qt::FDiagPattern:
4445 case Qt::DiagCrossPattern:
4448 tempImage = new QImage();
4449 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4450 initTexture(tempImage, alpha, QTextureData::Tiled);
4452 case Qt::TexturePattern:
4455 tempImage = new QImage();
4457 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4458 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4460 *tempImage = brush.textureImage();
4461 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4469 adjustSpanMethods();
4472 void QSpanData::adjustSpanMethods()
4482 unclipped_blend = 0;
4485 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4486 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4487 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4488 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4489 fillRect = rasterBuffer->drawHelper->fillRect;
4491 case LinearGradient:
4492 case RadialGradient:
4493 case ConicalGradient:
4494 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4497 unclipped_blend = qBlendTexture;
4498 if (!texture.imageData)
4499 unclipped_blend = 0;
4504 if (!unclipped_blend) {
4507 blend = unclipped_blend;
4508 } else if (clip->hasRectClip) {
4509 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4511 blend = qt_span_fill_clipped;
4515 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4518 // make sure we round off correctly in qdrawhelper.cpp
4519 delta.translate(1.0 / 65536, 1.0 / 65536);
4521 QTransform inv = (delta * matrix).inverted();
4534 const bool affine = !m13 && !m23;
4535 fast_matrix = affine
4536 && m11 * m11 + m21 * m21 < 1e4
4537 && m12 * m12 + m22 * m22 < 1e4
4541 adjustSpanMethods();
4544 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4546 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4548 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4549 if (!d || d->height == 0) {
4550 texture.imageData = 0;
4557 texture.bytesPerLine = 0;
4558 texture.format = QImage::Format_Invalid;
4559 texture.colorTable = 0;
4560 texture.hasAlpha = alpha != 256;
4562 texture.imageData = d->data;
4563 texture.width = d->width;
4564 texture.height = d->height;
4566 if (sourceRect.isNull()) {
4569 texture.x2 = texture.width;
4570 texture.y2 = texture.height;
4572 texture.x1 = sourceRect.x();
4573 texture.y1 = sourceRect.y();
4574 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4575 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4578 texture.bytesPerLine = d->bytes_per_line;
4580 texture.format = d->format;
4581 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4582 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4584 texture.const_alpha = alpha;
4585 texture.type = _type;
4587 adjustSpanMethods();
4592 \a x and \a y is relative to the midpoint of \a rect.
4594 static inline void drawEllipsePoints(int x, int y, int length,
4597 ProcessSpans pen_func, ProcessSpans brush_func,
4598 QSpanData *pen_data, QSpanData *brush_data)
4603 QT_FT_Span outline[4];
4604 const int midx = rect.x() + (rect.width() + 1) / 2;
4605 const int midy = rect.y() + (rect.height() + 1) / 2;
4611 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4612 outline[0].len = qMin(length, x - outline[0].x);
4614 outline[0].coverage = 255;
4618 outline[1].len = length;
4620 outline[1].coverage = 255;
4623 outline[2].x = outline[0].x;
4624 outline[2].len = outline[0].len;
4625 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4626 outline[2].coverage = 255;
4630 outline[3].len = length;
4631 outline[3].y = outline[2].y;
4632 outline[3].coverage = 255;
4634 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4638 fill[0].x = outline[0].x + outline[0].len - 1;
4639 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4640 fill[0].y = outline[1].y;
4641 fill[0].coverage = 255;
4644 fill[1].x = outline[2].x + outline[2].len - 1;
4645 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4646 fill[1].y = outline[3].y;
4647 fill[1].coverage = 255;
4649 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4650 n = qt_intersect_spans(fill, n, clip);
4652 brush_func(n, fill, brush_data);
4655 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4656 n = qt_intersect_spans(outline, n, clip);
4658 pen_func(n, outline, pen_data);
4664 Draws an ellipse using the integer point midpoint algorithm.
4666 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4667 ProcessSpans pen_func, ProcessSpans brush_func,
4668 QSpanData *pen_data, QSpanData *brush_data)
4670 const qreal a = qreal(rect.width()) / 2;
4671 const qreal b = qreal(rect.height()) / 2;
4672 qreal d = b*b - (a*a*b) + 0.25*a*a;
4675 int y = (rect.height() + 1) / 2;
4679 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4680 if (d < 0) { // select E
4683 } else { // select SE
4684 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4685 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4686 pen_func, brush_func, pen_data, brush_data);
4691 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4692 pen_func, brush_func, pen_data, brush_data);
4695 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4696 const int miny = rect.height() & 0x1;
4698 if (d < 0) { // select SE
4699 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4701 } else { // select S
4702 d += a*a*(-2*y + 3);
4705 drawEllipsePoints(x, y, 1, rect, clip,
4706 pen_func, brush_func, pen_data, brush_data);
4711 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4714 Draws the first \a pointCount points in the buffer \a points
4716 The default implementation converts the first \a pointCount QPoints in \a points
4717 to QPointFs and calls the floating point version of drawPoints.
4721 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4724 Reimplement this function to draw the largest ellipse that can be
4725 contained within rectangle \a rect.
4728 #ifdef QT_DEBUG_DRAW
4729 void dumpClip(int width, int height, const QClipData *clip)
4731 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4732 clipImg.fill(0xffff0000);
4739 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4741 for (int i = 0; i < clip->count; ++i) {
4742 const QSpan *span = ((QClipData *) clip)->spans() + i;
4743 for (int j = 0; j < span->len; ++j)
4744 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4745 x0 = qMin(x0, int(span->x));
4746 x1 = qMax(x1, int(span->x + span->len - 1));
4748 y0 = qMin(y0, int(span->y));
4749 y1 = qMax(y1, int(span->y));
4752 static int counter = 0;
4759 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4760 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));