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;
172 bool QRasterPaintEngine::clearTypeFontsEnabled()
174 static const bool result = winClearTypeFontsEnabled();
182 /********************************************************************************
185 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
186 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
187 static void qt_span_clip(int count, const QSpan *spans, void *userData);
193 Qt::ClipOperation operation;
199 LineDrawIncludeLastPixel
202 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
203 ProcessSpans pen_func, ProcessSpans brush_func,
204 QSpanData *pen_data, QSpanData *brush_data);
206 struct QRasterFloatPoint {
212 static const QRectF boundingRect(const QPointF *points, int pointCount)
214 const QPointF *e = points;
215 const QPointF *last = points + pointCount;
216 qreal minx, maxx, miny, maxy;
217 minx = maxx = e->x();
218 miny = maxy = e->y();
222 else if (e->x() > maxx)
226 else if (e->y() > maxy)
229 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
233 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
234 return (elementCount == 5 // 5-point polygon, check for closed rect
235 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
236 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
237 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
238 && pts[0] < pts[4] && pts[1] < pts[5]
240 (elementCount == 4 // 4-point polygon, check for unclosed rect
241 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
242 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
243 && pts[0] < pts[4] && pts[1] < pts[5]
248 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
250 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
253 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
255 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
258 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
259 qfixed c2x, qfixed c2y,
260 qfixed ex, qfixed ey,
263 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
264 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
265 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
269 #if !defined(QT_NO_DEBUG) && 0
270 static void qt_debug_path(const QPainterPath &path)
272 const char *names[] = {
279 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
280 for (int i=0; i<path.elementCount(); ++i) {
281 const QPainterPath::Element &e = path.elementAt(i);
282 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
283 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
288 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
289 QPaintEngineExPrivate(),
296 \class QRasterPaintEngine
301 \brief The QRasterPaintEngine class enables hardware acceleration
302 of painting operations in Qt for Embedded Linux.
304 Note that this functionality is only available in
305 \l{Qt for Embedded Linux}.
307 In \l{Qt for Embedded Linux}, painting is a pure software
308 implementation. But starting with Qt 4.2, it is
309 possible to add an accelerated graphics driver to take advantage
310 of available hardware resources.
312 Hardware acceleration is accomplished by creating a custom screen
313 driver, accelerating the copying from memory to the screen, and
314 implementing a custom paint engine accelerating the various
315 painting operations. Then a custom paint device (derived from the
316 QCustomRasterPaintDevice class) and a custom window surface
317 (derived from QWSWindowSurface) must be implemented to make
318 \l{Qt for Embedded Linux} aware of the accelerated driver.
320 \note The QRasterPaintEngine class does not support 8-bit images.
321 Instead, they need to be converted to a supported format, such as
322 QImage::Format_ARGB32_Premultiplied.
324 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
325 documentation for details.
327 \sa QCustomRasterPaintDevice, QPaintEngine
331 \fn Type QRasterPaintEngine::type() const
337 \relates QRasterPaintEngine
339 A struct equivalent to QT_FT_Span, containing a position (x,
340 y), the span's length in pixels and its color/coverage (a value
341 ranging from 0 to 255).
347 Creates a raster based paint engine for operating on the given
348 \a device, with the complete set of \l
349 {QPaintEngine::PaintEngineFeature}{paint engine features and
352 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
353 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
355 d_func()->device = device;
362 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
365 d_func()->device = device;
369 void QRasterPaintEngine::init()
371 Q_D(QRasterPaintEngine);
378 // The antialiasing raster.
379 d->grayRaster.reset(new QT_FT_Raster);
380 Q_CHECK_PTR(d->grayRaster.data());
381 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
382 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
385 d->rasterizer.reset(new QRasterizer);
386 d->rasterBuffer.reset(new QRasterBuffer());
387 d->outlineMapper.reset(new QOutlineMapper);
388 d->outlinemapper_xform_dirty = true;
390 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
391 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
392 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
394 d->baseClip.reset(new QClipData(d->device->height()));
395 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
397 d->image_filler.init(d->rasterBuffer.data(), this);
398 d->image_filler.type = QSpanData::Texture;
400 d->image_filler_xform.init(d->rasterBuffer.data(), this);
401 d->image_filler_xform.type = QSpanData::Texture;
403 d->solid_color_filler.init(d->rasterBuffer.data(), this);
404 d->solid_color_filler.type = QSpanData::Solid;
406 d->deviceDepth = d->device->depth();
408 d->mono_surface = false;
409 gccaps &= ~PorterDuff;
411 QImage::Format format = QImage::Format_Invalid;
413 switch (d->device->devType()) {
414 case QInternal::Pixmap:
415 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
417 case QInternal::Image:
418 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
421 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
427 case QImage::Format_MonoLSB:
428 case QImage::Format_Mono:
429 d->mono_surface = true;
431 case QImage::Format_ARGB8565_Premultiplied:
432 case QImage::Format_ARGB8555_Premultiplied:
433 case QImage::Format_ARGB6666_Premultiplied:
434 case QImage::Format_ARGB4444_Premultiplied:
435 case QImage::Format_ARGB32_Premultiplied:
436 case QImage::Format_ARGB32:
437 gccaps |= PorterDuff;
439 case QImage::Format_RGB32:
440 case QImage::Format_RGB444:
441 case QImage::Format_RGB555:
442 case QImage::Format_RGB666:
443 case QImage::Format_RGB888:
444 case QImage::Format_RGB16:
455 Destroys this paint engine.
457 QRasterPaintEngine::~QRasterPaintEngine()
459 Q_D(QRasterPaintEngine);
461 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
467 bool QRasterPaintEngine::begin(QPaintDevice *device)
469 Q_D(QRasterPaintEngine);
471 if (device->devType() == QInternal::Pixmap) {
472 QPixmap *pixmap = static_cast<QPixmap *>(device);
473 QPlatformPixmap *pd = pixmap->handle();
474 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
475 d->device = pd->buffer();
480 // Make sure QPaintEngine::paintDevice() returns the proper device.
483 Q_ASSERT(d->device->devType() == QInternal::Image
484 || d->device->devType() == QInternal::CustomRaster);
486 d->systemStateChanged();
488 QRasterPaintEngineState *s = state();
489 ensureOutlineMapper();
490 d->outlineMapper->m_clip_rect = d->deviceRect;
492 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
493 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
494 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
495 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
497 d->rasterizer->setClipRect(d->deviceRect);
499 s->penData.init(d->rasterBuffer.data(), this);
500 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
501 s->stroker = &d->basicStroker;
502 d->basicStroker.setClipRect(d->deviceRect);
504 s->brushData.init(d->rasterBuffer.data(), this);
505 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
507 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
509 setDirty(DirtyBrushOrigin);
512 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
513 << ") devType:" << device->devType()
514 << "devRect:" << d->deviceRect;
516 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
521 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
522 #if defined(Q_OS_WIN)
523 else if (clearTypeFontsEnabled())
528 QImage::Format format = static_cast<QImage *>(d->device)->format();
529 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
530 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
532 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
534 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
543 bool QRasterPaintEngine::end()
546 Q_D(QRasterPaintEngine);
547 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
549 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
559 void QRasterPaintEngine::releaseBuffer()
561 Q_D(QRasterPaintEngine);
562 d->rasterBuffer.reset(new QRasterBuffer);
568 QSize QRasterPaintEngine::size() const
570 Q_D(const QRasterPaintEngine);
571 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
578 void QRasterPaintEngine::saveBuffer(const QString &s) const
580 Q_D(const QRasterPaintEngine);
581 d->rasterBuffer->bufferImage().save(s, "PNG");
588 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
590 QRasterPaintEngineState *s = state();
591 // FALCON: get rid of this line, see drawImage call below.
593 QTransform::TransformationType txop = s->matrix.type();
597 case QTransform::TxNone:
598 s->flags.int_xform = true;
601 case QTransform::TxTranslate:
602 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
603 && qreal(int(s->matrix.dy())) == s->matrix.dy();
606 case QTransform::TxScale:
607 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
608 && qreal(int(s->matrix.dy())) == s->matrix.dy()
609 && qreal(int(s->matrix.m11())) == s->matrix.m11()
610 && qreal(int(s->matrix.m22())) == s->matrix.m22();
613 default: // shear / perspective...
614 s->flags.int_xform = false;
618 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
620 ensureOutlineMapper();
625 QRasterPaintEngineState::~QRasterPaintEngineState()
627 if (flags.has_clip_ownership)
632 QRasterPaintEngineState::QRasterPaintEngineState()
644 flags.fast_pen = true;
645 flags.antialiased = false;
646 flags.bilinear = false;
647 flags.fast_text = true;
648 flags.int_xform = true;
649 flags.tx_noshear = true;
650 flags.fast_images = true;
653 flags.has_clip_ownership = false;
658 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
663 , strokeFlags(s.strokeFlags)
664 , lastBrush(s.lastBrush)
665 , brushData(s.brushData)
666 , fillFlags(s.fillFlags)
667 , pixmapFlags(s.pixmapFlags)
668 , intOpacity(s.intOpacity)
672 , flag_bits(s.flag_bits)
674 brushData.tempImage = 0;
675 penData.tempImage = 0;
676 flags.has_clip_ownership = false;
682 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
684 QRasterPaintEngineState *s;
686 s = new QRasterPaintEngineState();
688 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
696 void QRasterPaintEngine::setState(QPainterState *s)
698 Q_D(QRasterPaintEngine);
699 QPaintEngineEx::setState(s);
700 d->rasterBuffer->compositionMode = s->composition_mode;
704 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
709 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
716 void QRasterPaintEngine::penChanged()
719 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
721 QRasterPaintEngineState *s = state();
722 s->strokeFlags |= DirtyPen;
723 s->dirty |= DirtyPen;
729 void QRasterPaintEngine::updatePen(const QPen &pen)
731 Q_D(QRasterPaintEngine);
732 QRasterPaintEngineState *s = state();
734 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
737 Qt::PenStyle pen_style = qpen_style(pen);
742 s->penData.clip = d->clip();
743 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
745 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
746 || pen.brush().transform().type() >= QTransform::TxNone) {
747 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
750 // Slightly ugly handling of an uncommon case... We need to change
751 // the pen because it is reused in draw_midpoint to decide dashed
753 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
754 pen_style = Qt::SolidLine;
755 s->lastPen.setStyle(Qt::SolidLine);
758 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
759 d->basicStroker.setCapStyle(qpen_capStyle(pen));
760 d->basicStroker.setMiterLimit(pen.miterLimit());
762 qreal penWidth = qpen_widthf(pen);
764 d->basicStroker.setStrokeWidth(1);
766 d->basicStroker.setStrokeWidth(penWidth);
768 if(pen_style == Qt::SolidLine) {
769 s->stroker = &d->basicStroker;
770 } else if (pen_style != Qt::NoPen) {
772 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
773 if (pen.isCosmetic()) {
774 d->dashStroker->setClipRect(d->deviceRect);
776 // ### I've seen this inverted devrect multiple places now...
777 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
778 d->dashStroker->setClipRect(clipRect);
780 d->dashStroker->setDashPattern(pen.dashPattern());
781 d->dashStroker->setDashOffset(pen.dashOffset());
782 s->stroker = d->dashStroker.data();
787 ensureRasterState(); // needed because of tx_noshear...
788 s->flags.fast_pen = pen_style > Qt::NoPen
790 && ((pen.isCosmetic() && penWidth <= 1)
791 || (!pen.isCosmetic() && s->flags.tx_noshear && penWidth * s->txscale <= 1));
793 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
803 void QRasterPaintEngine::brushOriginChanged()
805 QRasterPaintEngineState *s = state();
807 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
810 s->fillFlags |= DirtyBrushOrigin;
817 void QRasterPaintEngine::brushChanged()
819 QRasterPaintEngineState *s = state();
821 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
823 s->fillFlags |= DirtyBrush;
832 void QRasterPaintEngine::updateBrush(const QBrush &brush)
835 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
837 Q_D(QRasterPaintEngine);
838 QRasterPaintEngineState *s = state();
839 // must set clip prior to setup, as setup uses it...
840 s->brushData.clip = d->clip();
841 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
842 if (s->fillFlags & DirtyTransform
843 || brush.transform().type() >= QTransform::TxNone)
844 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
845 s->lastBrush = brush;
849 void QRasterPaintEngine::updateOutlineMapper()
851 Q_D(QRasterPaintEngine);
852 d->outlineMapper->setMatrix(state()->matrix);
855 void QRasterPaintEngine::updateRasterState()
857 QRasterPaintEngineState *s = state();
859 if (s->dirty & DirtyTransform)
860 updateMatrix(s->matrix);
862 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
863 const QPainter::CompositionMode mode = s->composition_mode;
864 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
865 && s->intOpacity == 256
866 && (mode == QPainter::CompositionMode_Source
867 || (mode == QPainter::CompositionMode_SourceOver
868 && qAlpha(s->penData.solid.color) == 255));
878 void QRasterPaintEngine::opacityChanged()
880 QRasterPaintEngineState *s = state();
883 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
886 s->fillFlags |= DirtyOpacity;
887 s->strokeFlags |= DirtyOpacity;
888 s->pixmapFlags |= DirtyOpacity;
889 s->dirty |= DirtyOpacity;
890 s->intOpacity = (int) (s->opacity * 256);
896 void QRasterPaintEngine::compositionModeChanged()
898 Q_D(QRasterPaintEngine);
899 QRasterPaintEngineState *s = state();
902 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
905 s->fillFlags |= DirtyCompositionMode;
906 s->dirty |= DirtyCompositionMode;
908 s->strokeFlags |= DirtyCompositionMode;
909 d->rasterBuffer->compositionMode = s->composition_mode;
911 d->recalculateFastImages();
917 void QRasterPaintEngine::renderHintsChanged()
919 QRasterPaintEngineState *s = state();
922 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
925 bool was_aa = s->flags.antialiased;
926 bool was_bilinear = s->flags.bilinear;
928 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
929 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
931 if (was_aa != s->flags.antialiased)
932 s->strokeFlags |= DirtyHints;
934 if (was_bilinear != s->flags.bilinear) {
935 s->strokeFlags |= DirtyPen;
936 s->fillFlags |= DirtyBrush;
939 Q_D(QRasterPaintEngine);
940 d->recalculateFastImages();
946 void QRasterPaintEngine::transformChanged()
948 QRasterPaintEngineState *s = state();
951 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
954 s->fillFlags |= DirtyTransform;
955 s->strokeFlags |= DirtyTransform;
957 s->dirty |= DirtyTransform;
959 Q_D(QRasterPaintEngine);
960 d->recalculateFastImages();
966 void QRasterPaintEngine::clipEnabledChanged()
968 QRasterPaintEngineState *s = state();
971 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
975 s->clip->enabled = s->clipEnabled;
976 s->fillFlags |= DirtyClipEnabled;
977 s->strokeFlags |= DirtyClipEnabled;
978 s->pixmapFlags |= DirtyClipEnabled;
982 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
984 SrcOverBlendFunc func,
989 if (alpha == 0 || !clip.isValid())
992 Q_ASSERT(img.depth() >= 8);
994 int srcBPL = img.bytesPerLine();
995 const uchar *srcBits = img.bits();
996 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
997 int iw = img.width();
998 int ih = img.height();
1000 if (!sr.isEmpty()) {
1003 // Adjust the image according to the source offset...
1004 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1007 // adapt the x parameters
1008 int x = qRound(pt.x());
1010 int cx2 = clip.x() + clip.width();
1013 srcBits += srcSize * d;
1018 int d = x + iw - cx2;
1024 // adapt the y paremeters...
1026 int cy2 = clip.y() + clip.height();
1027 int y = qRound(pt.y());
1030 srcBits += srcBPL * d;
1035 int d = y + ih - cy2;
1041 // call the blend function...
1042 int dstSize = rasterBuffer->bytesPerPixel();
1043 int dstBPL = rasterBuffer->bytesPerLine();
1044 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1051 void QRasterPaintEnginePrivate::systemStateChanged()
1053 QRect clipRect(0, 0,
1054 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1055 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1057 if (!systemClip.isEmpty()) {
1058 QRegion clippedDeviceRgn = systemClip & clipRect;
1059 deviceRect = clippedDeviceRgn.boundingRect();
1060 baseClip->setClipRegion(clippedDeviceRgn);
1062 deviceRect = clipRect;
1063 baseClip->setClipRect(deviceRect);
1065 #ifdef QT_DEBUG_DRAW
1066 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1069 exDeviceRect = deviceRect;
1071 Q_Q(QRasterPaintEngine);
1072 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1073 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1074 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1077 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1079 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1082 Q_Q(QRasterPaintEngine);
1083 bool bilinear = q->state()->flags.bilinear;
1085 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1086 spanData->setupMatrix(b.transform() * m, bilinear);
1088 if (m.type() <= QTransform::TxTranslate) {
1089 // specialize setupMatrix for translation matrices
1090 // to avoid needless matrix inversion
1098 spanData->dx = -m.dx();
1099 spanData->dy = -m.dy();
1100 spanData->txop = m.type();
1101 spanData->bilinear = bilinear;
1102 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1103 spanData->adjustSpanMethods();
1105 spanData->setupMatrix(m, bilinear);
1110 // #define QT_CLIPPING_RATIOS
1112 #ifdef QT_CLIPPING_RATIOS
1117 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1119 if (d->clip()->hasRectClip)
1121 if (d->clip()->hasRegionClip)
1125 if ((totalClips % 5000) == 0) {
1126 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1127 rectClips * 100.0 / (qreal) totalClips,
1128 regionClips * 100.0 / (qreal) totalClips,
1129 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1138 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1140 if (s->flags.has_clip_ownership)
1143 s->flags.has_clip_ownership = false;
1146 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1148 s->fillFlags |= QPaintEngine::DirtyClipPath;
1149 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1150 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1152 d->solid_color_filler.clip = d->clip();
1153 d->solid_color_filler.adjustSpanMethods();
1155 #ifdef QT_DEBUG_DRAW
1156 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1165 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1167 #ifdef QT_DEBUG_DRAW
1168 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1170 if (path.elements()) {
1171 for (int i=0; i<path.elementCount(); ++i) {
1172 qDebug() << " - " << path.elements()[i]
1173 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1176 for (int i=0; i<path.elementCount(); ++i) {
1177 qDebug() << " ---- "
1178 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1183 Q_D(QRasterPaintEngine);
1184 QRasterPaintEngineState *s = state();
1186 const qreal *points = path.points();
1187 const QPainterPath::ElementType *types = path.elements();
1189 // There are some cases that are not supported by clip(QRect)
1190 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1191 if (s->matrix.type() <= QTransform::TxScale
1192 && ((path.shape() == QVectorPath::RectangleHint)
1193 || (isRect(points, path.elementCount())
1194 && (!types || (types[0] == QPainterPath::MoveToElement
1195 && types[1] == QPainterPath::LineToElement
1196 && types[2] == QPainterPath::LineToElement
1197 && types[3] == QPainterPath::LineToElement))))) {
1198 #ifdef QT_DEBUG_DRAW
1199 qDebug() << " --- optimizing vector clip to rect clip...";
1202 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1203 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1208 if (op == Qt::NoClip) {
1209 qrasterpaintengine_state_setNoClip(s);
1212 QClipData *base = d->baseClip.data();
1214 // Intersect with current clip when available...
1215 if (op == Qt::IntersectClip && s->clip)
1218 // We always intersect, except when there is nothing to
1219 // intersect with, in which case we simplify the operation to
1221 Qt::ClipOperation isectOp = Qt::IntersectClip;
1223 isectOp = Qt::ReplaceClip;
1225 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1226 newClip->initialize();
1227 ClipData clipData = { base, newClip, isectOp };
1228 ensureOutlineMapper();
1229 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1233 if (s->flags.has_clip_ownership)
1237 s->flags.has_clip_ownership = true;
1239 qrasterpaintengine_dirty_clip(d, s);
1247 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1249 #ifdef QT_DEBUG_DRAW
1250 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1253 QRasterPaintEngineState *s = state();
1255 if (op == Qt::NoClip) {
1256 qrasterpaintengine_state_setNoClip(s);
1258 } else if (s->matrix.type() > QTransform::TxScale) {
1259 QPaintEngineEx::clip(rect, op);
1262 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1263 QPaintEngineEx::clip(rect, op);
1269 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1271 Q_D(QRasterPaintEngine);
1272 QRect clipRect = r & d->deviceRect;
1273 QRasterPaintEngineState *s = state();
1275 if (op == Qt::ReplaceClip || s->clip == 0) {
1277 // No current clip, hence we intersect with sysclip and be
1279 QRegion clipRegion = systemClip();
1280 QClipData *clip = new QClipData(d->rasterBuffer->height());
1282 if (clipRegion.isEmpty())
1283 clip->setClipRect(clipRect);
1285 clip->setClipRegion(clipRegion & clipRect);
1287 if (s->flags.has_clip_ownership)
1291 s->clip->enabled = true;
1292 s->flags.has_clip_ownership = true;
1294 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1295 QClipData *base = s->clip;
1298 if (base->hasRectClip || base->hasRegionClip) {
1299 if (!s->flags.has_clip_ownership) {
1300 s->clip = new QClipData(d->rasterBuffer->height());
1301 s->flags.has_clip_ownership = true;
1303 if (base->hasRectClip)
1304 s->clip->setClipRect(base->clipRect & clipRect);
1306 s->clip->setClipRegion(base->clipRegion & clipRect);
1307 s->clip->enabled = true;
1315 qrasterpaintengine_dirty_clip(d, s);
1323 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1325 #ifdef QT_DEBUG_DRAW
1326 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1329 Q_D(QRasterPaintEngine);
1331 if (region.rectCount() == 1) {
1332 clip(region.boundingRect(), op);
1336 QRasterPaintEngineState *s = state();
1337 const QClipData *clip = d->clip();
1338 const QClipData *baseClip = d->baseClip.data();
1340 if (op == Qt::NoClip) {
1341 qrasterpaintengine_state_setNoClip(s);
1342 } else if (s->matrix.type() > QTransform::TxScale
1343 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1344 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1345 QPaintEngineEx::clip(region, op);
1347 const QClipData *curClip;
1350 if (op == Qt::IntersectClip)
1355 if (s->flags.has_clip_ownership) {
1359 newClip = new QClipData(d->rasterBuffer->height());
1361 s->flags.has_clip_ownership = true;
1364 QRegion r = s->matrix.map(region);
1365 if (curClip->hasRectClip)
1366 newClip->setClipRegion(r & curClip->clipRect);
1367 else if (curClip->hasRegionClip)
1368 newClip->setClipRegion(r & curClip->clipRegion);
1370 qrasterpaintengine_dirty_clip(d, s);
1375 \fn const QClipData *QRasterPaintEngine::clipData() const
1384 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1386 #ifdef QT_DEBUG_DRAW
1387 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1390 if (!fillData->blend)
1393 Q_D(QRasterPaintEngine);
1395 const QRectF controlPointRect = path.controlPointRect();
1397 QRasterPaintEngineState *s = state();
1398 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1399 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1400 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1401 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1402 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1403 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1405 if (!s->flags.antialiased && !do_clip) {
1406 d->initializeRasterizer(fillData);
1407 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1411 ensureOutlineMapper();
1412 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1415 static void fillRect_normalized(const QRect &r, QSpanData *data,
1416 QRasterPaintEnginePrivate *pe)
1420 bool rectClipped = true;
1423 x1 = qMax(r.x(), data->clip->xmin);
1424 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1425 y1 = qMax(r.y(), data->clip->ymin);
1426 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1427 rectClipped = data->clip->hasRectClip;
1430 x1 = qMax(r.x(), pe->deviceRect.x());
1431 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1432 y1 = qMax(r.y(), pe->deviceRect.y());
1433 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1435 x1 = qMax(r.x(), 0);
1436 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1437 y1 = qMax(r.y(), 0);
1438 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1441 if (x2 <= x1 || y2 <= y1)
1444 const int width = x2 - x1;
1445 const int height = y2 - y1;
1447 bool isUnclipped = rectClipped
1448 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1450 if (pe && isUnclipped) {
1451 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1453 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1454 || (mode == QPainter::CompositionMode_SourceOver
1455 && qAlpha(data->solid.color) == 255)))
1457 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1463 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1465 const int nspans = 256;
1466 QT_FT_Span spans[nspans];
1468 Q_ASSERT(data->blend);
1471 int n = qMin(nspans, y2 - y);
1475 spans[i].len = width;
1477 spans[i].coverage = 255;
1481 blend(n, spans, data);
1489 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1491 #ifdef QT_DEBUG_DRAW
1492 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1494 Q_D(QRasterPaintEngine);
1495 ensureRasterState();
1496 QRasterPaintEngineState *s = state();
1500 if (s->brushData.blend) {
1501 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1502 const QRect *r = rects;
1503 const QRect *lastRect = rects + rectCount;
1505 int offset_x = int(s->matrix.dx());
1506 int offset_y = int(s->matrix.dy());
1507 while (r < lastRect) {
1508 QRect rect = r->normalized();
1509 QRect rr = rect.translated(offset_x, offset_y);
1510 fillRect_normalized(rr, &s->brushData, d);
1514 QRectVectorPath path;
1515 for (int i=0; i<rectCount; ++i) {
1517 fill(path, s->brush);
1523 if (s->penData.blend) {
1524 QRectVectorPath path;
1525 if (s->flags.fast_pen) {
1526 QCosmeticStroker stroker(s, d->deviceRect);
1527 for (int i = 0; i < rectCount; ++i) {
1529 stroker.drawPath(path);
1532 for (int i = 0; i < rectCount; ++i) {
1534 stroke(path, s->pen);
1543 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1545 #ifdef QT_DEBUG_DRAW
1546 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1548 #ifdef QT_FAST_SPANS
1549 Q_D(QRasterPaintEngine);
1550 ensureRasterState();
1551 QRasterPaintEngineState *s = state();
1554 if (s->flags.tx_noshear) {
1556 if (s->brushData.blend) {
1557 d->initializeRasterizer(&s->brushData);
1558 for (int i = 0; i < rectCount; ++i) {
1559 const QRectF &rect = rects[i].normalized();
1562 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1563 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1564 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1569 if (s->penData.blend) {
1570 QRectVectorPath path;
1571 if (s->flags.fast_pen) {
1572 QCosmeticStroker stroker(s, d->deviceRect);
1573 for (int i = 0; i < rectCount; ++i) {
1575 stroker.drawPath(path);
1578 for (int i = 0; i < rectCount; ++i) {
1580 QPaintEngineEx::stroke(path, s->lastPen);
1587 #endif // QT_FAST_SPANS
1588 QPaintEngineEx::drawRects(rects, rectCount);
1595 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1597 Q_D(QRasterPaintEngine);
1598 QRasterPaintEngineState *s = state();
1601 if (!s->penData.blend)
1604 if (s->flags.fast_pen) {
1605 QCosmeticStroker stroker(s, d->deviceRect);
1606 stroker.drawPath(path);
1607 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1608 qreal width = s->lastPen.isCosmetic()
1609 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1610 : qpen_widthf(s->lastPen) * s->txscale;
1612 qreal dashOffset = s->lastPen.dashOffset();
1614 qreal patternLength = 0;
1615 const QVector<qreal> pattern = s->lastPen.dashPattern();
1616 for (int i = 0; i < pattern.size(); ++i)
1617 patternLength += pattern.at(i);
1619 if (patternLength > 0) {
1620 int n = qFloor(dashOffset / patternLength);
1621 dashOffset -= n * patternLength;
1622 while (dashOffset >= pattern.at(dashIndex)) {
1623 dashOffset -= pattern.at(dashIndex);
1624 if (++dashIndex >= pattern.size())
1630 Q_D(QRasterPaintEngine);
1631 d->initializeRasterizer(&s->penData);
1632 int lineCount = path.elementCount() / 2;
1633 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1635 for (int i = 0; i < lineCount; ++i) {
1636 if (lines[i].p1() == lines[i].p2()) {
1637 if (s->lastPen.capStyle() != Qt::FlatCap) {
1638 QPointF p = lines[i].p1();
1639 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1640 QPointF(p.x() + width*0.5, p.y())));
1641 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1646 const QLineF line = s->matrix.map(lines[i]);
1647 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1648 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1649 width / line.length(),
1650 s->lastPen.capStyle() == Qt::SquareCap);
1652 d->rasterizeLine_dashed(line, width,
1653 &dashIndex, &dashOffset, &inDash);
1658 QPaintEngineEx::stroke(path, pen);
1661 static inline QRect toNormalizedFillRect(const QRectF &rect)
1663 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1664 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1665 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1666 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1673 return QRect(x1, y1, x2 - x1, y2 - y1);
1679 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1683 #ifdef QT_DEBUG_DRAW
1684 QRectF rf = path.controlPointRect();
1685 qDebug() << "QRasterPaintEngine::fill(): "
1686 << "size=" << path.elementCount()
1687 << ", hints=" << hex << path.hints()
1691 Q_D(QRasterPaintEngine);
1692 QRasterPaintEngineState *s = state();
1695 if (!s->brushData.blend)
1698 if (path.shape() == QVectorPath::RectangleHint) {
1699 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1700 const qreal *p = path.points();
1701 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1702 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1703 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1706 ensureRasterState();
1707 if (s->flags.tx_noshear) {
1708 d->initializeRasterizer(&s->brushData);
1709 // ### Is normalizing really necessary here?
1710 const qreal *p = path.points();
1711 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1713 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1714 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1715 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1721 // ### Optimize for non transformed ellipses and rectangles...
1722 QRectF cpRect = path.controlPointRect();
1723 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1724 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1727 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1728 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1729 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1730 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1732 // ### Falonc: implement....
1733 // if (!s->flags.antialiased && !do_clip) {
1734 // d->initializeRasterizer(&s->brushData);
1735 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1739 ensureOutlineMapper();
1740 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1743 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1745 Q_D(QRasterPaintEngine);
1746 QRasterPaintEngineState *s = state();
1748 if (!s->flags.antialiased) {
1749 uint txop = s->matrix.type();
1750 if (txop == QTransform::TxNone) {
1751 fillRect_normalized(toNormalizedFillRect(r), data, d);
1753 } else if (txop == QTransform::TxTranslate) {
1754 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1755 fillRect_normalized(rr, data, d);
1757 } else if (txop == QTransform::TxScale) {
1758 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1759 fillRect_normalized(rr, data, d);
1763 ensureRasterState();
1764 if (s->flags.tx_noshear) {
1765 d->initializeRasterizer(data);
1766 QRectF nr = r.normalized();
1767 if (!nr.isEmpty()) {
1768 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1769 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1770 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1777 ensureOutlineMapper();
1778 fillPath(path, data);
1784 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1786 #ifdef QT_DEBUG_DRAW
1787 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1789 QRasterPaintEngineState *s = state();
1792 if (!s->brushData.blend)
1795 fillRect(r, &s->brushData);
1801 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1803 #ifdef QT_DEBUG_DRAW
1804 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1806 Q_D(QRasterPaintEngine);
1807 QRasterPaintEngineState *s = state();
1809 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1810 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1811 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1814 d->solid_color_filler.clip = d->clip();
1815 d->solid_color_filler.adjustSpanMethods();
1816 fillRect(r, &d->solid_color_filler);
1819 static inline bool isAbove(const QPointF *a, const QPointF *b)
1821 return a->y() < b->y();
1824 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1829 Q_ASSERT(pointCount >= 2);
1831 QVector<const QPointF *> sorted;
1832 sorted.reserve(pointCount);
1834 upper->reserve(pointCount * 3 / 4);
1835 lower->reserve(pointCount * 3 / 4);
1837 for (int i = 0; i < pointCount; ++i)
1838 sorted << points + i;
1840 qSort(sorted.begin(), sorted.end(), isAbove);
1842 qreal splitY = sorted.at(sorted.size() / 2)->y();
1844 const QPointF *end = points + pointCount;
1845 const QPointF *last = end - 1;
1847 QVector<QPointF> *bin[2] = { upper, lower };
1849 for (const QPointF *p = points; p < end; ++p) {
1850 int side = p->y() < splitY;
1851 int lastSide = last->y() < splitY;
1853 if (side != lastSide) {
1854 if (qFuzzyCompare(p->y(), splitY)) {
1855 bin[!side]->append(*p);
1856 } else if (qFuzzyCompare(last->y(), splitY)) {
1857 bin[side]->append(*last);
1859 QPointF delta = *p - *last;
1860 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1862 bin[0]->append(intersection);
1863 bin[1]->append(intersection);
1867 bin[side]->append(*p);
1872 // give up if we couldn't reduce the point count
1873 return upper->size() < pointCount && lower->size() < pointCount;
1879 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1881 Q_D(QRasterPaintEngine);
1882 QRasterPaintEngineState *s = state();
1884 const int maxPoints = 0xffff;
1886 // max amount of points that raster engine can reliably handle
1887 if (pointCount > maxPoints) {
1888 QVector<QPointF> upper, lower;
1890 if (splitPolygon(points, pointCount, &upper, &lower)) {
1891 fillPolygon(upper.constData(), upper.size(), mode);
1892 fillPolygon(lower.constData(), lower.size(), mode);
1894 qWarning("Polygon too complex for filling.");
1899 // Compose polygon fill..,
1900 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1901 ensureOutlineMapper();
1902 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1905 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1907 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1913 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1915 Q_D(QRasterPaintEngine);
1916 QRasterPaintEngineState *s = state();
1918 #ifdef QT_DEBUG_DRAW
1919 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1920 for (int i=0; i<pointCount; ++i)
1921 qDebug() << " - " << points[i];
1923 Q_ASSERT(pointCount >= 2);
1925 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1926 QRectF r(points[0], points[2]);
1932 if (mode != PolylineMode) {
1935 if (s->brushData.blend)
1936 fillPolygon(points, pointCount, mode);
1939 // Do the outline...
1940 if (s->penData.blend) {
1941 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1942 if (s->flags.fast_pen) {
1943 QCosmeticStroker stroker(s, d->deviceRect);
1944 stroker.drawPath(vp);
1946 QPaintEngineEx::stroke(vp, s->lastPen);
1954 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1956 Q_D(QRasterPaintEngine);
1957 QRasterPaintEngineState *s = state();
1959 #ifdef QT_DEBUG_DRAW
1960 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1961 for (int i=0; i<pointCount; ++i)
1962 qDebug() << " - " << points[i];
1964 Q_ASSERT(pointCount >= 2);
1965 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1966 QRect r(points[0].x(),
1968 points[2].x() - points[0].x(),
1969 points[2].y() - points[0].y());
1977 if (mode != PolylineMode) {
1979 if (s->brushData.blend) {
1980 // Compose polygon fill..,
1981 ensureOutlineMapper();
1982 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1983 d->outlineMapper->moveTo(*points);
1984 const QPoint *p = points;
1985 const QPoint *ep = points + pointCount - 1;
1987 d->outlineMapper->lineTo(*(++p));
1989 d->outlineMapper->endOutline();
1992 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1994 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1998 // Do the outline...
1999 if (s->penData.blend) {
2000 int count = pointCount * 2;
2001 QVarLengthArray<qreal> fpoints(count);
2002 for (int i=0; i<count; ++i)
2003 fpoints[i] = ((int *) points)[i];
2004 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2006 if (s->flags.fast_pen) {
2007 QCosmeticStroker stroker(s, d->deviceRect);
2008 stroker.drawPath(vp);
2010 QPaintEngineEx::stroke(vp, s->lastPen);
2018 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2020 #ifdef QT_DEBUG_DRAW
2021 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2024 QPlatformPixmap *pd = pixmap.handle();
2025 if (pd->classId() == QPlatformPixmap::RasterClass) {
2026 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2027 if (image.depth() == 1) {
2028 Q_D(QRasterPaintEngine);
2029 QRasterPaintEngineState *s = state();
2030 if (s->matrix.type() <= QTransform::TxTranslate) {
2032 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2034 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2037 QRasterPaintEngine::drawImage(pos, image);
2040 const QImage image = pixmap.toImage();
2041 if (pixmap.depth() == 1) {
2042 Q_D(QRasterPaintEngine);
2043 QRasterPaintEngineState *s = state();
2044 if (s->matrix.type() <= QTransform::TxTranslate) {
2046 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2048 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2051 QRasterPaintEngine::drawImage(pos, image);
2059 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2061 #ifdef QT_DEBUG_DRAW
2062 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2065 QPlatformPixmap* pd = pixmap.handle();
2066 if (pd->classId() == QPlatformPixmap::RasterClass) {
2067 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2068 if (image.depth() == 1) {
2069 Q_D(QRasterPaintEngine);
2070 QRasterPaintEngineState *s = state();
2071 if (s->matrix.type() <= QTransform::TxTranslate
2072 && r.size() == sr.size()
2073 && r.size() == pixmap.size()) {
2075 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2078 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2081 drawImage(r, image, sr);
2084 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2085 const QImage image = pd->toImage(clippedSource);
2086 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2087 if (image.depth() == 1) {
2088 Q_D(QRasterPaintEngine);
2089 QRasterPaintEngineState *s = state();
2090 if (s->matrix.type() <= QTransform::TxTranslate
2091 && r.size() == sr.size()
2092 && r.size() == pixmap.size()) {
2094 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2097 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2100 drawImage(r, image, translatedSource);
2105 static inline int fast_ceil_positive(const qreal &v)
2107 const int iv = int(v);
2114 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2116 const int xmin = int(rect.x());
2117 const int xmax = int(fast_ceil_positive(rect.right()));
2118 const int ymin = int(rect.y());
2119 const int ymax = int(fast_ceil_positive(rect.bottom()));
2120 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2126 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2128 #ifdef QT_DEBUG_DRAW
2129 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2132 Q_D(QRasterPaintEngine);
2133 QRasterPaintEngineState *s = state();
2135 if (s->matrix.type() > QTransform::TxTranslate) {
2136 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2138 QRectF(0, 0, img.width(), img.height()));
2141 const QClipData *clip = d->clip();
2142 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2144 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2145 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2148 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2150 } else if (clip->hasRectClip) {
2151 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2159 d->image_filler.clip = clip;
2160 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2161 if (!d->image_filler.blend)
2163 d->image_filler.dx = -pt.x();
2164 d->image_filler.dy = -pt.y();
2165 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2167 fillRect_normalized(rr, &d->image_filler, d);
2172 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2174 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2185 inline RotationType qRotationType(const QTransform &transform)
2187 QTransform::TransformationType type = transform.type();
2189 if (type > QTransform::TxRotate)
2192 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2193 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2196 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2197 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2200 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2201 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2207 inline bool isPixelAligned(const QRectF &rect) {
2208 return QRectF(rect.toRect()) == rect;
2215 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2216 Qt::ImageConversionFlags)
2218 #ifdef QT_DEBUG_DRAW
2219 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2225 Q_D(QRasterPaintEngine);
2226 QRasterPaintEngineState *s = state();
2227 int sr_l = qFloor(sr.left());
2228 int sr_r = qCeil(sr.right()) - 1;
2229 int sr_t = qFloor(sr.top());
2230 int sr_b = qCeil(sr.bottom()) - 1;
2232 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2233 // as fillRect will apply the aliased coordinate delta we need to
2234 // subtract it here as we don't use it for image drawing
2235 QTransform old = s->matrix;
2236 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2238 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2239 QRgb color = img.pixel(sr_l, sr_t);
2240 switch (img.format()) {
2241 case QImage::Format_ARGB32_Premultiplied:
2242 case QImage::Format_ARGB8565_Premultiplied:
2243 case QImage::Format_ARGB6666_Premultiplied:
2244 case QImage::Format_ARGB8555_Premultiplied:
2245 case QImage::Format_ARGB4444_Premultiplied:
2246 // Combine premultiplied color with the opacity set on the painter.
2247 d->solid_color_filler.solid.color =
2248 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2249 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2252 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2256 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2257 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2261 d->solid_color_filler.clip = d->clip();
2262 d->solid_color_filler.adjustSpanMethods();
2263 fillRect(r, &d->solid_color_filler);
2269 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2271 const QClipData *clip = d->clip();
2273 if (s->matrix.type() > QTransform::TxTranslate
2275 && (!clip || clip->hasRectClip)
2276 && s->intOpacity == 256
2277 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2278 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2279 && d->rasterBuffer->format == img.format()
2280 && (d->rasterBuffer->format == QImage::Format_RGB16
2281 || d->rasterBuffer->format == QImage::Format_RGB32
2282 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2283 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2285 RotationType rotationType = qRotationType(s->matrix);
2287 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2288 QRectF transformedTargetRect = s->matrix.mapRect(r);
2290 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2291 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2293 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2294 if (clippedTransformedTargetRect.isNull())
2297 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2299 QRect clippedSourceRect
2300 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2301 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2303 uint dbpl = d->rasterBuffer->bytesPerLine();
2304 uint sbpl = img.bytesPerLine();
2306 uchar *dst = d->rasterBuffer->buffer();
2307 uint bpp = img.depth() >> 3;
2309 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2310 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2312 uint cw = clippedSourceRect.width();
2313 uint ch = clippedSourceRect.height();
2315 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2322 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2324 QRectF targetBounds = s->matrix.mapRect(r);
2325 bool exceedsPrecision = targetBounds.width() > 0xffff
2326 || targetBounds.height() > 0xffff;
2328 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2329 if (s->matrix.type() > QTransform::TxScale) {
2330 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2331 if (func && (!clip || clip->hasRectClip)) {
2332 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2333 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2334 s->matrix, s->intOpacity);
2338 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2339 if (func && (!clip || clip->hasRectClip)) {
2340 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2341 img.bits(), img.bytesPerLine(),
2342 qt_mapRect_non_normalizing(r, s->matrix), sr,
2343 !clip ? d->deviceRect : clip->clipRect,
2350 QTransform copy = s->matrix;
2351 copy.translate(r.x(), r.y());
2353 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2354 copy.translate(-sr.x(), -sr.y());
2356 d->image_filler_xform.clip = clip;
2357 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2358 if (!d->image_filler_xform.blend)
2360 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2362 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2363 QRectF rr = s->matrix.mapRect(r);
2365 const int x1 = qRound(rr.x());
2366 const int y1 = qRound(rr.y());
2367 const int x2 = qRound(rr.right());
2368 const int y2 = qRound(rr.bottom());
2370 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2374 #ifdef QT_FAST_SPANS
2375 ensureRasterState();
2376 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2377 d->initializeRasterizer(&d->image_filler_xform);
2378 d->rasterizer->setAntialiased(s->flags.antialiased);
2380 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2382 const QRectF &rect = r.normalized();
2383 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2384 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2386 if (s->flags.tx_noshear)
2387 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2389 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2393 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2396 QTransform m = s->matrix;
2397 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2398 m.m21(), m.m22(), m.m23(),
2399 m.m31() - offs, m.m32() - offs, m.m33());
2400 fillPath(path, &d->image_filler_xform);
2403 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2404 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2406 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2408 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2410 } else if (clip->hasRectClip) {
2411 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2417 d->image_filler.clip = clip;
2418 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2419 if (!d->image_filler.blend)
2421 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2422 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2425 rr.translate(s->matrix.dx(), s->matrix.dy());
2427 const int x1 = qRound(rr.x());
2428 const int y1 = qRound(rr.y());
2429 const int x2 = qRound(rr.right());
2430 const int y2 = qRound(rr.bottom());
2432 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2439 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2441 #ifdef QT_DEBUG_DRAW
2442 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2444 Q_D(QRasterPaintEngine);
2445 QRasterPaintEngineState *s = state();
2449 QPlatformPixmap *pd = pixmap.handle();
2450 if (pd->classId() == QPlatformPixmap::RasterClass) {
2451 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2453 image = pixmap.toImage();
2456 if (image.depth() == 1)
2457 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2459 if (s->matrix.type() > QTransform::TxTranslate) {
2460 QTransform copy = s->matrix;
2461 copy.translate(r.x(), r.y());
2462 copy.translate(-sr.x(), -sr.y());
2463 d->image_filler_xform.clip = d->clip();
2464 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2465 if (!d->image_filler_xform.blend)
2467 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2469 #ifdef QT_FAST_SPANS
2470 ensureRasterState();
2471 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2472 d->initializeRasterizer(&d->image_filler_xform);
2473 d->rasterizer->setAntialiased(s->flags.antialiased);
2475 const QRectF &rect = r.normalized();
2476 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2477 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2478 if (s->flags.tx_noshear)
2479 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2481 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2487 fillPath(path, &d->image_filler_xform);
2489 d->image_filler.clip = d->clip();
2491 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2492 if (!d->image_filler.blend)
2494 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2495 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2498 rr.translate(s->matrix.dx(), s->matrix.dy());
2499 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2505 static inline bool monoVal(const uchar* s, int x)
2507 return (s[x>>3] << (x&7)) & 0x80;
2513 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2515 Q_D(QRasterPaintEngine);
2516 return d->rasterBuffer.data();
2522 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2524 Q_D(QRasterPaintEngine);
2525 QRasterPaintEngineState *s = state();
2527 if (!s->penData.blend)
2530 QRasterBuffer *rb = d->rasterBuffer.data();
2532 const QRect rect(rx, ry, w, h);
2533 const QClipData *clip = d->clip();
2534 bool unclipped = false;
2536 // inlined QRect::intersects
2537 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2538 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2540 if (clip->hasRectClip) {
2541 unclipped = rx > clip->xmin
2542 && rx + w < clip->xmax
2544 && ry + h < clip->ymax;
2550 // inlined QRect::intersects
2551 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2552 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2556 // inlined QRect::contains
2557 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2558 && rect.top() >= 0 && rect.bottom() < rb->height();
2560 unclipped = contains && d->isUnclipped_normalized(rect);
2563 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2564 const uchar * scanline = static_cast<const uchar *>(src);
2566 if (s->flags.fast_text) {
2569 if (s->penData.bitmapBlit) {
2570 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2571 scanline, w, h, bpl);
2574 } else if (depth == 8) {
2575 if (s->penData.alphamapBlit) {
2576 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2577 scanline, w, h, bpl, 0);
2580 } else if (depth == 32) {
2581 // (A)RGB Alpha mask where the alpha component is not used.
2582 if (s->penData.alphaRGBBlit) {
2583 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2584 (const uint *) scanline, w, h, bpl / 4, 0);
2588 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2589 // (A)RGB Alpha mask where the alpha component is not used.
2591 int nx = qMax(0, rx);
2592 int ny = qMax(0, ry);
2594 // Move scanline pointer to compensate for moved x and y
2595 int xdiff = nx - rx;
2596 int ydiff = ny - ry;
2597 scanline += ydiff * bpl;
2598 scanline += xdiff * (depth == 32 ? 4 : 1);
2603 if (nx + w > d->rasterBuffer->width())
2604 w = d->rasterBuffer->width() - nx;
2605 if (ny + h > d->rasterBuffer->height())
2606 h = d->rasterBuffer->height() - ny;
2611 if (depth == 8 && s->penData.alphamapBlit) {
2612 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2613 scanline, w, h, bpl, clip);
2614 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2615 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2616 (const uint *) scanline, w, h, bpl / 4, clip);
2631 scanline += bpl * y0;
2635 w = qMin(w, rb->width() - qMax(0, rx));
2636 h = qMin(h, rb->height() - qMax(0, ry));
2638 if (w <= 0 || h <= 0)
2641 const int NSPANS = 256;
2642 QSpan spans[NSPANS];
2645 const int x1 = x0 + w;
2646 const int y1 = y0 + h;
2649 for (int y = y0; y < y1; ++y) {
2650 for (int x = x0; x < x1; ) {
2651 if (!monoVal(scanline, x)) {
2656 if (current == NSPANS) {
2657 blend(current, spans, &s->penData);
2660 spans[current].x = x + rx;
2661 spans[current].y = y + ry;
2662 spans[current].coverage = 255;
2665 // extend span until we find a different one.
2666 while (x < x1 && monoVal(scanline, x)) {
2670 spans[current].len = len;
2675 } else if (depth == 8) {
2676 for (int y = y0; y < y1; ++y) {
2677 for (int x = x0; x < x1; ) {
2678 // Skip those with 0 coverage
2679 if (scanline[x] == 0) {
2684 if (current == NSPANS) {
2685 blend(current, spans, &s->penData);
2688 int coverage = scanline[x];
2689 spans[current].x = x + rx;
2690 spans[current].y = y + ry;
2691 spans[current].coverage = coverage;
2695 // extend span until we find a different one.
2696 while (x < x1 && scanline[x] == coverage) {
2700 spans[current].len = len;
2705 } else { // 32-bit alpha...
2706 uint *sl = (uint *) src;
2707 for (int y = y0; y < y1; ++y) {
2708 for (int x = x0; x < x1; ) {
2709 // Skip those with 0 coverage
2710 if ((sl[x] & 0x00ffffff) == 0) {
2715 if (current == NSPANS) {
2716 blend(current, spans, &s->penData);
2719 uint rgbCoverage = sl[x];
2720 int coverage = qGreen(rgbCoverage);
2721 spans[current].x = x + rx;
2722 spans[current].y = y + ry;
2723 spans[current].coverage = coverage;
2727 // extend span until we find a different one.
2728 while (x < x1 && sl[x] == rgbCoverage) {
2732 spans[current].len = len;
2735 sl += bpl / sizeof(uint);
2738 // qDebug() << "alphaPenBlt: num spans=" << current
2739 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2740 // Call span func for current set of spans.
2742 blend(current, spans, &s->penData);
2748 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2749 const QFixedPoint *positions, QFontEngine *fontEngine)
2751 Q_D(QRasterPaintEngine);
2752 QRasterPaintEngineState *s = state();
2754 if (fontEngine->hasInternalCaching()) {
2755 QFontEngine::GlyphFormat neededFormat =
2756 painter()->device()->devType() == QInternal::Widget
2757 ? QFontEngine::Format_None
2758 : QFontEngine::Format_A8;
2760 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2761 neededFormat = QFontEngine::Format_Mono;
2763 for (int i = 0; i < numGlyphs; i++) {
2764 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2767 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2769 if (alphaMap == 0 || alphaMap->isNull())
2772 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2773 qFloor(positions[i].x) + offset.x(),
2774 qFloor(positions[i].y) + offset.y(),
2775 alphaMap->width(), alphaMap->height());
2777 fontEngine->unlockAlphaMapForGlyph();
2781 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2783 QImageTextureGlyphCache *cache =
2784 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2786 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2787 fontEngine->setGlyphCache(0, cache);
2790 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2791 cache->fillInPendingGlyphs();
2793 const QImage &image = cache->image();
2794 int bpl = image.bytesPerLine();
2796 int depth = image.depth();
2800 leftShift = 2; // multiply by 4
2801 else if (depth == 1)
2802 rightShift = 3; // divide by 8
2804 int margin = fontEngine->glyphMargin(glyphType);
2805 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2806 const uchar *bits = image.bits();
2807 for (int i=0; i<numGlyphs; ++i) {
2809 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2810 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2811 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2815 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2816 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2818 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2821 // c.baseLineX, c.baseLineY,
2824 // positions[i].x.toInt(), positions[i].y.toInt());
2826 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2834 * Returns true if the rectangle is completely within the current clip
2835 * state of the paint engine.
2837 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2839 const QClipData *cl = clip();
2841 // inline contains() for performance (we know the rects are normalized)
2842 const QRect &r1 = deviceRect;
2843 return (r.left() >= r1.left() && r.right() <= r1.right()
2844 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2848 if (cl->hasRectClip) {
2849 // currently all painting functions clips to deviceRect internally
2850 if (cl->clipRect == deviceRect)
2853 // inline contains() for performance (we know the rects are normalized)
2854 const QRect &r1 = cl->clipRect;
2855 return (r.left() >= r1.left() && r.right() <= r1.right()
2856 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2858 return qt_region_strictContains(cl->clipRegion, r);
2862 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2865 Q_Q(const QRasterPaintEngine);
2866 const QRasterPaintEngineState *s = q->state();
2867 const QClipData *cl = clip();
2869 QRect r = rect.normalized();
2870 // inline contains() for performance (we know the rects are normalized)
2871 const QRect &r1 = deviceRect;
2872 return (r.left() >= r1.left() && r.right() <= r1.right()
2873 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2877 // currently all painting functions that call this function clip to deviceRect internally
2878 if (cl->hasRectClip && cl->clipRect == deviceRect)
2881 if (s->flags.antialiased)
2884 QRect r = rect.normalized();
2886 r.setX(r.x() - penWidth);
2887 r.setY(r.y() - penWidth);
2888 r.setWidth(r.width() + 2 * penWidth);
2889 r.setHeight(r.height() + 2 * penWidth);
2892 if (cl->hasRectClip) {
2893 // inline contains() for performance (we know the rects are normalized)
2894 const QRect &r1 = cl->clipRect;
2895 return (r.left() >= r1.left() && r.right() <= r1.right()
2896 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2898 return qt_region_strictContains(cl->clipRegion, r);
2902 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2905 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2909 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2910 const QSpanData *data) const
2912 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2916 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2917 const QSpanData *data) const
2919 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2923 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2924 const QSpanData *data) const
2926 Q_Q(const QRasterPaintEngine);
2927 const QRasterPaintEngineState *s = q->state();
2929 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2931 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2932 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2935 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2936 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2938 QFixed clipLeft = QFixed::fromReal(clip.left());
2939 QFixed clipRight = QFixed::fromReal(clip.right());
2940 QFixed clipTop = QFixed::fromReal(clip.top());
2941 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2944 while (first < numGlyphs) {
2945 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2946 QFixed left = metrics.x + positions[first].x;
2947 QFixed top = metrics.y + positions[first].y;
2948 QFixed right = left + metrics.width;
2949 QFixed bottom = top + metrics.height;
2950 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2954 int last = numGlyphs - 1;
2955 while (last > first) {
2956 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2957 QFixed left = metrics.x + positions[last].x;
2958 QFixed top = metrics.y + positions[last].y;
2959 QFixed right = left + metrics.width;
2960 QFixed bottom = top + metrics.height;
2961 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2965 return QPair<int, int>(first, last + 1);
2971 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2973 if (textItem->numGlyphs == 0)
2977 ensureRasterState();
2979 QFontEngine *fontEngine = textItem->fontEngine();
2980 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2981 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2983 } else if (state()->matrix.type() < QTransform::TxProject) {
2985 QTransform invMat = state()->matrix.inverted(&invertible);
2989 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2990 textItem->fontEngine(), textItem->glyphs,
2991 textItem->glyphPositions, textItem->numGlyphs);
2992 QStaticTextItem copy = *textItem;
2993 copy.glyphs += range.first;
2994 copy.glyphPositions += range.first;
2995 copy.numGlyphs = range.second - range.first;
2996 QPaintEngineEx::drawStaticTextItem(©);
2998 QPaintEngineEx::drawStaticTextItem(textItem);
3005 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3007 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3009 #ifdef QT_DEBUG_DRAW
3010 Q_D(QRasterPaintEngine);
3011 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3012 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3016 if (ti.glyphs.numGlyphs == 0)
3019 ensureRasterState();
3021 QRasterPaintEngineState *s = state();
3022 QTransform matrix = s->matrix;
3024 if (!supportsTransformations(ti.fontEngine)) {
3025 QVarLengthArray<QFixedPoint> positions;
3026 QVarLengthArray<glyph_t> glyphs;
3028 matrix.translate(p.x(), p.y());
3029 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3031 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3032 } else if (matrix.type() < QTransform::TxProject) {
3034 QTransform invMat = matrix.inverted(&invertible);
3038 QVarLengthArray<QFixedPoint> positions;
3039 QVarLengthArray<glyph_t> glyphs;
3041 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3042 ti.flags, glyphs, positions);
3043 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3044 ti.fontEngine, glyphs.data(), positions.data(),
3047 if (range.first >= range.second)
3050 QStaticTextItem staticTextItem;
3051 staticTextItem.color = s->pen.color();
3052 staticTextItem.font = s->font;
3053 staticTextItem.setFontEngine(ti.fontEngine);
3054 staticTextItem.numGlyphs = range.second - range.first;
3055 staticTextItem.glyphs = glyphs.data() + range.first;
3056 staticTextItem.glyphPositions = positions.data() + range.first;
3057 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3059 QPaintEngineEx::drawTextItem(p, ti);
3066 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3068 Q_D(QRasterPaintEngine);
3069 QRasterPaintEngineState *s = state();
3072 if (!s->penData.blend)
3075 if (!s->flags.fast_pen) {
3076 QPaintEngineEx::drawPoints(points, pointCount);
3080 QCosmeticStroker stroker(s, d->deviceRect);
3081 stroker.drawPoints(points, pointCount);
3085 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3087 Q_D(QRasterPaintEngine);
3088 QRasterPaintEngineState *s = state();
3091 if (!s->penData.blend)
3094 if (!s->flags.fast_pen) {
3095 QPaintEngineEx::drawPoints(points, pointCount);
3099 QCosmeticStroker stroker(s, d->deviceRect);
3100 stroker.drawPoints(points, pointCount);
3106 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3108 #ifdef QT_DEBUG_DRAW
3109 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3111 Q_D(QRasterPaintEngine);
3112 QRasterPaintEngineState *s = state();
3115 if (!s->penData.blend)
3118 if (s->flags.fast_pen) {
3119 QCosmeticStroker stroker(s, d->deviceRect);
3120 for (int i=0; i<lineCount; ++i) {
3121 const QLine &l = lines[i];
3122 stroker.drawLine(l.p1(), l.p2());
3125 QPaintEngineEx::drawLines(lines, lineCount);
3129 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3135 Q_Q(QRasterPaintEngine);
3136 QRasterPaintEngineState *s = q->state();
3138 const QPen &pen = s->lastPen;
3139 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3140 const QVector<qreal> pattern = pen.dashPattern();
3142 qreal patternLength = 0;
3143 for (int i = 0; i < pattern.size(); ++i)
3144 patternLength += pattern.at(i);
3146 if (patternLength <= 0)
3149 qreal length = line.length();
3150 Q_ASSERT(length > 0);
3151 while (length > 0) {
3152 const bool rasterize = *inDash;
3153 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3156 if (dash >= length) {
3158 *dashOffset += dash / width;
3162 *inDash = !(*inDash);
3163 if (++*dashIndex >= pattern.size())
3170 if (rasterize && dash > 0)
3171 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3178 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3180 #ifdef QT_DEBUG_DRAW
3181 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3183 Q_D(QRasterPaintEngine);
3184 QRasterPaintEngineState *s = state();
3187 if (!s->penData.blend)
3189 if (s->flags.fast_pen) {
3190 QCosmeticStroker stroker(s, d->deviceRect);
3191 for (int i=0; i<lineCount; ++i) {
3192 QLineF line = lines[i];
3193 stroker.drawLine(line.p1(), line.p2());
3196 QPaintEngineEx::drawLines(lines, lineCount);
3204 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3206 Q_D(QRasterPaintEngine);
3207 QRasterPaintEngineState *s = state();
3210 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3211 || (qpen_style(s->lastPen) == Qt::NoPen))
3212 && !s->flags.antialiased
3213 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3215 && s->matrix.type() <= QTransform::TxScale) // no shear
3218 const QRectF r = s->matrix.mapRect(rect);
3219 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3220 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3221 const QRect brect = QRect(int(r.x()), int(r.y()),
3222 int_dim(r.x(), r.width()),
3223 int_dim(r.y(), r.height()));
3225 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3226 &s->penData, &s->brushData);
3230 QPaintEngineEx::drawEllipse(rect);
3238 void QRasterPaintEngine::setDC(HDC hdc) {
3239 Q_D(QRasterPaintEngine);
3246 HDC QRasterPaintEngine::getDC() const
3248 Q_D(const QRasterPaintEngine);
3255 void QRasterPaintEngine::releaseDC(HDC) const
3264 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3266 const QTransform &m = state()->matrix;
3267 return supportsTransformations(fontEngine, m);
3273 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3275 if (m.type() >= QTransform::TxProject)
3278 return !shouldDrawCachedGlyphs(fontEngine, m);
3284 QPoint QRasterPaintEngine::coordinateOffset() const
3286 return QPoint(0, 0);
3289 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3294 Q_D(QRasterPaintEngine);
3296 Q_ASSERT(image.depth() == 1);
3298 const int spanCount = 256;
3299 QT_FT_Span spans[spanCount];
3303 int w = image.width();
3304 int h = image.height();
3305 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3306 int ymin = qMax(qRound(pos.y()), 0);
3307 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3308 int xmin = qMax(qRound(pos.x()), 0);
3310 int x_offset = xmin - qRound(pos.x());
3312 QImage::Format format = image.format();
3313 for (int y = ymin; y < ymax; ++y) {
3314 const uchar *src = image.scanLine(y - qRound(pos.y()));
3315 if (format == QImage::Format_MonoLSB) {
3316 for (int x = 0; x < xmax - xmin; ++x) {
3317 int src_x = x + x_offset;
3318 uchar pixel = src[src_x >> 3];
3323 if (pixel & (0x1 << (src_x & 7))) {
3324 spans[n].x = xmin + x;
3326 spans[n].coverage = 255;
3328 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3332 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3335 if (n == spanCount) {
3336 fg->blend(n, spans, fg);
3342 for (int x = 0; x < xmax - xmin; ++x) {
3343 int src_x = x + x_offset;
3344 uchar pixel = src[src_x >> 3];
3349 if (pixel & (0x80 >> (x & 7))) {
3350 spans[n].x = xmin + x;
3352 spans[n].coverage = 255;
3354 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3358 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3361 if (n == spanCount) {
3362 fg->blend(n, spans, fg);
3370 fg->blend(n, spans, fg);
3376 \enum QRasterPaintEngine::ClipType
3379 \value RectClip Indicates that the currently set clip is a single rectangle.
3380 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3385 Returns the type of the clip currently set.
3387 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3389 Q_D(const QRasterPaintEngine);
3391 const QClipData *clip = d->clip();
3392 if (!clip || clip->hasRectClip)
3400 Returns the bounding rect of the currently set clip.
3402 QRect QRasterPaintEngine::clipBoundingRect() const
3404 Q_D(const QRasterPaintEngine);
3406 const QClipData *clip = d->clip();
3409 return d->deviceRect;
3411 if (clip->hasRectClip)
3412 return clip->clipRect;
3414 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3417 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3419 Q_Q(QRasterPaintEngine);
3420 QRasterPaintEngineState *s = q->state();
3422 rasterizer->setAntialiased(s->flags.antialiased);
3424 QRect clipRect(deviceRect);
3426 // ### get from optimized rectbased QClipData
3428 const QClipData *c = clip();
3430 const QRect r(QPoint(c->xmin, c->ymin),
3431 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3432 clipRect = clipRect.intersected(r);
3433 blend = data->blend;
3435 blend = data->unclipped_blend;
3438 rasterizer->setClipRect(clipRect);
3439 rasterizer->initialize(blend, data);
3442 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3443 ProcessSpans callback,
3444 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3446 if (!callback || !outline)
3449 Q_Q(QRasterPaintEngine);
3450 QRasterPaintEngineState *s = q->state();
3452 if (!s->flags.antialiased) {
3453 initializeRasterizer(spanData);
3455 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3459 rasterizer->rasterize(outline, fillRule);
3463 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3467 int q_gray_rendered_spans(QT_FT_Raster raster);
3470 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3472 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3475 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3476 ProcessSpans callback,
3477 void *userData, QRasterBuffer *)
3479 if (!callback || !outline)
3482 Q_Q(QRasterPaintEngine);
3483 QRasterPaintEngineState *s = q->state();
3485 if (!s->flags.antialiased) {
3486 rasterizer->setAntialiased(s->flags.antialiased);
3487 rasterizer->setClipRect(deviceRect);
3488 rasterizer->initialize(callback, userData);
3490 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3494 rasterizer->rasterize(outline, fillRule);
3498 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3499 // minimize memory reallocations. However if initial size for
3500 // raster pool is changed for lower value, reallocations will
3502 int rasterPoolSize = MINIMUM_POOL_SIZE;
3503 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3504 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3505 uchar *rasterPoolOnHeap = 0;
3507 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3509 void *data = userData;
3511 QT_FT_BBox clip_box = { deviceRect.x(),
3513 deviceRect.x() + deviceRect.width(),
3514 deviceRect.y() + deviceRect.height() };
3516 QT_FT_Raster_Params rasterParams;
3517 rasterParams.target = 0;
3518 rasterParams.source = outline;
3519 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3520 rasterParams.gray_spans = 0;
3521 rasterParams.black_spans = 0;
3522 rasterParams.bit_test = 0;
3523 rasterParams.bit_set = 0;
3524 rasterParams.user = data;
3525 rasterParams.clip_box = clip_box;
3530 int rendered_spans = 0;
3534 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3535 rasterParams.gray_spans = callback;
3536 rasterParams.skip_spans = rendered_spans;
3537 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3539 // Out of memory, reallocate some more and try again...
3540 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3541 rasterPoolSize *= 2;
3542 if (rasterPoolSize > 1024 * 1024) {
3543 qWarning("QPainter: Rasterization of primitive failed");
3547 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3549 free(rasterPoolOnHeap);
3550 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3552 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3554 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3556 qt_ft_grays_raster.raster_done(*grayRaster.data());
3557 qt_ft_grays_raster.raster_new(grayRaster.data());
3558 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3564 free(rasterPoolOnHeap);
3567 void QRasterPaintEnginePrivate::recalculateFastImages()
3569 Q_Q(QRasterPaintEngine);
3570 QRasterPaintEngineState *s = q->state();
3572 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3573 && s->matrix.type() <= QTransform::TxShear;
3576 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3578 Q_Q(const QRasterPaintEngine);
3579 const QRasterPaintEngineState *s = q->state();
3581 return s->flags.fast_images
3582 && (mode == QPainter::CompositionMode_SourceOver
3583 || (mode == QPainter::CompositionMode_Source
3584 && !image.hasAlphaChannel()));
3587 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3589 Q_ASSERT(image.depth() == 1);
3591 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3592 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3594 QRgb fg = PREMUL(color.rgba());
3597 int height = sourceImage.height();
3598 int width = sourceImage.width();
3599 for (int y=0; y<height; ++y) {
3600 uchar *source = sourceImage.scanLine(y);
3601 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3602 if (!source || !target)
3603 QT_THROW(std::bad_alloc()); // we must have run out of memory
3604 for (int x=0; x < width; ++x)
3605 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3610 QRasterBuffer::~QRasterBuffer()
3614 void QRasterBuffer::init()
3616 compositionMode = QPainter::CompositionMode_SourceOver;
3617 monoDestinationWithClut = false;
3622 QImage::Format QRasterBuffer::prepare(QImage *image)
3624 m_buffer = (uchar *)image->bits();
3625 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3626 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3627 bytes_per_pixel = image->depth()/8;
3628 bytes_per_line = image->bytesPerLine();
3630 format = image->format();
3631 drawHelper = qDrawHelper + format;
3632 if (image->depth() == 1 && image->colorTable().size() == 2) {
3633 monoDestinationWithClut = true;
3634 destColor0 = PREMUL(image->colorTable()[0]);
3635 destColor1 = PREMUL(image->colorTable()[1]);
3641 void QRasterBuffer::resetBuffer(int val)
3643 memset(m_buffer, val, m_height*bytes_per_line);
3646 QClipData::QClipData(int height)
3648 clipSpanHeight = height;
3653 xmin = xmax = ymin = ymax = 0;
3657 hasRectClip = hasRegionClip = false;
3660 QClipData::~QClipData()
3668 void QClipData::initialize()
3674 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3676 Q_CHECK_PTR(m_clipLines);
3678 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3679 allocated = clipSpanHeight;
3680 Q_CHECK_PTR(m_spans);
3686 m_clipLines[y].spans = 0;
3687 m_clipLines[y].count = 0;
3691 const int len = clipRect.width();
3694 QSpan *span = m_spans + count;
3698 span->coverage = 255;
3701 m_clipLines[y].spans = span;
3702 m_clipLines[y].count = 1;
3706 while (y < clipSpanHeight) {
3707 m_clipLines[y].spans = 0;
3708 m_clipLines[y].count = 0;
3711 } else if (hasRegionClip) {
3713 const QVector<QRect> rects = clipRegion.rects();
3714 const int numRects = rects.size();
3717 const int maxSpans = (ymax - ymin) * numRects;
3718 if (maxSpans > allocated) {
3719 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3720 allocated = maxSpans;
3725 int firstInBand = 0;
3727 while (firstInBand < numRects) {
3728 const int currMinY = rects.at(firstInBand).y();
3729 const int currMaxY = currMinY + rects.at(firstInBand).height();
3731 while (y < currMinY) {
3732 m_clipLines[y].spans = 0;
3733 m_clipLines[y].count = 0;
3737 int lastInBand = firstInBand;
3738 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3741 while (y < currMaxY) {
3743 m_clipLines[y].spans = m_spans + count;
3744 m_clipLines[y].count = lastInBand - firstInBand + 1;
3746 for (int r = firstInBand; r <= lastInBand; ++r) {
3747 const QRect &currRect = rects.at(r);
3748 QSpan *span = m_spans + count;
3749 span->x = currRect.x();
3750 span->len = currRect.width();
3752 span->coverage = 255;
3758 firstInBand = lastInBand + 1;
3761 Q_ASSERT(count <= allocated);
3763 while (y < clipSpanHeight) {
3764 m_clipLines[y].spans = 0;
3765 m_clipLines[y].count = 0;
3771 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3776 free(m_clipLines); // same for clipLines
3782 void QClipData::fixup()
3787 ymin = ymax = xmin = xmax = 0;
3792 ymin = m_spans[0].y;
3793 ymax = m_spans[count-1].y + 1;
3797 const int firstLeft = m_spans[0].x;
3798 const int firstRight = m_spans[0].x + m_spans[0].len;
3801 for (int i = 0; i < count; ++i) {
3802 QT_FT_Span_& span = m_spans[i];
3805 if (span.y != y + 1 && y != -1)
3808 m_clipLines[y].spans = &span;
3809 m_clipLines[y].count = 1;
3811 ++m_clipLines[y].count;
3813 const int spanLeft = span.x;
3814 const int spanRight = spanLeft + span.len;
3816 if (spanLeft < xmin)
3819 if (spanRight > xmax)
3822 if (spanLeft != firstLeft || spanRight != firstRight)
3828 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3833 Convert \a rect to clip spans.
3835 void QClipData::setClipRect(const QRect &rect)
3837 if (hasRectClip && rect == clipRect)
3840 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3842 hasRegionClip = false;
3846 xmax = rect.x() + rect.width();
3847 ymin = qMin(rect.y(), clipSpanHeight);
3848 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3855 // qDebug() << xmin << xmax << ymin << ymax;
3859 Convert \a region to clip spans.
3861 void QClipData::setClipRegion(const QRegion ®ion)
3863 if (region.rectCount() == 1) {
3864 setClipRect(region.rects().at(0));
3868 hasRegionClip = true;
3869 hasRectClip = false;
3870 clipRegion = region;
3872 { // set bounding rect
3873 const QRect rect = region.boundingRect();
3875 xmax = rect.x() + rect.width();
3877 ymax = rect.y() + rect.height();
3889 spans must be sorted on y
3891 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3892 const QSpan *spans, const QSpan *end,
3893 QSpan **outSpans, int available)
3895 const_cast<QClipData *>(clip)->initialize();
3897 QSpan *out = *outSpans;
3899 const QSpan *clipSpans = clip->m_spans + *currentClip;
3900 const QSpan *clipEnd = clip->m_spans + clip->count;
3902 while (available && spans < end ) {
3903 if (clipSpans >= clipEnd) {
3907 if (clipSpans->y > spans->y) {
3911 if (spans->y != clipSpans->y) {
3912 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3913 clipSpans = clip->m_clipLines[spans->y].spans;
3918 Q_ASSERT(spans->y == clipSpans->y);
3921 int sx2 = sx1 + spans->len;
3922 int cx1 = clipSpans->x;
3923 int cx2 = cx1 + clipSpans->len;
3925 if (cx1 < sx1 && cx2 < sx1) {
3928 } else if (sx1 < cx1 && sx2 < cx1) {
3932 int x = qMax(sx1, cx1);
3933 int len = qMin(sx2, cx2) - x;
3935 out->x = qMax(sx1, cx1);
3936 out->len = qMin(sx2, cx2) - out->x;
3938 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3950 *currentClip = clipSpans - clip->m_spans;
3954 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3956 // qDebug() << "qt_span_fill_clipped" << spanCount;
3957 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3959 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3961 const int NSPANS = 256;
3962 QSpan cspans[NSPANS];
3963 int currentClip = 0;
3964 const QSpan *end = spans + spanCount;
3965 while (spans < end) {
3966 QSpan *clipped = cspans;
3967 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3968 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3969 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3971 if (clipped - cspans)
3972 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3978 Clip spans to \a{clip}-rectangle.
3979 Returns number of unclipped spans
3981 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3984 const short minx = clip.left();
3985 const short miny = clip.top();
3986 const short maxx = clip.right();
3987 const short maxy = clip.bottom();
3990 for (int i = 0; i < numSpans; ++i) {
3991 if (spans[i].y > maxy)
3993 if (spans[i].y < miny
3994 || spans[i].x > maxx
3995 || spans[i].x + spans[i].len <= minx) {
3998 if (spans[i].x < minx) {
3999 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4002 spans[n].x = spans[i].x;
4003 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4005 if (spans[n].len == 0)
4007 spans[n].y = spans[i].y;
4008 spans[n].coverage = spans[i].coverage;
4015 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4018 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4019 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4021 Q_ASSERT(fillData->clip);
4022 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4024 // hw: check if this const_cast<> is safe!!!
4025 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4026 fillData->clip->clipRect);
4028 fillData->unclipped_blend(count, spans, fillData);
4031 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4033 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4035 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4036 // for (int i = 0; i < qMin(count, 10); ++i) {
4037 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4040 switch (clipData->operation) {
4042 case Qt::IntersectClip:
4044 QClipData *newClip = clipData->newClip;
4045 newClip->initialize();
4047 int currentClip = 0;
4048 const QSpan *end = spans + count;
4049 while (spans < end) {
4050 QSpan *newspans = newClip->m_spans + newClip->count;
4051 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4052 &newspans, newClip->allocated - newClip->count);
4053 newClip->count = newspans - newClip->m_spans;
4055 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4056 newClip->allocated *= 2;
4062 case Qt::ReplaceClip:
4063 clipData->newClip->appendSpans(spans, count);
4071 QImage QRasterBuffer::bufferImage() const
4073 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4075 for (int y = 0; y < m_height; ++y) {
4076 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4078 for (int x=0; x<m_width; ++x) {
4079 uint argb = span[x];
4080 image.setPixel(x, y, argb);
4088 void QRasterBuffer::flushToARGBImage(QImage *target) const
4090 int w = qMin(m_width, target->width());
4091 int h = qMin(m_height, target->height());
4093 for (int y=0; y<h; ++y) {
4094 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4095 QRgb *dest = (QRgb *) target->scanLine(y);
4096 for (int x=0; x<w; ++x) {
4097 QRgb pixel = sourceLine[x];
4098 int alpha = qAlpha(pixel);
4102 dest[x] = (alpha << 24)
4103 | ((255*qRed(pixel)/alpha) << 16)
4104 | ((255*qGreen(pixel)/alpha) << 8)
4105 | ((255*qBlue(pixel)/alpha) << 0);
4112 class QGradientCache
4116 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4117 stops(s), opacity(op), interpolationMode(mode) {}
4118 uint buffer[GRADIENT_STOPTABLE_SIZE];
4119 QGradientStops stops;
4121 QGradient::InterpolationMode interpolationMode;
4124 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4127 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4128 quint64 hash_val = 0;
4130 QGradientStops stops = gradient.stops();
4131 for (int i = 0; i < stops.size() && i <= 2; i++)
4132 hash_val += stops[i].second.rgba();
4134 QMutexLocker lock(&mutex);
4135 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4137 if (it == cache.constEnd())
4138 return addCacheElement(hash_val, gradient, opacity);
4141 const CacheInfo &cache_info = it.value();
4142 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4143 return cache_info.buffer;
4145 } while (it != cache.constEnd() && it.key() == hash_val);
4146 // an exact match for these stops and opacity was not found, create new cache
4147 return addCacheElement(hash_val, gradient, opacity);
4151 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4153 inline int maxCacheSize() const { return 60; }
4154 inline void generateGradientColorTable(const QGradient& g,
4156 int size, int opacity) const;
4157 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4158 if (cache.size() == maxCacheSize()) {
4159 // may remove more than 1, but OK
4160 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4162 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4163 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4164 return cache.insert(hash_val, cache_entry).value().buffer;
4167 QGradientColorTableHash cache;
4171 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4173 QGradientStops stops = gradient.stops();
4174 int stopCount = stops.count();
4175 Q_ASSERT(stopCount > 0);
4177 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4179 if (stopCount == 2) {
4180 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4181 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4183 qreal first_stop = stops[0].first;
4184 qreal second_stop = stops[1].first;
4186 if (second_stop < first_stop) {
4187 qSwap(first_color, second_color);
4188 qSwap(first_stop, second_stop);
4191 if (colorInterpolation) {
4192 first_color = PREMUL(first_color);
4193 second_color = PREMUL(second_color);
4196 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4197 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4199 uint red_first = qRed(first_color) << 16;
4200 uint green_first = qGreen(first_color) << 16;
4201 uint blue_first = qBlue(first_color) << 16;
4202 uint alpha_first = qAlpha(first_color) << 16;
4204 uint red_second = qRed(second_color) << 16;
4205 uint green_second = qGreen(second_color) << 16;
4206 uint blue_second = qBlue(second_color) << 16;
4207 uint alpha_second = qAlpha(second_color) << 16;
4210 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4211 if (colorInterpolation)
4212 colorTable[i] = first_color;
4214 colorTable[i] = PREMUL(first_color);
4217 if (i < second_index) {
4218 qreal reciprocal = qreal(1) / (second_index - first_index);
4220 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4221 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4222 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4223 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4226 red_first += 1 << 15;
4227 green_first += 1 << 15;
4228 blue_first += 1 << 15;
4229 alpha_first += 1 << 15;
4231 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4232 red_first += red_delta;
4233 green_first += green_delta;
4234 blue_first += blue_delta;
4235 alpha_first += alpha_delta;
4237 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4238 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4240 if (colorInterpolation)
4241 colorTable[i] = color;
4243 colorTable[i] = PREMUL(color);
4247 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4248 if (colorInterpolation)
4249 colorTable[i] = second_color;
4251 colorTable[i] = PREMUL(second_color);
4257 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4258 if (stopCount == 1) {
4259 current_color = PREMUL(current_color);
4260 for (int i = 0; i < size; ++i)
4261 colorTable[i] = current_color;
4265 // The position where the gradient begins and ends
4266 qreal begin_pos = stops[0].first;
4267 qreal end_pos = stops[stopCount-1].first;
4269 int pos = 0; // The position in the color table.
4272 qreal incr = 1 / qreal(size); // the double increment.
4273 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4275 // Up to first point
4276 colorTable[pos++] = PREMUL(current_color);
4277 while (dpos <= begin_pos) {
4278 colorTable[pos] = colorTable[pos - 1];
4283 int current_stop = 0; // We always interpolate between current and current + 1.
4285 qreal t; // position between current left and right stops
4286 qreal t_delta; // the t increment per entry in the color table
4288 if (dpos < end_pos) {
4290 while (dpos > stops[current_stop+1].first)
4293 if (current_stop != 0)
4294 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4295 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4297 if (colorInterpolation) {
4298 current_color = PREMUL(current_color);
4299 next_color = PREMUL(next_color);
4302 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4303 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4304 t = (dpos - stops[current_stop].first) * c;
4308 Q_ASSERT(current_stop < stopCount);
4310 int dist = qRound(t);
4311 int idist = 256 - dist;
4313 if (colorInterpolation)
4314 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4316 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4321 if (dpos >= end_pos)
4327 while (dpos > stops[current_stop+skip+1].first)
4331 current_stop += skip;
4333 current_color = next_color;
4335 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4336 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4338 if (colorInterpolation) {
4340 current_color = PREMUL(current_color);
4341 next_color = PREMUL(next_color);
4344 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4345 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4346 t = (dpos - stops[current_stop].first) * c;
4353 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4354 while (pos < size - 1) {
4355 colorTable[pos] = current_color;
4359 // Make sure the last color stop is represented at the end of the table
4360 colorTable[size - 1] = current_color;
4363 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4366 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4372 m11 = m22 = m33 = 1.;
4373 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4374 clip = pe ? pe->d_func()->clip() : 0;
4377 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4379 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4381 Qt::BrushStyle brushStyle = qbrush_style(brush);
4382 switch (brushStyle) {
4383 case Qt::SolidPattern: {
4385 QColor c = qbrush_color(brush);
4386 QRgb rgba = c.rgba();
4387 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4388 if ((solid.color & 0xff000000) == 0
4389 && compositionMode == QPainter::CompositionMode_SourceOver) {
4395 case Qt::LinearGradientPattern:
4397 type = LinearGradient;
4398 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4399 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4400 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4401 gradient.spread = g->spread();
4403 QLinearGradientData &linearData = gradient.linear;
4405 linearData.origin.x = g->start().x();
4406 linearData.origin.y = g->start().y();
4407 linearData.end.x = g->finalStop().x();
4408 linearData.end.y = g->finalStop().y();
4412 case Qt::RadialGradientPattern:
4414 type = RadialGradient;
4415 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4416 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4417 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4418 gradient.spread = g->spread();
4420 QRadialGradientData &radialData = gradient.radial;
4422 QPointF center = g->center();
4423 radialData.center.x = center.x();
4424 radialData.center.y = center.y();
4425 radialData.center.radius = g->centerRadius();
4426 QPointF focal = g->focalPoint();
4427 radialData.focal.x = focal.x();
4428 radialData.focal.y = focal.y();
4429 radialData.focal.radius = g->focalRadius();
4433 case Qt::ConicalGradientPattern:
4435 type = ConicalGradient;
4436 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4437 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4438 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4439 gradient.spread = QGradient::RepeatSpread;
4441 QConicalGradientData &conicalData = gradient.conical;
4443 QPointF center = g->center();
4444 conicalData.center.x = center.x();
4445 conicalData.center.y = center.y();
4446 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4450 case Qt::Dense1Pattern:
4451 case Qt::Dense2Pattern:
4452 case Qt::Dense3Pattern:
4453 case Qt::Dense4Pattern:
4454 case Qt::Dense5Pattern:
4455 case Qt::Dense6Pattern:
4456 case Qt::Dense7Pattern:
4457 case Qt::HorPattern:
4458 case Qt::VerPattern:
4459 case Qt::CrossPattern:
4460 case Qt::BDiagPattern:
4461 case Qt::FDiagPattern:
4462 case Qt::DiagCrossPattern:
4465 tempImage = new QImage();
4466 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4467 initTexture(tempImage, alpha, QTextureData::Tiled);
4469 case Qt::TexturePattern:
4472 tempImage = new QImage();
4474 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4475 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4477 *tempImage = brush.textureImage();
4478 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4486 adjustSpanMethods();
4489 void QSpanData::adjustSpanMethods()
4499 unclipped_blend = 0;
4502 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4503 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4504 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4505 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4506 fillRect = rasterBuffer->drawHelper->fillRect;
4508 case LinearGradient:
4509 case RadialGradient:
4510 case ConicalGradient:
4511 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4514 unclipped_blend = qBlendTexture;
4515 if (!texture.imageData)
4516 unclipped_blend = 0;
4521 if (!unclipped_blend) {
4524 blend = unclipped_blend;
4525 } else if (clip->hasRectClip) {
4526 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4528 blend = qt_span_fill_clipped;
4532 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4535 // make sure we round off correctly in qdrawhelper.cpp
4536 delta.translate(1.0 / 65536, 1.0 / 65536);
4538 QTransform inv = (delta * matrix).inverted();
4551 const bool affine = !m13 && !m23;
4552 fast_matrix = affine
4553 && m11 * m11 + m21 * m21 < 1e4
4554 && m12 * m12 + m22 * m22 < 1e4
4558 adjustSpanMethods();
4561 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4563 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4564 if (!d || d->height == 0) {
4565 texture.imageData = 0;
4572 texture.bytesPerLine = 0;
4573 texture.format = QImage::Format_Invalid;
4574 texture.colorTable = 0;
4575 texture.hasAlpha = alpha != 256;
4577 texture.imageData = d->data;
4578 texture.width = d->width;
4579 texture.height = d->height;
4581 if (sourceRect.isNull()) {
4584 texture.x2 = texture.width;
4585 texture.y2 = texture.height;
4587 texture.x1 = sourceRect.x();
4588 texture.y1 = sourceRect.y();
4589 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4590 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4593 texture.bytesPerLine = d->bytes_per_line;
4595 texture.format = d->format;
4596 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4597 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4599 texture.const_alpha = alpha;
4600 texture.type = _type;
4602 adjustSpanMethods();
4607 \a x and \a y is relative to the midpoint of \a rect.
4609 static inline void drawEllipsePoints(int x, int y, int length,
4612 ProcessSpans pen_func, ProcessSpans brush_func,
4613 QSpanData *pen_data, QSpanData *brush_data)
4618 QT_FT_Span outline[4];
4619 const int midx = rect.x() + (rect.width() + 1) / 2;
4620 const int midy = rect.y() + (rect.height() + 1) / 2;
4626 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4627 outline[0].len = qMin(length, x - outline[0].x);
4629 outline[0].coverage = 255;
4633 outline[1].len = length;
4635 outline[1].coverage = 255;
4638 outline[2].x = outline[0].x;
4639 outline[2].len = outline[0].len;
4640 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4641 outline[2].coverage = 255;
4645 outline[3].len = length;
4646 outline[3].y = outline[2].y;
4647 outline[3].coverage = 255;
4649 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4653 fill[0].x = outline[0].x + outline[0].len - 1;
4654 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4655 fill[0].y = outline[1].y;
4656 fill[0].coverage = 255;
4659 fill[1].x = outline[2].x + outline[2].len - 1;
4660 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4661 fill[1].y = outline[3].y;
4662 fill[1].coverage = 255;
4664 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4665 n = qt_intersect_spans(fill, n, clip);
4667 brush_func(n, fill, brush_data);
4670 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4671 n = qt_intersect_spans(outline, n, clip);
4673 pen_func(n, outline, pen_data);
4679 Draws an ellipse using the integer point midpoint algorithm.
4681 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4682 ProcessSpans pen_func, ProcessSpans brush_func,
4683 QSpanData *pen_data, QSpanData *brush_data)
4685 const qreal a = qreal(rect.width()) / 2;
4686 const qreal b = qreal(rect.height()) / 2;
4687 qreal d = b*b - (a*a*b) + 0.25*a*a;
4690 int y = (rect.height() + 1) / 2;
4694 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4695 if (d < 0) { // select E
4698 } else { // select SE
4699 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4700 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4701 pen_func, brush_func, pen_data, brush_data);
4706 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4707 pen_func, brush_func, pen_data, brush_data);
4710 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4711 const int miny = rect.height() & 0x1;
4713 if (d < 0) { // select SE
4714 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4716 } else { // select S
4717 d += a*a*(-2*y + 3);
4720 drawEllipsePoints(x, y, 1, rect, clip,
4721 pen_func, brush_func, pen_data, brush_data);
4726 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
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++));