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 // assumes that rect has positive width and height
2101 static inline const QRect toRect_normalized(const QRectF &rect)
2103 const int x = qRound(rect.x());
2104 const int y = qRound(rect.y());
2105 const int w = int(rect.width() + qreal(0.5));
2106 const int h = int(rect.height() + qreal(0.5));
2108 return QRect(x, y, w, h);
2111 static inline int fast_ceil_positive(const qreal &v)
2113 const int iv = int(v);
2120 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2122 const int xmin = int(rect.x());
2123 const int xmax = int(fast_ceil_positive(rect.right()));
2124 const int ymin = int(rect.y());
2125 const int ymax = int(fast_ceil_positive(rect.bottom()));
2126 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2132 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2134 #ifdef QT_DEBUG_DRAW
2135 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2138 Q_D(QRasterPaintEngine);
2139 QRasterPaintEngineState *s = state();
2141 if (s->matrix.type() > QTransform::TxTranslate) {
2142 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2144 QRectF(0, 0, img.width(), img.height()));
2147 const QClipData *clip = d->clip();
2148 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2150 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2151 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2154 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2156 } else if (clip->hasRectClip) {
2157 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2165 d->image_filler.clip = clip;
2166 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2167 if (!d->image_filler.blend)
2169 d->image_filler.dx = -pt.x();
2170 d->image_filler.dy = -pt.y();
2171 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2173 fillRect_normalized(rr, &d->image_filler, d);
2178 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2180 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2191 inline RotationType qRotationType(const QTransform &transform)
2193 QTransform::TransformationType type = transform.type();
2195 if (type > QTransform::TxRotate)
2198 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2199 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2202 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2203 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2206 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2207 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2213 inline bool isPixelAligned(const QRectF &rect) {
2214 return QRectF(rect.toRect()) == rect;
2221 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2222 Qt::ImageConversionFlags)
2224 #ifdef QT_DEBUG_DRAW
2225 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2231 Q_D(QRasterPaintEngine);
2232 QRasterPaintEngineState *s = state();
2233 int sr_l = qFloor(sr.left());
2234 int sr_r = qCeil(sr.right()) - 1;
2235 int sr_t = qFloor(sr.top());
2236 int sr_b = qCeil(sr.bottom()) - 1;
2238 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2239 // as fillRect will apply the aliased coordinate delta we need to
2240 // subtract it here as we don't use it for image drawing
2241 QTransform old = s->matrix;
2242 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2244 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2245 QRgb color = img.pixel(sr_l, sr_t);
2246 switch (img.format()) {
2247 case QImage::Format_ARGB32_Premultiplied:
2248 case QImage::Format_ARGB8565_Premultiplied:
2249 case QImage::Format_ARGB6666_Premultiplied:
2250 case QImage::Format_ARGB8555_Premultiplied:
2251 case QImage::Format_ARGB4444_Premultiplied:
2252 // Combine premultiplied color with the opacity set on the painter.
2253 d->solid_color_filler.solid.color =
2254 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2255 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2258 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2262 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2263 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2267 d->solid_color_filler.clip = d->clip();
2268 d->solid_color_filler.adjustSpanMethods();
2269 fillRect(r, &d->solid_color_filler);
2275 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2277 const QClipData *clip = d->clip();
2279 if (s->matrix.type() > QTransform::TxTranslate
2281 && (!clip || clip->hasRectClip)
2282 && s->intOpacity == 256
2283 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2284 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2285 && d->rasterBuffer->format == img.format()
2286 && (d->rasterBuffer->format == QImage::Format_RGB16
2287 || d->rasterBuffer->format == QImage::Format_RGB32
2288 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2289 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2291 RotationType rotationType = qRotationType(s->matrix);
2293 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2294 QRectF transformedTargetRect = s->matrix.mapRect(r);
2296 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2297 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2299 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2300 if (clippedTransformedTargetRect.isNull())
2303 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2305 QRect clippedSourceRect
2306 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2307 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2309 uint dbpl = d->rasterBuffer->bytesPerLine();
2310 uint sbpl = img.bytesPerLine();
2312 uchar *dst = d->rasterBuffer->buffer();
2313 uint bpp = img.depth() >> 3;
2315 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2316 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2318 uint cw = clippedSourceRect.width();
2319 uint ch = clippedSourceRect.height();
2321 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2328 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2330 QRectF targetBounds = s->matrix.mapRect(r);
2331 bool exceedsPrecision = targetBounds.width() > 0xffff
2332 || targetBounds.height() > 0xffff;
2334 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2335 if (s->matrix.type() > QTransform::TxScale) {
2336 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2337 if (func && (!clip || clip->hasRectClip)) {
2338 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2339 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2340 s->matrix, s->intOpacity);
2344 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2345 if (func && (!clip || clip->hasRectClip)) {
2346 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2347 img.bits(), img.bytesPerLine(),
2348 qt_mapRect_non_normalizing(r, s->matrix), sr,
2349 !clip ? d->deviceRect : clip->clipRect,
2356 QTransform copy = s->matrix;
2357 copy.translate(r.x(), r.y());
2359 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2360 copy.translate(-sr.x(), -sr.y());
2362 d->image_filler_xform.clip = clip;
2363 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2364 if (!d->image_filler_xform.blend)
2366 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2368 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2369 QRectF rr = s->matrix.mapRect(r);
2371 const int x1 = qRound(rr.x());
2372 const int y1 = qRound(rr.y());
2373 const int x2 = qRound(rr.right());
2374 const int y2 = qRound(rr.bottom());
2376 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2380 #ifdef QT_FAST_SPANS
2381 ensureRasterState();
2382 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2383 d->initializeRasterizer(&d->image_filler_xform);
2384 d->rasterizer->setAntialiased(s->flags.antialiased);
2386 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2388 const QRectF &rect = r.normalized();
2389 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2390 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2392 if (s->flags.tx_noshear)
2393 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2395 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2399 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2402 QTransform m = s->matrix;
2403 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2404 m.m21(), m.m22(), m.m23(),
2405 m.m31() - offs, m.m32() - offs, m.m33());
2406 fillPath(path, &d->image_filler_xform);
2409 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2410 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2412 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2414 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2416 } else if (clip->hasRectClip) {
2417 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2423 d->image_filler.clip = clip;
2424 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2425 if (!d->image_filler.blend)
2427 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2428 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2431 rr.translate(s->matrix.dx(), s->matrix.dy());
2433 const int x1 = qRound(rr.x());
2434 const int y1 = qRound(rr.y());
2435 const int x2 = qRound(rr.right());
2436 const int y2 = qRound(rr.bottom());
2438 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2445 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2447 #ifdef QT_DEBUG_DRAW
2448 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2450 Q_D(QRasterPaintEngine);
2451 QRasterPaintEngineState *s = state();
2455 QPlatformPixmap *pd = pixmap.handle();
2456 if (pd->classId() == QPlatformPixmap::RasterClass) {
2457 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2459 image = pixmap.toImage();
2462 if (image.depth() == 1)
2463 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2465 if (s->matrix.type() > QTransform::TxTranslate) {
2466 QTransform copy = s->matrix;
2467 copy.translate(r.x(), r.y());
2468 copy.translate(-sr.x(), -sr.y());
2469 d->image_filler_xform.clip = d->clip();
2470 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2471 if (!d->image_filler_xform.blend)
2473 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2475 #ifdef QT_FAST_SPANS
2476 ensureRasterState();
2477 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2478 d->initializeRasterizer(&d->image_filler_xform);
2479 d->rasterizer->setAntialiased(s->flags.antialiased);
2481 const QRectF &rect = r.normalized();
2482 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2483 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2484 if (s->flags.tx_noshear)
2485 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2487 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2493 fillPath(path, &d->image_filler_xform);
2495 d->image_filler.clip = d->clip();
2497 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2498 if (!d->image_filler.blend)
2500 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2501 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2504 rr.translate(s->matrix.dx(), s->matrix.dy());
2505 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2511 static inline bool monoVal(const uchar* s, int x)
2513 return (s[x>>3] << (x&7)) & 0x80;
2519 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2521 Q_D(QRasterPaintEngine);
2522 return d->rasterBuffer.data();
2528 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2530 Q_D(QRasterPaintEngine);
2531 QRasterPaintEngineState *s = state();
2533 if (!s->penData.blend)
2536 QRasterBuffer *rb = d->rasterBuffer.data();
2538 const QRect rect(rx, ry, w, h);
2539 const QClipData *clip = d->clip();
2540 bool unclipped = false;
2542 // inlined QRect::intersects
2543 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2544 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2546 if (clip->hasRectClip) {
2547 unclipped = rx > clip->xmin
2548 && rx + w < clip->xmax
2550 && ry + h < clip->ymax;
2556 // inlined QRect::intersects
2557 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2558 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2562 // inlined QRect::contains
2563 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2564 && rect.top() >= 0 && rect.bottom() < rb->height();
2566 unclipped = contains && d->isUnclipped_normalized(rect);
2569 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2570 const uchar * scanline = static_cast<const uchar *>(src);
2572 if (s->flags.fast_text) {
2575 if (s->penData.bitmapBlit) {
2576 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2577 scanline, w, h, bpl);
2580 } else if (depth == 8) {
2581 if (s->penData.alphamapBlit) {
2582 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2583 scanline, w, h, bpl, 0);
2586 } else if (depth == 32) {
2587 // (A)RGB Alpha mask where the alpha component is not used.
2588 if (s->penData.alphaRGBBlit) {
2589 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2590 (const uint *) scanline, w, h, bpl / 4, 0);
2594 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2595 // (A)RGB Alpha mask where the alpha component is not used.
2597 int nx = qMax(0, rx);
2598 int ny = qMax(0, ry);
2600 // Move scanline pointer to compensate for moved x and y
2601 int xdiff = nx - rx;
2602 int ydiff = ny - ry;
2603 scanline += ydiff * bpl;
2604 scanline += xdiff * (depth == 32 ? 4 : 1);
2609 if (nx + w > d->rasterBuffer->width())
2610 w = d->rasterBuffer->width() - nx;
2611 if (ny + h > d->rasterBuffer->height())
2612 h = d->rasterBuffer->height() - ny;
2617 if (depth == 8 && s->penData.alphamapBlit) {
2618 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2619 scanline, w, h, bpl, clip);
2620 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2621 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2622 (const uint *) scanline, w, h, bpl / 4, clip);
2637 scanline += bpl * y0;
2641 w = qMin(w, rb->width() - qMax(0, rx));
2642 h = qMin(h, rb->height() - qMax(0, ry));
2644 if (w <= 0 || h <= 0)
2647 const int NSPANS = 256;
2648 QSpan spans[NSPANS];
2651 const int x1 = x0 + w;
2652 const int y1 = y0 + h;
2655 for (int y = y0; y < y1; ++y) {
2656 for (int x = x0; x < x1; ) {
2657 if (!monoVal(scanline, x)) {
2662 if (current == NSPANS) {
2663 blend(current, spans, &s->penData);
2666 spans[current].x = x + rx;
2667 spans[current].y = y + ry;
2668 spans[current].coverage = 255;
2671 // extend span until we find a different one.
2672 while (x < x1 && monoVal(scanline, x)) {
2676 spans[current].len = len;
2681 } else if (depth == 8) {
2682 for (int y = y0; y < y1; ++y) {
2683 for (int x = x0; x < x1; ) {
2684 // Skip those with 0 coverage
2685 if (scanline[x] == 0) {
2690 if (current == NSPANS) {
2691 blend(current, spans, &s->penData);
2694 int coverage = scanline[x];
2695 spans[current].x = x + rx;
2696 spans[current].y = y + ry;
2697 spans[current].coverage = coverage;
2701 // extend span until we find a different one.
2702 while (x < x1 && scanline[x] == coverage) {
2706 spans[current].len = len;
2711 } else { // 32-bit alpha...
2712 uint *sl = (uint *) src;
2713 for (int y = y0; y < y1; ++y) {
2714 for (int x = x0; x < x1; ) {
2715 // Skip those with 0 coverage
2716 if ((sl[x] & 0x00ffffff) == 0) {
2721 if (current == NSPANS) {
2722 blend(current, spans, &s->penData);
2725 uint rgbCoverage = sl[x];
2726 int coverage = qGreen(rgbCoverage);
2727 spans[current].x = x + rx;
2728 spans[current].y = y + ry;
2729 spans[current].coverage = coverage;
2733 // extend span until we find a different one.
2734 while (x < x1 && sl[x] == rgbCoverage) {
2738 spans[current].len = len;
2741 sl += bpl / sizeof(uint);
2744 // qDebug() << "alphaPenBlt: num spans=" << current
2745 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2746 // Call span func for current set of spans.
2748 blend(current, spans, &s->penData);
2751 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2752 const QFixedPoint *positions, QFontEngine *fontEngine)
2754 Q_D(QRasterPaintEngine);
2755 QRasterPaintEngineState *s = state();
2757 if (fontEngine->hasInternalCaching()) {
2758 QFontEngine::GlyphFormat neededFormat =
2759 painter()->device()->devType() == QInternal::Widget
2760 ? QFontEngine::Format_None
2761 : QFontEngine::Format_A8;
2763 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2764 neededFormat = QFontEngine::Format_Mono;
2766 for (int i = 0; i < numGlyphs; i++) {
2767 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2770 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2772 if (alphaMap == 0 || alphaMap->isNull())
2775 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2776 qFloor(positions[i].x) + offset.x(),
2777 qFloor(positions[i].y) + offset.y(),
2778 alphaMap->width(), alphaMap->height());
2780 fontEngine->unlockAlphaMapForGlyph();
2784 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2786 QImageTextureGlyphCache *cache =
2787 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2789 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2790 fontEngine->setGlyphCache(0, cache);
2793 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2794 cache->fillInPendingGlyphs();
2796 const QImage &image = cache->image();
2797 int bpl = image.bytesPerLine();
2799 int depth = image.depth();
2803 leftShift = 2; // multiply by 4
2804 else if (depth == 1)
2805 rightShift = 3; // divide by 8
2807 int margin = fontEngine->glyphMargin(glyphType);
2808 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2809 const uchar *bits = image.bits();
2810 for (int i=0; i<numGlyphs; ++i) {
2812 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2813 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2814 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2818 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2819 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2821 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2824 // c.baseLineX, c.baseLineY,
2827 // positions[i].x.toInt(), positions[i].y.toInt());
2829 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2837 * Returns true if the rectangle is completely within the current clip
2838 * state of the paint engine.
2840 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2842 const QClipData *cl = clip();
2844 // inline contains() for performance (we know the rects are normalized)
2845 const QRect &r1 = deviceRect;
2846 return (r.left() >= r1.left() && r.right() <= r1.right()
2847 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2851 if (cl->hasRectClip) {
2852 // currently all painting functions clips to deviceRect internally
2853 if (cl->clipRect == deviceRect)
2856 // inline contains() for performance (we know the rects are normalized)
2857 const QRect &r1 = cl->clipRect;
2858 return (r.left() >= r1.left() && r.right() <= r1.right()
2859 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2861 return qt_region_strictContains(cl->clipRegion, r);
2865 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2868 Q_Q(const QRasterPaintEngine);
2869 const QRasterPaintEngineState *s = q->state();
2870 const QClipData *cl = clip();
2872 QRect r = rect.normalized();
2873 // inline contains() for performance (we know the rects are normalized)
2874 const QRect &r1 = deviceRect;
2875 return (r.left() >= r1.left() && r.right() <= r1.right()
2876 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2880 // currently all painting functions that call this function clip to deviceRect internally
2881 if (cl->hasRectClip && cl->clipRect == deviceRect)
2884 if (s->flags.antialiased)
2887 QRect r = rect.normalized();
2889 r.setX(r.x() - penWidth);
2890 r.setY(r.y() - penWidth);
2891 r.setWidth(r.width() + 2 * penWidth);
2892 r.setHeight(r.height() + 2 * penWidth);
2895 if (cl->hasRectClip) {
2896 // inline contains() for performance (we know the rects are normalized)
2897 const QRect &r1 = cl->clipRect;
2898 return (r.left() >= r1.left() && r.right() <= r1.right()
2899 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2901 return qt_region_strictContains(cl->clipRegion, r);
2905 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2908 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2912 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2913 const QSpanData *data) const
2915 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2919 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2920 const QSpanData *data) const
2922 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2926 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2927 const QSpanData *data) const
2929 Q_Q(const QRasterPaintEngine);
2930 const QRasterPaintEngineState *s = q->state();
2932 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2934 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2935 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2938 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2939 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2941 QFixed clipLeft = QFixed::fromReal(clip.left());
2942 QFixed clipRight = QFixed::fromReal(clip.right());
2943 QFixed clipTop = QFixed::fromReal(clip.top());
2944 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2947 while (first < numGlyphs) {
2948 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2949 QFixed left = metrics.x + positions[first].x;
2950 QFixed top = metrics.y + positions[first].y;
2951 QFixed right = left + metrics.width;
2952 QFixed bottom = top + metrics.height;
2953 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2957 int last = numGlyphs - 1;
2958 while (last > first) {
2959 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2960 QFixed left = metrics.x + positions[last].x;
2961 QFixed top = metrics.y + positions[last].y;
2962 QFixed right = left + metrics.width;
2963 QFixed bottom = top + metrics.height;
2964 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2968 return QPair<int, int>(first, last + 1);
2974 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2976 if (textItem->numGlyphs == 0)
2980 ensureRasterState();
2982 QFontEngine *fontEngine = textItem->fontEngine();
2983 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2984 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2986 } else if (state()->matrix.type() < QTransform::TxProject) {
2988 QTransform invMat = state()->matrix.inverted(&invertible);
2992 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2993 textItem->fontEngine(), textItem->glyphs,
2994 textItem->glyphPositions, textItem->numGlyphs);
2995 QStaticTextItem copy = *textItem;
2996 copy.glyphs += range.first;
2997 copy.glyphPositions += range.first;
2998 copy.numGlyphs = range.second - range.first;
2999 QPaintEngineEx::drawStaticTextItem(©);
3001 QPaintEngineEx::drawStaticTextItem(textItem);
3008 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3010 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3012 #ifdef QT_DEBUG_DRAW
3013 Q_D(QRasterPaintEngine);
3014 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3015 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3019 if (ti.glyphs.numGlyphs == 0)
3022 ensureRasterState();
3024 QRasterPaintEngineState *s = state();
3025 QTransform matrix = s->matrix;
3027 if (!supportsTransformations(ti.fontEngine)) {
3028 QVarLengthArray<QFixedPoint> positions;
3029 QVarLengthArray<glyph_t> glyphs;
3031 matrix.translate(p.x(), p.y());
3032 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3034 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3035 } else if (matrix.type() < QTransform::TxProject) {
3037 QTransform invMat = matrix.inverted(&invertible);
3041 QVarLengthArray<QFixedPoint> positions;
3042 QVarLengthArray<glyph_t> glyphs;
3044 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3045 ti.flags, glyphs, positions);
3046 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3047 ti.fontEngine, glyphs.data(), positions.data(),
3050 if (range.first >= range.second)
3053 QStaticTextItem staticTextItem;
3054 staticTextItem.color = s->pen.color();
3055 staticTextItem.font = s->font;
3056 staticTextItem.setFontEngine(ti.fontEngine);
3057 staticTextItem.numGlyphs = range.second - range.first;
3058 staticTextItem.glyphs = glyphs.data() + range.first;
3059 staticTextItem.glyphPositions = positions.data() + range.first;
3060 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3062 QPaintEngineEx::drawTextItem(p, ti);
3069 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3071 Q_D(QRasterPaintEngine);
3072 QRasterPaintEngineState *s = state();
3075 if (!s->penData.blend)
3078 if (!s->flags.fast_pen) {
3079 QPaintEngineEx::drawPoints(points, pointCount);
3083 QCosmeticStroker stroker(s, d->deviceRect);
3084 stroker.drawPoints(points, pointCount);
3088 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3090 Q_D(QRasterPaintEngine);
3091 QRasterPaintEngineState *s = state();
3094 if (!s->penData.blend)
3097 if (!s->flags.fast_pen) {
3098 QPaintEngineEx::drawPoints(points, pointCount);
3102 QCosmeticStroker stroker(s, d->deviceRect);
3103 stroker.drawPoints(points, pointCount);
3109 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3111 #ifdef QT_DEBUG_DRAW
3112 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3114 Q_D(QRasterPaintEngine);
3115 QRasterPaintEngineState *s = state();
3118 if (!s->penData.blend)
3121 if (s->flags.fast_pen) {
3122 QCosmeticStroker stroker(s, d->deviceRect);
3123 for (int i=0; i<lineCount; ++i) {
3124 const QLine &l = lines[i];
3125 stroker.drawLine(l.p1(), l.p2());
3128 QPaintEngineEx::drawLines(lines, lineCount);
3132 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3138 Q_Q(QRasterPaintEngine);
3139 QRasterPaintEngineState *s = q->state();
3141 const QPen &pen = s->lastPen;
3142 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3143 const QVector<qreal> pattern = pen.dashPattern();
3145 qreal patternLength = 0;
3146 for (int i = 0; i < pattern.size(); ++i)
3147 patternLength += pattern.at(i);
3149 if (patternLength <= 0)
3152 qreal length = line.length();
3153 Q_ASSERT(length > 0);
3154 while (length > 0) {
3155 const bool rasterize = *inDash;
3156 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3159 if (dash >= length) {
3161 *dashOffset += dash / width;
3165 *inDash = !(*inDash);
3166 if (++*dashIndex >= pattern.size())
3173 if (rasterize && dash > 0)
3174 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3181 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3183 #ifdef QT_DEBUG_DRAW
3184 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3186 Q_D(QRasterPaintEngine);
3187 QRasterPaintEngineState *s = state();
3190 if (!s->penData.blend)
3192 if (s->flags.fast_pen) {
3193 QCosmeticStroker stroker(s, d->deviceRect);
3194 for (int i=0; i<lineCount; ++i) {
3195 QLineF line = lines[i];
3196 stroker.drawLine(line.p1(), line.p2());
3199 QPaintEngineEx::drawLines(lines, lineCount);
3207 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3209 Q_D(QRasterPaintEngine);
3210 QRasterPaintEngineState *s = state();
3213 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3214 || (qpen_style(s->lastPen) == Qt::NoPen))
3215 && !s->flags.antialiased
3216 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3218 && s->matrix.type() <= QTransform::TxScale) // no shear
3221 const QRectF r = s->matrix.mapRect(rect);
3222 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3223 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3224 const QRect brect = QRect(int(r.x()), int(r.y()),
3225 int_dim(r.x(), r.width()),
3226 int_dim(r.y(), r.height()));
3228 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3229 &s->penData, &s->brushData);
3233 QPaintEngineEx::drawEllipse(rect);
3244 void QRasterPaintEngine::setDC(HDC hdc) {
3245 Q_D(QRasterPaintEngine);
3252 HDC QRasterPaintEngine::getDC() const
3254 Q_D(const QRasterPaintEngine);
3261 void QRasterPaintEngine::releaseDC(HDC) const
3267 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3269 const QTransform &m = state()->matrix;
3270 return supportsTransformations(fontEngine, m);
3273 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3275 if (m.type() >= QTransform::TxProject)
3278 return !shouldDrawCachedGlyphs(fontEngine, m);
3284 QPoint QRasterPaintEngine::coordinateOffset() const
3286 return QPoint(0, 0);
3289 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3294 Q_D(QRasterPaintEngine);
3296 Q_ASSERT(image.depth() == 1);
3298 const int spanCount = 256;
3299 QT_FT_Span spans[spanCount];
3303 int w = image.width();
3304 int h = image.height();
3305 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3306 int ymin = qMax(qRound(pos.y()), 0);
3307 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3308 int xmin = qMax(qRound(pos.x()), 0);
3310 int x_offset = xmin - qRound(pos.x());
3312 QImage::Format format = image.format();
3313 for (int y = ymin; y < ymax; ++y) {
3314 const uchar *src = image.scanLine(y - qRound(pos.y()));
3315 if (format == QImage::Format_MonoLSB) {
3316 for (int x = 0; x < xmax - xmin; ++x) {
3317 int src_x = x + x_offset;
3318 uchar pixel = src[src_x >> 3];
3323 if (pixel & (0x1 << (src_x & 7))) {
3324 spans[n].x = xmin + x;
3326 spans[n].coverage = 255;
3328 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3332 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3335 if (n == spanCount) {
3336 fg->blend(n, spans, fg);
3342 for (int x = 0; x < xmax - xmin; ++x) {
3343 int src_x = x + x_offset;
3344 uchar pixel = src[src_x >> 3];
3349 if (pixel & (0x80 >> (x & 7))) {
3350 spans[n].x = xmin + x;
3352 spans[n].coverage = 255;
3354 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3358 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3361 if (n == spanCount) {
3362 fg->blend(n, spans, fg);
3370 fg->blend(n, spans, fg);
3376 \enum QRasterPaintEngine::ClipType
3379 \value RectClip Indicates that the currently set clip is a single rectangle.
3380 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3385 Returns the type of the clip currently set.
3387 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3389 Q_D(const QRasterPaintEngine);
3391 const QClipData *clip = d->clip();
3392 if (!clip || clip->hasRectClip)
3400 Returns the bounding rect of the currently set clip.
3402 QRect QRasterPaintEngine::clipBoundingRect() const
3404 Q_D(const QRasterPaintEngine);
3406 const QClipData *clip = d->clip();
3409 return d->deviceRect;
3411 if (clip->hasRectClip)
3412 return clip->clipRect;
3414 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3417 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3419 Q_Q(QRasterPaintEngine);
3420 QRasterPaintEngineState *s = q->state();
3422 rasterizer->setAntialiased(s->flags.antialiased);
3424 QRect clipRect(deviceRect);
3426 // ### get from optimized rectbased QClipData
3428 const QClipData *c = clip();
3430 const QRect r(QPoint(c->xmin, c->ymin),
3431 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3432 clipRect = clipRect.intersected(r);
3433 blend = data->blend;
3435 blend = data->unclipped_blend;
3438 rasterizer->setClipRect(clipRect);
3439 rasterizer->initialize(blend, data);
3442 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3443 ProcessSpans callback,
3444 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3446 if (!callback || !outline)
3449 Q_Q(QRasterPaintEngine);
3450 QRasterPaintEngineState *s = q->state();
3452 if (!s->flags.antialiased) {
3453 initializeRasterizer(spanData);
3455 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3459 rasterizer->rasterize(outline, fillRule);
3463 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3467 int q_gray_rendered_spans(QT_FT_Raster raster);
3470 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3472 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3475 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3476 ProcessSpans callback,
3477 void *userData, QRasterBuffer *)
3479 if (!callback || !outline)
3482 Q_Q(QRasterPaintEngine);
3483 QRasterPaintEngineState *s = q->state();
3485 if (!s->flags.antialiased) {
3486 rasterizer->setAntialiased(s->flags.antialiased);
3487 rasterizer->setClipRect(deviceRect);
3488 rasterizer->initialize(callback, userData);
3490 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3494 rasterizer->rasterize(outline, fillRule);
3498 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3499 // minimize memory reallocations. However if initial size for
3500 // raster pool is changed for lower value, reallocations will
3502 int rasterPoolSize = MINIMUM_POOL_SIZE;
3503 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3504 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3505 uchar *rasterPoolOnHeap = 0;
3507 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3509 void *data = userData;
3511 QT_FT_BBox clip_box = { deviceRect.x(),
3513 deviceRect.x() + deviceRect.width(),
3514 deviceRect.y() + deviceRect.height() };
3516 QT_FT_Raster_Params rasterParams;
3517 rasterParams.target = 0;
3518 rasterParams.source = outline;
3519 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3520 rasterParams.gray_spans = 0;
3521 rasterParams.black_spans = 0;
3522 rasterParams.bit_test = 0;
3523 rasterParams.bit_set = 0;
3524 rasterParams.user = data;
3525 rasterParams.clip_box = clip_box;
3530 int rendered_spans = 0;
3534 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3535 rasterParams.gray_spans = callback;
3536 rasterParams.skip_spans = rendered_spans;
3537 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3539 // Out of memory, reallocate some more and try again...
3540 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3541 rasterPoolSize *= 2;
3542 if (rasterPoolSize > 1024 * 1024) {
3543 qWarning("QPainter: Rasterization of primitive failed");
3547 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3549 free(rasterPoolOnHeap);
3550 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3552 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3554 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3556 qt_ft_grays_raster.raster_done(*grayRaster.data());
3557 qt_ft_grays_raster.raster_new(grayRaster.data());
3558 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3564 free(rasterPoolOnHeap);
3567 void QRasterPaintEnginePrivate::recalculateFastImages()
3569 Q_Q(QRasterPaintEngine);
3570 QRasterPaintEngineState *s = q->state();
3572 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3573 && s->matrix.type() <= QTransform::TxShear;
3576 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3578 Q_Q(const QRasterPaintEngine);
3579 const QRasterPaintEngineState *s = q->state();
3581 return s->flags.fast_images
3582 && (mode == QPainter::CompositionMode_SourceOver
3583 || (mode == QPainter::CompositionMode_Source
3584 && !image.hasAlphaChannel()));
3587 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3589 Q_ASSERT(image.depth() == 1);
3591 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3592 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3594 QRgb fg = PREMUL(color.rgba());
3597 int height = sourceImage.height();
3598 int width = sourceImage.width();
3599 for (int y=0; y<height; ++y) {
3600 uchar *source = sourceImage.scanLine(y);
3601 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3602 if (!source || !target)
3603 QT_THROW(std::bad_alloc()); // we must have run out of memory
3604 for (int x=0; x < width; ++x)
3605 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3610 QRasterBuffer::~QRasterBuffer()
3614 void QRasterBuffer::init()
3616 compositionMode = QPainter::CompositionMode_SourceOver;
3617 monoDestinationWithClut = false;
3622 QImage::Format QRasterBuffer::prepare(QImage *image)
3624 m_buffer = (uchar *)image->bits();
3625 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3626 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3627 bytes_per_pixel = image->depth()/8;
3628 bytes_per_line = image->bytesPerLine();
3630 format = image->format();
3631 drawHelper = qDrawHelper + format;
3632 if (image->depth() == 1 && image->colorTable().size() == 2) {
3633 monoDestinationWithClut = true;
3634 destColor0 = PREMUL(image->colorTable()[0]);
3635 destColor1 = PREMUL(image->colorTable()[1]);
3641 void QRasterBuffer::resetBuffer(int val)
3643 memset(m_buffer, val, m_height*bytes_per_line);
3646 QClipData::QClipData(int height)
3648 clipSpanHeight = height;
3653 xmin = xmax = ymin = ymax = 0;
3657 hasRectClip = hasRegionClip = false;
3660 QClipData::~QClipData()
3668 void QClipData::initialize()
3674 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3676 Q_CHECK_PTR(m_clipLines);
3678 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3679 allocated = clipSpanHeight;
3680 Q_CHECK_PTR(m_spans);
3686 m_clipLines[y].spans = 0;
3687 m_clipLines[y].count = 0;
3691 const int len = clipRect.width();
3694 QSpan *span = m_spans + count;
3698 span->coverage = 255;
3701 m_clipLines[y].spans = span;
3702 m_clipLines[y].count = 1;
3706 while (y < clipSpanHeight) {
3707 m_clipLines[y].spans = 0;
3708 m_clipLines[y].count = 0;
3711 } else if (hasRegionClip) {
3713 const QVector<QRect> rects = clipRegion.rects();
3714 const int numRects = rects.size();
3717 const int maxSpans = (ymax - ymin) * numRects;
3718 if (maxSpans > allocated) {
3719 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3720 allocated = maxSpans;
3725 int firstInBand = 0;
3727 while (firstInBand < numRects) {
3728 const int currMinY = rects.at(firstInBand).y();
3729 const int currMaxY = currMinY + rects.at(firstInBand).height();
3731 while (y < currMinY) {
3732 m_clipLines[y].spans = 0;
3733 m_clipLines[y].count = 0;
3737 int lastInBand = firstInBand;
3738 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3741 while (y < currMaxY) {
3743 m_clipLines[y].spans = m_spans + count;
3744 m_clipLines[y].count = lastInBand - firstInBand + 1;
3746 for (int r = firstInBand; r <= lastInBand; ++r) {
3747 const QRect &currRect = rects.at(r);
3748 QSpan *span = m_spans + count;
3749 span->x = currRect.x();
3750 span->len = currRect.width();
3752 span->coverage = 255;
3758 firstInBand = lastInBand + 1;
3761 Q_ASSERT(count <= allocated);
3763 while (y < clipSpanHeight) {
3764 m_clipLines[y].spans = 0;
3765 m_clipLines[y].count = 0;
3771 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3776 free(m_clipLines); // same for clipLines
3782 void QClipData::fixup()
3787 ymin = ymax = xmin = xmax = 0;
3792 ymin = m_spans[0].y;
3793 ymax = m_spans[count-1].y + 1;
3797 const int firstLeft = m_spans[0].x;
3798 const int firstRight = m_spans[0].x + m_spans[0].len;
3801 for (int i = 0; i < count; ++i) {
3802 QT_FT_Span_& span = m_spans[i];
3805 if (span.y != y + 1 && y != -1)
3808 m_clipLines[y].spans = &span;
3809 m_clipLines[y].count = 1;
3811 ++m_clipLines[y].count;
3813 const int spanLeft = span.x;
3814 const int spanRight = spanLeft + span.len;
3816 if (spanLeft < xmin)
3819 if (spanRight > xmax)
3822 if (spanLeft != firstLeft || spanRight != firstRight)
3828 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3833 Convert \a rect to clip spans.
3835 void QClipData::setClipRect(const QRect &rect)
3837 if (hasRectClip && rect == clipRect)
3840 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3842 hasRegionClip = false;
3846 xmax = rect.x() + rect.width();
3847 ymin = qMin(rect.y(), clipSpanHeight);
3848 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3855 // qDebug() << xmin << xmax << ymin << ymax;
3859 Convert \a region to clip spans.
3861 void QClipData::setClipRegion(const QRegion ®ion)
3863 if (region.rectCount() == 1) {
3864 setClipRect(region.rects().at(0));
3868 hasRegionClip = true;
3869 hasRectClip = false;
3870 clipRegion = region;
3872 { // set bounding rect
3873 const QRect rect = region.boundingRect();
3875 xmax = rect.x() + rect.width();
3877 ymax = rect.y() + rect.height();
3889 spans must be sorted on y
3891 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3892 const QSpan *spans, const QSpan *end,
3893 QSpan **outSpans, int available)
3895 const_cast<QClipData *>(clip)->initialize();
3897 QSpan *out = *outSpans;
3899 const QSpan *clipSpans = clip->m_spans + *currentClip;
3900 const QSpan *clipEnd = clip->m_spans + clip->count;
3902 while (available && spans < end ) {
3903 if (clipSpans >= clipEnd) {
3907 if (clipSpans->y > spans->y) {
3911 if (spans->y != clipSpans->y) {
3912 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3913 clipSpans = clip->m_clipLines[spans->y].spans;
3918 Q_ASSERT(spans->y == clipSpans->y);
3921 int sx2 = sx1 + spans->len;
3922 int cx1 = clipSpans->x;
3923 int cx2 = cx1 + clipSpans->len;
3925 if (cx1 < sx1 && cx2 < sx1) {
3928 } else if (sx1 < cx1 && sx2 < cx1) {
3932 int x = qMax(sx1, cx1);
3933 int len = qMin(sx2, cx2) - x;
3935 out->x = qMax(sx1, cx1);
3936 out->len = qMin(sx2, cx2) - out->x;
3938 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3950 *currentClip = clipSpans - clip->m_spans;
3954 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3956 // qDebug() << "qt_span_fill_clipped" << spanCount;
3957 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3959 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3961 const int NSPANS = 256;
3962 QSpan cspans[NSPANS];
3963 int currentClip = 0;
3964 const QSpan *end = spans + spanCount;
3965 while (spans < end) {
3966 QSpan *clipped = cspans;
3967 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3968 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3969 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3971 if (clipped - cspans)
3972 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3978 Clip spans to \a{clip}-rectangle.
3979 Returns number of unclipped spans
3981 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3984 const short minx = clip.left();
3985 const short miny = clip.top();
3986 const short maxx = clip.right();
3987 const short maxy = clip.bottom();
3990 for (int i = 0; i < numSpans; ++i) {
3991 if (spans[i].y > maxy)
3993 if (spans[i].y < miny
3994 || spans[i].x > maxx
3995 || spans[i].x + spans[i].len <= minx) {
3998 if (spans[i].x < minx) {
3999 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4002 spans[n].x = spans[i].x;
4003 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4005 if (spans[n].len == 0)
4007 spans[n].y = spans[i].y;
4008 spans[n].coverage = spans[i].coverage;
4015 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4018 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4019 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4021 Q_ASSERT(fillData->clip);
4022 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4024 // hw: check if this const_cast<> is safe!!!
4025 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4026 fillData->clip->clipRect);
4028 fillData->unclipped_blend(count, spans, fillData);
4031 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4033 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4035 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4036 // for (int i = 0; i < qMin(count, 10); ++i) {
4037 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4040 switch (clipData->operation) {
4042 case Qt::IntersectClip:
4044 QClipData *newClip = clipData->newClip;
4045 newClip->initialize();
4047 int currentClip = 0;
4048 const QSpan *end = spans + count;
4049 while (spans < end) {
4050 QSpan *newspans = newClip->m_spans + newClip->count;
4051 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4052 &newspans, newClip->allocated - newClip->count);
4053 newClip->count = newspans - newClip->m_spans;
4055 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4056 newClip->allocated *= 2;
4062 case Qt::ReplaceClip:
4063 clipData->newClip->appendSpans(spans, count);
4071 QImage QRasterBuffer::bufferImage() const
4073 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4075 for (int y = 0; y < m_height; ++y) {
4076 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4078 for (int x=0; x<m_width; ++x) {
4079 uint argb = span[x];
4080 image.setPixel(x, y, argb);
4088 void QRasterBuffer::flushToARGBImage(QImage *target) const
4090 int w = qMin(m_width, target->width());
4091 int h = qMin(m_height, target->height());
4093 for (int y=0; y<h; ++y) {
4094 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4095 QRgb *dest = (QRgb *) target->scanLine(y);
4096 for (int x=0; x<w; ++x) {
4097 QRgb pixel = sourceLine[x];
4098 int alpha = qAlpha(pixel);
4102 dest[x] = (alpha << 24)
4103 | ((255*qRed(pixel)/alpha) << 16)
4104 | ((255*qGreen(pixel)/alpha) << 8)
4105 | ((255*qBlue(pixel)/alpha) << 0);
4112 class QGradientCache
4116 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4117 stops(s), opacity(op), interpolationMode(mode) {}
4118 uint buffer[GRADIENT_STOPTABLE_SIZE];
4119 QGradientStops stops;
4121 QGradient::InterpolationMode interpolationMode;
4124 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4127 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4128 quint64 hash_val = 0;
4130 QGradientStops stops = gradient.stops();
4131 for (int i = 0; i < stops.size() && i <= 2; i++)
4132 hash_val += stops[i].second.rgba();
4134 QMutexLocker lock(&mutex);
4135 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4137 if (it == cache.constEnd())
4138 return addCacheElement(hash_val, gradient, opacity);
4141 const CacheInfo &cache_info = it.value();
4142 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4143 return cache_info.buffer;
4145 } while (it != cache.constEnd() && it.key() == hash_val);
4146 // an exact match for these stops and opacity was not found, create new cache
4147 return addCacheElement(hash_val, gradient, opacity);
4151 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4153 inline int maxCacheSize() const { return 60; }
4154 inline void generateGradientColorTable(const QGradient& g,
4156 int size, int opacity) const;
4157 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4158 if (cache.size() == maxCacheSize()) {
4159 // may remove more than 1, but OK
4160 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4162 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4163 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4164 return cache.insert(hash_val, cache_entry).value().buffer;
4167 QGradientColorTableHash cache;
4171 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4173 QGradientStops stops = gradient.stops();
4174 int stopCount = stops.count();
4175 Q_ASSERT(stopCount > 0);
4177 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4179 if (stopCount == 2) {
4180 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4181 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4183 qreal first_stop = stops[0].first;
4184 qreal second_stop = stops[1].first;
4186 if (second_stop < first_stop) {
4187 qSwap(first_color, second_color);
4188 qSwap(first_stop, second_stop);
4191 if (colorInterpolation) {
4192 first_color = PREMUL(first_color);
4193 second_color = PREMUL(second_color);
4196 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4197 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4199 uint red_first = qRed(first_color) << 16;
4200 uint green_first = qGreen(first_color) << 16;
4201 uint blue_first = qBlue(first_color) << 16;
4202 uint alpha_first = qAlpha(first_color) << 16;
4204 uint red_second = qRed(second_color) << 16;
4205 uint green_second = qGreen(second_color) << 16;
4206 uint blue_second = qBlue(second_color) << 16;
4207 uint alpha_second = qAlpha(second_color) << 16;
4210 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4211 if (colorInterpolation)
4212 colorTable[i] = first_color;
4214 colorTable[i] = PREMUL(first_color);
4217 if (i < second_index) {
4218 qreal reciprocal = qreal(1) / (second_index - first_index);
4220 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4221 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4222 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4223 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4226 red_first += 1 << 15;
4227 green_first += 1 << 15;
4228 blue_first += 1 << 15;
4229 alpha_first += 1 << 15;
4231 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4232 red_first += red_delta;
4233 green_first += green_delta;
4234 blue_first += blue_delta;
4235 alpha_first += alpha_delta;
4237 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4238 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4240 if (colorInterpolation)
4241 colorTable[i] = color;
4243 colorTable[i] = PREMUL(color);
4247 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4248 if (colorInterpolation)
4249 colorTable[i] = second_color;
4251 colorTable[i] = PREMUL(second_color);
4257 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4258 if (stopCount == 1) {
4259 current_color = PREMUL(current_color);
4260 for (int i = 0; i < size; ++i)
4261 colorTable[i] = current_color;
4265 // The position where the gradient begins and ends
4266 qreal begin_pos = stops[0].first;
4267 qreal end_pos = stops[stopCount-1].first;
4269 int pos = 0; // The position in the color table.
4272 qreal incr = 1 / qreal(size); // the double increment.
4273 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4275 // Up to first point
4276 colorTable[pos++] = PREMUL(current_color);
4277 while (dpos <= begin_pos) {
4278 colorTable[pos] = colorTable[pos - 1];
4283 int current_stop = 0; // We always interpolate between current and current + 1.
4285 qreal t; // position between current left and right stops
4286 qreal t_delta; // the t increment per entry in the color table
4288 if (dpos < end_pos) {
4290 while (dpos > stops[current_stop+1].first)
4293 if (current_stop != 0)
4294 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4295 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4297 if (colorInterpolation) {
4298 current_color = PREMUL(current_color);
4299 next_color = PREMUL(next_color);
4302 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4303 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4304 t = (dpos - stops[current_stop].first) * c;
4308 Q_ASSERT(current_stop < stopCount);
4310 int dist = qRound(t);
4311 int idist = 256 - dist;
4313 if (colorInterpolation)
4314 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4316 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4321 if (dpos >= end_pos)
4327 while (dpos > stops[current_stop+skip+1].first)
4331 current_stop += skip;
4333 current_color = next_color;
4335 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4336 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4338 if (colorInterpolation) {
4340 current_color = PREMUL(current_color);
4341 next_color = PREMUL(next_color);
4344 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4345 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4346 t = (dpos - stops[current_stop].first) * c;
4353 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4354 while (pos < size - 1) {
4355 colorTable[pos] = current_color;
4359 // Make sure the last color stop is represented at the end of the table
4360 colorTable[size - 1] = current_color;
4363 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4366 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4372 m11 = m22 = m33 = 1.;
4373 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4374 clip = pe ? pe->d_func()->clip() : 0;
4377 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4379 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4381 Qt::BrushStyle brushStyle = qbrush_style(brush);
4382 switch (brushStyle) {
4383 case Qt::SolidPattern: {
4385 QColor c = qbrush_color(brush);
4386 QRgb rgba = c.rgba();
4387 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4388 if ((solid.color & 0xff000000) == 0
4389 && compositionMode == QPainter::CompositionMode_SourceOver) {
4395 case Qt::LinearGradientPattern:
4397 type = LinearGradient;
4398 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4399 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4400 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4401 gradient.spread = g->spread();
4403 QLinearGradientData &linearData = gradient.linear;
4405 linearData.origin.x = g->start().x();
4406 linearData.origin.y = g->start().y();
4407 linearData.end.x = g->finalStop().x();
4408 linearData.end.y = g->finalStop().y();
4412 case Qt::RadialGradientPattern:
4414 type = RadialGradient;
4415 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4416 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4417 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4418 gradient.spread = g->spread();
4420 QRadialGradientData &radialData = gradient.radial;
4422 QPointF center = g->center();
4423 radialData.center.x = center.x();
4424 radialData.center.y = center.y();
4425 radialData.center.radius = g->centerRadius();
4426 QPointF focal = g->focalPoint();
4427 radialData.focal.x = focal.x();
4428 radialData.focal.y = focal.y();
4429 radialData.focal.radius = g->focalRadius();
4433 case Qt::ConicalGradientPattern:
4435 type = ConicalGradient;
4436 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4437 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4438 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4439 gradient.spread = QGradient::RepeatSpread;
4441 QConicalGradientData &conicalData = gradient.conical;
4443 QPointF center = g->center();
4444 conicalData.center.x = center.x();
4445 conicalData.center.y = center.y();
4446 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4450 case Qt::Dense1Pattern:
4451 case Qt::Dense2Pattern:
4452 case Qt::Dense3Pattern:
4453 case Qt::Dense4Pattern:
4454 case Qt::Dense5Pattern:
4455 case Qt::Dense6Pattern:
4456 case Qt::Dense7Pattern:
4457 case Qt::HorPattern:
4458 case Qt::VerPattern:
4459 case Qt::CrossPattern:
4460 case Qt::BDiagPattern:
4461 case Qt::FDiagPattern:
4462 case Qt::DiagCrossPattern:
4465 tempImage = new QImage();
4466 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4467 initTexture(tempImage, alpha, QTextureData::Tiled);
4469 case Qt::TexturePattern:
4472 tempImage = new QImage();
4474 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4475 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4477 *tempImage = brush.textureImage();
4478 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4486 adjustSpanMethods();
4489 void QSpanData::adjustSpanMethods()
4499 unclipped_blend = 0;
4502 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4503 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4504 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4505 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4506 fillRect = rasterBuffer->drawHelper->fillRect;
4508 case LinearGradient:
4509 case RadialGradient:
4510 case ConicalGradient:
4511 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4514 unclipped_blend = qBlendTexture;
4515 if (!texture.imageData)
4516 unclipped_blend = 0;
4521 if (!unclipped_blend) {
4524 blend = unclipped_blend;
4525 } else if (clip->hasRectClip) {
4526 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4528 blend = qt_span_fill_clipped;
4532 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4535 // make sure we round off correctly in qdrawhelper.cpp
4536 delta.translate(1.0 / 65536, 1.0 / 65536);
4538 QTransform inv = (delta * matrix).inverted();
4551 const bool affine = !m13 && !m23;
4552 fast_matrix = affine
4553 && m11 * m11 + m21 * m21 < 1e4
4554 && m12 * m12 + m22 * m22 < 1e4
4558 adjustSpanMethods();
4561 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4563 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4564 if (!d || d->height == 0) {
4565 texture.imageData = 0;
4572 texture.bytesPerLine = 0;
4573 texture.format = QImage::Format_Invalid;
4574 texture.colorTable = 0;
4575 texture.hasAlpha = alpha != 256;
4577 texture.imageData = d->data;
4578 texture.width = d->width;
4579 texture.height = d->height;
4581 if (sourceRect.isNull()) {
4584 texture.x2 = texture.width;
4585 texture.y2 = texture.height;
4587 texture.x1 = sourceRect.x();
4588 texture.y1 = sourceRect.y();
4589 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4590 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4593 texture.bytesPerLine = d->bytes_per_line;
4595 texture.format = d->format;
4596 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4597 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4599 texture.const_alpha = alpha;
4600 texture.type = _type;
4602 adjustSpanMethods();
4607 \a x and \a y is relative to the midpoint of \a rect.
4609 static inline void drawEllipsePoints(int x, int y, int length,
4612 ProcessSpans pen_func, ProcessSpans brush_func,
4613 QSpanData *pen_data, QSpanData *brush_data)
4618 QT_FT_Span outline[4];
4619 const int midx = rect.x() + (rect.width() + 1) / 2;
4620 const int midy = rect.y() + (rect.height() + 1) / 2;
4626 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4627 outline[0].len = qMin(length, x - outline[0].x);
4629 outline[0].coverage = 255;
4633 outline[1].len = length;
4635 outline[1].coverage = 255;
4638 outline[2].x = outline[0].x;
4639 outline[2].len = outline[0].len;
4640 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4641 outline[2].coverage = 255;
4645 outline[3].len = length;
4646 outline[3].y = outline[2].y;
4647 outline[3].coverage = 255;
4649 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4653 fill[0].x = outline[0].x + outline[0].len - 1;
4654 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4655 fill[0].y = outline[1].y;
4656 fill[0].coverage = 255;
4659 fill[1].x = outline[2].x + outline[2].len - 1;
4660 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4661 fill[1].y = outline[3].y;
4662 fill[1].coverage = 255;
4664 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4665 n = qt_intersect_spans(fill, n, clip);
4667 brush_func(n, fill, brush_data);
4670 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4671 n = qt_intersect_spans(outline, n, clip);
4673 pen_func(n, outline, pen_data);
4679 Draws an ellipse using the integer point midpoint algorithm.
4681 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4682 ProcessSpans pen_func, ProcessSpans brush_func,
4683 QSpanData *pen_data, QSpanData *brush_data)
4685 const qreal a = qreal(rect.width()) / 2;
4686 const qreal b = qreal(rect.height()) / 2;
4687 qreal d = b*b - (a*a*b) + 0.25*a*a;
4690 int y = (rect.height() + 1) / 2;
4694 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4695 if (d < 0) { // select E
4698 } else { // select SE
4699 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4700 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4701 pen_func, brush_func, pen_data, brush_data);
4706 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4707 pen_func, brush_func, pen_data, brush_data);
4710 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4711 const int miny = rect.height() & 0x1;
4713 if (d < 0) { // select SE
4714 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4716 } else { // select S
4717 d += a*a*(-2*y + 3);
4720 drawEllipsePoints(x, y, 1, rect, clip,
4721 pen_func, brush_func, pen_data, brush_data);
4726 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4729 Draws the first \a pointCount points in the buffer \a points
4731 The default implementation converts the first \a pointCount QPoints in \a points
4732 to QPointFs and calls the floating point version of drawPoints.
4736 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4739 Reimplement this function to draw the largest ellipse that can be
4740 contained within rectangle \a rect.
4743 #ifdef QT_DEBUG_DRAW
4744 void dumpClip(int width, int height, const QClipData *clip)
4746 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4747 clipImg.fill(0xffff0000);
4754 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4756 for (int i = 0; i < clip->count; ++i) {
4757 const QSpan *span = ((QClipData *) clip)->spans() + i;
4758 for (int j = 0; j < span->len; ++j)
4759 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4760 x0 = qMin(x0, int(span->x));
4761 x1 = qMax(x1, int(span->x + span->len - 1));
4763 y0 = qMin(y0, int(span->y));
4764 y1 = qMax(y1, int(span->y));
4767 static int counter = 0;
4774 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4775 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));