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 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1937 fillPolygon(points, pointCount, mode);
1938 d->outlineMapper->setCoordinateRounding(false);
1942 // Do the outline...
1943 if (s->penData.blend) {
1944 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1945 if (s->flags.fast_pen) {
1946 QCosmeticStroker stroker(s, d->deviceRect);
1947 stroker.drawPath(vp);
1949 QPaintEngineEx::stroke(vp, s->lastPen);
1957 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1959 Q_D(QRasterPaintEngine);
1960 QRasterPaintEngineState *s = state();
1962 #ifdef QT_DEBUG_DRAW
1963 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1964 for (int i=0; i<pointCount; ++i)
1965 qDebug() << " - " << points[i];
1967 Q_ASSERT(pointCount >= 2);
1968 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1969 QRect r(points[0].x(),
1971 points[2].x() - points[0].x(),
1972 points[2].y() - points[0].y());
1980 if (mode != PolylineMode) {
1982 if (s->brushData.blend) {
1983 // Compose polygon fill..,
1984 ensureOutlineMapper();
1985 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1986 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1987 d->outlineMapper->moveTo(*points);
1988 const QPoint *p = points;
1989 const QPoint *ep = points + pointCount - 1;
1991 d->outlineMapper->lineTo(*(++p));
1993 d->outlineMapper->endOutline();
1996 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1998 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1999 d->outlineMapper->setCoordinateRounding(false);
2003 // Do the outline...
2004 if (s->penData.blend) {
2005 int count = pointCount * 2;
2006 QVarLengthArray<qreal> fpoints(count);
2007 for (int i=0; i<count; ++i)
2008 fpoints[i] = ((int *) points)[i];
2009 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2011 if (s->flags.fast_pen) {
2012 QCosmeticStroker stroker(s, d->deviceRect);
2013 stroker.drawPath(vp);
2015 QPaintEngineEx::stroke(vp, s->lastPen);
2023 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2025 #ifdef QT_DEBUG_DRAW
2026 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2029 QPlatformPixmap *pd = pixmap.handle();
2030 if (pd->classId() == QPlatformPixmap::RasterClass) {
2031 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2032 if (image.depth() == 1) {
2033 Q_D(QRasterPaintEngine);
2034 QRasterPaintEngineState *s = state();
2035 if (s->matrix.type() <= QTransform::TxTranslate) {
2037 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2039 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2042 QRasterPaintEngine::drawImage(pos, image);
2045 const QImage image = pixmap.toImage();
2046 if (pixmap.depth() == 1) {
2047 Q_D(QRasterPaintEngine);
2048 QRasterPaintEngineState *s = state();
2049 if (s->matrix.type() <= QTransform::TxTranslate) {
2051 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2053 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2056 QRasterPaintEngine::drawImage(pos, image);
2064 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2066 #ifdef QT_DEBUG_DRAW
2067 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2070 QPlatformPixmap* pd = pixmap.handle();
2071 if (pd->classId() == QPlatformPixmap::RasterClass) {
2072 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2073 if (image.depth() == 1) {
2074 Q_D(QRasterPaintEngine);
2075 QRasterPaintEngineState *s = state();
2076 if (s->matrix.type() <= QTransform::TxTranslate
2077 && r.size() == sr.size()
2078 && r.size() == pixmap.size()) {
2080 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2083 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2086 drawImage(r, image, sr);
2089 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2090 const QImage image = pd->toImage(clippedSource);
2091 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2092 if (image.depth() == 1) {
2093 Q_D(QRasterPaintEngine);
2094 QRasterPaintEngineState *s = state();
2095 if (s->matrix.type() <= QTransform::TxTranslate
2096 && r.size() == sr.size()
2097 && r.size() == pixmap.size()) {
2099 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2102 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2105 drawImage(r, image, translatedSource);
2110 static inline int fast_ceil_positive(const qreal &v)
2112 const int iv = int(v);
2119 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2121 const int xmin = int(rect.x());
2122 const int xmax = int(fast_ceil_positive(rect.right()));
2123 const int ymin = int(rect.y());
2124 const int ymax = int(fast_ceil_positive(rect.bottom()));
2125 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2131 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2133 #ifdef QT_DEBUG_DRAW
2134 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2137 Q_D(QRasterPaintEngine);
2138 QRasterPaintEngineState *s = state();
2140 if (s->matrix.type() > QTransform::TxTranslate) {
2141 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2143 QRectF(0, 0, img.width(), img.height()));
2146 const QClipData *clip = d->clip();
2147 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2149 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2150 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2153 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2155 } else if (clip->hasRectClip) {
2156 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2164 d->image_filler.clip = clip;
2165 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2166 if (!d->image_filler.blend)
2168 d->image_filler.dx = -pt.x();
2169 d->image_filler.dy = -pt.y();
2170 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2172 fillRect_normalized(rr, &d->image_filler, d);
2177 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2179 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2190 inline RotationType qRotationType(const QTransform &transform)
2192 QTransform::TransformationType type = transform.type();
2194 if (type > QTransform::TxRotate)
2197 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2198 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2201 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2202 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2205 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2206 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2212 inline bool isPixelAligned(const QRectF &rect) {
2213 return QRectF(rect.toRect()) == rect;
2220 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2221 Qt::ImageConversionFlags)
2223 #ifdef QT_DEBUG_DRAW
2224 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2230 Q_D(QRasterPaintEngine);
2231 QRasterPaintEngineState *s = state();
2232 int sr_l = qFloor(sr.left());
2233 int sr_r = qCeil(sr.right()) - 1;
2234 int sr_t = qFloor(sr.top());
2235 int sr_b = qCeil(sr.bottom()) - 1;
2237 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2238 // as fillRect will apply the aliased coordinate delta we need to
2239 // subtract it here as we don't use it for image drawing
2240 QTransform old = s->matrix;
2241 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2243 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2244 QRgb color = img.pixel(sr_l, sr_t);
2245 switch (img.format()) {
2246 case QImage::Format_ARGB32_Premultiplied:
2247 case QImage::Format_ARGB8565_Premultiplied:
2248 case QImage::Format_ARGB6666_Premultiplied:
2249 case QImage::Format_ARGB8555_Premultiplied:
2250 case QImage::Format_ARGB4444_Premultiplied:
2251 // Combine premultiplied color with the opacity set on the painter.
2252 d->solid_color_filler.solid.color =
2253 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2254 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2257 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2261 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2262 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2266 d->solid_color_filler.clip = d->clip();
2267 d->solid_color_filler.adjustSpanMethods();
2268 fillRect(r, &d->solid_color_filler);
2274 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2276 const QClipData *clip = d->clip();
2278 if (s->matrix.type() > QTransform::TxTranslate
2280 && (!clip || clip->hasRectClip)
2281 && s->intOpacity == 256
2282 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2283 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2284 && d->rasterBuffer->format == img.format()
2285 && (d->rasterBuffer->format == QImage::Format_RGB16
2286 || d->rasterBuffer->format == QImage::Format_RGB32
2287 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2288 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2290 RotationType rotationType = qRotationType(s->matrix);
2292 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2293 QRectF transformedTargetRect = s->matrix.mapRect(r);
2295 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2296 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2298 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2299 if (clippedTransformedTargetRect.isNull())
2302 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2304 QRect clippedSourceRect
2305 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2306 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2308 uint dbpl = d->rasterBuffer->bytesPerLine();
2309 uint sbpl = img.bytesPerLine();
2311 uchar *dst = d->rasterBuffer->buffer();
2312 uint bpp = img.depth() >> 3;
2314 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2315 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2317 uint cw = clippedSourceRect.width();
2318 uint ch = clippedSourceRect.height();
2320 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2327 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2329 QRectF targetBounds = s->matrix.mapRect(r);
2330 bool exceedsPrecision = targetBounds.width() > 0xffff
2331 || targetBounds.height() > 0xffff;
2333 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2334 if (s->matrix.type() > QTransform::TxScale) {
2335 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2336 if (func && (!clip || clip->hasRectClip)) {
2337 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2338 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2339 s->matrix, s->intOpacity);
2343 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2344 if (func && (!clip || clip->hasRectClip)) {
2345 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2346 img.bits(), img.bytesPerLine(),
2347 qt_mapRect_non_normalizing(r, s->matrix), sr,
2348 !clip ? d->deviceRect : clip->clipRect,
2355 QTransform copy = s->matrix;
2356 copy.translate(r.x(), r.y());
2358 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2359 copy.translate(-sr.x(), -sr.y());
2361 d->image_filler_xform.clip = clip;
2362 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2363 if (!d->image_filler_xform.blend)
2365 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2367 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2368 QRectF rr = s->matrix.mapRect(r);
2370 const int x1 = qRound(rr.x());
2371 const int y1 = qRound(rr.y());
2372 const int x2 = qRound(rr.right());
2373 const int y2 = qRound(rr.bottom());
2375 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2379 #ifdef QT_FAST_SPANS
2380 ensureRasterState();
2381 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2382 d->initializeRasterizer(&d->image_filler_xform);
2383 d->rasterizer->setAntialiased(s->flags.antialiased);
2385 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2387 const QRectF &rect = r.normalized();
2388 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2389 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2391 if (s->flags.tx_noshear)
2392 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2394 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2398 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2401 QTransform m = s->matrix;
2402 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2403 m.m21(), m.m22(), m.m23(),
2404 m.m31() - offs, m.m32() - offs, m.m33());
2405 fillPath(path, &d->image_filler_xform);
2408 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2409 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2411 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2413 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2415 } else if (clip->hasRectClip) {
2416 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2422 d->image_filler.clip = clip;
2423 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2424 if (!d->image_filler.blend)
2426 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2427 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2430 rr.translate(s->matrix.dx(), s->matrix.dy());
2432 const int x1 = qRound(rr.x());
2433 const int y1 = qRound(rr.y());
2434 const int x2 = qRound(rr.right());
2435 const int y2 = qRound(rr.bottom());
2437 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2444 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2446 #ifdef QT_DEBUG_DRAW
2447 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2449 Q_D(QRasterPaintEngine);
2450 QRasterPaintEngineState *s = state();
2454 QPlatformPixmap *pd = pixmap.handle();
2455 if (pd->classId() == QPlatformPixmap::RasterClass) {
2456 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2458 image = pixmap.toImage();
2461 if (image.depth() == 1)
2462 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2464 if (s->matrix.type() > QTransform::TxTranslate) {
2465 QTransform copy = s->matrix;
2466 copy.translate(r.x(), r.y());
2467 copy.translate(-sr.x(), -sr.y());
2468 d->image_filler_xform.clip = d->clip();
2469 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2470 if (!d->image_filler_xform.blend)
2472 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2474 #ifdef QT_FAST_SPANS
2475 ensureRasterState();
2476 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2477 d->initializeRasterizer(&d->image_filler_xform);
2478 d->rasterizer->setAntialiased(s->flags.antialiased);
2480 const QRectF &rect = r.normalized();
2481 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2482 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2483 if (s->flags.tx_noshear)
2484 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2486 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2492 fillPath(path, &d->image_filler_xform);
2494 d->image_filler.clip = d->clip();
2496 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2497 if (!d->image_filler.blend)
2499 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2500 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2503 rr.translate(s->matrix.dx(), s->matrix.dy());
2504 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2510 static inline bool monoVal(const uchar* s, int x)
2512 return (s[x>>3] << (x&7)) & 0x80;
2518 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2520 Q_D(QRasterPaintEngine);
2521 return d->rasterBuffer.data();
2527 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2529 Q_D(QRasterPaintEngine);
2530 QRasterPaintEngineState *s = state();
2532 if (!s->penData.blend)
2535 QRasterBuffer *rb = d->rasterBuffer.data();
2537 const QRect rect(rx, ry, w, h);
2538 const QClipData *clip = d->clip();
2539 bool unclipped = false;
2541 // inlined QRect::intersects
2542 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2543 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2545 if (clip->hasRectClip) {
2546 unclipped = rx > clip->xmin
2547 && rx + w < clip->xmax
2549 && ry + h < clip->ymax;
2555 // inlined QRect::intersects
2556 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2557 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2561 // inlined QRect::contains
2562 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2563 && rect.top() >= 0 && rect.bottom() < rb->height();
2565 unclipped = contains && d->isUnclipped_normalized(rect);
2568 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2569 const uchar * scanline = static_cast<const uchar *>(src);
2571 if (s->flags.fast_text) {
2574 if (s->penData.bitmapBlit) {
2575 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2576 scanline, w, h, bpl);
2579 } else if (depth == 8) {
2580 if (s->penData.alphamapBlit) {
2581 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2582 scanline, w, h, bpl, 0);
2585 } else if (depth == 32) {
2586 // (A)RGB Alpha mask where the alpha component is not used.
2587 if (s->penData.alphaRGBBlit) {
2588 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2589 (const uint *) scanline, w, h, bpl / 4, 0);
2593 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2594 // (A)RGB Alpha mask where the alpha component is not used.
2596 int nx = qMax(0, rx);
2597 int ny = qMax(0, ry);
2599 // Move scanline pointer to compensate for moved x and y
2600 int xdiff = nx - rx;
2601 int ydiff = ny - ry;
2602 scanline += ydiff * bpl;
2603 scanline += xdiff * (depth == 32 ? 4 : 1);
2608 if (nx + w > d->rasterBuffer->width())
2609 w = d->rasterBuffer->width() - nx;
2610 if (ny + h > d->rasterBuffer->height())
2611 h = d->rasterBuffer->height() - ny;
2616 if (depth == 8 && s->penData.alphamapBlit) {
2617 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2618 scanline, w, h, bpl, clip);
2619 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2620 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2621 (const uint *) scanline, w, h, bpl / 4, clip);
2636 scanline += bpl * y0;
2640 w = qMin(w, rb->width() - qMax(0, rx));
2641 h = qMin(h, rb->height() - qMax(0, ry));
2643 if (w <= 0 || h <= 0)
2646 const int NSPANS = 256;
2647 QSpan spans[NSPANS];
2650 const int x1 = x0 + w;
2651 const int y1 = y0 + h;
2654 for (int y = y0; y < y1; ++y) {
2655 for (int x = x0; x < x1; ) {
2656 if (!monoVal(scanline, x)) {
2661 if (current == NSPANS) {
2662 blend(current, spans, &s->penData);
2665 spans[current].x = x + rx;
2666 spans[current].y = y + ry;
2667 spans[current].coverage = 255;
2670 // extend span until we find a different one.
2671 while (x < x1 && monoVal(scanline, x)) {
2675 spans[current].len = len;
2680 } else if (depth == 8) {
2681 for (int y = y0; y < y1; ++y) {
2682 for (int x = x0; x < x1; ) {
2683 // Skip those with 0 coverage
2684 if (scanline[x] == 0) {
2689 if (current == NSPANS) {
2690 blend(current, spans, &s->penData);
2693 int coverage = scanline[x];
2694 spans[current].x = x + rx;
2695 spans[current].y = y + ry;
2696 spans[current].coverage = coverage;
2700 // extend span until we find a different one.
2701 while (x < x1 && scanline[x] == coverage) {
2705 spans[current].len = len;
2710 } else { // 32-bit alpha...
2711 uint *sl = (uint *) src;
2712 for (int y = y0; y < y1; ++y) {
2713 for (int x = x0; x < x1; ) {
2714 // Skip those with 0 coverage
2715 if ((sl[x] & 0x00ffffff) == 0) {
2720 if (current == NSPANS) {
2721 blend(current, spans, &s->penData);
2724 uint rgbCoverage = sl[x];
2725 int coverage = qGreen(rgbCoverage);
2726 spans[current].x = x + rx;
2727 spans[current].y = y + ry;
2728 spans[current].coverage = coverage;
2732 // extend span until we find a different one.
2733 while (x < x1 && sl[x] == rgbCoverage) {
2737 spans[current].len = len;
2740 sl += bpl / sizeof(uint);
2743 // qDebug() << "alphaPenBlt: num spans=" << current
2744 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2745 // Call span func for current set of spans.
2747 blend(current, spans, &s->penData);
2753 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2754 const QFixedPoint *positions, QFontEngine *fontEngine)
2756 Q_D(QRasterPaintEngine);
2757 QRasterPaintEngineState *s = state();
2759 if (fontEngine->hasInternalCaching()) {
2760 QFontEngine::GlyphFormat neededFormat =
2761 painter()->device()->devType() == QInternal::Widget
2762 ? QFontEngine::Format_None
2763 : QFontEngine::Format_A8;
2765 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2766 neededFormat = QFontEngine::Format_Mono;
2768 for (int i = 0; i < numGlyphs; i++) {
2769 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2772 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2774 if (alphaMap == 0 || alphaMap->isNull())
2777 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2778 qFloor(positions[i].x) + offset.x(),
2779 qFloor(positions[i].y) + offset.y(),
2780 alphaMap->width(), alphaMap->height());
2782 fontEngine->unlockAlphaMapForGlyph();
2786 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2788 QImageTextureGlyphCache *cache =
2789 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2791 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2792 fontEngine->setGlyphCache(0, cache);
2795 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2796 cache->fillInPendingGlyphs();
2798 const QImage &image = cache->image();
2799 int bpl = image.bytesPerLine();
2801 int depth = image.depth();
2805 leftShift = 2; // multiply by 4
2806 else if (depth == 1)
2807 rightShift = 3; // divide by 8
2809 int margin = fontEngine->glyphMargin(glyphType);
2810 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2811 const uchar *bits = image.bits();
2812 for (int i=0; i<numGlyphs; ++i) {
2814 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2815 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2816 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2820 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2821 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2823 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2826 // c.baseLineX, c.baseLineY,
2829 // positions[i].x.toInt(), positions[i].y.toInt());
2831 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2839 * Returns true if the rectangle is completely within the current clip
2840 * state of the paint engine.
2842 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2844 const QClipData *cl = clip();
2846 // inline contains() for performance (we know the rects are normalized)
2847 const QRect &r1 = deviceRect;
2848 return (r.left() >= r1.left() && r.right() <= r1.right()
2849 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2853 if (cl->hasRectClip) {
2854 // currently all painting functions clips to deviceRect internally
2855 if (cl->clipRect == deviceRect)
2858 // inline contains() for performance (we know the rects are normalized)
2859 const QRect &r1 = cl->clipRect;
2860 return (r.left() >= r1.left() && r.right() <= r1.right()
2861 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2863 return qt_region_strictContains(cl->clipRegion, r);
2867 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2870 Q_Q(const QRasterPaintEngine);
2871 const QRasterPaintEngineState *s = q->state();
2872 const QClipData *cl = clip();
2874 QRect r = rect.normalized();
2875 // inline contains() for performance (we know the rects are normalized)
2876 const QRect &r1 = deviceRect;
2877 return (r.left() >= r1.left() && r.right() <= r1.right()
2878 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2882 // currently all painting functions that call this function clip to deviceRect internally
2883 if (cl->hasRectClip && cl->clipRect == deviceRect)
2886 if (s->flags.antialiased)
2889 QRect r = rect.normalized();
2891 r.setX(r.x() - penWidth);
2892 r.setY(r.y() - penWidth);
2893 r.setWidth(r.width() + 2 * penWidth);
2894 r.setHeight(r.height() + 2 * penWidth);
2897 if (cl->hasRectClip) {
2898 // inline contains() for performance (we know the rects are normalized)
2899 const QRect &r1 = cl->clipRect;
2900 return (r.left() >= r1.left() && r.right() <= r1.right()
2901 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2903 return qt_region_strictContains(cl->clipRegion, r);
2907 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2910 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2914 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2915 const QSpanData *data) const
2917 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2921 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2922 const QSpanData *data) const
2924 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2928 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2929 const QSpanData *data) const
2931 Q_Q(const QRasterPaintEngine);
2932 const QRasterPaintEngineState *s = q->state();
2934 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2936 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2937 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2940 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2941 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2943 QFixed clipLeft = QFixed::fromReal(clip.left());
2944 QFixed clipRight = QFixed::fromReal(clip.right());
2945 QFixed clipTop = QFixed::fromReal(clip.top());
2946 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2949 while (first < numGlyphs) {
2950 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2951 QFixed left = metrics.x + positions[first].x;
2952 QFixed top = metrics.y + positions[first].y;
2953 QFixed right = left + metrics.width;
2954 QFixed bottom = top + metrics.height;
2955 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2959 int last = numGlyphs - 1;
2960 while (last > first) {
2961 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2962 QFixed left = metrics.x + positions[last].x;
2963 QFixed top = metrics.y + positions[last].y;
2964 QFixed right = left + metrics.width;
2965 QFixed bottom = top + metrics.height;
2966 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2970 return QPair<int, int>(first, last + 1);
2976 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2978 if (textItem->numGlyphs == 0)
2982 ensureRasterState();
2984 QFontEngine *fontEngine = textItem->fontEngine();
2985 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2986 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2988 } else if (state()->matrix.type() < QTransform::TxProject) {
2990 QTransform invMat = state()->matrix.inverted(&invertible);
2994 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2995 textItem->fontEngine(), textItem->glyphs,
2996 textItem->glyphPositions, textItem->numGlyphs);
2997 QStaticTextItem copy = *textItem;
2998 copy.glyphs += range.first;
2999 copy.glyphPositions += range.first;
3000 copy.numGlyphs = range.second - range.first;
3001 QPaintEngineEx::drawStaticTextItem(©);
3003 QPaintEngineEx::drawStaticTextItem(textItem);
3010 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3012 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3014 #ifdef QT_DEBUG_DRAW
3015 Q_D(QRasterPaintEngine);
3016 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3017 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3021 if (ti.glyphs.numGlyphs == 0)
3024 ensureRasterState();
3026 QRasterPaintEngineState *s = state();
3027 QTransform matrix = s->matrix;
3029 if (!supportsTransformations(ti.fontEngine)) {
3030 QVarLengthArray<QFixedPoint> positions;
3031 QVarLengthArray<glyph_t> glyphs;
3033 matrix.translate(p.x(), p.y());
3034 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3036 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3037 } else if (matrix.type() < QTransform::TxProject) {
3039 QTransform invMat = matrix.inverted(&invertible);
3043 QVarLengthArray<QFixedPoint> positions;
3044 QVarLengthArray<glyph_t> glyphs;
3046 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3047 ti.flags, glyphs, positions);
3048 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3049 ti.fontEngine, glyphs.data(), positions.data(),
3052 if (range.first >= range.second)
3055 QStaticTextItem staticTextItem;
3056 staticTextItem.color = s->pen.color();
3057 staticTextItem.font = s->font;
3058 staticTextItem.setFontEngine(ti.fontEngine);
3059 staticTextItem.numGlyphs = range.second - range.first;
3060 staticTextItem.glyphs = glyphs.data() + range.first;
3061 staticTextItem.glyphPositions = positions.data() + range.first;
3062 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3064 QPaintEngineEx::drawTextItem(p, ti);
3071 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3073 Q_D(QRasterPaintEngine);
3074 QRasterPaintEngineState *s = state();
3077 if (!s->penData.blend)
3080 if (!s->flags.fast_pen) {
3081 QPaintEngineEx::drawPoints(points, pointCount);
3085 QCosmeticStroker stroker(s, d->deviceRect);
3086 stroker.drawPoints(points, pointCount);
3090 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3092 Q_D(QRasterPaintEngine);
3093 QRasterPaintEngineState *s = state();
3096 if (!s->penData.blend)
3099 if (!s->flags.fast_pen) {
3100 QPaintEngineEx::drawPoints(points, pointCount);
3104 QCosmeticStroker stroker(s, d->deviceRect);
3105 stroker.drawPoints(points, pointCount);
3111 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3113 #ifdef QT_DEBUG_DRAW
3114 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3116 Q_D(QRasterPaintEngine);
3117 QRasterPaintEngineState *s = state();
3120 if (!s->penData.blend)
3123 if (s->flags.fast_pen) {
3124 QCosmeticStroker stroker(s, d->deviceRect);
3125 for (int i=0; i<lineCount; ++i) {
3126 const QLine &l = lines[i];
3127 stroker.drawLine(l.p1(), l.p2());
3130 QPaintEngineEx::drawLines(lines, lineCount);
3134 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3140 Q_Q(QRasterPaintEngine);
3141 QRasterPaintEngineState *s = q->state();
3143 const QPen &pen = s->lastPen;
3144 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3145 const QVector<qreal> pattern = pen.dashPattern();
3147 qreal patternLength = 0;
3148 for (int i = 0; i < pattern.size(); ++i)
3149 patternLength += pattern.at(i);
3151 if (patternLength <= 0)
3154 qreal length = line.length();
3155 Q_ASSERT(length > 0);
3156 while (length > 0) {
3157 const bool rasterize = *inDash;
3158 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3161 if (dash >= length) {
3163 *dashOffset += dash / width;
3167 *inDash = !(*inDash);
3168 if (++*dashIndex >= pattern.size())
3175 if (rasterize && dash > 0)
3176 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3183 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3185 #ifdef QT_DEBUG_DRAW
3186 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3188 Q_D(QRasterPaintEngine);
3189 QRasterPaintEngineState *s = state();
3192 if (!s->penData.blend)
3194 if (s->flags.fast_pen) {
3195 QCosmeticStroker stroker(s, d->deviceRect);
3196 for (int i=0; i<lineCount; ++i) {
3197 QLineF line = lines[i];
3198 stroker.drawLine(line.p1(), line.p2());
3201 QPaintEngineEx::drawLines(lines, lineCount);
3209 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3211 Q_D(QRasterPaintEngine);
3212 QRasterPaintEngineState *s = state();
3215 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3216 || (qpen_style(s->lastPen) == Qt::NoPen))
3217 && !s->flags.antialiased
3218 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3220 && s->matrix.type() <= QTransform::TxScale) // no shear
3223 const QRectF r = s->matrix.mapRect(rect);
3224 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3225 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3226 const QRect brect = QRect(int(r.x()), int(r.y()),
3227 int_dim(r.x(), r.width()),
3228 int_dim(r.y(), r.height()));
3230 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3231 &s->penData, &s->brushData);
3235 QPaintEngineEx::drawEllipse(rect);
3243 void QRasterPaintEngine::setDC(HDC hdc) {
3244 Q_D(QRasterPaintEngine);
3251 HDC QRasterPaintEngine::getDC() const
3253 Q_D(const QRasterPaintEngine);
3260 void QRasterPaintEngine::releaseDC(HDC) const
3269 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3271 const QTransform &m = state()->matrix;
3272 return supportsTransformations(fontEngine, m);
3278 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3280 if (m.type() >= QTransform::TxProject)
3283 return !shouldDrawCachedGlyphs(fontEngine, m);
3289 QPoint QRasterPaintEngine::coordinateOffset() const
3291 return QPoint(0, 0);
3294 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3299 Q_D(QRasterPaintEngine);
3301 Q_ASSERT(image.depth() == 1);
3303 const int spanCount = 256;
3304 QT_FT_Span spans[spanCount];
3308 int w = image.width();
3309 int h = image.height();
3310 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3311 int ymin = qMax(qRound(pos.y()), 0);
3312 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3313 int xmin = qMax(qRound(pos.x()), 0);
3315 int x_offset = xmin - qRound(pos.x());
3317 QImage::Format format = image.format();
3318 for (int y = ymin; y < ymax; ++y) {
3319 const uchar *src = image.scanLine(y - qRound(pos.y()));
3320 if (format == QImage::Format_MonoLSB) {
3321 for (int x = 0; x < xmax - xmin; ++x) {
3322 int src_x = x + x_offset;
3323 uchar pixel = src[src_x >> 3];
3328 if (pixel & (0x1 << (src_x & 7))) {
3329 spans[n].x = xmin + x;
3331 spans[n].coverage = 255;
3333 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3337 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3340 if (n == spanCount) {
3341 fg->blend(n, spans, fg);
3347 for (int x = 0; x < xmax - xmin; ++x) {
3348 int src_x = x + x_offset;
3349 uchar pixel = src[src_x >> 3];
3354 if (pixel & (0x80 >> (x & 7))) {
3355 spans[n].x = xmin + x;
3357 spans[n].coverage = 255;
3359 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3363 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3366 if (n == spanCount) {
3367 fg->blend(n, spans, fg);
3375 fg->blend(n, spans, fg);
3381 \enum QRasterPaintEngine::ClipType
3384 \value RectClip Indicates that the currently set clip is a single rectangle.
3385 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3390 Returns the type of the clip currently set.
3392 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3394 Q_D(const QRasterPaintEngine);
3396 const QClipData *clip = d->clip();
3397 if (!clip || clip->hasRectClip)
3405 Returns the bounding rect of the currently set clip.
3407 QRect QRasterPaintEngine::clipBoundingRect() const
3409 Q_D(const QRasterPaintEngine);
3411 const QClipData *clip = d->clip();
3414 return d->deviceRect;
3416 if (clip->hasRectClip)
3417 return clip->clipRect;
3419 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3422 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3424 Q_Q(QRasterPaintEngine);
3425 QRasterPaintEngineState *s = q->state();
3427 rasterizer->setAntialiased(s->flags.antialiased);
3429 QRect clipRect(deviceRect);
3431 // ### get from optimized rectbased QClipData
3433 const QClipData *c = clip();
3435 const QRect r(QPoint(c->xmin, c->ymin),
3436 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3437 clipRect = clipRect.intersected(r);
3438 blend = data->blend;
3440 blend = data->unclipped_blend;
3443 rasterizer->setClipRect(clipRect);
3444 rasterizer->initialize(blend, data);
3447 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3448 ProcessSpans callback,
3449 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3451 if (!callback || !outline)
3454 Q_Q(QRasterPaintEngine);
3455 QRasterPaintEngineState *s = q->state();
3457 if (!s->flags.antialiased) {
3458 initializeRasterizer(spanData);
3460 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3464 rasterizer->rasterize(outline, fillRule);
3468 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3472 int q_gray_rendered_spans(QT_FT_Raster raster);
3475 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3477 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3480 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3481 ProcessSpans callback,
3482 void *userData, QRasterBuffer *)
3484 if (!callback || !outline)
3487 Q_Q(QRasterPaintEngine);
3488 QRasterPaintEngineState *s = q->state();
3490 if (!s->flags.antialiased) {
3491 rasterizer->setAntialiased(s->flags.antialiased);
3492 rasterizer->setClipRect(deviceRect);
3493 rasterizer->initialize(callback, userData);
3495 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3499 rasterizer->rasterize(outline, fillRule);
3503 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3504 // minimize memory reallocations. However if initial size for
3505 // raster pool is changed for lower value, reallocations will
3507 int rasterPoolSize = MINIMUM_POOL_SIZE;
3508 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3509 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3510 uchar *rasterPoolOnHeap = 0;
3512 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3514 void *data = userData;
3516 QT_FT_BBox clip_box = { deviceRect.x(),
3518 deviceRect.x() + deviceRect.width(),
3519 deviceRect.y() + deviceRect.height() };
3521 QT_FT_Raster_Params rasterParams;
3522 rasterParams.target = 0;
3523 rasterParams.source = outline;
3524 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3525 rasterParams.gray_spans = 0;
3526 rasterParams.black_spans = 0;
3527 rasterParams.bit_test = 0;
3528 rasterParams.bit_set = 0;
3529 rasterParams.user = data;
3530 rasterParams.clip_box = clip_box;
3535 int rendered_spans = 0;
3539 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3540 rasterParams.gray_spans = callback;
3541 rasterParams.skip_spans = rendered_spans;
3542 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3544 // Out of memory, reallocate some more and try again...
3545 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3546 rasterPoolSize *= 2;
3547 if (rasterPoolSize > 1024 * 1024) {
3548 qWarning("QPainter: Rasterization of primitive failed");
3552 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3554 free(rasterPoolOnHeap);
3555 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3557 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3559 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3561 qt_ft_grays_raster.raster_done(*grayRaster.data());
3562 qt_ft_grays_raster.raster_new(grayRaster.data());
3563 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3569 free(rasterPoolOnHeap);
3572 void QRasterPaintEnginePrivate::recalculateFastImages()
3574 Q_Q(QRasterPaintEngine);
3575 QRasterPaintEngineState *s = q->state();
3577 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3578 && s->matrix.type() <= QTransform::TxShear;
3581 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3583 Q_Q(const QRasterPaintEngine);
3584 const QRasterPaintEngineState *s = q->state();
3586 return s->flags.fast_images
3587 && (mode == QPainter::CompositionMode_SourceOver
3588 || (mode == QPainter::CompositionMode_Source
3589 && !image.hasAlphaChannel()));
3592 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3594 Q_ASSERT(image.depth() == 1);
3596 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3597 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3599 QRgb fg = PREMUL(color.rgba());
3602 int height = sourceImage.height();
3603 int width = sourceImage.width();
3604 for (int y=0; y<height; ++y) {
3605 uchar *source = sourceImage.scanLine(y);
3606 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3607 if (!source || !target)
3608 QT_THROW(std::bad_alloc()); // we must have run out of memory
3609 for (int x=0; x < width; ++x)
3610 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3615 QRasterBuffer::~QRasterBuffer()
3619 void QRasterBuffer::init()
3621 compositionMode = QPainter::CompositionMode_SourceOver;
3622 monoDestinationWithClut = false;
3627 QImage::Format QRasterBuffer::prepare(QImage *image)
3629 m_buffer = (uchar *)image->bits();
3630 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3631 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3632 bytes_per_pixel = image->depth()/8;
3633 bytes_per_line = image->bytesPerLine();
3635 format = image->format();
3636 drawHelper = qDrawHelper + format;
3637 if (image->depth() == 1 && image->colorTable().size() == 2) {
3638 monoDestinationWithClut = true;
3639 destColor0 = PREMUL(image->colorTable()[0]);
3640 destColor1 = PREMUL(image->colorTable()[1]);
3646 void QRasterBuffer::resetBuffer(int val)
3648 memset(m_buffer, val, m_height*bytes_per_line);
3651 QClipData::QClipData(int height)
3653 clipSpanHeight = height;
3658 xmin = xmax = ymin = ymax = 0;
3662 hasRectClip = hasRegionClip = false;
3665 QClipData::~QClipData()
3673 void QClipData::initialize()
3679 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3681 Q_CHECK_PTR(m_clipLines);
3683 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3684 allocated = clipSpanHeight;
3685 Q_CHECK_PTR(m_spans);
3691 m_clipLines[y].spans = 0;
3692 m_clipLines[y].count = 0;
3696 const int len = clipRect.width();
3699 QSpan *span = m_spans + count;
3703 span->coverage = 255;
3706 m_clipLines[y].spans = span;
3707 m_clipLines[y].count = 1;
3711 while (y < clipSpanHeight) {
3712 m_clipLines[y].spans = 0;
3713 m_clipLines[y].count = 0;
3716 } else if (hasRegionClip) {
3718 const QVector<QRect> rects = clipRegion.rects();
3719 const int numRects = rects.size();
3722 const int maxSpans = (ymax - ymin) * numRects;
3723 if (maxSpans > allocated) {
3724 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3725 allocated = maxSpans;
3730 int firstInBand = 0;
3732 while (firstInBand < numRects) {
3733 const int currMinY = rects.at(firstInBand).y();
3734 const int currMaxY = currMinY + rects.at(firstInBand).height();
3736 while (y < currMinY) {
3737 m_clipLines[y].spans = 0;
3738 m_clipLines[y].count = 0;
3742 int lastInBand = firstInBand;
3743 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3746 while (y < currMaxY) {
3748 m_clipLines[y].spans = m_spans + count;
3749 m_clipLines[y].count = lastInBand - firstInBand + 1;
3751 for (int r = firstInBand; r <= lastInBand; ++r) {
3752 const QRect &currRect = rects.at(r);
3753 QSpan *span = m_spans + count;
3754 span->x = currRect.x();
3755 span->len = currRect.width();
3757 span->coverage = 255;
3763 firstInBand = lastInBand + 1;
3766 Q_ASSERT(count <= allocated);
3768 while (y < clipSpanHeight) {
3769 m_clipLines[y].spans = 0;
3770 m_clipLines[y].count = 0;
3776 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3781 free(m_clipLines); // same for clipLines
3787 void QClipData::fixup()
3792 ymin = ymax = xmin = xmax = 0;
3797 ymin = m_spans[0].y;
3798 ymax = m_spans[count-1].y + 1;
3802 const int firstLeft = m_spans[0].x;
3803 const int firstRight = m_spans[0].x + m_spans[0].len;
3806 for (int i = 0; i < count; ++i) {
3807 QT_FT_Span_& span = m_spans[i];
3810 if (span.y != y + 1 && y != -1)
3813 m_clipLines[y].spans = &span;
3814 m_clipLines[y].count = 1;
3816 ++m_clipLines[y].count;
3818 const int spanLeft = span.x;
3819 const int spanRight = spanLeft + span.len;
3821 if (spanLeft < xmin)
3824 if (spanRight > xmax)
3827 if (spanLeft != firstLeft || spanRight != firstRight)
3833 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3838 Convert \a rect to clip spans.
3840 void QClipData::setClipRect(const QRect &rect)
3842 if (hasRectClip && rect == clipRect)
3845 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3847 hasRegionClip = false;
3851 xmax = rect.x() + rect.width();
3852 ymin = qMin(rect.y(), clipSpanHeight);
3853 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3860 // qDebug() << xmin << xmax << ymin << ymax;
3864 Convert \a region to clip spans.
3866 void QClipData::setClipRegion(const QRegion ®ion)
3868 if (region.rectCount() == 1) {
3869 setClipRect(region.rects().at(0));
3873 hasRegionClip = true;
3874 hasRectClip = false;
3875 clipRegion = region;
3877 { // set bounding rect
3878 const QRect rect = region.boundingRect();
3880 xmax = rect.x() + rect.width();
3882 ymax = rect.y() + rect.height();
3894 spans must be sorted on y
3896 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3897 const QSpan *spans, const QSpan *end,
3898 QSpan **outSpans, int available)
3900 const_cast<QClipData *>(clip)->initialize();
3902 QSpan *out = *outSpans;
3904 const QSpan *clipSpans = clip->m_spans + *currentClip;
3905 const QSpan *clipEnd = clip->m_spans + clip->count;
3907 while (available && spans < end ) {
3908 if (clipSpans >= clipEnd) {
3912 if (clipSpans->y > spans->y) {
3916 if (spans->y != clipSpans->y) {
3917 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3918 clipSpans = clip->m_clipLines[spans->y].spans;
3923 Q_ASSERT(spans->y == clipSpans->y);
3926 int sx2 = sx1 + spans->len;
3927 int cx1 = clipSpans->x;
3928 int cx2 = cx1 + clipSpans->len;
3930 if (cx1 < sx1 && cx2 < sx1) {
3933 } else if (sx1 < cx1 && sx2 < cx1) {
3937 int x = qMax(sx1, cx1);
3938 int len = qMin(sx2, cx2) - x;
3940 out->x = qMax(sx1, cx1);
3941 out->len = qMin(sx2, cx2) - out->x;
3943 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3955 *currentClip = clipSpans - clip->m_spans;
3959 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3961 // qDebug() << "qt_span_fill_clipped" << spanCount;
3962 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3964 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3966 const int NSPANS = 256;
3967 QSpan cspans[NSPANS];
3968 int currentClip = 0;
3969 const QSpan *end = spans + spanCount;
3970 while (spans < end) {
3971 QSpan *clipped = cspans;
3972 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3973 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3974 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3976 if (clipped - cspans)
3977 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3983 Clip spans to \a{clip}-rectangle.
3984 Returns number of unclipped spans
3986 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3989 const short minx = clip.left();
3990 const short miny = clip.top();
3991 const short maxx = clip.right();
3992 const short maxy = clip.bottom();
3995 for (int i = 0; i < numSpans; ++i) {
3996 if (spans[i].y > maxy)
3998 if (spans[i].y < miny
3999 || spans[i].x > maxx
4000 || spans[i].x + spans[i].len <= minx) {
4003 if (spans[i].x < minx) {
4004 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4007 spans[n].x = spans[i].x;
4008 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4010 if (spans[n].len == 0)
4012 spans[n].y = spans[i].y;
4013 spans[n].coverage = spans[i].coverage;
4020 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4023 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4024 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4026 Q_ASSERT(fillData->clip);
4027 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4029 // hw: check if this const_cast<> is safe!!!
4030 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4031 fillData->clip->clipRect);
4033 fillData->unclipped_blend(count, spans, fillData);
4036 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4038 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4040 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4041 // for (int i = 0; i < qMin(count, 10); ++i) {
4042 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4045 switch (clipData->operation) {
4047 case Qt::IntersectClip:
4049 QClipData *newClip = clipData->newClip;
4050 newClip->initialize();
4052 int currentClip = 0;
4053 const QSpan *end = spans + count;
4054 while (spans < end) {
4055 QSpan *newspans = newClip->m_spans + newClip->count;
4056 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4057 &newspans, newClip->allocated - newClip->count);
4058 newClip->count = newspans - newClip->m_spans;
4060 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4061 newClip->allocated *= 2;
4067 case Qt::ReplaceClip:
4068 clipData->newClip->appendSpans(spans, count);
4076 QImage QRasterBuffer::bufferImage() const
4078 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4080 for (int y = 0; y < m_height; ++y) {
4081 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4083 for (int x=0; x<m_width; ++x) {
4084 uint argb = span[x];
4085 image.setPixel(x, y, argb);
4093 void QRasterBuffer::flushToARGBImage(QImage *target) const
4095 int w = qMin(m_width, target->width());
4096 int h = qMin(m_height, target->height());
4098 for (int y=0; y<h; ++y) {
4099 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4100 QRgb *dest = (QRgb *) target->scanLine(y);
4101 for (int x=0; x<w; ++x) {
4102 QRgb pixel = sourceLine[x];
4103 int alpha = qAlpha(pixel);
4107 dest[x] = (alpha << 24)
4108 | ((255*qRed(pixel)/alpha) << 16)
4109 | ((255*qGreen(pixel)/alpha) << 8)
4110 | ((255*qBlue(pixel)/alpha) << 0);
4117 class QGradientCache
4121 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4122 stops(s), opacity(op), interpolationMode(mode) {}
4123 uint buffer[GRADIENT_STOPTABLE_SIZE];
4124 QGradientStops stops;
4126 QGradient::InterpolationMode interpolationMode;
4129 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4132 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4133 quint64 hash_val = 0;
4135 QGradientStops stops = gradient.stops();
4136 for (int i = 0; i < stops.size() && i <= 2; i++)
4137 hash_val += stops[i].second.rgba();
4139 QMutexLocker lock(&mutex);
4140 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4142 if (it == cache.constEnd())
4143 return addCacheElement(hash_val, gradient, opacity);
4146 const CacheInfo &cache_info = it.value();
4147 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4148 return cache_info.buffer;
4150 } while (it != cache.constEnd() && it.key() == hash_val);
4151 // an exact match for these stops and opacity was not found, create new cache
4152 return addCacheElement(hash_val, gradient, opacity);
4156 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4158 inline int maxCacheSize() const { return 60; }
4159 inline void generateGradientColorTable(const QGradient& g,
4161 int size, int opacity) const;
4162 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4163 if (cache.size() == maxCacheSize()) {
4164 // may remove more than 1, but OK
4165 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4167 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4168 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4169 return cache.insert(hash_val, cache_entry).value().buffer;
4172 QGradientColorTableHash cache;
4176 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4178 QGradientStops stops = gradient.stops();
4179 int stopCount = stops.count();
4180 Q_ASSERT(stopCount > 0);
4182 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4184 if (stopCount == 2) {
4185 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4186 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4188 qreal first_stop = stops[0].first;
4189 qreal second_stop = stops[1].first;
4191 if (second_stop < first_stop) {
4192 qSwap(first_color, second_color);
4193 qSwap(first_stop, second_stop);
4196 if (colorInterpolation) {
4197 first_color = PREMUL(first_color);
4198 second_color = PREMUL(second_color);
4201 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4202 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4204 uint red_first = qRed(first_color) << 16;
4205 uint green_first = qGreen(first_color) << 16;
4206 uint blue_first = qBlue(first_color) << 16;
4207 uint alpha_first = qAlpha(first_color) << 16;
4209 uint red_second = qRed(second_color) << 16;
4210 uint green_second = qGreen(second_color) << 16;
4211 uint blue_second = qBlue(second_color) << 16;
4212 uint alpha_second = qAlpha(second_color) << 16;
4215 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4216 if (colorInterpolation)
4217 colorTable[i] = first_color;
4219 colorTable[i] = PREMUL(first_color);
4222 if (i < second_index) {
4223 qreal reciprocal = qreal(1) / (second_index - first_index);
4225 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4226 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4227 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4228 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4231 red_first += 1 << 15;
4232 green_first += 1 << 15;
4233 blue_first += 1 << 15;
4234 alpha_first += 1 << 15;
4236 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4237 red_first += red_delta;
4238 green_first += green_delta;
4239 blue_first += blue_delta;
4240 alpha_first += alpha_delta;
4242 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4243 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4245 if (colorInterpolation)
4246 colorTable[i] = color;
4248 colorTable[i] = PREMUL(color);
4252 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4253 if (colorInterpolation)
4254 colorTable[i] = second_color;
4256 colorTable[i] = PREMUL(second_color);
4262 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4263 if (stopCount == 1) {
4264 current_color = PREMUL(current_color);
4265 for (int i = 0; i < size; ++i)
4266 colorTable[i] = current_color;
4270 // The position where the gradient begins and ends
4271 qreal begin_pos = stops[0].first;
4272 qreal end_pos = stops[stopCount-1].first;
4274 int pos = 0; // The position in the color table.
4277 qreal incr = 1 / qreal(size); // the double increment.
4278 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4280 // Up to first point
4281 colorTable[pos++] = PREMUL(current_color);
4282 while (dpos <= begin_pos) {
4283 colorTable[pos] = colorTable[pos - 1];
4288 int current_stop = 0; // We always interpolate between current and current + 1.
4290 qreal t; // position between current left and right stops
4291 qreal t_delta; // the t increment per entry in the color table
4293 if (dpos < end_pos) {
4295 while (dpos > stops[current_stop+1].first)
4298 if (current_stop != 0)
4299 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4300 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4302 if (colorInterpolation) {
4303 current_color = PREMUL(current_color);
4304 next_color = PREMUL(next_color);
4307 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4308 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4309 t = (dpos - stops[current_stop].first) * c;
4313 Q_ASSERT(current_stop < stopCount);
4315 int dist = qRound(t);
4316 int idist = 256 - dist;
4318 if (colorInterpolation)
4319 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4321 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4326 if (dpos >= end_pos)
4332 while (dpos > stops[current_stop+skip+1].first)
4336 current_stop += skip;
4338 current_color = next_color;
4340 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4341 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4343 if (colorInterpolation) {
4345 current_color = PREMUL(current_color);
4346 next_color = PREMUL(next_color);
4349 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4350 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4351 t = (dpos - stops[current_stop].first) * c;
4358 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4359 while (pos < size - 1) {
4360 colorTable[pos] = current_color;
4364 // Make sure the last color stop is represented at the end of the table
4365 colorTable[size - 1] = current_color;
4368 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4371 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4377 m11 = m22 = m33 = 1.;
4378 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4379 clip = pe ? pe->d_func()->clip() : 0;
4382 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4384 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4386 Qt::BrushStyle brushStyle = qbrush_style(brush);
4387 switch (brushStyle) {
4388 case Qt::SolidPattern: {
4390 QColor c = qbrush_color(brush);
4391 QRgb rgba = c.rgba();
4392 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4393 if ((solid.color & 0xff000000) == 0
4394 && compositionMode == QPainter::CompositionMode_SourceOver) {
4400 case Qt::LinearGradientPattern:
4402 type = LinearGradient;
4403 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4404 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4405 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4406 gradient.spread = g->spread();
4408 QLinearGradientData &linearData = gradient.linear;
4410 linearData.origin.x = g->start().x();
4411 linearData.origin.y = g->start().y();
4412 linearData.end.x = g->finalStop().x();
4413 linearData.end.y = g->finalStop().y();
4417 case Qt::RadialGradientPattern:
4419 type = RadialGradient;
4420 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4421 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4422 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4423 gradient.spread = g->spread();
4425 QRadialGradientData &radialData = gradient.radial;
4427 QPointF center = g->center();
4428 radialData.center.x = center.x();
4429 radialData.center.y = center.y();
4430 radialData.center.radius = g->centerRadius();
4431 QPointF focal = g->focalPoint();
4432 radialData.focal.x = focal.x();
4433 radialData.focal.y = focal.y();
4434 radialData.focal.radius = g->focalRadius();
4438 case Qt::ConicalGradientPattern:
4440 type = ConicalGradient;
4441 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4442 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4443 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4444 gradient.spread = QGradient::RepeatSpread;
4446 QConicalGradientData &conicalData = gradient.conical;
4448 QPointF center = g->center();
4449 conicalData.center.x = center.x();
4450 conicalData.center.y = center.y();
4451 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4455 case Qt::Dense1Pattern:
4456 case Qt::Dense2Pattern:
4457 case Qt::Dense3Pattern:
4458 case Qt::Dense4Pattern:
4459 case Qt::Dense5Pattern:
4460 case Qt::Dense6Pattern:
4461 case Qt::Dense7Pattern:
4462 case Qt::HorPattern:
4463 case Qt::VerPattern:
4464 case Qt::CrossPattern:
4465 case Qt::BDiagPattern:
4466 case Qt::FDiagPattern:
4467 case Qt::DiagCrossPattern:
4470 tempImage = new QImage();
4471 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4472 initTexture(tempImage, alpha, QTextureData::Tiled);
4474 case Qt::TexturePattern:
4477 tempImage = new QImage();
4479 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4480 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4482 *tempImage = brush.textureImage();
4483 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4491 adjustSpanMethods();
4494 void QSpanData::adjustSpanMethods()
4504 unclipped_blend = 0;
4507 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4508 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4509 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4510 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4511 fillRect = rasterBuffer->drawHelper->fillRect;
4513 case LinearGradient:
4514 case RadialGradient:
4515 case ConicalGradient:
4516 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4519 unclipped_blend = qBlendTexture;
4520 if (!texture.imageData)
4521 unclipped_blend = 0;
4526 if (!unclipped_blend) {
4529 blend = unclipped_blend;
4530 } else if (clip->hasRectClip) {
4531 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4533 blend = qt_span_fill_clipped;
4537 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4540 // make sure we round off correctly in qdrawhelper.cpp
4541 delta.translate(1.0 / 65536, 1.0 / 65536);
4543 QTransform inv = (delta * matrix).inverted();
4556 const bool affine = !m13 && !m23;
4557 fast_matrix = affine
4558 && m11 * m11 + m21 * m21 < 1e4
4559 && m12 * m12 + m22 * m22 < 1e4
4563 adjustSpanMethods();
4566 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4568 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4569 if (!d || d->height == 0) {
4570 texture.imageData = 0;
4577 texture.bytesPerLine = 0;
4578 texture.format = QImage::Format_Invalid;
4579 texture.colorTable = 0;
4580 texture.hasAlpha = alpha != 256;
4582 texture.imageData = d->data;
4583 texture.width = d->width;
4584 texture.height = d->height;
4586 if (sourceRect.isNull()) {
4589 texture.x2 = texture.width;
4590 texture.y2 = texture.height;
4592 texture.x1 = sourceRect.x();
4593 texture.y1 = sourceRect.y();
4594 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4595 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4598 texture.bytesPerLine = d->bytes_per_line;
4600 texture.format = d->format;
4601 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4602 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4604 texture.const_alpha = alpha;
4605 texture.type = _type;
4607 adjustSpanMethods();
4612 \a x and \a y is relative to the midpoint of \a rect.
4614 static inline void drawEllipsePoints(int x, int y, int length,
4617 ProcessSpans pen_func, ProcessSpans brush_func,
4618 QSpanData *pen_data, QSpanData *brush_data)
4623 QT_FT_Span outline[4];
4624 const int midx = rect.x() + (rect.width() + 1) / 2;
4625 const int midy = rect.y() + (rect.height() + 1) / 2;
4631 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4632 outline[0].len = qMin(length, x - outline[0].x);
4634 outline[0].coverage = 255;
4638 outline[1].len = length;
4640 outline[1].coverage = 255;
4643 outline[2].x = outline[0].x;
4644 outline[2].len = outline[0].len;
4645 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4646 outline[2].coverage = 255;
4650 outline[3].len = length;
4651 outline[3].y = outline[2].y;
4652 outline[3].coverage = 255;
4654 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4658 fill[0].x = outline[0].x + outline[0].len - 1;
4659 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4660 fill[0].y = outline[1].y;
4661 fill[0].coverage = 255;
4664 fill[1].x = outline[2].x + outline[2].len - 1;
4665 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4666 fill[1].y = outline[3].y;
4667 fill[1].coverage = 255;
4669 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4670 n = qt_intersect_spans(fill, n, clip);
4672 brush_func(n, fill, brush_data);
4675 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4676 n = qt_intersect_spans(outline, n, clip);
4678 pen_func(n, outline, pen_data);
4684 Draws an ellipse using the integer point midpoint algorithm.
4686 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4687 ProcessSpans pen_func, ProcessSpans brush_func,
4688 QSpanData *pen_data, QSpanData *brush_data)
4690 const qreal a = qreal(rect.width()) / 2;
4691 const qreal b = qreal(rect.height()) / 2;
4692 qreal d = b*b - (a*a*b) + 0.25*a*a;
4695 int y = (rect.height() + 1) / 2;
4699 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4700 if (d < 0) { // select E
4703 } else { // select SE
4704 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4705 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4706 pen_func, brush_func, pen_data, brush_data);
4711 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4712 pen_func, brush_func, pen_data, brush_data);
4715 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4716 const int miny = rect.height() & 0x1;
4718 if (d < 0) { // select SE
4719 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4721 } else { // select S
4722 d += a*a*(-2*y + 3);
4725 drawEllipsePoints(x, y, 1, rect, clip,
4726 pen_func, brush_func, pen_data, brush_data);
4731 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4737 #ifdef QT_DEBUG_DRAW
4738 void dumpClip(int width, int height, const QClipData *clip)
4740 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4741 clipImg.fill(0xffff0000);
4748 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4750 for (int i = 0; i < clip->count; ++i) {
4751 const QSpan *span = ((QClipData *) clip)->spans() + i;
4752 for (int j = 0; j < span->len; ++j)
4753 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4754 x0 = qMin(x0, int(span->x));
4755 x1 = qMax(x1, int(span->x + span->len - 1));
4757 y0 = qMin(y0, int(span->y));
4758 y1 = qMax(y1, int(span->y));
4761 static int counter = 0;
4768 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4769 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));