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