1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** 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"
77 # include <qvarlengtharray.h>
78 # include <private/qfontengine_p.h>
79 # include <qt_windows.h>
87 class QRectVectorPath : public QVectorPath {
89 inline void set(const QRect &r) {
91 qreal right = r.x() + r.width();
93 qreal bottom = r.y() + r.height();
104 inline void set(const QRectF &r) {
106 qreal right = r.x() + r.width();
108 qreal bottom = r.y() + r.height();
118 inline QRectVectorPath(const QRect &r)
119 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
123 inline QRectVectorPath(const QRectF &r)
124 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
128 inline QRectVectorPath()
129 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
135 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
137 #define qreal_to_fixed_26_6(f) (int(f * 64))
138 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
139 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
141 // #define QT_DEBUG_DRAW
143 void dumpClip(int width, int height, const QClipData *clip);
146 #define QT_FAST_SPANS
149 // A little helper macro to get a better approximation of dimensions.
150 // If we have a rect that starting at 0.5 of width 3.5 it should span
152 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
154 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
158 static inline bool winClearTypeFontsEnabled()
161 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
162 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
163 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
165 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
166 return result == FE_FONTSMOOTHINGCLEARTYPE;
169 bool QRasterPaintEngine::clearTypeFontsEnabled()
171 static const bool result = winClearTypeFontsEnabled();
179 /********************************************************************************
182 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
183 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
184 static void qt_span_clip(int count, const QSpan *spans, void *userData);
190 Qt::ClipOperation operation;
196 LineDrawIncludeLastPixel
199 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
200 ProcessSpans pen_func, ProcessSpans brush_func,
201 QSpanData *pen_data, QSpanData *brush_data);
203 struct QRasterFloatPoint {
209 static const QRectF boundingRect(const QPointF *points, int pointCount)
211 const QPointF *e = points;
212 const QPointF *last = points + pointCount;
213 qreal minx, maxx, miny, maxy;
214 minx = maxx = e->x();
215 miny = maxy = e->y();
219 else if (e->x() > maxx)
223 else if (e->y() > maxy)
226 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
230 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
231 return (elementCount == 5 // 5-point polygon, check for closed rect
232 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
233 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
234 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
235 && pts[0] < pts[4] && pts[1] < pts[5]
237 (elementCount == 4 // 4-point polygon, check for unclosed rect
238 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
239 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
240 && pts[0] < pts[4] && pts[1] < pts[5]
245 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
247 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
250 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
252 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
255 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
256 qfixed c2x, qfixed c2y,
257 qfixed ex, qfixed ey,
260 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
261 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
262 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
266 #if !defined(QT_NO_DEBUG) && 0
267 static void qt_debug_path(const QPainterPath &path)
269 const char *names[] = {
276 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
277 for (int i=0; i<path.elementCount(); ++i) {
278 const QPainterPath::Element &e = path.elementAt(i);
279 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
280 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
285 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
286 QPaintEngineExPrivate(),
293 \class QRasterPaintEngine
298 \brief The QRasterPaintEngine class enables hardware acceleration
299 of painting operations in Qt for Embedded Linux.
301 Note that this functionality is only available in
302 \l{Qt for Embedded Linux}.
304 In \l{Qt for Embedded Linux}, painting is a pure software
305 implementation. But starting with Qt 4.2, it is
306 possible to add an accelerated graphics driver to take advantage
307 of available hardware resources.
309 Hardware acceleration is accomplished by creating a custom screen
310 driver, accelerating the copying from memory to the screen, and
311 implementing a custom paint engine accelerating the various
312 painting operations. Then a custom paint device (derived from the
313 QCustomRasterPaintDevice class) and a custom window surface
314 (derived from QWSWindowSurface) must be implemented to make
315 \l{Qt for Embedded Linux} aware of the accelerated driver.
317 \note The QRasterPaintEngine class does not support 8-bit images.
318 Instead, they need to be converted to a supported format, such as
319 QImage::Format_ARGB32_Premultiplied.
321 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
322 documentation for details.
324 \sa QCustomRasterPaintDevice, QPaintEngine
328 \fn Type QRasterPaintEngine::type() const
334 \relates QRasterPaintEngine
336 A struct equivalent to QT_FT_Span, containing a position (x,
337 y), the span's length in pixels and its color/coverage (a value
338 ranging from 0 to 255).
344 Creates a raster based paint engine for operating on the given
345 \a device, with the complete set of \l
346 {QPaintEngine::PaintEngineFeature}{paint engine features and
349 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
350 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
352 d_func()->device = device;
359 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
362 d_func()->device = device;
366 void QRasterPaintEngine::init()
368 Q_D(QRasterPaintEngine);
375 // The antialiasing raster.
376 d->grayRaster.reset(new QT_FT_Raster);
377 Q_CHECK_PTR(d->grayRaster.data());
378 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
379 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
382 d->rasterizer.reset(new QRasterizer);
383 d->rasterBuffer.reset(new QRasterBuffer());
384 d->outlineMapper.reset(new QOutlineMapper);
385 d->outlinemapper_xform_dirty = true;
387 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
388 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
389 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
391 d->baseClip.reset(new QClipData(d->device->height()));
392 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
394 d->image_filler.init(d->rasterBuffer.data(), this);
395 d->image_filler.type = QSpanData::Texture;
397 d->image_filler_xform.init(d->rasterBuffer.data(), this);
398 d->image_filler_xform.type = QSpanData::Texture;
400 d->solid_color_filler.init(d->rasterBuffer.data(), this);
401 d->solid_color_filler.type = QSpanData::Solid;
403 d->deviceDepth = d->device->depth();
405 d->mono_surface = false;
406 gccaps &= ~PorterDuff;
408 QImage::Format format = QImage::Format_Invalid;
410 switch (d->device->devType()) {
411 case QInternal::Pixmap:
412 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
414 case QInternal::Image:
415 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
418 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
424 case QImage::Format_MonoLSB:
425 case QImage::Format_Mono:
426 d->mono_surface = true;
428 case QImage::Format_ARGB8565_Premultiplied:
429 case QImage::Format_ARGB8555_Premultiplied:
430 case QImage::Format_ARGB6666_Premultiplied:
431 case QImage::Format_ARGB4444_Premultiplied:
432 case QImage::Format_ARGB32_Premultiplied:
433 case QImage::Format_ARGB32:
434 gccaps |= PorterDuff;
436 case QImage::Format_RGB32:
437 case QImage::Format_RGB444:
438 case QImage::Format_RGB555:
439 case QImage::Format_RGB666:
440 case QImage::Format_RGB888:
441 case QImage::Format_RGB16:
452 Destroys this paint engine.
454 QRasterPaintEngine::~QRasterPaintEngine()
456 Q_D(QRasterPaintEngine);
458 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
464 bool QRasterPaintEngine::begin(QPaintDevice *device)
466 Q_D(QRasterPaintEngine);
468 if (device->devType() == QInternal::Pixmap) {
469 QPixmap *pixmap = static_cast<QPixmap *>(device);
470 QPlatformPixmap *pd = pixmap->handle();
471 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
472 d->device = pd->buffer();
477 // Make sure QPaintEngine::paintDevice() returns the proper device.
480 Q_ASSERT(d->device->devType() == QInternal::Image
481 || d->device->devType() == QInternal::CustomRaster);
483 d->systemStateChanged();
485 QRasterPaintEngineState *s = state();
486 ensureOutlineMapper();
487 d->outlineMapper->m_clip_rect = d->deviceRect;
489 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
490 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
491 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
492 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
494 d->rasterizer->setClipRect(d->deviceRect);
496 s->penData.init(d->rasterBuffer.data(), this);
497 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
498 s->stroker = &d->basicStroker;
499 d->basicStroker.setClipRect(d->deviceRect);
501 s->brushData.init(d->rasterBuffer.data(), this);
502 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
504 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
506 setDirty(DirtyBrushOrigin);
509 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
510 << ") devType:" << device->devType()
511 << "devRect:" << d->deviceRect;
513 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
518 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
519 #if defined(Q_OS_WIN)
520 else if (clearTypeFontsEnabled())
525 QImage::Format format = static_cast<QImage *>(d->device)->format();
526 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
527 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
529 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
531 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
540 bool QRasterPaintEngine::end()
543 Q_D(QRasterPaintEngine);
544 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
546 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
556 void QRasterPaintEngine::releaseBuffer()
558 Q_D(QRasterPaintEngine);
559 d->rasterBuffer.reset(new QRasterBuffer);
565 QSize QRasterPaintEngine::size() const
567 Q_D(const QRasterPaintEngine);
568 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
575 void QRasterPaintEngine::saveBuffer(const QString &s) const
577 Q_D(const QRasterPaintEngine);
578 d->rasterBuffer->bufferImage().save(s, "PNG");
585 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
587 QRasterPaintEngineState *s = state();
588 // FALCON: get rid of this line, see drawImage call below.
590 QTransform::TransformationType txop = s->matrix.type();
594 case QTransform::TxNone:
595 s->flags.int_xform = true;
598 case QTransform::TxTranslate:
599 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
600 && qreal(int(s->matrix.dy())) == s->matrix.dy();
603 case QTransform::TxScale:
604 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
605 && qreal(int(s->matrix.dy())) == s->matrix.dy()
606 && qreal(int(s->matrix.m11())) == s->matrix.m11()
607 && qreal(int(s->matrix.m22())) == s->matrix.m22();
610 default: // shear / perspective...
611 s->flags.int_xform = false;
615 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
617 ensureOutlineMapper();
622 QRasterPaintEngineState::~QRasterPaintEngineState()
624 if (flags.has_clip_ownership)
629 QRasterPaintEngineState::QRasterPaintEngineState()
641 flags.fast_pen = true;
642 flags.antialiased = false;
643 flags.bilinear = false;
644 flags.fast_text = true;
645 flags.int_xform = true;
646 flags.tx_noshear = true;
647 flags.fast_images = true;
650 flags.has_clip_ownership = false;
655 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
660 , strokeFlags(s.strokeFlags)
661 , lastBrush(s.lastBrush)
662 , brushData(s.brushData)
663 , fillFlags(s.fillFlags)
664 , pixmapFlags(s.pixmapFlags)
665 , intOpacity(s.intOpacity)
669 , flag_bits(s.flag_bits)
671 brushData.tempImage = 0;
672 penData.tempImage = 0;
673 flags.has_clip_ownership = false;
679 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
681 QRasterPaintEngineState *s;
683 s = new QRasterPaintEngineState();
685 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
693 void QRasterPaintEngine::setState(QPainterState *s)
695 Q_D(QRasterPaintEngine);
696 QPaintEngineEx::setState(s);
697 d->rasterBuffer->compositionMode = s->composition_mode;
701 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
706 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
713 void QRasterPaintEngine::penChanged()
716 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
718 QRasterPaintEngineState *s = state();
719 s->strokeFlags |= DirtyPen;
720 s->dirty |= DirtyPen;
726 void QRasterPaintEngine::updatePen(const QPen &pen)
728 Q_D(QRasterPaintEngine);
729 QRasterPaintEngineState *s = state();
731 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
734 Qt::PenStyle pen_style = qpen_style(pen);
739 s->penData.clip = d->clip();
740 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
742 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
743 || pen.brush().transform().type() >= QTransform::TxNone) {
744 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
747 // Slightly ugly handling of an uncommon case... We need to change
748 // the pen because it is reused in draw_midpoint to decide dashed
750 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
751 pen_style = Qt::SolidLine;
752 s->lastPen.setStyle(Qt::SolidLine);
755 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
756 d->basicStroker.setCapStyle(qpen_capStyle(pen));
757 d->basicStroker.setMiterLimit(pen.miterLimit());
759 qreal penWidth = qpen_widthf(pen);
761 d->basicStroker.setStrokeWidth(1);
763 d->basicStroker.setStrokeWidth(penWidth);
765 if(pen_style == Qt::SolidLine) {
766 s->stroker = &d->basicStroker;
767 } else if (pen_style != Qt::NoPen) {
769 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
770 if (pen.isCosmetic()) {
771 d->dashStroker->setClipRect(d->deviceRect);
773 // ### I've seen this inverted devrect multiple places now...
774 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
775 d->dashStroker->setClipRect(clipRect);
777 d->dashStroker->setDashPattern(pen.dashPattern());
778 d->dashStroker->setDashOffset(pen.dashOffset());
779 s->stroker = d->dashStroker.data();
784 ensureRasterState(); // needed because of tx_noshear...
785 s->flags.fast_pen = pen_style > Qt::NoPen
787 && ((pen.isCosmetic() && penWidth <= 1)
788 || (!pen.isCosmetic() && s->flags.tx_noshear && penWidth * s->txscale <= 1));
790 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
800 void QRasterPaintEngine::brushOriginChanged()
802 QRasterPaintEngineState *s = state();
804 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
807 s->fillFlags |= DirtyBrushOrigin;
814 void QRasterPaintEngine::brushChanged()
816 QRasterPaintEngineState *s = state();
818 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
820 s->fillFlags |= DirtyBrush;
829 void QRasterPaintEngine::updateBrush(const QBrush &brush)
832 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
834 Q_D(QRasterPaintEngine);
835 QRasterPaintEngineState *s = state();
836 // must set clip prior to setup, as setup uses it...
837 s->brushData.clip = d->clip();
838 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
839 if (s->fillFlags & DirtyTransform
840 || brush.transform().type() >= QTransform::TxNone)
841 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
842 s->lastBrush = brush;
846 void QRasterPaintEngine::updateOutlineMapper()
848 Q_D(QRasterPaintEngine);
849 d->outlineMapper->setMatrix(state()->matrix);
852 void QRasterPaintEngine::updateRasterState()
854 QRasterPaintEngineState *s = state();
856 if (s->dirty & DirtyTransform)
857 updateMatrix(s->matrix);
859 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
860 const QPainter::CompositionMode mode = s->composition_mode;
861 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
862 && s->intOpacity == 256
863 && (mode == QPainter::CompositionMode_Source
864 || (mode == QPainter::CompositionMode_SourceOver
865 && qAlpha(s->penData.solid.color) == 255));
875 void QRasterPaintEngine::opacityChanged()
877 QRasterPaintEngineState *s = state();
880 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
883 s->fillFlags |= DirtyOpacity;
884 s->strokeFlags |= DirtyOpacity;
885 s->pixmapFlags |= DirtyOpacity;
886 s->dirty |= DirtyOpacity;
887 s->intOpacity = (int) (s->opacity * 256);
893 void QRasterPaintEngine::compositionModeChanged()
895 Q_D(QRasterPaintEngine);
896 QRasterPaintEngineState *s = state();
899 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
902 s->fillFlags |= DirtyCompositionMode;
903 s->dirty |= DirtyCompositionMode;
905 s->strokeFlags |= DirtyCompositionMode;
906 d->rasterBuffer->compositionMode = s->composition_mode;
908 d->recalculateFastImages();
914 void QRasterPaintEngine::renderHintsChanged()
916 QRasterPaintEngineState *s = state();
919 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
922 bool was_aa = s->flags.antialiased;
923 bool was_bilinear = s->flags.bilinear;
925 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
926 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
928 if (was_aa != s->flags.antialiased)
929 s->strokeFlags |= DirtyHints;
931 if (was_bilinear != s->flags.bilinear) {
932 s->strokeFlags |= DirtyPen;
933 s->fillFlags |= DirtyBrush;
936 Q_D(QRasterPaintEngine);
937 d->recalculateFastImages();
943 void QRasterPaintEngine::transformChanged()
945 QRasterPaintEngineState *s = state();
948 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
951 s->fillFlags |= DirtyTransform;
952 s->strokeFlags |= DirtyTransform;
954 s->dirty |= DirtyTransform;
956 Q_D(QRasterPaintEngine);
957 d->recalculateFastImages();
963 void QRasterPaintEngine::clipEnabledChanged()
965 QRasterPaintEngineState *s = state();
968 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
972 s->clip->enabled = s->clipEnabled;
973 s->fillFlags |= DirtyClipEnabled;
974 s->strokeFlags |= DirtyClipEnabled;
975 s->pixmapFlags |= DirtyClipEnabled;
979 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
981 SrcOverBlendFunc func,
986 if (alpha == 0 || !clip.isValid())
989 Q_ASSERT(img.depth() >= 8);
991 int srcBPL = img.bytesPerLine();
992 const uchar *srcBits = img.bits();
993 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
994 int iw = img.width();
995 int ih = img.height();
1000 // Adjust the image according to the source offset...
1001 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1004 // adapt the x parameters
1005 int x = qRound(pt.x());
1007 int cx2 = clip.x() + clip.width();
1010 srcBits += srcSize * d;
1015 int d = x + iw - cx2;
1021 // adapt the y paremeters...
1023 int cy2 = clip.y() + clip.height();
1024 int y = qRound(pt.y());
1027 srcBits += srcBPL * d;
1032 int d = y + ih - cy2;
1038 // call the blend function...
1039 int dstSize = rasterBuffer->bytesPerPixel();
1040 int dstBPL = rasterBuffer->bytesPerLine();
1041 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1048 void QRasterPaintEnginePrivate::systemStateChanged()
1050 QRect clipRect(0, 0,
1051 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1052 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1054 if (!systemClip.isEmpty()) {
1055 QRegion clippedDeviceRgn = systemClip & clipRect;
1056 deviceRect = clippedDeviceRgn.boundingRect();
1057 baseClip->setClipRegion(clippedDeviceRgn);
1059 deviceRect = clipRect;
1060 baseClip->setClipRect(deviceRect);
1062 #ifdef QT_DEBUG_DRAW
1063 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1066 exDeviceRect = deviceRect;
1068 Q_Q(QRasterPaintEngine);
1069 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1070 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1071 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1074 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1076 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1079 Q_Q(QRasterPaintEngine);
1080 bool bilinear = q->state()->flags.bilinear;
1082 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1083 spanData->setupMatrix(b.transform() * m, bilinear);
1085 if (m.type() <= QTransform::TxTranslate) {
1086 // specialize setupMatrix for translation matrices
1087 // to avoid needless matrix inversion
1095 spanData->dx = -m.dx();
1096 spanData->dy = -m.dy();
1097 spanData->txop = m.type();
1098 spanData->bilinear = bilinear;
1099 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1100 spanData->adjustSpanMethods();
1102 spanData->setupMatrix(m, bilinear);
1107 // #define QT_CLIPPING_RATIOS
1109 #ifdef QT_CLIPPING_RATIOS
1114 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1116 if (d->clip()->hasRectClip)
1118 if (d->clip()->hasRegionClip)
1122 if ((totalClips % 5000) == 0) {
1123 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1124 rectClips * 100.0 / (qreal) totalClips,
1125 regionClips * 100.0 / (qreal) totalClips,
1126 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1135 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1137 if (s->flags.has_clip_ownership)
1140 s->flags.has_clip_ownership = false;
1143 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1145 s->fillFlags |= QPaintEngine::DirtyClipPath;
1146 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1147 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1149 d->solid_color_filler.clip = d->clip();
1150 d->solid_color_filler.adjustSpanMethods();
1152 #ifdef QT_DEBUG_DRAW
1153 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1162 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1164 #ifdef QT_DEBUG_DRAW
1165 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1167 if (path.elements()) {
1168 for (int i=0; i<path.elementCount(); ++i) {
1169 qDebug() << " - " << path.elements()[i]
1170 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1173 for (int i=0; i<path.elementCount(); ++i) {
1174 qDebug() << " ---- "
1175 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1180 Q_D(QRasterPaintEngine);
1181 QRasterPaintEngineState *s = state();
1183 const qreal *points = path.points();
1184 const QPainterPath::ElementType *types = path.elements();
1186 // There are some cases that are not supported by clip(QRect)
1187 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1188 if (s->matrix.type() <= QTransform::TxScale
1189 && ((path.shape() == QVectorPath::RectangleHint)
1190 || (isRect(points, path.elementCount())
1191 && (!types || (types[0] == QPainterPath::MoveToElement
1192 && types[1] == QPainterPath::LineToElement
1193 && types[2] == QPainterPath::LineToElement
1194 && types[3] == QPainterPath::LineToElement))))) {
1195 #ifdef QT_DEBUG_DRAW
1196 qDebug() << " --- optimizing vector clip to rect clip...";
1199 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1200 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1205 if (op == Qt::NoClip) {
1206 qrasterpaintengine_state_setNoClip(s);
1209 QClipData *base = d->baseClip.data();
1211 // Intersect with current clip when available...
1212 if (op == Qt::IntersectClip && s->clip)
1215 // We always intersect, except when there is nothing to
1216 // intersect with, in which case we simplify the operation to
1218 Qt::ClipOperation isectOp = Qt::IntersectClip;
1220 isectOp = Qt::ReplaceClip;
1222 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1223 newClip->initialize();
1224 ClipData clipData = { base, newClip, isectOp };
1225 ensureOutlineMapper();
1226 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1230 if (s->flags.has_clip_ownership)
1234 s->flags.has_clip_ownership = true;
1236 qrasterpaintengine_dirty_clip(d, s);
1244 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1246 #ifdef QT_DEBUG_DRAW
1247 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1250 QRasterPaintEngineState *s = state();
1252 if (op == Qt::NoClip) {
1253 qrasterpaintengine_state_setNoClip(s);
1255 } else if (s->matrix.type() > QTransform::TxScale) {
1256 QPaintEngineEx::clip(rect, op);
1259 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1260 QPaintEngineEx::clip(rect, op);
1266 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1268 Q_D(QRasterPaintEngine);
1269 QRect clipRect = r & d->deviceRect;
1270 QRasterPaintEngineState *s = state();
1272 if (op == Qt::ReplaceClip || s->clip == 0) {
1274 // No current clip, hence we intersect with sysclip and be
1276 QRegion clipRegion = systemClip();
1277 QClipData *clip = new QClipData(d->rasterBuffer->height());
1279 if (clipRegion.isEmpty())
1280 clip->setClipRect(clipRect);
1282 clip->setClipRegion(clipRegion & clipRect);
1284 if (s->flags.has_clip_ownership)
1288 s->clip->enabled = true;
1289 s->flags.has_clip_ownership = true;
1291 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1292 QClipData *base = s->clip;
1295 if (base->hasRectClip || base->hasRegionClip) {
1296 if (!s->flags.has_clip_ownership) {
1297 s->clip = new QClipData(d->rasterBuffer->height());
1298 s->flags.has_clip_ownership = true;
1300 if (base->hasRectClip)
1301 s->clip->setClipRect(base->clipRect & clipRect);
1303 s->clip->setClipRegion(base->clipRegion & clipRect);
1304 s->clip->enabled = true;
1312 qrasterpaintengine_dirty_clip(d, s);
1320 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1322 #ifdef QT_DEBUG_DRAW
1323 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1326 Q_D(QRasterPaintEngine);
1328 if (region.rectCount() == 1) {
1329 clip(region.boundingRect(), op);
1333 QRasterPaintEngineState *s = state();
1334 const QClipData *clip = d->clip();
1335 const QClipData *baseClip = d->baseClip.data();
1337 if (op == Qt::NoClip) {
1338 qrasterpaintengine_state_setNoClip(s);
1339 } else if (s->matrix.type() > QTransform::TxScale
1340 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1341 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1342 QPaintEngineEx::clip(region, op);
1344 const QClipData *curClip;
1347 if (op == Qt::IntersectClip)
1352 if (s->flags.has_clip_ownership) {
1356 newClip = new QClipData(d->rasterBuffer->height());
1358 s->flags.has_clip_ownership = true;
1361 QRegion r = s->matrix.map(region);
1362 if (curClip->hasRectClip)
1363 newClip->setClipRegion(r & curClip->clipRect);
1364 else if (curClip->hasRegionClip)
1365 newClip->setClipRegion(r & curClip->clipRegion);
1367 qrasterpaintengine_dirty_clip(d, s);
1374 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1376 #ifdef QT_DEBUG_DRAW
1377 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1380 if (!fillData->blend)
1383 Q_D(QRasterPaintEngine);
1385 const QRectF controlPointRect = path.controlPointRect();
1387 QRasterPaintEngineState *s = state();
1388 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1389 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1390 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1391 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1392 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1393 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1395 if (!s->flags.antialiased && !do_clip) {
1396 d->initializeRasterizer(fillData);
1397 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1401 ensureOutlineMapper();
1402 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1405 static void fillRect_normalized(const QRect &r, QSpanData *data,
1406 QRasterPaintEnginePrivate *pe)
1410 bool rectClipped = true;
1413 x1 = qMax(r.x(), data->clip->xmin);
1414 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1415 y1 = qMax(r.y(), data->clip->ymin);
1416 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1417 rectClipped = data->clip->hasRectClip;
1420 x1 = qMax(r.x(), pe->deviceRect.x());
1421 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1422 y1 = qMax(r.y(), pe->deviceRect.y());
1423 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1425 x1 = qMax(r.x(), 0);
1426 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1427 y1 = qMax(r.y(), 0);
1428 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1431 if (x2 <= x1 || y2 <= y1)
1434 const int width = x2 - x1;
1435 const int height = y2 - y1;
1437 bool isUnclipped = rectClipped
1438 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1440 if (pe && isUnclipped) {
1441 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1443 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1444 || (mode == QPainter::CompositionMode_SourceOver
1445 && qAlpha(data->solid.color) == 255)))
1447 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1453 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1455 const int nspans = 256;
1456 QT_FT_Span spans[nspans];
1458 Q_ASSERT(data->blend);
1461 int n = qMin(nspans, y2 - y);
1465 spans[i].len = width;
1467 spans[i].coverage = 255;
1471 blend(n, spans, data);
1479 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1481 #ifdef QT_DEBUG_DRAW
1482 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1484 Q_D(QRasterPaintEngine);
1485 ensureRasterState();
1486 QRasterPaintEngineState *s = state();
1490 if (s->brushData.blend) {
1491 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1492 const QRect *r = rects;
1493 const QRect *lastRect = rects + rectCount;
1495 int offset_x = int(s->matrix.dx());
1496 int offset_y = int(s->matrix.dy());
1497 while (r < lastRect) {
1498 QRect rect = r->normalized();
1499 QRect rr = rect.translated(offset_x, offset_y);
1500 fillRect_normalized(rr, &s->brushData, d);
1504 QRectVectorPath path;
1505 for (int i=0; i<rectCount; ++i) {
1507 fill(path, s->brush);
1513 if (s->penData.blend) {
1514 QRectVectorPath path;
1515 if (s->flags.fast_pen) {
1516 QCosmeticStroker stroker(s, d->deviceRect);
1517 for (int i = 0; i < rectCount; ++i) {
1519 stroker.drawPath(path);
1522 for (int i = 0; i < rectCount; ++i) {
1524 stroke(path, s->pen);
1533 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1535 #ifdef QT_DEBUG_DRAW
1536 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1538 #ifdef QT_FAST_SPANS
1539 Q_D(QRasterPaintEngine);
1540 ensureRasterState();
1541 QRasterPaintEngineState *s = state();
1544 if (s->flags.tx_noshear) {
1546 if (s->brushData.blend) {
1547 d->initializeRasterizer(&s->brushData);
1548 for (int i = 0; i < rectCount; ++i) {
1549 const QRectF &rect = rects[i].normalized();
1552 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1553 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1554 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1559 if (s->penData.blend) {
1560 QRectVectorPath path;
1561 if (s->flags.fast_pen) {
1562 QCosmeticStroker stroker(s, d->deviceRect);
1563 for (int i = 0; i < rectCount; ++i) {
1565 stroker.drawPath(path);
1568 for (int i = 0; i < rectCount; ++i) {
1570 QPaintEngineEx::stroke(path, s->lastPen);
1577 #endif // QT_FAST_SPANS
1578 QPaintEngineEx::drawRects(rects, rectCount);
1585 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1587 Q_D(QRasterPaintEngine);
1588 QRasterPaintEngineState *s = state();
1591 if (!s->penData.blend)
1594 if (s->flags.fast_pen) {
1595 QCosmeticStroker stroker(s, d->deviceRect);
1596 stroker.drawPath(path);
1597 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1598 qreal width = s->lastPen.isCosmetic()
1599 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1600 : qpen_widthf(s->lastPen) * s->txscale;
1602 qreal dashOffset = s->lastPen.dashOffset();
1604 qreal patternLength = 0;
1605 const QVector<qreal> pattern = s->lastPen.dashPattern();
1606 for (int i = 0; i < pattern.size(); ++i)
1607 patternLength += pattern.at(i);
1609 if (patternLength > 0) {
1610 int n = qFloor(dashOffset / patternLength);
1611 dashOffset -= n * patternLength;
1612 while (dashOffset >= pattern.at(dashIndex)) {
1613 dashOffset -= pattern.at(dashIndex);
1614 if (++dashIndex >= pattern.size())
1620 Q_D(QRasterPaintEngine);
1621 d->initializeRasterizer(&s->penData);
1622 int lineCount = path.elementCount() / 2;
1623 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1625 for (int i = 0; i < lineCount; ++i) {
1626 if (lines[i].p1() == lines[i].p2()) {
1627 if (s->lastPen.capStyle() != Qt::FlatCap) {
1628 QPointF p = lines[i].p1();
1629 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1630 QPointF(p.x() + width*0.5, p.y())));
1631 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1636 const QLineF line = s->matrix.map(lines[i]);
1637 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1638 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1639 width / line.length(),
1640 s->lastPen.capStyle() == Qt::SquareCap);
1642 d->rasterizeLine_dashed(line, width,
1643 &dashIndex, &dashOffset, &inDash);
1648 QPaintEngineEx::stroke(path, pen);
1651 static inline QRect toNormalizedFillRect(const QRectF &rect)
1653 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1654 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1655 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1656 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1663 return QRect(x1, y1, x2 - x1, y2 - y1);
1669 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1673 #ifdef QT_DEBUG_DRAW
1674 QRectF rf = path.controlPointRect();
1675 qDebug() << "QRasterPaintEngine::fill(): "
1676 << "size=" << path.elementCount()
1677 << ", hints=" << hex << path.hints()
1681 Q_D(QRasterPaintEngine);
1682 QRasterPaintEngineState *s = state();
1685 if (!s->brushData.blend)
1688 if (path.shape() == QVectorPath::RectangleHint) {
1689 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1690 const qreal *p = path.points();
1691 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1692 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1693 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1696 ensureRasterState();
1697 if (s->flags.tx_noshear) {
1698 d->initializeRasterizer(&s->brushData);
1699 // ### Is normalizing really necessary here?
1700 const qreal *p = path.points();
1701 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1703 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1704 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1705 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1711 // ### Optimize for non transformed ellipses and rectangles...
1712 QRectF cpRect = path.controlPointRect();
1713 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1714 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1717 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1718 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1719 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1720 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1722 // ### Falonc: implement....
1723 // if (!s->flags.antialiased && !do_clip) {
1724 // d->initializeRasterizer(&s->brushData);
1725 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1729 ensureOutlineMapper();
1730 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1733 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1735 Q_D(QRasterPaintEngine);
1736 QRasterPaintEngineState *s = state();
1738 if (!s->flags.antialiased) {
1739 uint txop = s->matrix.type();
1740 if (txop == QTransform::TxNone) {
1741 fillRect_normalized(toNormalizedFillRect(r), data, d);
1743 } else if (txop == QTransform::TxTranslate) {
1744 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1745 fillRect_normalized(rr, data, d);
1747 } else if (txop == QTransform::TxScale) {
1748 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1749 fillRect_normalized(rr, data, d);
1753 ensureRasterState();
1754 if (s->flags.tx_noshear) {
1755 d->initializeRasterizer(data);
1756 QRectF nr = r.normalized();
1757 if (!nr.isEmpty()) {
1758 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1759 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1760 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1767 ensureOutlineMapper();
1768 fillPath(path, data);
1774 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1776 #ifdef QT_DEBUG_DRAW
1777 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1779 QRasterPaintEngineState *s = state();
1782 if (!s->brushData.blend)
1785 fillRect(r, &s->brushData);
1791 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1793 #ifdef QT_DEBUG_DRAW
1794 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1796 Q_D(QRasterPaintEngine);
1797 QRasterPaintEngineState *s = state();
1799 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1800 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1801 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1804 d->solid_color_filler.clip = d->clip();
1805 d->solid_color_filler.adjustSpanMethods();
1806 fillRect(r, &d->solid_color_filler);
1809 static inline bool isAbove(const QPointF *a, const QPointF *b)
1811 return a->y() < b->y();
1814 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1819 Q_ASSERT(pointCount >= 2);
1821 QVector<const QPointF *> sorted;
1822 sorted.reserve(pointCount);
1824 upper->reserve(pointCount * 3 / 4);
1825 lower->reserve(pointCount * 3 / 4);
1827 for (int i = 0; i < pointCount; ++i)
1828 sorted << points + i;
1830 qSort(sorted.begin(), sorted.end(), isAbove);
1832 qreal splitY = sorted.at(sorted.size() / 2)->y();
1834 const QPointF *end = points + pointCount;
1835 const QPointF *last = end - 1;
1837 QVector<QPointF> *bin[2] = { upper, lower };
1839 for (const QPointF *p = points; p < end; ++p) {
1840 int side = p->y() < splitY;
1841 int lastSide = last->y() < splitY;
1843 if (side != lastSide) {
1844 if (qFuzzyCompare(p->y(), splitY)) {
1845 bin[!side]->append(*p);
1846 } else if (qFuzzyCompare(last->y(), splitY)) {
1847 bin[side]->append(*last);
1849 QPointF delta = *p - *last;
1850 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1852 bin[0]->append(intersection);
1853 bin[1]->append(intersection);
1857 bin[side]->append(*p);
1862 // give up if we couldn't reduce the point count
1863 return upper->size() < pointCount && lower->size() < pointCount;
1869 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1871 Q_D(QRasterPaintEngine);
1872 QRasterPaintEngineState *s = state();
1874 const int maxPoints = 0xffff;
1876 // max amount of points that raster engine can reliably handle
1877 if (pointCount > maxPoints) {
1878 QVector<QPointF> upper, lower;
1880 if (splitPolygon(points, pointCount, &upper, &lower)) {
1881 fillPolygon(upper.constData(), upper.size(), mode);
1882 fillPolygon(lower.constData(), lower.size(), mode);
1884 qWarning("Polygon too complex for filling.");
1889 // Compose polygon fill..,
1890 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1891 ensureOutlineMapper();
1892 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1895 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1897 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1903 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1905 Q_D(QRasterPaintEngine);
1906 QRasterPaintEngineState *s = state();
1908 #ifdef QT_DEBUG_DRAW
1909 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1910 for (int i=0; i<pointCount; ++i)
1911 qDebug() << " - " << points[i];
1913 Q_ASSERT(pointCount >= 2);
1915 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1916 QRectF r(points[0], points[2]);
1922 if (mode != PolylineMode) {
1925 if (s->brushData.blend) {
1926 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1927 fillPolygon(points, pointCount, mode);
1928 d->outlineMapper->setCoordinateRounding(false);
1932 // Do the outline...
1933 if (s->penData.blend) {
1934 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1935 if (s->flags.fast_pen) {
1936 QCosmeticStroker stroker(s, d->deviceRect);
1937 stroker.drawPath(vp);
1939 QPaintEngineEx::stroke(vp, s->lastPen);
1947 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1949 Q_D(QRasterPaintEngine);
1950 QRasterPaintEngineState *s = state();
1952 #ifdef QT_DEBUG_DRAW
1953 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1954 for (int i=0; i<pointCount; ++i)
1955 qDebug() << " - " << points[i];
1957 Q_ASSERT(pointCount >= 2);
1958 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1959 QRect r(points[0].x(),
1961 points[2].x() - points[0].x(),
1962 points[2].y() - points[0].y());
1970 if (mode != PolylineMode) {
1972 if (s->brushData.blend) {
1973 // Compose polygon fill..,
1974 ensureOutlineMapper();
1975 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1976 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1977 d->outlineMapper->moveTo(*points);
1978 const QPoint *p = points;
1979 const QPoint *ep = points + pointCount - 1;
1981 d->outlineMapper->lineTo(*(++p));
1983 d->outlineMapper->endOutline();
1986 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1988 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1989 d->outlineMapper->setCoordinateRounding(false);
1993 // Do the outline...
1994 if (s->penData.blend) {
1995 int count = pointCount * 2;
1996 QVarLengthArray<qreal> fpoints(count);
1997 for (int i=0; i<count; ++i)
1998 fpoints[i] = ((int *) points)[i];
1999 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2001 if (s->flags.fast_pen) {
2002 QCosmeticStroker stroker(s, d->deviceRect);
2003 stroker.drawPath(vp);
2005 QPaintEngineEx::stroke(vp, s->lastPen);
2013 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2015 #ifdef QT_DEBUG_DRAW
2016 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2019 QPlatformPixmap *pd = pixmap.handle();
2020 if (pd->classId() == QPlatformPixmap::RasterClass) {
2021 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2022 if (image.depth() == 1) {
2023 Q_D(QRasterPaintEngine);
2024 QRasterPaintEngineState *s = state();
2025 if (s->matrix.type() <= QTransform::TxTranslate) {
2027 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2029 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2032 QRasterPaintEngine::drawImage(pos, image);
2035 const QImage image = pixmap.toImage();
2036 if (pixmap.depth() == 1) {
2037 Q_D(QRasterPaintEngine);
2038 QRasterPaintEngineState *s = state();
2039 if (s->matrix.type() <= QTransform::TxTranslate) {
2041 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2043 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2046 QRasterPaintEngine::drawImage(pos, image);
2054 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2056 #ifdef QT_DEBUG_DRAW
2057 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2060 QPlatformPixmap* pd = pixmap.handle();
2061 if (pd->classId() == QPlatformPixmap::RasterClass) {
2062 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2063 if (image.depth() == 1) {
2064 Q_D(QRasterPaintEngine);
2065 QRasterPaintEngineState *s = state();
2066 if (s->matrix.type() <= QTransform::TxTranslate
2067 && r.size() == sr.size()
2068 && r.size() == pixmap.size()) {
2070 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2073 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2076 drawImage(r, image, sr);
2079 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2080 const QImage image = pd->toImage(clippedSource);
2081 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2082 if (image.depth() == 1) {
2083 Q_D(QRasterPaintEngine);
2084 QRasterPaintEngineState *s = state();
2085 if (s->matrix.type() <= QTransform::TxTranslate
2086 && r.size() == sr.size()
2087 && r.size() == pixmap.size()) {
2089 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2092 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2095 drawImage(r, image, translatedSource);
2100 static inline int fast_ceil_positive(const qreal &v)
2102 const int iv = int(v);
2109 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2111 const int xmin = int(rect.x());
2112 const int xmax = int(fast_ceil_positive(rect.right()));
2113 const int ymin = int(rect.y());
2114 const int ymax = int(fast_ceil_positive(rect.bottom()));
2115 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2121 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2123 #ifdef QT_DEBUG_DRAW
2124 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2127 Q_D(QRasterPaintEngine);
2128 QRasterPaintEngineState *s = state();
2130 if (s->matrix.type() > QTransform::TxTranslate) {
2131 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2133 QRectF(0, 0, img.width(), img.height()));
2136 const QClipData *clip = d->clip();
2137 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2139 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2140 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2143 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2145 } else if (clip->hasRectClip) {
2146 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2154 d->image_filler.clip = clip;
2155 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2156 if (!d->image_filler.blend)
2158 d->image_filler.dx = -pt.x();
2159 d->image_filler.dy = -pt.y();
2160 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2162 fillRect_normalized(rr, &d->image_filler, d);
2167 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2169 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2180 inline RotationType qRotationType(const QTransform &transform)
2182 QTransform::TransformationType type = transform.type();
2184 if (type > QTransform::TxRotate)
2187 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2188 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2191 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2192 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2195 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2196 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2202 inline bool isPixelAligned(const QRectF &rect) {
2203 return QRectF(rect.toRect()) == rect;
2210 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2211 Qt::ImageConversionFlags)
2213 #ifdef QT_DEBUG_DRAW
2214 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2220 Q_D(QRasterPaintEngine);
2221 QRasterPaintEngineState *s = state();
2222 int sr_l = qFloor(sr.left());
2223 int sr_r = qCeil(sr.right()) - 1;
2224 int sr_t = qFloor(sr.top());
2225 int sr_b = qCeil(sr.bottom()) - 1;
2227 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2228 // as fillRect will apply the aliased coordinate delta we need to
2229 // subtract it here as we don't use it for image drawing
2230 QTransform old = s->matrix;
2231 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2233 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2234 QRgb color = img.pixel(sr_l, sr_t);
2235 switch (img.format()) {
2236 case QImage::Format_ARGB32_Premultiplied:
2237 case QImage::Format_ARGB8565_Premultiplied:
2238 case QImage::Format_ARGB6666_Premultiplied:
2239 case QImage::Format_ARGB8555_Premultiplied:
2240 case QImage::Format_ARGB4444_Premultiplied:
2241 // Combine premultiplied color with the opacity set on the painter.
2242 d->solid_color_filler.solid.color =
2243 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2244 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2247 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2251 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2252 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2256 d->solid_color_filler.clip = d->clip();
2257 d->solid_color_filler.adjustSpanMethods();
2258 fillRect(r, &d->solid_color_filler);
2264 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2266 const QClipData *clip = d->clip();
2268 if (s->matrix.type() > QTransform::TxTranslate
2270 && (!clip || clip->hasRectClip)
2271 && s->intOpacity == 256
2272 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2273 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2274 && d->rasterBuffer->format == img.format()
2275 && (d->rasterBuffer->format == QImage::Format_RGB16
2276 || d->rasterBuffer->format == QImage::Format_RGB32
2277 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2278 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2280 RotationType rotationType = qRotationType(s->matrix);
2282 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2283 QRectF transformedTargetRect = s->matrix.mapRect(r);
2285 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2286 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2288 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2289 if (clippedTransformedTargetRect.isNull())
2292 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2294 QRect clippedSourceRect
2295 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2296 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2298 uint dbpl = d->rasterBuffer->bytesPerLine();
2299 uint sbpl = img.bytesPerLine();
2301 uchar *dst = d->rasterBuffer->buffer();
2302 uint bpp = img.depth() >> 3;
2304 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2305 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2307 uint cw = clippedSourceRect.width();
2308 uint ch = clippedSourceRect.height();
2310 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2317 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2319 QRectF targetBounds = s->matrix.mapRect(r);
2320 bool exceedsPrecision = targetBounds.width() > 0xffff
2321 || targetBounds.height() > 0xffff;
2323 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2324 if (s->matrix.type() > QTransform::TxScale) {
2325 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2326 if (func && (!clip || clip->hasRectClip)) {
2327 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2328 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2329 s->matrix, s->intOpacity);
2333 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2334 if (func && (!clip || clip->hasRectClip)) {
2335 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2336 img.bits(), img.bytesPerLine(),
2337 qt_mapRect_non_normalizing(r, s->matrix), sr,
2338 !clip ? d->deviceRect : clip->clipRect,
2345 QTransform copy = s->matrix;
2346 copy.translate(r.x(), r.y());
2348 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2349 copy.translate(-sr.x(), -sr.y());
2351 d->image_filler_xform.clip = clip;
2352 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2353 if (!d->image_filler_xform.blend)
2355 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2357 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2358 QRectF rr = s->matrix.mapRect(r);
2360 const int x1 = qRound(rr.x());
2361 const int y1 = qRound(rr.y());
2362 const int x2 = qRound(rr.right());
2363 const int y2 = qRound(rr.bottom());
2365 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2369 #ifdef QT_FAST_SPANS
2370 ensureRasterState();
2371 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2372 d->initializeRasterizer(&d->image_filler_xform);
2373 d->rasterizer->setAntialiased(s->flags.antialiased);
2375 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2377 const QRectF &rect = r.normalized();
2378 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2379 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2381 if (s->flags.tx_noshear)
2382 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2384 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2388 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2391 QTransform m = s->matrix;
2392 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2393 m.m21(), m.m22(), m.m23(),
2394 m.m31() - offs, m.m32() - offs, m.m33());
2395 fillPath(path, &d->image_filler_xform);
2398 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2399 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2401 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2403 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2405 } else if (clip->hasRectClip) {
2406 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2412 d->image_filler.clip = clip;
2413 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2414 if (!d->image_filler.blend)
2416 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2417 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2420 rr.translate(s->matrix.dx(), s->matrix.dy());
2422 const int x1 = qRound(rr.x());
2423 const int y1 = qRound(rr.y());
2424 const int x2 = qRound(rr.right());
2425 const int y2 = qRound(rr.bottom());
2427 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2434 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2436 #ifdef QT_DEBUG_DRAW
2437 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2439 Q_D(QRasterPaintEngine);
2440 QRasterPaintEngineState *s = state();
2444 QPlatformPixmap *pd = pixmap.handle();
2445 if (pd->classId() == QPlatformPixmap::RasterClass) {
2446 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2448 image = pixmap.toImage();
2451 if (image.depth() == 1)
2452 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2454 if (s->matrix.type() > QTransform::TxTranslate) {
2455 QTransform copy = s->matrix;
2456 copy.translate(r.x(), r.y());
2457 copy.translate(-sr.x(), -sr.y());
2458 d->image_filler_xform.clip = d->clip();
2459 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2460 if (!d->image_filler_xform.blend)
2462 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2464 #ifdef QT_FAST_SPANS
2465 ensureRasterState();
2466 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2467 d->initializeRasterizer(&d->image_filler_xform);
2468 d->rasterizer->setAntialiased(s->flags.antialiased);
2470 const QRectF &rect = r.normalized();
2471 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2472 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2473 if (s->flags.tx_noshear)
2474 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2476 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2482 fillPath(path, &d->image_filler_xform);
2484 d->image_filler.clip = d->clip();
2486 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2487 if (!d->image_filler.blend)
2489 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2490 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2493 rr.translate(s->matrix.dx(), s->matrix.dy());
2494 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2500 static inline bool monoVal(const uchar* s, int x)
2502 return (s[x>>3] << (x&7)) & 0x80;
2508 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2510 Q_D(QRasterPaintEngine);
2511 return d->rasterBuffer.data();
2517 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2519 Q_D(QRasterPaintEngine);
2520 QRasterPaintEngineState *s = state();
2522 if (!s->penData.blend)
2525 QRasterBuffer *rb = d->rasterBuffer.data();
2527 const QRect rect(rx, ry, w, h);
2528 const QClipData *clip = d->clip();
2529 bool unclipped = false;
2531 // inlined QRect::intersects
2532 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2533 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2535 if (clip->hasRectClip) {
2536 unclipped = rx > clip->xmin
2537 && rx + w < clip->xmax
2539 && ry + h < clip->ymax;
2545 // inlined QRect::intersects
2546 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2547 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2551 // inlined QRect::contains
2552 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2553 && rect.top() >= 0 && rect.bottom() < rb->height();
2555 unclipped = contains && d->isUnclipped_normalized(rect);
2558 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2559 const uchar * scanline = static_cast<const uchar *>(src);
2561 if (s->flags.fast_text) {
2564 if (s->penData.bitmapBlit) {
2565 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2566 scanline, w, h, bpl);
2569 } else if (depth == 8) {
2570 if (s->penData.alphamapBlit) {
2571 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2572 scanline, w, h, bpl, 0);
2575 } else if (depth == 32) {
2576 // (A)RGB Alpha mask where the alpha component is not used.
2577 if (s->penData.alphaRGBBlit) {
2578 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2579 (const uint *) scanline, w, h, bpl / 4, 0);
2583 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2584 // (A)RGB Alpha mask where the alpha component is not used.
2586 int nx = qMax(0, rx);
2587 int ny = qMax(0, ry);
2589 // Move scanline pointer to compensate for moved x and y
2590 int xdiff = nx - rx;
2591 int ydiff = ny - ry;
2592 scanline += ydiff * bpl;
2593 scanline += xdiff * (depth == 32 ? 4 : 1);
2598 if (nx + w > d->rasterBuffer->width())
2599 w = d->rasterBuffer->width() - nx;
2600 if (ny + h > d->rasterBuffer->height())
2601 h = d->rasterBuffer->height() - ny;
2606 if (depth == 8 && s->penData.alphamapBlit) {
2607 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2608 scanline, w, h, bpl, clip);
2609 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2610 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2611 (const uint *) scanline, w, h, bpl / 4, clip);
2626 scanline += bpl * y0;
2630 w = qMin(w, rb->width() - qMax(0, rx));
2631 h = qMin(h, rb->height() - qMax(0, ry));
2633 if (w <= 0 || h <= 0)
2636 const int NSPANS = 256;
2637 QSpan spans[NSPANS];
2640 const int x1 = x0 + w;
2641 const int y1 = y0 + h;
2644 for (int y = y0; y < y1; ++y) {
2645 for (int x = x0; x < x1; ) {
2646 if (!monoVal(scanline, x)) {
2651 if (current == NSPANS) {
2652 blend(current, spans, &s->penData);
2655 spans[current].x = x + rx;
2656 spans[current].y = y + ry;
2657 spans[current].coverage = 255;
2660 // extend span until we find a different one.
2661 while (x < x1 && monoVal(scanline, x)) {
2665 spans[current].len = len;
2670 } else if (depth == 8) {
2671 for (int y = y0; y < y1; ++y) {
2672 for (int x = x0; x < x1; ) {
2673 // Skip those with 0 coverage
2674 if (scanline[x] == 0) {
2679 if (current == NSPANS) {
2680 blend(current, spans, &s->penData);
2683 int coverage = scanline[x];
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 && scanline[x] == coverage) {
2695 spans[current].len = len;
2700 } else { // 32-bit alpha...
2701 uint *sl = (uint *) src;
2702 for (int y = y0; y < y1; ++y) {
2703 for (int x = x0; x < x1; ) {
2704 // Skip those with 0 coverage
2705 if ((sl[x] & 0x00ffffff) == 0) {
2710 if (current == NSPANS) {
2711 blend(current, spans, &s->penData);
2714 uint rgbCoverage = sl[x];
2715 int coverage = qGreen(rgbCoverage);
2716 spans[current].x = x + rx;
2717 spans[current].y = y + ry;
2718 spans[current].coverage = coverage;
2722 // extend span until we find a different one.
2723 while (x < x1 && sl[x] == rgbCoverage) {
2727 spans[current].len = len;
2730 sl += bpl / sizeof(uint);
2733 // qDebug() << "alphaPenBlt: num spans=" << current
2734 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2735 // Call span func for current set of spans.
2737 blend(current, spans, &s->penData);
2740 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2741 const QFixedPoint *positions, QFontEngine *fontEngine)
2743 Q_D(QRasterPaintEngine);
2744 QRasterPaintEngineState *s = state();
2746 if (fontEngine->hasInternalCaching()) {
2747 QFontEngine::GlyphFormat neededFormat =
2748 painter()->device()->devType() == QInternal::Widget
2749 ? QFontEngine::Format_None
2750 : QFontEngine::Format_A8;
2752 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2753 neededFormat = QFontEngine::Format_Mono;
2755 for (int i = 0; i < numGlyphs; i++) {
2756 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2759 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2761 if (alphaMap == 0 || alphaMap->isNull())
2764 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2765 qFloor(positions[i].x) + offset.x(),
2766 qFloor(positions[i].y) + offset.y(),
2767 alphaMap->width(), alphaMap->height());
2769 fontEngine->unlockAlphaMapForGlyph();
2773 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2775 QImageTextureGlyphCache *cache =
2776 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2778 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2779 fontEngine->setGlyphCache(0, cache);
2782 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2783 cache->fillInPendingGlyphs();
2785 const QImage &image = cache->image();
2786 int bpl = image.bytesPerLine();
2788 int depth = image.depth();
2792 leftShift = 2; // multiply by 4
2793 else if (depth == 1)
2794 rightShift = 3; // divide by 8
2796 int margin = fontEngine->glyphMargin(glyphType);
2797 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2798 const uchar *bits = image.bits();
2799 for (int i=0; i<numGlyphs; ++i) {
2801 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2802 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2803 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2807 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2808 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2810 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2813 // c.baseLineX, c.baseLineY,
2816 // positions[i].x.toInt(), positions[i].y.toInt());
2818 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2826 * Returns true if the rectangle is completely within the current clip
2827 * state of the paint engine.
2829 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2831 const QClipData *cl = clip();
2833 // inline contains() for performance (we know the rects are normalized)
2834 const QRect &r1 = deviceRect;
2835 return (r.left() >= r1.left() && r.right() <= r1.right()
2836 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2840 if (cl->hasRectClip) {
2841 // currently all painting functions clips to deviceRect internally
2842 if (cl->clipRect == deviceRect)
2845 // inline contains() for performance (we know the rects are normalized)
2846 const QRect &r1 = cl->clipRect;
2847 return (r.left() >= r1.left() && r.right() <= r1.right()
2848 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2850 return qt_region_strictContains(cl->clipRegion, r);
2854 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2857 Q_Q(const QRasterPaintEngine);
2858 const QRasterPaintEngineState *s = q->state();
2859 const QClipData *cl = clip();
2861 QRect r = rect.normalized();
2862 // inline contains() for performance (we know the rects are normalized)
2863 const QRect &r1 = deviceRect;
2864 return (r.left() >= r1.left() && r.right() <= r1.right()
2865 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2869 // currently all painting functions that call this function clip to deviceRect internally
2870 if (cl->hasRectClip && cl->clipRect == deviceRect)
2873 if (s->flags.antialiased)
2876 QRect r = rect.normalized();
2878 r.setX(r.x() - penWidth);
2879 r.setY(r.y() - penWidth);
2880 r.setWidth(r.width() + 2 * penWidth);
2881 r.setHeight(r.height() + 2 * penWidth);
2884 if (cl->hasRectClip) {
2885 // inline contains() for performance (we know the rects are normalized)
2886 const QRect &r1 = cl->clipRect;
2887 return (r.left() >= r1.left() && r.right() <= r1.right()
2888 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2890 return qt_region_strictContains(cl->clipRegion, r);
2894 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2897 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2901 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2902 const QSpanData *data) const
2904 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2908 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2909 const QSpanData *data) const
2911 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2915 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2916 const QSpanData *data) const
2918 Q_Q(const QRasterPaintEngine);
2919 const QRasterPaintEngineState *s = q->state();
2921 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2923 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2924 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2927 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2928 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2930 QFixed clipLeft = QFixed::fromReal(clip.left());
2931 QFixed clipRight = QFixed::fromReal(clip.right());
2932 QFixed clipTop = QFixed::fromReal(clip.top());
2933 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2936 while (first < numGlyphs) {
2937 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2938 QFixed left = metrics.x + positions[first].x;
2939 QFixed top = metrics.y + positions[first].y;
2940 QFixed right = left + metrics.width;
2941 QFixed bottom = top + metrics.height;
2942 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2946 int last = numGlyphs - 1;
2947 while (last > first) {
2948 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2949 QFixed left = metrics.x + positions[last].x;
2950 QFixed top = metrics.y + positions[last].y;
2951 QFixed right = left + metrics.width;
2952 QFixed bottom = top + metrics.height;
2953 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2957 return QPair<int, int>(first, last + 1);
2963 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2965 if (textItem->numGlyphs == 0)
2969 ensureRasterState();
2971 QFontEngine *fontEngine = textItem->fontEngine();
2972 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2973 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2975 } else if (state()->matrix.type() < QTransform::TxProject) {
2977 QTransform invMat = state()->matrix.inverted(&invertible);
2981 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2982 textItem->fontEngine(), textItem->glyphs,
2983 textItem->glyphPositions, textItem->numGlyphs);
2984 QStaticTextItem copy = *textItem;
2985 copy.glyphs += range.first;
2986 copy.glyphPositions += range.first;
2987 copy.numGlyphs = range.second - range.first;
2988 QPaintEngineEx::drawStaticTextItem(©);
2990 QPaintEngineEx::drawStaticTextItem(textItem);
2997 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
2999 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3001 #ifdef QT_DEBUG_DRAW
3002 Q_D(QRasterPaintEngine);
3003 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3004 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3008 if (ti.glyphs.numGlyphs == 0)
3011 ensureRasterState();
3013 QRasterPaintEngineState *s = state();
3014 QTransform matrix = s->matrix;
3016 if (!supportsTransformations(ti.fontEngine)) {
3017 QVarLengthArray<QFixedPoint> positions;
3018 QVarLengthArray<glyph_t> glyphs;
3020 matrix.translate(p.x(), p.y());
3021 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3023 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3024 } else if (matrix.type() < QTransform::TxProject) {
3026 QTransform invMat = matrix.inverted(&invertible);
3030 QVarLengthArray<QFixedPoint> positions;
3031 QVarLengthArray<glyph_t> glyphs;
3033 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3034 ti.flags, glyphs, positions);
3035 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3036 ti.fontEngine, glyphs.data(), positions.data(),
3039 if (range.first >= range.second)
3042 QStaticTextItem staticTextItem;
3043 staticTextItem.color = s->pen.color();
3044 staticTextItem.font = s->font;
3045 staticTextItem.setFontEngine(ti.fontEngine);
3046 staticTextItem.numGlyphs = range.second - range.first;
3047 staticTextItem.glyphs = glyphs.data() + range.first;
3048 staticTextItem.glyphPositions = positions.data() + range.first;
3049 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3051 QPaintEngineEx::drawTextItem(p, ti);
3058 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3060 Q_D(QRasterPaintEngine);
3061 QRasterPaintEngineState *s = state();
3064 if (!s->penData.blend)
3067 if (!s->flags.fast_pen) {
3068 QPaintEngineEx::drawPoints(points, pointCount);
3072 QCosmeticStroker stroker(s, d->deviceRect);
3073 stroker.drawPoints(points, pointCount);
3077 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3079 Q_D(QRasterPaintEngine);
3080 QRasterPaintEngineState *s = state();
3083 if (!s->penData.blend)
3086 if (!s->flags.fast_pen) {
3087 QPaintEngineEx::drawPoints(points, pointCount);
3091 QCosmeticStroker stroker(s, d->deviceRect);
3092 stroker.drawPoints(points, pointCount);
3098 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3100 #ifdef QT_DEBUG_DRAW
3101 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3103 Q_D(QRasterPaintEngine);
3104 QRasterPaintEngineState *s = state();
3107 if (!s->penData.blend)
3110 if (s->flags.fast_pen) {
3111 QCosmeticStroker stroker(s, d->deviceRect);
3112 for (int i=0; i<lineCount; ++i) {
3113 const QLine &l = lines[i];
3114 stroker.drawLine(l.p1(), l.p2());
3117 QPaintEngineEx::drawLines(lines, lineCount);
3121 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3127 Q_Q(QRasterPaintEngine);
3128 QRasterPaintEngineState *s = q->state();
3130 const QPen &pen = s->lastPen;
3131 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3132 const QVector<qreal> pattern = pen.dashPattern();
3134 qreal patternLength = 0;
3135 for (int i = 0; i < pattern.size(); ++i)
3136 patternLength += pattern.at(i);
3138 if (patternLength <= 0)
3141 qreal length = line.length();
3142 Q_ASSERT(length > 0);
3143 while (length > 0) {
3144 const bool rasterize = *inDash;
3145 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3148 if (dash >= length) {
3150 *dashOffset += dash / width;
3154 *inDash = !(*inDash);
3155 if (++*dashIndex >= pattern.size())
3162 if (rasterize && dash > 0)
3163 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3170 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3172 #ifdef QT_DEBUG_DRAW
3173 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3175 Q_D(QRasterPaintEngine);
3176 QRasterPaintEngineState *s = state();
3179 if (!s->penData.blend)
3181 if (s->flags.fast_pen) {
3182 QCosmeticStroker stroker(s, d->deviceRect);
3183 for (int i=0; i<lineCount; ++i) {
3184 QLineF line = lines[i];
3185 stroker.drawLine(line.p1(), line.p2());
3188 QPaintEngineEx::drawLines(lines, lineCount);
3196 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3198 Q_D(QRasterPaintEngine);
3199 QRasterPaintEngineState *s = state();
3202 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3203 || (qpen_style(s->lastPen) == Qt::NoPen))
3204 && !s->flags.antialiased
3205 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3207 && s->matrix.type() <= QTransform::TxScale) // no shear
3210 const QRectF r = s->matrix.mapRect(rect);
3211 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3212 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3213 const QRect brect = QRect(int(r.x()), int(r.y()),
3214 int_dim(r.x(), r.width()),
3215 int_dim(r.y(), r.height()));
3217 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3218 &s->penData, &s->brushData);
3222 QPaintEngineEx::drawEllipse(rect);
3230 void QRasterPaintEngine::setDC(HDC hdc) {
3231 Q_D(QRasterPaintEngine);
3238 HDC QRasterPaintEngine::getDC() const
3240 Q_D(const QRasterPaintEngine);
3247 void QRasterPaintEngine::releaseDC(HDC) const
3253 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3255 const QTransform &m = state()->matrix;
3256 return supportsTransformations(fontEngine, m);
3259 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3261 if (m.type() >= QTransform::TxProject)
3264 return !shouldDrawCachedGlyphs(fontEngine, m);
3270 QPoint QRasterPaintEngine::coordinateOffset() const
3272 return QPoint(0, 0);
3275 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3280 Q_D(QRasterPaintEngine);
3282 Q_ASSERT(image.depth() == 1);
3284 const int spanCount = 256;
3285 QT_FT_Span spans[spanCount];
3289 int w = image.width();
3290 int h = image.height();
3291 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3292 int ymin = qMax(qRound(pos.y()), 0);
3293 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3294 int xmin = qMax(qRound(pos.x()), 0);
3296 int x_offset = xmin - qRound(pos.x());
3298 QImage::Format format = image.format();
3299 for (int y = ymin; y < ymax; ++y) {
3300 const uchar *src = image.scanLine(y - qRound(pos.y()));
3301 if (format == QImage::Format_MonoLSB) {
3302 for (int x = 0; x < xmax - xmin; ++x) {
3303 int src_x = x + x_offset;
3304 uchar pixel = src[src_x >> 3];
3309 if (pixel & (0x1 << (src_x & 7))) {
3310 spans[n].x = xmin + x;
3312 spans[n].coverage = 255;
3314 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3318 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3321 if (n == spanCount) {
3322 fg->blend(n, spans, fg);
3328 for (int x = 0; x < xmax - xmin; ++x) {
3329 int src_x = x + x_offset;
3330 uchar pixel = src[src_x >> 3];
3335 if (pixel & (0x80 >> (x & 7))) {
3336 spans[n].x = xmin + x;
3338 spans[n].coverage = 255;
3340 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3344 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3347 if (n == spanCount) {
3348 fg->blend(n, spans, fg);
3356 fg->blend(n, spans, fg);
3362 \enum QRasterPaintEngine::ClipType
3365 \value RectClip Indicates that the currently set clip is a single rectangle.
3366 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3371 Returns the type of the clip currently set.
3373 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3375 Q_D(const QRasterPaintEngine);
3377 const QClipData *clip = d->clip();
3378 if (!clip || clip->hasRectClip)
3386 Returns the bounding rect of the currently set clip.
3388 QRect QRasterPaintEngine::clipBoundingRect() const
3390 Q_D(const QRasterPaintEngine);
3392 const QClipData *clip = d->clip();
3395 return d->deviceRect;
3397 if (clip->hasRectClip)
3398 return clip->clipRect;
3400 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3403 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3405 Q_Q(QRasterPaintEngine);
3406 QRasterPaintEngineState *s = q->state();
3408 rasterizer->setAntialiased(s->flags.antialiased);
3410 QRect clipRect(deviceRect);
3412 // ### get from optimized rectbased QClipData
3414 const QClipData *c = clip();
3416 const QRect r(QPoint(c->xmin, c->ymin),
3417 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3418 clipRect = clipRect.intersected(r);
3419 blend = data->blend;
3421 blend = data->unclipped_blend;
3424 rasterizer->setClipRect(clipRect);
3425 rasterizer->initialize(blend, data);
3428 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3429 ProcessSpans callback,
3430 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3432 if (!callback || !outline)
3435 Q_Q(QRasterPaintEngine);
3436 QRasterPaintEngineState *s = q->state();
3438 if (!s->flags.antialiased) {
3439 initializeRasterizer(spanData);
3441 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3445 rasterizer->rasterize(outline, fillRule);
3449 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3453 int q_gray_rendered_spans(QT_FT_Raster raster);
3456 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3458 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3461 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3462 ProcessSpans callback,
3463 void *userData, QRasterBuffer *)
3465 if (!callback || !outline)
3468 Q_Q(QRasterPaintEngine);
3469 QRasterPaintEngineState *s = q->state();
3471 if (!s->flags.antialiased) {
3472 rasterizer->setAntialiased(s->flags.antialiased);
3473 rasterizer->setClipRect(deviceRect);
3474 rasterizer->initialize(callback, userData);
3476 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3480 rasterizer->rasterize(outline, fillRule);
3484 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3485 // minimize memory reallocations. However if initial size for
3486 // raster pool is changed for lower value, reallocations will
3488 int rasterPoolSize = MINIMUM_POOL_SIZE;
3489 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3490 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3491 uchar *rasterPoolOnHeap = 0;
3493 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3495 void *data = userData;
3497 QT_FT_BBox clip_box = { deviceRect.x(),
3499 deviceRect.x() + deviceRect.width(),
3500 deviceRect.y() + deviceRect.height() };
3502 QT_FT_Raster_Params rasterParams;
3503 rasterParams.target = 0;
3504 rasterParams.source = outline;
3505 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3506 rasterParams.gray_spans = 0;
3507 rasterParams.black_spans = 0;
3508 rasterParams.bit_test = 0;
3509 rasterParams.bit_set = 0;
3510 rasterParams.user = data;
3511 rasterParams.clip_box = clip_box;
3516 int rendered_spans = 0;
3520 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3521 rasterParams.gray_spans = callback;
3522 rasterParams.skip_spans = rendered_spans;
3523 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3525 // Out of memory, reallocate some more and try again...
3526 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3527 rasterPoolSize *= 2;
3528 if (rasterPoolSize > 1024 * 1024) {
3529 qWarning("QPainter: Rasterization of primitive failed");
3533 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3535 free(rasterPoolOnHeap);
3536 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3538 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3540 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3542 qt_ft_grays_raster.raster_done(*grayRaster.data());
3543 qt_ft_grays_raster.raster_new(grayRaster.data());
3544 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3550 free(rasterPoolOnHeap);
3553 void QRasterPaintEnginePrivate::recalculateFastImages()
3555 Q_Q(QRasterPaintEngine);
3556 QRasterPaintEngineState *s = q->state();
3558 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3559 && s->matrix.type() <= QTransform::TxShear;
3562 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3564 Q_Q(const QRasterPaintEngine);
3565 const QRasterPaintEngineState *s = q->state();
3567 return s->flags.fast_images
3568 && (mode == QPainter::CompositionMode_SourceOver
3569 || (mode == QPainter::CompositionMode_Source
3570 && !image.hasAlphaChannel()));
3573 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3575 Q_ASSERT(image.depth() == 1);
3577 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3578 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3580 QRgb fg = PREMUL(color.rgba());
3583 int height = sourceImage.height();
3584 int width = sourceImage.width();
3585 for (int y=0; y<height; ++y) {
3586 uchar *source = sourceImage.scanLine(y);
3587 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3588 if (!source || !target)
3589 QT_THROW(std::bad_alloc()); // we must have run out of memory
3590 for (int x=0; x < width; ++x)
3591 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3596 QRasterBuffer::~QRasterBuffer()
3600 void QRasterBuffer::init()
3602 compositionMode = QPainter::CompositionMode_SourceOver;
3603 monoDestinationWithClut = false;
3608 QImage::Format QRasterBuffer::prepare(QImage *image)
3610 m_buffer = (uchar *)image->bits();
3611 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3612 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3613 bytes_per_pixel = image->depth()/8;
3614 bytes_per_line = image->bytesPerLine();
3616 format = image->format();
3617 drawHelper = qDrawHelper + format;
3618 if (image->depth() == 1 && image->colorTable().size() == 2) {
3619 monoDestinationWithClut = true;
3620 destColor0 = PREMUL(image->colorTable()[0]);
3621 destColor1 = PREMUL(image->colorTable()[1]);
3627 void QRasterBuffer::resetBuffer(int val)
3629 memset(m_buffer, val, m_height*bytes_per_line);
3632 QClipData::QClipData(int height)
3634 clipSpanHeight = height;
3639 xmin = xmax = ymin = ymax = 0;
3643 hasRectClip = hasRegionClip = false;
3646 QClipData::~QClipData()
3654 void QClipData::initialize()
3660 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3662 Q_CHECK_PTR(m_clipLines);
3664 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3665 allocated = clipSpanHeight;
3666 Q_CHECK_PTR(m_spans);
3672 m_clipLines[y].spans = 0;
3673 m_clipLines[y].count = 0;
3677 const int len = clipRect.width();
3680 QSpan *span = m_spans + count;
3684 span->coverage = 255;
3687 m_clipLines[y].spans = span;
3688 m_clipLines[y].count = 1;
3692 while (y < clipSpanHeight) {
3693 m_clipLines[y].spans = 0;
3694 m_clipLines[y].count = 0;
3697 } else if (hasRegionClip) {
3699 const QVector<QRect> rects = clipRegion.rects();
3700 const int numRects = rects.size();
3703 const int maxSpans = (ymax - ymin) * numRects;
3704 if (maxSpans > allocated) {
3705 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3706 allocated = maxSpans;
3711 int firstInBand = 0;
3713 while (firstInBand < numRects) {
3714 const int currMinY = rects.at(firstInBand).y();
3715 const int currMaxY = currMinY + rects.at(firstInBand).height();
3717 while (y < currMinY) {
3718 m_clipLines[y].spans = 0;
3719 m_clipLines[y].count = 0;
3723 int lastInBand = firstInBand;
3724 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3727 while (y < currMaxY) {
3729 m_clipLines[y].spans = m_spans + count;
3730 m_clipLines[y].count = lastInBand - firstInBand + 1;
3732 for (int r = firstInBand; r <= lastInBand; ++r) {
3733 const QRect &currRect = rects.at(r);
3734 QSpan *span = m_spans + count;
3735 span->x = currRect.x();
3736 span->len = currRect.width();
3738 span->coverage = 255;
3744 firstInBand = lastInBand + 1;
3747 Q_ASSERT(count <= allocated);
3749 while (y < clipSpanHeight) {
3750 m_clipLines[y].spans = 0;
3751 m_clipLines[y].count = 0;
3757 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3762 free(m_clipLines); // same for clipLines
3768 void QClipData::fixup()
3773 ymin = ymax = xmin = xmax = 0;
3778 ymin = m_spans[0].y;
3779 ymax = m_spans[count-1].y + 1;
3783 const int firstLeft = m_spans[0].x;
3784 const int firstRight = m_spans[0].x + m_spans[0].len;
3787 for (int i = 0; i < count; ++i) {
3788 QT_FT_Span_& span = m_spans[i];
3791 if (span.y != y + 1 && y != -1)
3794 m_clipLines[y].spans = &span;
3795 m_clipLines[y].count = 1;
3797 ++m_clipLines[y].count;
3799 const int spanLeft = span.x;
3800 const int spanRight = spanLeft + span.len;
3802 if (spanLeft < xmin)
3805 if (spanRight > xmax)
3808 if (spanLeft != firstLeft || spanRight != firstRight)
3814 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3819 Convert \a rect to clip spans.
3821 void QClipData::setClipRect(const QRect &rect)
3823 if (hasRectClip && rect == clipRect)
3826 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3828 hasRegionClip = false;
3832 xmax = rect.x() + rect.width();
3833 ymin = qMin(rect.y(), clipSpanHeight);
3834 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3841 // qDebug() << xmin << xmax << ymin << ymax;
3845 Convert \a region to clip spans.
3847 void QClipData::setClipRegion(const QRegion ®ion)
3849 if (region.rectCount() == 1) {
3850 setClipRect(region.rects().at(0));
3854 hasRegionClip = true;
3855 hasRectClip = false;
3856 clipRegion = region;
3858 { // set bounding rect
3859 const QRect rect = region.boundingRect();
3861 xmax = rect.x() + rect.width();
3863 ymax = rect.y() + rect.height();
3875 spans must be sorted on y
3877 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3878 const QSpan *spans, const QSpan *end,
3879 QSpan **outSpans, int available)
3881 const_cast<QClipData *>(clip)->initialize();
3883 QSpan *out = *outSpans;
3885 const QSpan *clipSpans = clip->m_spans + *currentClip;
3886 const QSpan *clipEnd = clip->m_spans + clip->count;
3888 while (available && spans < end ) {
3889 if (clipSpans >= clipEnd) {
3893 if (clipSpans->y > spans->y) {
3897 if (spans->y != clipSpans->y) {
3898 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3899 clipSpans = clip->m_clipLines[spans->y].spans;
3904 Q_ASSERT(spans->y == clipSpans->y);
3907 int sx2 = sx1 + spans->len;
3908 int cx1 = clipSpans->x;
3909 int cx2 = cx1 + clipSpans->len;
3911 if (cx1 < sx1 && cx2 < sx1) {
3914 } else if (sx1 < cx1 && sx2 < cx1) {
3918 int x = qMax(sx1, cx1);
3919 int len = qMin(sx2, cx2) - x;
3921 out->x = qMax(sx1, cx1);
3922 out->len = qMin(sx2, cx2) - out->x;
3924 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3936 *currentClip = clipSpans - clip->m_spans;
3940 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3942 // qDebug() << "qt_span_fill_clipped" << spanCount;
3943 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3945 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3947 const int NSPANS = 256;
3948 QSpan cspans[NSPANS];
3949 int currentClip = 0;
3950 const QSpan *end = spans + spanCount;
3951 while (spans < end) {
3952 QSpan *clipped = cspans;
3953 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3954 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3955 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3957 if (clipped - cspans)
3958 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3964 Clip spans to \a{clip}-rectangle.
3965 Returns number of unclipped spans
3967 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3970 const short minx = clip.left();
3971 const short miny = clip.top();
3972 const short maxx = clip.right();
3973 const short maxy = clip.bottom();
3976 for (int i = 0; i < numSpans; ++i) {
3977 if (spans[i].y > maxy)
3979 if (spans[i].y < miny
3980 || spans[i].x > maxx
3981 || spans[i].x + spans[i].len <= minx) {
3984 if (spans[i].x < minx) {
3985 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
3988 spans[n].x = spans[i].x;
3989 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
3991 if (spans[n].len == 0)
3993 spans[n].y = spans[i].y;
3994 spans[n].coverage = spans[i].coverage;
4001 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4004 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4005 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4007 Q_ASSERT(fillData->clip);
4008 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4010 // hw: check if this const_cast<> is safe!!!
4011 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4012 fillData->clip->clipRect);
4014 fillData->unclipped_blend(count, spans, fillData);
4017 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4019 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4021 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4022 // for (int i = 0; i < qMin(count, 10); ++i) {
4023 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4026 switch (clipData->operation) {
4028 case Qt::IntersectClip:
4030 QClipData *newClip = clipData->newClip;
4031 newClip->initialize();
4033 int currentClip = 0;
4034 const QSpan *end = spans + count;
4035 while (spans < end) {
4036 QSpan *newspans = newClip->m_spans + newClip->count;
4037 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4038 &newspans, newClip->allocated - newClip->count);
4039 newClip->count = newspans - newClip->m_spans;
4041 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4042 newClip->allocated *= 2;
4048 case Qt::ReplaceClip:
4049 clipData->newClip->appendSpans(spans, count);
4057 QImage QRasterBuffer::bufferImage() const
4059 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4061 for (int y = 0; y < m_height; ++y) {
4062 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4064 for (int x=0; x<m_width; ++x) {
4065 uint argb = span[x];
4066 image.setPixel(x, y, argb);
4074 void QRasterBuffer::flushToARGBImage(QImage *target) const
4076 int w = qMin(m_width, target->width());
4077 int h = qMin(m_height, target->height());
4079 for (int y=0; y<h; ++y) {
4080 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4081 QRgb *dest = (QRgb *) target->scanLine(y);
4082 for (int x=0; x<w; ++x) {
4083 QRgb pixel = sourceLine[x];
4084 int alpha = qAlpha(pixel);
4088 dest[x] = (alpha << 24)
4089 | ((255*qRed(pixel)/alpha) << 16)
4090 | ((255*qGreen(pixel)/alpha) << 8)
4091 | ((255*qBlue(pixel)/alpha) << 0);
4098 class QGradientCache
4102 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4103 stops(s), opacity(op), interpolationMode(mode) {}
4104 uint buffer[GRADIENT_STOPTABLE_SIZE];
4105 QGradientStops stops;
4107 QGradient::InterpolationMode interpolationMode;
4110 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4113 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4114 quint64 hash_val = 0;
4116 QGradientStops stops = gradient.stops();
4117 for (int i = 0; i < stops.size() && i <= 2; i++)
4118 hash_val += stops[i].second.rgba();
4120 QMutexLocker lock(&mutex);
4121 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4123 if (it == cache.constEnd())
4124 return addCacheElement(hash_val, gradient, opacity);
4127 const CacheInfo &cache_info = it.value();
4128 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4129 return cache_info.buffer;
4131 } while (it != cache.constEnd() && it.key() == hash_val);
4132 // an exact match for these stops and opacity was not found, create new cache
4133 return addCacheElement(hash_val, gradient, opacity);
4137 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4139 inline int maxCacheSize() const { return 60; }
4140 inline void generateGradientColorTable(const QGradient& g,
4142 int size, int opacity) const;
4143 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4144 if (cache.size() == maxCacheSize()) {
4145 // may remove more than 1, but OK
4146 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4148 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4149 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4150 return cache.insert(hash_val, cache_entry).value().buffer;
4153 QGradientColorTableHash cache;
4157 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4159 QGradientStops stops = gradient.stops();
4160 int stopCount = stops.count();
4161 Q_ASSERT(stopCount > 0);
4163 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4165 if (stopCount == 2) {
4166 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4167 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4169 qreal first_stop = stops[0].first;
4170 qreal second_stop = stops[1].first;
4172 if (second_stop < first_stop) {
4173 qSwap(first_color, second_color);
4174 qSwap(first_stop, second_stop);
4177 if (colorInterpolation) {
4178 first_color = PREMUL(first_color);
4179 second_color = PREMUL(second_color);
4182 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4183 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4185 uint red_first = qRed(first_color) << 16;
4186 uint green_first = qGreen(first_color) << 16;
4187 uint blue_first = qBlue(first_color) << 16;
4188 uint alpha_first = qAlpha(first_color) << 16;
4190 uint red_second = qRed(second_color) << 16;
4191 uint green_second = qGreen(second_color) << 16;
4192 uint blue_second = qBlue(second_color) << 16;
4193 uint alpha_second = qAlpha(second_color) << 16;
4196 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4197 if (colorInterpolation)
4198 colorTable[i] = first_color;
4200 colorTable[i] = PREMUL(first_color);
4203 if (i < second_index) {
4204 qreal reciprocal = qreal(1) / (second_index - first_index);
4206 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4207 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4208 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4209 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4212 red_first += 1 << 15;
4213 green_first += 1 << 15;
4214 blue_first += 1 << 15;
4215 alpha_first += 1 << 15;
4217 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4218 red_first += red_delta;
4219 green_first += green_delta;
4220 blue_first += blue_delta;
4221 alpha_first += alpha_delta;
4223 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4224 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4226 if (colorInterpolation)
4227 colorTable[i] = color;
4229 colorTable[i] = PREMUL(color);
4233 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4234 if (colorInterpolation)
4235 colorTable[i] = second_color;
4237 colorTable[i] = PREMUL(second_color);
4243 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4244 if (stopCount == 1) {
4245 current_color = PREMUL(current_color);
4246 for (int i = 0; i < size; ++i)
4247 colorTable[i] = current_color;
4251 // The position where the gradient begins and ends
4252 qreal begin_pos = stops[0].first;
4253 qreal end_pos = stops[stopCount-1].first;
4255 int pos = 0; // The position in the color table.
4258 qreal incr = 1 / qreal(size); // the double increment.
4259 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4261 // Up to first point
4262 colorTable[pos++] = PREMUL(current_color);
4263 while (dpos <= begin_pos) {
4264 colorTable[pos] = colorTable[pos - 1];
4269 int current_stop = 0; // We always interpolate between current and current + 1.
4271 qreal t; // position between current left and right stops
4272 qreal t_delta; // the t increment per entry in the color table
4274 if (dpos < end_pos) {
4276 while (dpos > stops[current_stop+1].first)
4279 if (current_stop != 0)
4280 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4281 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4283 if (colorInterpolation) {
4284 current_color = PREMUL(current_color);
4285 next_color = PREMUL(next_color);
4288 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4289 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4290 t = (dpos - stops[current_stop].first) * c;
4294 Q_ASSERT(current_stop < stopCount);
4296 int dist = qRound(t);
4297 int idist = 256 - dist;
4299 if (colorInterpolation)
4300 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4302 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4307 if (dpos >= end_pos)
4313 while (dpos > stops[current_stop+skip+1].first)
4317 current_stop += skip;
4319 current_color = next_color;
4321 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4322 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4324 if (colorInterpolation) {
4326 current_color = PREMUL(current_color);
4327 next_color = PREMUL(next_color);
4330 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4331 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4332 t = (dpos - stops[current_stop].first) * c;
4339 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4340 while (pos < size - 1) {
4341 colorTable[pos] = current_color;
4345 // Make sure the last color stop is represented at the end of the table
4346 colorTable[size - 1] = current_color;
4349 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4352 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4358 m11 = m22 = m33 = 1.;
4359 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4360 clip = pe ? pe->d_func()->clip() : 0;
4363 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4365 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4367 Qt::BrushStyle brushStyle = qbrush_style(brush);
4368 switch (brushStyle) {
4369 case Qt::SolidPattern: {
4371 QColor c = qbrush_color(brush);
4372 QRgb rgba = c.rgba();
4373 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4374 if ((solid.color & 0xff000000) == 0
4375 && compositionMode == QPainter::CompositionMode_SourceOver) {
4381 case Qt::LinearGradientPattern:
4383 type = LinearGradient;
4384 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4385 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4386 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4387 gradient.spread = g->spread();
4389 QLinearGradientData &linearData = gradient.linear;
4391 linearData.origin.x = g->start().x();
4392 linearData.origin.y = g->start().y();
4393 linearData.end.x = g->finalStop().x();
4394 linearData.end.y = g->finalStop().y();
4398 case Qt::RadialGradientPattern:
4400 type = RadialGradient;
4401 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4402 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4403 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4404 gradient.spread = g->spread();
4406 QRadialGradientData &radialData = gradient.radial;
4408 QPointF center = g->center();
4409 radialData.center.x = center.x();
4410 radialData.center.y = center.y();
4411 radialData.center.radius = g->centerRadius();
4412 QPointF focal = g->focalPoint();
4413 radialData.focal.x = focal.x();
4414 radialData.focal.y = focal.y();
4415 radialData.focal.radius = g->focalRadius();
4419 case Qt::ConicalGradientPattern:
4421 type = ConicalGradient;
4422 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4423 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4424 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4425 gradient.spread = QGradient::RepeatSpread;
4427 QConicalGradientData &conicalData = gradient.conical;
4429 QPointF center = g->center();
4430 conicalData.center.x = center.x();
4431 conicalData.center.y = center.y();
4432 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4436 case Qt::Dense1Pattern:
4437 case Qt::Dense2Pattern:
4438 case Qt::Dense3Pattern:
4439 case Qt::Dense4Pattern:
4440 case Qt::Dense5Pattern:
4441 case Qt::Dense6Pattern:
4442 case Qt::Dense7Pattern:
4443 case Qt::HorPattern:
4444 case Qt::VerPattern:
4445 case Qt::CrossPattern:
4446 case Qt::BDiagPattern:
4447 case Qt::FDiagPattern:
4448 case Qt::DiagCrossPattern:
4451 tempImage = new QImage();
4452 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4453 initTexture(tempImage, alpha, QTextureData::Tiled);
4455 case Qt::TexturePattern:
4458 tempImage = new QImage();
4460 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4461 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4463 *tempImage = brush.textureImage();
4464 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4472 adjustSpanMethods();
4475 void QSpanData::adjustSpanMethods()
4485 unclipped_blend = 0;
4488 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4489 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4490 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4491 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4492 fillRect = rasterBuffer->drawHelper->fillRect;
4494 case LinearGradient:
4495 case RadialGradient:
4496 case ConicalGradient:
4497 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4500 unclipped_blend = qBlendTexture;
4501 if (!texture.imageData)
4502 unclipped_blend = 0;
4507 if (!unclipped_blend) {
4510 blend = unclipped_blend;
4511 } else if (clip->hasRectClip) {
4512 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4514 blend = qt_span_fill_clipped;
4518 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4521 // make sure we round off correctly in qdrawhelper.cpp
4522 delta.translate(1.0 / 65536, 1.0 / 65536);
4524 QTransform inv = (delta * matrix).inverted();
4537 const bool affine = !m13 && !m23;
4538 fast_matrix = affine
4539 && m11 * m11 + m21 * m21 < 1e4
4540 && m12 * m12 + m22 * m22 < 1e4
4544 adjustSpanMethods();
4547 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4549 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4550 if (!d || d->height == 0) {
4551 texture.imageData = 0;
4558 texture.bytesPerLine = 0;
4559 texture.format = QImage::Format_Invalid;
4560 texture.colorTable = 0;
4561 texture.hasAlpha = alpha != 256;
4563 texture.imageData = d->data;
4564 texture.width = d->width;
4565 texture.height = d->height;
4567 if (sourceRect.isNull()) {
4570 texture.x2 = texture.width;
4571 texture.y2 = texture.height;
4573 texture.x1 = sourceRect.x();
4574 texture.y1 = sourceRect.y();
4575 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4576 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4579 texture.bytesPerLine = d->bytes_per_line;
4581 texture.format = d->format;
4582 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4583 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4585 texture.const_alpha = alpha;
4586 texture.type = _type;
4588 adjustSpanMethods();
4593 \a x and \a y is relative to the midpoint of \a rect.
4595 static inline void drawEllipsePoints(int x, int y, int length,
4598 ProcessSpans pen_func, ProcessSpans brush_func,
4599 QSpanData *pen_data, QSpanData *brush_data)
4604 QT_FT_Span outline[4];
4605 const int midx = rect.x() + (rect.width() + 1) / 2;
4606 const int midy = rect.y() + (rect.height() + 1) / 2;
4612 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4613 outline[0].len = qMin(length, x - outline[0].x);
4615 outline[0].coverage = 255;
4619 outline[1].len = length;
4621 outline[1].coverage = 255;
4624 outline[2].x = outline[0].x;
4625 outline[2].len = outline[0].len;
4626 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4627 outline[2].coverage = 255;
4631 outline[3].len = length;
4632 outline[3].y = outline[2].y;
4633 outline[3].coverage = 255;
4635 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4639 fill[0].x = outline[0].x + outline[0].len - 1;
4640 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4641 fill[0].y = outline[1].y;
4642 fill[0].coverage = 255;
4645 fill[1].x = outline[2].x + outline[2].len - 1;
4646 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4647 fill[1].y = outline[3].y;
4648 fill[1].coverage = 255;
4650 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4651 n = qt_intersect_spans(fill, n, clip);
4653 brush_func(n, fill, brush_data);
4656 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4657 n = qt_intersect_spans(outline, n, clip);
4659 pen_func(n, outline, pen_data);
4665 Draws an ellipse using the integer point midpoint algorithm.
4667 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4668 ProcessSpans pen_func, ProcessSpans brush_func,
4669 QSpanData *pen_data, QSpanData *brush_data)
4671 const qreal a = qreal(rect.width()) / 2;
4672 const qreal b = qreal(rect.height()) / 2;
4673 qreal d = b*b - (a*a*b) + 0.25*a*a;
4676 int y = (rect.height() + 1) / 2;
4680 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4681 if (d < 0) { // select E
4684 } else { // select SE
4685 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4686 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4687 pen_func, brush_func, pen_data, brush_data);
4692 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4693 pen_func, brush_func, pen_data, brush_data);
4696 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4697 const int miny = rect.height() & 0x1;
4699 if (d < 0) { // select SE
4700 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4702 } else { // select S
4703 d += a*a*(-2*y + 3);
4706 drawEllipsePoints(x, y, 1, rect, clip,
4707 pen_func, brush_func, pen_data, brush_data);
4712 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4715 Draws the first \a pointCount points in the buffer \a points
4717 The default implementation converts the first \a pointCount QPoints in \a points
4718 to QPointFs and calls the floating point version of drawPoints.
4722 #ifdef QT_DEBUG_DRAW
4723 void dumpClip(int width, int height, const QClipData *clip)
4725 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4726 clipImg.fill(0xffff0000);
4733 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4735 for (int i = 0; i < clip->count; ++i) {
4736 const QSpan *span = ((QClipData *) clip)->spans() + i;
4737 for (int j = 0; j < span->len; ++j)
4738 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4739 x0 = qMin(x0, int(span->x));
4740 x1 = qMax(x1, int(span->x + span->len - 1));
4742 y0 = qMin(y0, int(span->y));
4743 y1 = qMax(y1, int(span->y));
4746 static int counter = 0;
4753 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4754 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));