1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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.legacy_rounding = false;
648 flags.fast_text = true;
649 flags.int_xform = true;
650 flags.tx_noshear = true;
651 flags.fast_images = true;
654 flags.has_clip_ownership = false;
659 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
664 , strokeFlags(s.strokeFlags)
665 , lastBrush(s.lastBrush)
666 , brushData(s.brushData)
667 , fillFlags(s.fillFlags)
668 , pixmapFlags(s.pixmapFlags)
669 , intOpacity(s.intOpacity)
673 , flag_bits(s.flag_bits)
675 brushData.tempImage = 0;
676 penData.tempImage = 0;
677 flags.has_clip_ownership = false;
683 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
685 QRasterPaintEngineState *s;
687 s = new QRasterPaintEngineState();
689 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
697 void QRasterPaintEngine::setState(QPainterState *s)
699 Q_D(QRasterPaintEngine);
700 QPaintEngineEx::setState(s);
701 d->rasterBuffer->compositionMode = s->composition_mode;
705 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
710 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
717 void QRasterPaintEngine::penChanged()
720 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
722 QRasterPaintEngineState *s = state();
723 s->strokeFlags |= DirtyPen;
724 s->dirty |= DirtyPen;
730 void QRasterPaintEngine::updatePen(const QPen &pen)
732 Q_D(QRasterPaintEngine);
733 QRasterPaintEngineState *s = state();
735 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
738 Qt::PenStyle pen_style = qpen_style(pen);
743 s->penData.clip = d->clip();
744 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
746 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
747 || pen.brush().transform().type() >= QTransform::TxNone) {
748 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
751 // Slightly ugly handling of an uncommon case... We need to change
752 // the pen because it is reused in draw_midpoint to decide dashed
754 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
755 pen_style = Qt::SolidLine;
756 s->lastPen.setStyle(Qt::SolidLine);
759 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
760 d->basicStroker.setCapStyle(qpen_capStyle(pen));
761 d->basicStroker.setMiterLimit(pen.miterLimit());
763 qreal penWidth = qpen_widthf(pen);
765 d->basicStroker.setStrokeWidth(1);
767 d->basicStroker.setStrokeWidth(penWidth);
769 if(pen_style == Qt::SolidLine) {
770 s->stroker = &d->basicStroker;
771 } else if (pen_style != Qt::NoPen) {
773 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
774 if (qt_pen_is_cosmetic(pen, s->renderHints)) {
775 d->dashStroker->setClipRect(d->deviceRect);
777 // ### I've seen this inverted devrect multiple places now...
778 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
779 d->dashStroker->setClipRect(clipRect);
781 d->dashStroker->setDashPattern(pen.dashPattern());
782 d->dashStroker->setDashOffset(pen.dashOffset());
783 s->stroker = d->dashStroker.data();
788 ensureRasterState(); // needed because of tx_noshear...
789 bool cosmetic = qt_pen_is_cosmetic(pen, s->renderHints);
790 s->flags.fast_pen = pen_style > Qt::NoPen
792 && ((cosmetic && penWidth <= 1)
793 || (!cosmetic && s->flags.tx_noshear && penWidth * s->txscale <= 1));
795 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
805 void QRasterPaintEngine::brushOriginChanged()
807 QRasterPaintEngineState *s = state();
809 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
812 s->fillFlags |= DirtyBrushOrigin;
819 void QRasterPaintEngine::brushChanged()
821 QRasterPaintEngineState *s = state();
823 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
825 s->fillFlags |= DirtyBrush;
834 void QRasterPaintEngine::updateBrush(const QBrush &brush)
837 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
839 Q_D(QRasterPaintEngine);
840 QRasterPaintEngineState *s = state();
841 // must set clip prior to setup, as setup uses it...
842 s->brushData.clip = d->clip();
843 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
844 if (s->fillFlags & DirtyTransform
845 || brush.transform().type() >= QTransform::TxNone)
846 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
847 s->lastBrush = brush;
851 void QRasterPaintEngine::updateOutlineMapper()
853 Q_D(QRasterPaintEngine);
854 d->outlineMapper->setMatrix(state()->matrix);
857 void QRasterPaintEngine::updateRasterState()
859 QRasterPaintEngineState *s = state();
861 if (s->dirty & DirtyTransform)
862 updateMatrix(s->matrix);
864 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
865 const QPainter::CompositionMode mode = s->composition_mode;
866 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
867 && s->intOpacity == 256
868 && (mode == QPainter::CompositionMode_Source
869 || (mode == QPainter::CompositionMode_SourceOver
870 && qAlpha(s->penData.solid.color) == 255));
880 void QRasterPaintEngine::opacityChanged()
882 QRasterPaintEngineState *s = state();
885 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
888 s->fillFlags |= DirtyOpacity;
889 s->strokeFlags |= DirtyOpacity;
890 s->pixmapFlags |= DirtyOpacity;
891 s->dirty |= DirtyOpacity;
892 s->intOpacity = (int) (s->opacity * 256);
898 void QRasterPaintEngine::compositionModeChanged()
900 Q_D(QRasterPaintEngine);
901 QRasterPaintEngineState *s = state();
904 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
907 s->fillFlags |= DirtyCompositionMode;
908 s->dirty |= DirtyCompositionMode;
910 s->strokeFlags |= DirtyCompositionMode;
911 d->rasterBuffer->compositionMode = s->composition_mode;
913 d->recalculateFastImages();
919 void QRasterPaintEngine::renderHintsChanged()
921 QRasterPaintEngineState *s = state();
924 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
927 bool was_aa = s->flags.antialiased;
928 bool was_bilinear = s->flags.bilinear;
930 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
931 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
932 s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);
934 if (was_aa != s->flags.antialiased)
935 s->strokeFlags |= DirtyHints;
937 if (was_bilinear != s->flags.bilinear) {
938 s->strokeFlags |= DirtyPen;
939 s->fillFlags |= DirtyBrush;
942 Q_D(QRasterPaintEngine);
943 d->recalculateFastImages();
949 void QRasterPaintEngine::transformChanged()
951 QRasterPaintEngineState *s = state();
954 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
957 s->fillFlags |= DirtyTransform;
958 s->strokeFlags |= DirtyTransform;
960 s->dirty |= DirtyTransform;
962 Q_D(QRasterPaintEngine);
963 d->recalculateFastImages();
969 void QRasterPaintEngine::clipEnabledChanged()
971 QRasterPaintEngineState *s = state();
974 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
978 s->clip->enabled = s->clipEnabled;
979 s->fillFlags |= DirtyClipEnabled;
980 s->strokeFlags |= DirtyClipEnabled;
981 s->pixmapFlags |= DirtyClipEnabled;
985 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
987 SrcOverBlendFunc func,
992 if (alpha == 0 || !clip.isValid())
995 Q_ASSERT(img.depth() >= 8);
997 int srcBPL = img.bytesPerLine();
998 const uchar *srcBits = img.bits();
999 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1000 int iw = img.width();
1001 int ih = img.height();
1003 if (!sr.isEmpty()) {
1006 // Adjust the image according to the source offset...
1007 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1010 // adapt the x parameters
1011 int x = qRound(pt.x());
1013 int cx2 = clip.x() + clip.width();
1016 srcBits += srcSize * d;
1021 int d = x + iw - cx2;
1027 // adapt the y paremeters...
1029 int cy2 = clip.y() + clip.height();
1030 int y = qRound(pt.y());
1033 srcBits += srcBPL * d;
1038 int d = y + ih - cy2;
1044 // call the blend function...
1045 int dstSize = rasterBuffer->bytesPerPixel();
1046 int dstBPL = rasterBuffer->bytesPerLine();
1047 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1054 void QRasterPaintEnginePrivate::systemStateChanged()
1056 QRect clipRect(0, 0,
1057 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1058 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1060 if (!systemClip.isEmpty()) {
1061 QRegion clippedDeviceRgn = systemClip & clipRect;
1062 deviceRect = clippedDeviceRgn.boundingRect();
1063 baseClip->setClipRegion(clippedDeviceRgn);
1065 deviceRect = clipRect;
1066 baseClip->setClipRect(deviceRect);
1068 #ifdef QT_DEBUG_DRAW
1069 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1072 exDeviceRect = deviceRect;
1074 Q_Q(QRasterPaintEngine);
1075 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1076 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1077 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1080 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1082 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1085 Q_Q(QRasterPaintEngine);
1086 bool bilinear = q->state()->flags.bilinear;
1088 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1089 spanData->setupMatrix(b.transform() * m, bilinear);
1091 if (m.type() <= QTransform::TxTranslate) {
1092 // specialize setupMatrix for translation matrices
1093 // to avoid needless matrix inversion
1101 spanData->dx = -m.dx();
1102 spanData->dy = -m.dy();
1103 spanData->txop = m.type();
1104 spanData->bilinear = bilinear;
1105 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1106 spanData->adjustSpanMethods();
1108 spanData->setupMatrix(m, bilinear);
1113 // #define QT_CLIPPING_RATIOS
1115 #ifdef QT_CLIPPING_RATIOS
1120 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1122 if (d->clip()->hasRectClip)
1124 if (d->clip()->hasRegionClip)
1128 if ((totalClips % 5000) == 0) {
1129 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1130 rectClips * 100.0 / (qreal) totalClips,
1131 regionClips * 100.0 / (qreal) totalClips,
1132 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1141 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1143 if (s->flags.has_clip_ownership)
1146 s->flags.has_clip_ownership = false;
1149 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1151 s->fillFlags |= QPaintEngine::DirtyClipPath;
1152 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1153 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1155 d->solid_color_filler.clip = d->clip();
1156 d->solid_color_filler.adjustSpanMethods();
1158 #ifdef QT_DEBUG_DRAW
1159 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1168 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1170 #ifdef QT_DEBUG_DRAW
1171 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1173 if (path.elements()) {
1174 for (int i=0; i<path.elementCount(); ++i) {
1175 qDebug() << " - " << path.elements()[i]
1176 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1179 for (int i=0; i<path.elementCount(); ++i) {
1180 qDebug() << " ---- "
1181 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1186 Q_D(QRasterPaintEngine);
1187 QRasterPaintEngineState *s = state();
1189 const qreal *points = path.points();
1190 const QPainterPath::ElementType *types = path.elements();
1192 // There are some cases that are not supported by clip(QRect)
1193 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1194 if (s->matrix.type() <= QTransform::TxScale
1195 && ((path.shape() == QVectorPath::RectangleHint)
1196 || (isRect(points, path.elementCount())
1197 && (!types || (types[0] == QPainterPath::MoveToElement
1198 && types[1] == QPainterPath::LineToElement
1199 && types[2] == QPainterPath::LineToElement
1200 && types[3] == QPainterPath::LineToElement))))) {
1201 #ifdef QT_DEBUG_DRAW
1202 qDebug() << " --- optimizing vector clip to rect clip...";
1205 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1206 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1211 if (op == Qt::NoClip) {
1212 qrasterpaintengine_state_setNoClip(s);
1215 QClipData *base = d->baseClip.data();
1217 // Intersect with current clip when available...
1218 if (op == Qt::IntersectClip && s->clip)
1221 // We always intersect, except when there is nothing to
1222 // intersect with, in which case we simplify the operation to
1224 Qt::ClipOperation isectOp = Qt::IntersectClip;
1226 isectOp = Qt::ReplaceClip;
1228 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1229 newClip->initialize();
1230 ClipData clipData = { base, newClip, isectOp };
1231 ensureOutlineMapper();
1232 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1236 if (s->flags.has_clip_ownership)
1240 s->flags.has_clip_ownership = true;
1242 qrasterpaintengine_dirty_clip(d, s);
1250 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1252 #ifdef QT_DEBUG_DRAW
1253 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1256 QRasterPaintEngineState *s = state();
1258 if (op == Qt::NoClip) {
1259 qrasterpaintengine_state_setNoClip(s);
1261 } else if (s->matrix.type() > QTransform::TxScale) {
1262 QPaintEngineEx::clip(rect, op);
1265 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1266 QPaintEngineEx::clip(rect, op);
1272 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1274 Q_D(QRasterPaintEngine);
1275 QRect clipRect = r & d->deviceRect;
1276 QRasterPaintEngineState *s = state();
1278 if (op == Qt::ReplaceClip || s->clip == 0) {
1280 // No current clip, hence we intersect with sysclip and be
1282 QRegion clipRegion = systemClip();
1283 QClipData *clip = new QClipData(d->rasterBuffer->height());
1285 if (clipRegion.isEmpty())
1286 clip->setClipRect(clipRect);
1288 clip->setClipRegion(clipRegion & clipRect);
1290 if (s->flags.has_clip_ownership)
1294 s->clip->enabled = true;
1295 s->flags.has_clip_ownership = true;
1297 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1298 QClipData *base = s->clip;
1301 if (base->hasRectClip || base->hasRegionClip) {
1302 if (!s->flags.has_clip_ownership) {
1303 s->clip = new QClipData(d->rasterBuffer->height());
1304 s->flags.has_clip_ownership = true;
1306 if (base->hasRectClip)
1307 s->clip->setClipRect(base->clipRect & clipRect);
1309 s->clip->setClipRegion(base->clipRegion & clipRect);
1310 s->clip->enabled = true;
1318 qrasterpaintengine_dirty_clip(d, s);
1326 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1328 #ifdef QT_DEBUG_DRAW
1329 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1332 Q_D(QRasterPaintEngine);
1334 if (region.rectCount() == 1) {
1335 clip(region.boundingRect(), op);
1339 QRasterPaintEngineState *s = state();
1340 const QClipData *clip = d->clip();
1341 const QClipData *baseClip = d->baseClip.data();
1343 if (op == Qt::NoClip) {
1344 qrasterpaintengine_state_setNoClip(s);
1345 } else if (s->matrix.type() > QTransform::TxScale
1346 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1347 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1348 QPaintEngineEx::clip(region, op);
1350 const QClipData *curClip;
1353 if (op == Qt::IntersectClip)
1358 if (s->flags.has_clip_ownership) {
1362 newClip = new QClipData(d->rasterBuffer->height());
1364 s->flags.has_clip_ownership = true;
1367 QRegion r = s->matrix.map(region);
1368 if (curClip->hasRectClip)
1369 newClip->setClipRegion(r & curClip->clipRect);
1370 else if (curClip->hasRegionClip)
1371 newClip->setClipRegion(r & curClip->clipRegion);
1373 qrasterpaintengine_dirty_clip(d, s);
1378 \fn const QClipData *QRasterPaintEngine::clipData() const
1387 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1389 #ifdef QT_DEBUG_DRAW
1390 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1393 if (!fillData->blend)
1396 Q_D(QRasterPaintEngine);
1398 const QRectF controlPointRect = path.controlPointRect();
1400 QRasterPaintEngineState *s = state();
1401 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1402 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1403 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1404 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1405 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1406 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1408 if (!s->flags.antialiased && !do_clip) {
1409 d->initializeRasterizer(fillData);
1410 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1414 ensureOutlineMapper();
1415 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1418 static void fillRect_normalized(const QRect &r, QSpanData *data,
1419 QRasterPaintEnginePrivate *pe)
1423 bool rectClipped = true;
1426 x1 = qMax(r.x(), data->clip->xmin);
1427 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1428 y1 = qMax(r.y(), data->clip->ymin);
1429 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1430 rectClipped = data->clip->hasRectClip;
1433 x1 = qMax(r.x(), pe->deviceRect.x());
1434 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1435 y1 = qMax(r.y(), pe->deviceRect.y());
1436 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1438 x1 = qMax(r.x(), 0);
1439 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1440 y1 = qMax(r.y(), 0);
1441 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1444 if (x2 <= x1 || y2 <= y1)
1447 const int width = x2 - x1;
1448 const int height = y2 - y1;
1450 bool isUnclipped = rectClipped
1451 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1453 if (pe && isUnclipped) {
1454 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1456 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1457 || (mode == QPainter::CompositionMode_SourceOver
1458 && qAlpha(data->solid.color) == 255)))
1460 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1466 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1468 const int nspans = 256;
1469 QT_FT_Span spans[nspans];
1471 Q_ASSERT(data->blend);
1474 int n = qMin(nspans, y2 - y);
1478 spans[i].len = width;
1480 spans[i].coverage = 255;
1484 blend(n, spans, data);
1492 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1494 #ifdef QT_DEBUG_DRAW
1495 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1497 Q_D(QRasterPaintEngine);
1498 ensureRasterState();
1499 QRasterPaintEngineState *s = state();
1503 if (s->brushData.blend) {
1504 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1505 const QRect *r = rects;
1506 const QRect *lastRect = rects + rectCount;
1508 int offset_x = int(s->matrix.dx());
1509 int offset_y = int(s->matrix.dy());
1510 while (r < lastRect) {
1511 QRect rect = r->normalized();
1512 QRect rr = rect.translated(offset_x, offset_y);
1513 fillRect_normalized(rr, &s->brushData, d);
1517 QRectVectorPath path;
1518 for (int i=0; i<rectCount; ++i) {
1520 fill(path, s->brush);
1526 if (s->penData.blend) {
1527 QRectVectorPath path;
1528 if (s->flags.fast_pen) {
1529 QCosmeticStroker stroker(s, d->deviceRect);
1530 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1531 for (int i = 0; i < rectCount; ++i) {
1533 stroker.drawPath(path);
1536 for (int i = 0; i < rectCount; ++i) {
1538 stroke(path, s->pen);
1547 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1549 #ifdef QT_DEBUG_DRAW
1550 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1552 #ifdef QT_FAST_SPANS
1553 Q_D(QRasterPaintEngine);
1554 ensureRasterState();
1555 QRasterPaintEngineState *s = state();
1558 if (s->flags.tx_noshear) {
1560 if (s->brushData.blend) {
1561 d->initializeRasterizer(&s->brushData);
1562 for (int i = 0; i < rectCount; ++i) {
1563 const QRectF &rect = rects[i].normalized();
1566 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1567 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1568 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1573 if (s->penData.blend) {
1574 QRectVectorPath path;
1575 if (s->flags.fast_pen) {
1576 QCosmeticStroker stroker(s, d->deviceRect);
1577 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1578 for (int i = 0; i < rectCount; ++i) {
1580 stroker.drawPath(path);
1583 for (int i = 0; i < rectCount; ++i) {
1585 QPaintEngineEx::stroke(path, s->lastPen);
1592 #endif // QT_FAST_SPANS
1593 QPaintEngineEx::drawRects(rects, rectCount);
1600 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1602 Q_D(QRasterPaintEngine);
1603 QRasterPaintEngineState *s = state();
1606 if (!s->penData.blend)
1609 if (s->flags.fast_pen) {
1610 QCosmeticStroker stroker(s, d->deviceRect);
1611 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1612 stroker.drawPath(path);
1613 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1614 qreal width = qt_pen_is_cosmetic(s->lastPen, s->renderHints)
1615 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1616 : qpen_widthf(s->lastPen) * s->txscale;
1618 qreal dashOffset = s->lastPen.dashOffset();
1620 qreal patternLength = 0;
1621 const QVector<qreal> pattern = s->lastPen.dashPattern();
1622 for (int i = 0; i < pattern.size(); ++i)
1623 patternLength += pattern.at(i);
1625 if (patternLength > 0) {
1626 int n = qFloor(dashOffset / patternLength);
1627 dashOffset -= n * patternLength;
1628 while (dashOffset >= pattern.at(dashIndex)) {
1629 dashOffset -= pattern.at(dashIndex);
1630 if (++dashIndex >= pattern.size())
1636 Q_D(QRasterPaintEngine);
1637 d->initializeRasterizer(&s->penData);
1638 int lineCount = path.elementCount() / 2;
1639 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1641 for (int i = 0; i < lineCount; ++i) {
1642 if (lines[i].p1() == lines[i].p2()) {
1643 if (s->lastPen.capStyle() != Qt::FlatCap) {
1644 QPointF p = lines[i].p1();
1645 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1646 QPointF(p.x() + width*0.5, p.y())));
1647 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1652 const QLineF line = s->matrix.map(lines[i]);
1653 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1654 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1655 width / line.length(),
1656 s->lastPen.capStyle() == Qt::SquareCap);
1658 d->rasterizeLine_dashed(line, width,
1659 &dashIndex, &dashOffset, &inDash);
1664 QPaintEngineEx::stroke(path, pen);
1667 QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1669 QRasterPaintEngineState *s = state();
1671 qreal delta = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
1673 int x1 = qRound(rect.x() + delta);
1674 int y1 = qRound(rect.y() + delta);
1675 int x2 = qRound(rect.right() + delta);
1676 int y2 = qRound(rect.bottom() + delta);
1683 return QRect(x1, y1, x2 - x1, y2 - y1);
1689 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1693 #ifdef QT_DEBUG_DRAW
1694 QRectF rf = path.controlPointRect();
1695 qDebug() << "QRasterPaintEngine::fill(): "
1696 << "size=" << path.elementCount()
1697 << ", hints=" << hex << path.hints()
1701 Q_D(QRasterPaintEngine);
1702 QRasterPaintEngineState *s = state();
1705 if (!s->brushData.blend)
1708 if (path.shape() == QVectorPath::RectangleHint) {
1709 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1710 const qreal *p = path.points();
1711 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1712 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1713 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1716 ensureRasterState();
1717 if (s->flags.tx_noshear) {
1718 d->initializeRasterizer(&s->brushData);
1719 // ### Is normalizing really necessary here?
1720 const qreal *p = path.points();
1721 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1723 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1724 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1725 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1731 // ### Optimize for non transformed ellipses and rectangles...
1732 QRectF cpRect = path.controlPointRect();
1733 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1734 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1737 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1738 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1739 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1740 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1742 // ### Falonc: implement....
1743 // if (!s->flags.antialiased && !do_clip) {
1744 // d->initializeRasterizer(&s->brushData);
1745 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1749 ensureOutlineMapper();
1750 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1753 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1755 Q_D(QRasterPaintEngine);
1756 QRasterPaintEngineState *s = state();
1758 if (!s->flags.antialiased) {
1759 uint txop = s->matrix.type();
1760 if (txop == QTransform::TxNone) {
1761 fillRect_normalized(toNormalizedFillRect(r), data, d);
1763 } else if (txop == QTransform::TxTranslate) {
1764 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1765 fillRect_normalized(rr, data, d);
1767 } else if (txop == QTransform::TxScale) {
1768 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1769 fillRect_normalized(rr, data, d);
1773 ensureRasterState();
1774 if (s->flags.tx_noshear) {
1775 d->initializeRasterizer(data);
1776 QRectF nr = r.normalized();
1777 if (!nr.isEmpty()) {
1778 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1779 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1780 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1787 ensureOutlineMapper();
1788 fillPath(path, data);
1794 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1796 #ifdef QT_DEBUG_DRAW
1797 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1799 QRasterPaintEngineState *s = state();
1802 if (!s->brushData.blend)
1805 fillRect(r, &s->brushData);
1811 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1813 #ifdef QT_DEBUG_DRAW
1814 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1816 Q_D(QRasterPaintEngine);
1817 QRasterPaintEngineState *s = state();
1819 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1820 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1821 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1824 d->solid_color_filler.clip = d->clip();
1825 d->solid_color_filler.adjustSpanMethods();
1826 fillRect(r, &d->solid_color_filler);
1829 static inline bool isAbove(const QPointF *a, const QPointF *b)
1831 return a->y() < b->y();
1834 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1839 Q_ASSERT(pointCount >= 2);
1841 QVector<const QPointF *> sorted;
1842 sorted.reserve(pointCount);
1844 upper->reserve(pointCount * 3 / 4);
1845 lower->reserve(pointCount * 3 / 4);
1847 for (int i = 0; i < pointCount; ++i)
1848 sorted << points + i;
1850 qSort(sorted.begin(), sorted.end(), isAbove);
1852 qreal splitY = sorted.at(sorted.size() / 2)->y();
1854 const QPointF *end = points + pointCount;
1855 const QPointF *last = end - 1;
1857 QVector<QPointF> *bin[2] = { upper, lower };
1859 for (const QPointF *p = points; p < end; ++p) {
1860 int side = p->y() < splitY;
1861 int lastSide = last->y() < splitY;
1863 if (side != lastSide) {
1864 if (qFuzzyCompare(p->y(), splitY)) {
1865 bin[!side]->append(*p);
1866 } else if (qFuzzyCompare(last->y(), splitY)) {
1867 bin[side]->append(*last);
1869 QPointF delta = *p - *last;
1870 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1872 bin[0]->append(intersection);
1873 bin[1]->append(intersection);
1877 bin[side]->append(*p);
1882 // give up if we couldn't reduce the point count
1883 return upper->size() < pointCount && lower->size() < pointCount;
1889 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1891 Q_D(QRasterPaintEngine);
1892 QRasterPaintEngineState *s = state();
1894 const int maxPoints = 0xffff;
1896 // max amount of points that raster engine can reliably handle
1897 if (pointCount > maxPoints) {
1898 QVector<QPointF> upper, lower;
1900 if (splitPolygon(points, pointCount, &upper, &lower)) {
1901 fillPolygon(upper.constData(), upper.size(), mode);
1902 fillPolygon(lower.constData(), lower.size(), mode);
1904 qWarning("Polygon too complex for filling.");
1909 // Compose polygon fill..,
1910 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1911 ensureOutlineMapper();
1912 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1915 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1917 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1923 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1925 Q_D(QRasterPaintEngine);
1926 QRasterPaintEngineState *s = state();
1928 #ifdef QT_DEBUG_DRAW
1929 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1930 for (int i=0; i<pointCount; ++i)
1931 qDebug() << " - " << points[i];
1933 Q_ASSERT(pointCount >= 2);
1935 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1936 QRectF r(points[0], points[2]);
1942 if (mode != PolylineMode) {
1945 if (s->brushData.blend)
1946 fillPolygon(points, pointCount, mode);
1949 // Do the outline...
1950 if (s->penData.blend) {
1951 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1952 if (s->flags.fast_pen) {
1953 QCosmeticStroker stroker(s, d->deviceRect);
1954 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1955 stroker.drawPath(vp);
1957 QPaintEngineEx::stroke(vp, s->lastPen);
1965 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1967 Q_D(QRasterPaintEngine);
1968 QRasterPaintEngineState *s = state();
1970 #ifdef QT_DEBUG_DRAW
1971 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1972 for (int i=0; i<pointCount; ++i)
1973 qDebug() << " - " << points[i];
1975 Q_ASSERT(pointCount >= 2);
1976 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1977 QRect r(points[0].x(),
1979 points[2].x() - points[0].x(),
1980 points[2].y() - points[0].y());
1988 if (mode != PolylineMode) {
1990 if (s->brushData.blend) {
1991 // Compose polygon fill..,
1992 ensureOutlineMapper();
1993 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1994 d->outlineMapper->moveTo(*points);
1995 const QPoint *p = points;
1996 const QPoint *ep = points + pointCount - 1;
1998 d->outlineMapper->lineTo(*(++p));
2000 d->outlineMapper->endOutline();
2003 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2005 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2009 // Do the outline...
2010 if (s->penData.blend) {
2011 int count = pointCount * 2;
2012 QVarLengthArray<qreal> fpoints(count);
2013 for (int i=0; i<count; ++i)
2014 fpoints[i] = ((int *) points)[i];
2015 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2017 if (s->flags.fast_pen) {
2018 QCosmeticStroker stroker(s, d->deviceRect);
2019 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2020 stroker.drawPath(vp);
2022 QPaintEngineEx::stroke(vp, s->lastPen);
2030 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2032 #ifdef QT_DEBUG_DRAW
2033 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2036 QPlatformPixmap *pd = pixmap.handle();
2037 if (pd->classId() == QPlatformPixmap::RasterClass) {
2038 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2039 if (image.depth() == 1) {
2040 Q_D(QRasterPaintEngine);
2041 QRasterPaintEngineState *s = state();
2042 if (s->matrix.type() <= QTransform::TxTranslate) {
2044 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2046 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2049 QRasterPaintEngine::drawImage(pos, image);
2052 const QImage image = pixmap.toImage();
2053 if (pixmap.depth() == 1) {
2054 Q_D(QRasterPaintEngine);
2055 QRasterPaintEngineState *s = state();
2056 if (s->matrix.type() <= QTransform::TxTranslate) {
2058 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2060 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2063 QRasterPaintEngine::drawImage(pos, image);
2071 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2073 #ifdef QT_DEBUG_DRAW
2074 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2077 QPlatformPixmap* pd = pixmap.handle();
2078 if (pd->classId() == QPlatformPixmap::RasterClass) {
2079 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2080 if (image.depth() == 1) {
2081 Q_D(QRasterPaintEngine);
2082 QRasterPaintEngineState *s = state();
2083 if (s->matrix.type() <= QTransform::TxTranslate
2084 && r.size() == sr.size()
2085 && r.size() == pixmap.size()) {
2087 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2090 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2093 drawImage(r, image, sr);
2096 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2097 const QImage image = pd->toImage(clippedSource);
2098 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2099 if (image.depth() == 1) {
2100 Q_D(QRasterPaintEngine);
2101 QRasterPaintEngineState *s = state();
2102 if (s->matrix.type() <= QTransform::TxTranslate
2103 && r.size() == sr.size()
2104 && r.size() == pixmap.size()) {
2106 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2109 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2112 drawImage(r, image, translatedSource);
2117 static inline int fast_ceil_positive(const qreal &v)
2119 const int iv = int(v);
2126 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2128 const int xmin = int(rect.x());
2129 const int xmax = int(fast_ceil_positive(rect.right()));
2130 const int ymin = int(rect.y());
2131 const int ymax = int(fast_ceil_positive(rect.bottom()));
2132 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2138 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2140 #ifdef QT_DEBUG_DRAW
2141 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2144 Q_D(QRasterPaintEngine);
2145 QRasterPaintEngineState *s = state();
2147 if (s->matrix.type() > QTransform::TxTranslate) {
2148 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2150 QRectF(0, 0, img.width(), img.height()));
2153 const QClipData *clip = d->clip();
2154 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2156 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2157 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2160 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2162 } else if (clip->hasRectClip) {
2163 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2171 d->image_filler.clip = clip;
2172 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2173 if (!d->image_filler.blend)
2175 d->image_filler.dx = -pt.x();
2176 d->image_filler.dy = -pt.y();
2177 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2179 fillRect_normalized(rr, &d->image_filler, d);
2184 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2186 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2197 inline RotationType qRotationType(const QTransform &transform)
2199 QTransform::TransformationType type = transform.type();
2201 if (type > QTransform::TxRotate)
2204 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2205 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2208 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2209 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2212 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2213 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2219 inline bool isPixelAligned(const QRectF &rect) {
2220 return QRectF(rect.toRect()) == rect;
2227 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2228 Qt::ImageConversionFlags)
2230 #ifdef QT_DEBUG_DRAW
2231 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2237 Q_D(QRasterPaintEngine);
2238 QRasterPaintEngineState *s = state();
2239 int sr_l = qFloor(sr.left());
2240 int sr_r = qCeil(sr.right()) - 1;
2241 int sr_t = qFloor(sr.top());
2242 int sr_b = qCeil(sr.bottom()) - 1;
2244 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2245 // as fillRect will apply the aliased coordinate delta we need to
2246 // subtract it here as we don't use it for image drawing
2247 QTransform old = s->matrix;
2249 if (s->flags.legacy_rounding)
2250 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2252 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2253 QRgb color = img.pixel(sr_l, sr_t);
2254 switch (img.format()) {
2255 case QImage::Format_ARGB32_Premultiplied:
2256 case QImage::Format_ARGB8565_Premultiplied:
2257 case QImage::Format_ARGB6666_Premultiplied:
2258 case QImage::Format_ARGB8555_Premultiplied:
2259 case QImage::Format_ARGB4444_Premultiplied:
2260 // Combine premultiplied color with the opacity set on the painter.
2261 d->solid_color_filler.solid.color =
2262 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2263 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2266 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2270 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2271 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2275 d->solid_color_filler.clip = d->clip();
2276 d->solid_color_filler.adjustSpanMethods();
2277 fillRect(r, &d->solid_color_filler);
2283 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2285 const QClipData *clip = d->clip();
2287 if (s->matrix.type() > QTransform::TxTranslate
2289 && (!clip || clip->hasRectClip)
2290 && s->intOpacity == 256
2291 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2292 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2293 && d->rasterBuffer->format == img.format()
2294 && (d->rasterBuffer->format == QImage::Format_RGB16
2295 || d->rasterBuffer->format == QImage::Format_RGB32
2296 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2297 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2299 RotationType rotationType = qRotationType(s->matrix);
2301 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2302 QRectF transformedTargetRect = s->matrix.mapRect(r);
2304 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2305 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2307 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2308 if (clippedTransformedTargetRect.isNull())
2311 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2313 QRect clippedSourceRect
2314 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2315 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2317 uint dbpl = d->rasterBuffer->bytesPerLine();
2318 uint sbpl = img.bytesPerLine();
2320 uchar *dst = d->rasterBuffer->buffer();
2321 uint bpp = img.depth() >> 3;
2323 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2324 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2326 uint cw = clippedSourceRect.width();
2327 uint ch = clippedSourceRect.height();
2329 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2336 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2338 QRectF targetBounds = s->matrix.mapRect(r);
2339 bool exceedsPrecision = targetBounds.width() > 0xffff
2340 || targetBounds.height() > 0xffff;
2342 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2343 if (s->matrix.type() > QTransform::TxScale) {
2344 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2345 if (func && (!clip || clip->hasRectClip)) {
2346 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2347 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2348 s->matrix, s->intOpacity);
2352 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2353 if (func && (!clip || clip->hasRectClip)) {
2354 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2355 img.bits(), img.bytesPerLine(),
2356 qt_mapRect_non_normalizing(r, s->matrix), sr,
2357 !clip ? d->deviceRect : clip->clipRect,
2364 QTransform copy = s->matrix;
2365 copy.translate(r.x(), r.y());
2367 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2368 copy.translate(-sr.x(), -sr.y());
2370 d->image_filler_xform.clip = clip;
2371 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2372 if (!d->image_filler_xform.blend)
2374 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2376 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2377 QRectF rr = s->matrix.mapRect(r);
2379 const int x1 = qRound(rr.x());
2380 const int y1 = qRound(rr.y());
2381 const int x2 = qRound(rr.right());
2382 const int y2 = qRound(rr.bottom());
2384 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2388 #ifdef QT_FAST_SPANS
2389 ensureRasterState();
2390 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2391 d->initializeRasterizer(&d->image_filler_xform);
2392 d->rasterizer->setAntialiased(s->flags.antialiased);
2393 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2395 const QPointF offs = s->flags.legacy_rounding ? QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta) : QPointF();
2397 const QRectF &rect = r.normalized();
2398 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2399 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2401 if (s->flags.tx_noshear)
2402 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2404 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2408 const qreal offs = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
2411 QTransform m = s->matrix;
2412 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2413 m.m21(), m.m22(), m.m23(),
2414 m.m31() - offs, m.m32() - offs, m.m33());
2415 fillPath(path, &d->image_filler_xform);
2418 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2419 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2421 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2423 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2425 } else if (clip->hasRectClip) {
2426 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2432 d->image_filler.clip = clip;
2433 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2434 if (!d->image_filler.blend)
2436 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2437 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2440 rr.translate(s->matrix.dx(), s->matrix.dy());
2442 const int x1 = qRound(rr.x());
2443 const int y1 = qRound(rr.y());
2444 const int x2 = qRound(rr.right());
2445 const int y2 = qRound(rr.bottom());
2447 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2454 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2456 #ifdef QT_DEBUG_DRAW
2457 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2459 Q_D(QRasterPaintEngine);
2460 QRasterPaintEngineState *s = state();
2464 QPlatformPixmap *pd = pixmap.handle();
2465 if (pd->classId() == QPlatformPixmap::RasterClass) {
2466 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2468 image = pixmap.toImage();
2471 if (image.depth() == 1)
2472 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2474 if (s->matrix.type() > QTransform::TxTranslate) {
2475 QTransform copy = s->matrix;
2476 copy.translate(r.x(), r.y());
2477 copy.translate(-sr.x(), -sr.y());
2478 d->image_filler_xform.clip = d->clip();
2479 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2480 if (!d->image_filler_xform.blend)
2482 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2484 #ifdef QT_FAST_SPANS
2485 ensureRasterState();
2486 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2487 d->initializeRasterizer(&d->image_filler_xform);
2488 d->rasterizer->setAntialiased(s->flags.antialiased);
2489 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2491 const QRectF &rect = r.normalized();
2492 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2493 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2494 if (s->flags.tx_noshear)
2495 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2497 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2503 fillPath(path, &d->image_filler_xform);
2505 d->image_filler.clip = d->clip();
2507 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2508 if (!d->image_filler.blend)
2510 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2511 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2514 rr.translate(s->matrix.dx(), s->matrix.dy());
2515 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2521 static inline bool monoVal(const uchar* s, int x)
2523 return (s[x>>3] << (x&7)) & 0x80;
2529 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2531 Q_D(QRasterPaintEngine);
2532 return d->rasterBuffer.data();
2538 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2540 Q_D(QRasterPaintEngine);
2541 QRasterPaintEngineState *s = state();
2543 if (!s->penData.blend)
2546 QRasterBuffer *rb = d->rasterBuffer.data();
2548 const QRect rect(rx, ry, w, h);
2549 const QClipData *clip = d->clip();
2550 bool unclipped = false;
2552 // inlined QRect::intersects
2553 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2554 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2556 if (clip->hasRectClip) {
2557 unclipped = rx > clip->xmin
2558 && rx + w < clip->xmax
2560 && ry + h < clip->ymax;
2566 // inlined QRect::intersects
2567 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2568 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2572 // inlined QRect::contains
2573 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2574 && rect.top() >= 0 && rect.bottom() < rb->height();
2576 unclipped = contains && d->isUnclipped_normalized(rect);
2579 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2580 const uchar * scanline = static_cast<const uchar *>(src);
2582 if (s->flags.fast_text) {
2585 if (s->penData.bitmapBlit) {
2586 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2587 scanline, w, h, bpl);
2590 } else if (depth == 8) {
2591 if (s->penData.alphamapBlit) {
2592 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2593 scanline, w, h, bpl, 0);
2596 } else if (depth == 32) {
2597 // (A)RGB Alpha mask where the alpha component is not used.
2598 if (s->penData.alphaRGBBlit) {
2599 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2600 (const uint *) scanline, w, h, bpl / 4, 0);
2604 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2605 // (A)RGB Alpha mask where the alpha component is not used.
2607 int nx = qMax(0, rx);
2608 int ny = qMax(0, ry);
2610 // Move scanline pointer to compensate for moved x and y
2611 int xdiff = nx - rx;
2612 int ydiff = ny - ry;
2613 scanline += ydiff * bpl;
2614 scanline += xdiff * (depth == 32 ? 4 : 1);
2619 if (nx + w > d->rasterBuffer->width())
2620 w = d->rasterBuffer->width() - nx;
2621 if (ny + h > d->rasterBuffer->height())
2622 h = d->rasterBuffer->height() - ny;
2627 if (depth == 8 && s->penData.alphamapBlit) {
2628 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2629 scanline, w, h, bpl, clip);
2630 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2631 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2632 (const uint *) scanline, w, h, bpl / 4, clip);
2647 scanline += bpl * y0;
2651 w = qMin(w, rb->width() - qMax(0, rx));
2652 h = qMin(h, rb->height() - qMax(0, ry));
2654 if (w <= 0 || h <= 0)
2657 const int NSPANS = 256;
2658 QSpan spans[NSPANS];
2661 const int x1 = x0 + w;
2662 const int y1 = y0 + h;
2665 for (int y = y0; y < y1; ++y) {
2666 for (int x = x0; x < x1; ) {
2667 if (!monoVal(scanline, x)) {
2672 if (current == NSPANS) {
2673 blend(current, spans, &s->penData);
2676 spans[current].x = x + rx;
2677 spans[current].y = y + ry;
2678 spans[current].coverage = 255;
2681 // extend span until we find a different one.
2682 while (x < x1 && monoVal(scanline, x)) {
2686 spans[current].len = len;
2691 } else if (depth == 8) {
2692 for (int y = y0; y < y1; ++y) {
2693 for (int x = x0; x < x1; ) {
2694 // Skip those with 0 coverage
2695 if (scanline[x] == 0) {
2700 if (current == NSPANS) {
2701 blend(current, spans, &s->penData);
2704 int coverage = scanline[x];
2705 spans[current].x = x + rx;
2706 spans[current].y = y + ry;
2707 spans[current].coverage = coverage;
2711 // extend span until we find a different one.
2712 while (x < x1 && scanline[x] == coverage) {
2716 spans[current].len = len;
2721 } else { // 32-bit alpha...
2722 uint *sl = (uint *) src;
2723 for (int y = y0; y < y1; ++y) {
2724 for (int x = x0; x < x1; ) {
2725 // Skip those with 0 coverage
2726 if ((sl[x] & 0x00ffffff) == 0) {
2731 if (current == NSPANS) {
2732 blend(current, spans, &s->penData);
2735 uint rgbCoverage = sl[x];
2736 int coverage = qGreen(rgbCoverage);
2737 spans[current].x = x + rx;
2738 spans[current].y = y + ry;
2739 spans[current].coverage = coverage;
2743 // extend span until we find a different one.
2744 while (x < x1 && sl[x] == rgbCoverage) {
2748 spans[current].len = len;
2751 sl += bpl / sizeof(uint);
2754 // qDebug() << "alphaPenBlt: num spans=" << current
2755 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2756 // Call span func for current set of spans.
2758 blend(current, spans, &s->penData);
2764 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2765 const QFixedPoint *positions, QFontEngine *fontEngine)
2767 Q_D(QRasterPaintEngine);
2768 QRasterPaintEngineState *s = state();
2770 if (fontEngine->hasInternalCaching()) {
2771 QFontEngine::GlyphFormat neededFormat =
2772 painter()->device()->devType() == QInternal::Widget
2773 ? QFontEngine::Format_None
2774 : QFontEngine::Format_A8;
2776 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2777 neededFormat = QFontEngine::Format_Mono;
2779 for (int i = 0; i < numGlyphs; i++) {
2780 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2783 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2785 if (alphaMap == 0 || alphaMap->isNull())
2788 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2789 qFloor(positions[i].x) + offset.x(),
2790 qFloor(positions[i].y) + offset.y(),
2791 alphaMap->width(), alphaMap->height());
2793 fontEngine->unlockAlphaMapForGlyph();
2797 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2799 QImageTextureGlyphCache *cache =
2800 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2802 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2803 fontEngine->setGlyphCache(0, cache);
2806 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2807 cache->fillInPendingGlyphs();
2809 const QImage &image = cache->image();
2810 int bpl = image.bytesPerLine();
2812 int depth = image.depth();
2816 leftShift = 2; // multiply by 4
2817 else if (depth == 1)
2818 rightShift = 3; // divide by 8
2820 int margin = fontEngine->glyphMargin(glyphType);
2821 const QFixed offs = s->flags.legacy_rounding ? QFixed::fromReal(aliasedCoordinateDelta) : QFixed();
2822 const uchar *bits = image.bits();
2823 for (int i=0; i<numGlyphs; ++i) {
2825 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2826 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2827 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2831 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2832 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2834 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2837 // c.baseLineX, c.baseLineY,
2840 // positions[i].x.toInt(), positions[i].y.toInt());
2842 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2850 * Returns true if the rectangle is completely within the current clip
2851 * state of the paint engine.
2853 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2855 const QClipData *cl = clip();
2857 // inline contains() for performance (we know the rects are normalized)
2858 const QRect &r1 = deviceRect;
2859 return (r.left() >= r1.left() && r.right() <= r1.right()
2860 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2864 if (cl->hasRectClip) {
2865 // currently all painting functions clips to deviceRect internally
2866 if (cl->clipRect == deviceRect)
2869 // inline contains() for performance (we know the rects are normalized)
2870 const QRect &r1 = cl->clipRect;
2871 return (r.left() >= r1.left() && r.right() <= r1.right()
2872 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2874 return qt_region_strictContains(cl->clipRegion, r);
2878 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2881 Q_Q(const QRasterPaintEngine);
2882 const QRasterPaintEngineState *s = q->state();
2883 const QClipData *cl = clip();
2885 QRect r = rect.normalized();
2886 // inline contains() for performance (we know the rects are normalized)
2887 const QRect &r1 = deviceRect;
2888 return (r.left() >= r1.left() && r.right() <= r1.right()
2889 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2893 // currently all painting functions that call this function clip to deviceRect internally
2894 if (cl->hasRectClip && cl->clipRect == deviceRect)
2897 if (s->flags.antialiased)
2900 QRect r = rect.normalized();
2902 r.setX(r.x() - penWidth);
2903 r.setY(r.y() - penWidth);
2904 r.setWidth(r.width() + 2 * penWidth);
2905 r.setHeight(r.height() + 2 * penWidth);
2908 if (cl->hasRectClip) {
2909 // inline contains() for performance (we know the rects are normalized)
2910 const QRect &r1 = cl->clipRect;
2911 return (r.left() >= r1.left() && r.right() <= r1.right()
2912 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2914 return qt_region_strictContains(cl->clipRegion, r);
2918 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2921 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2925 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2926 const QSpanData *data) const
2928 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2932 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2933 const QSpanData *data) const
2935 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2939 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2940 const QSpanData *data) const
2942 Q_Q(const QRasterPaintEngine);
2943 const QRasterPaintEngineState *s = q->state();
2945 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2947 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2948 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2951 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2952 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2954 QFixed clipLeft = QFixed::fromReal(clip.left());
2955 QFixed clipRight = QFixed::fromReal(clip.right());
2956 QFixed clipTop = QFixed::fromReal(clip.top());
2957 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2960 while (first < numGlyphs) {
2961 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2962 QFixed left = metrics.x + positions[first].x;
2963 QFixed top = metrics.y + positions[first].y;
2964 QFixed right = left + metrics.width;
2965 QFixed bottom = top + metrics.height;
2966 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2970 int last = numGlyphs - 1;
2971 while (last > first) {
2972 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2973 QFixed left = metrics.x + positions[last].x;
2974 QFixed top = metrics.y + positions[last].y;
2975 QFixed right = left + metrics.width;
2976 QFixed bottom = top + metrics.height;
2977 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2981 return QPair<int, int>(first, last + 1);
2987 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2989 if (textItem->numGlyphs == 0)
2993 ensureRasterState();
2995 QFontEngine *fontEngine = textItem->fontEngine();
2996 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2997 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2999 } else if (state()->matrix.type() < QTransform::TxProject) {
3001 QTransform invMat = state()->matrix.inverted(&invertible);
3005 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3006 textItem->fontEngine(), textItem->glyphs,
3007 textItem->glyphPositions, textItem->numGlyphs);
3008 QStaticTextItem copy = *textItem;
3009 copy.glyphs += range.first;
3010 copy.glyphPositions += range.first;
3011 copy.numGlyphs = range.second - range.first;
3012 QPaintEngineEx::drawStaticTextItem(©);
3014 QPaintEngineEx::drawStaticTextItem(textItem);
3021 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3023 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3025 #ifdef QT_DEBUG_DRAW
3026 Q_D(QRasterPaintEngine);
3027 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3028 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3032 if (ti.glyphs.numGlyphs == 0)
3035 ensureRasterState();
3037 QRasterPaintEngineState *s = state();
3038 QTransform matrix = s->matrix;
3040 if (!supportsTransformations(ti.fontEngine)) {
3041 QVarLengthArray<QFixedPoint> positions;
3042 QVarLengthArray<glyph_t> glyphs;
3044 matrix.translate(p.x(), p.y());
3045 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3047 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3048 } else if (matrix.type() < QTransform::TxProject) {
3050 QTransform invMat = matrix.inverted(&invertible);
3054 QVarLengthArray<QFixedPoint> positions;
3055 QVarLengthArray<glyph_t> glyphs;
3057 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3058 ti.flags, glyphs, positions);
3059 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3060 ti.fontEngine, glyphs.data(), positions.data(),
3063 if (range.first >= range.second)
3066 QStaticTextItem staticTextItem;
3067 staticTextItem.color = s->pen.color();
3068 staticTextItem.font = s->font;
3069 staticTextItem.setFontEngine(ti.fontEngine);
3070 staticTextItem.numGlyphs = range.second - range.first;
3071 staticTextItem.glyphs = glyphs.data() + range.first;
3072 staticTextItem.glyphPositions = positions.data() + range.first;
3073 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3075 QPaintEngineEx::drawTextItem(p, ti);
3082 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3084 Q_D(QRasterPaintEngine);
3085 QRasterPaintEngineState *s = state();
3088 if (!s->penData.blend)
3091 if (!s->flags.fast_pen) {
3092 QPaintEngineEx::drawPoints(points, pointCount);
3096 QCosmeticStroker stroker(s, d->deviceRect);
3097 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3098 stroker.drawPoints(points, pointCount);
3102 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3104 Q_D(QRasterPaintEngine);
3105 QRasterPaintEngineState *s = state();
3108 if (!s->penData.blend)
3111 if (!s->flags.fast_pen) {
3112 QPaintEngineEx::drawPoints(points, pointCount);
3116 QCosmeticStroker stroker(s, d->deviceRect);
3117 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3118 stroker.drawPoints(points, pointCount);
3124 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3126 #ifdef QT_DEBUG_DRAW
3127 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3129 Q_D(QRasterPaintEngine);
3130 QRasterPaintEngineState *s = state();
3133 if (!s->penData.blend)
3136 if (s->flags.fast_pen) {
3137 QCosmeticStroker stroker(s, d->deviceRect);
3138 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3139 for (int i=0; i<lineCount; ++i) {
3140 const QLine &l = lines[i];
3141 stroker.drawLine(l.p1(), l.p2());
3144 QPaintEngineEx::drawLines(lines, lineCount);
3148 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3154 Q_Q(QRasterPaintEngine);
3155 QRasterPaintEngineState *s = q->state();
3157 const QPen &pen = s->lastPen;
3158 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3159 const QVector<qreal> pattern = pen.dashPattern();
3161 qreal patternLength = 0;
3162 for (int i = 0; i < pattern.size(); ++i)
3163 patternLength += pattern.at(i);
3165 if (patternLength <= 0)
3168 qreal length = line.length();
3169 Q_ASSERT(length > 0);
3170 while (length > 0) {
3171 const bool rasterize = *inDash;
3172 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3175 if (dash >= length) {
3177 *dashOffset += dash / width;
3181 *inDash = !(*inDash);
3182 if (++*dashIndex >= pattern.size())
3189 if (rasterize && dash > 0)
3190 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3197 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3199 #ifdef QT_DEBUG_DRAW
3200 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3202 Q_D(QRasterPaintEngine);
3203 QRasterPaintEngineState *s = state();
3206 if (!s->penData.blend)
3208 if (s->flags.fast_pen) {
3209 QCosmeticStroker stroker(s, d->deviceRect);
3210 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3211 for (int i=0; i<lineCount; ++i) {
3212 QLineF line = lines[i];
3213 stroker.drawLine(line.p1(), line.p2());
3216 QPaintEngineEx::drawLines(lines, lineCount);
3224 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3226 Q_D(QRasterPaintEngine);
3227 QRasterPaintEngineState *s = state();
3230 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3231 || (qpen_style(s->lastPen) == Qt::NoPen))
3232 && !s->flags.antialiased
3233 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3235 && s->matrix.type() <= QTransform::TxScale) // no shear
3238 const QRectF r = s->matrix.mapRect(rect);
3239 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3240 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3241 const QRect brect = QRect(int(r.x()), int(r.y()),
3242 int_dim(r.x(), r.width()),
3243 int_dim(r.y(), r.height()));
3245 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3246 &s->penData, &s->brushData);
3250 QPaintEngineEx::drawEllipse(rect);
3258 void QRasterPaintEngine::setDC(HDC hdc) {
3259 Q_D(QRasterPaintEngine);
3266 HDC QRasterPaintEngine::getDC() const
3268 Q_D(const QRasterPaintEngine);
3275 void QRasterPaintEngine::releaseDC(HDC) const
3284 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3286 const QTransform &m = state()->matrix;
3287 return supportsTransformations(fontEngine, m);
3293 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3295 if (m.type() >= QTransform::TxProject)
3298 return !shouldDrawCachedGlyphs(fontEngine, m);
3304 QPoint QRasterPaintEngine::coordinateOffset() const
3306 return QPoint(0, 0);
3309 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3314 Q_D(QRasterPaintEngine);
3316 Q_ASSERT(image.depth() == 1);
3318 const int spanCount = 256;
3319 QT_FT_Span spans[spanCount];
3323 int w = image.width();
3324 int h = image.height();
3325 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3326 int ymin = qMax(qRound(pos.y()), 0);
3327 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3328 int xmin = qMax(qRound(pos.x()), 0);
3330 int x_offset = xmin - qRound(pos.x());
3332 QImage::Format format = image.format();
3333 for (int y = ymin; y < ymax; ++y) {
3334 const uchar *src = image.scanLine(y - qRound(pos.y()));
3335 if (format == QImage::Format_MonoLSB) {
3336 for (int x = 0; x < xmax - xmin; ++x) {
3337 int src_x = x + x_offset;
3338 uchar pixel = src[src_x >> 3];
3343 if (pixel & (0x1 << (src_x & 7))) {
3344 spans[n].x = xmin + x;
3346 spans[n].coverage = 255;
3348 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3352 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3355 if (n == spanCount) {
3356 fg->blend(n, spans, fg);
3362 for (int x = 0; x < xmax - xmin; ++x) {
3363 int src_x = x + x_offset;
3364 uchar pixel = src[src_x >> 3];
3369 if (pixel & (0x80 >> (x & 7))) {
3370 spans[n].x = xmin + x;
3372 spans[n].coverage = 255;
3374 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3378 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3381 if (n == spanCount) {
3382 fg->blend(n, spans, fg);
3390 fg->blend(n, spans, fg);
3396 \enum QRasterPaintEngine::ClipType
3399 \value RectClip Indicates that the currently set clip is a single rectangle.
3400 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3405 Returns the type of the clip currently set.
3407 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3409 Q_D(const QRasterPaintEngine);
3411 const QClipData *clip = d->clip();
3412 if (!clip || clip->hasRectClip)
3420 Returns the bounding rect of the currently set clip.
3422 QRect QRasterPaintEngine::clipBoundingRect() const
3424 Q_D(const QRasterPaintEngine);
3426 const QClipData *clip = d->clip();
3429 return d->deviceRect;
3431 if (clip->hasRectClip)
3432 return clip->clipRect;
3434 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3437 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3439 Q_Q(QRasterPaintEngine);
3440 QRasterPaintEngineState *s = q->state();
3442 rasterizer->setAntialiased(s->flags.antialiased);
3443 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3445 QRect clipRect(deviceRect);
3447 // ### get from optimized rectbased QClipData
3449 const QClipData *c = clip();
3451 const QRect r(QPoint(c->xmin, c->ymin),
3452 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3453 clipRect = clipRect.intersected(r);
3454 blend = data->blend;
3456 blend = data->unclipped_blend;
3459 rasterizer->setClipRect(clipRect);
3460 rasterizer->initialize(blend, data);
3463 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3464 ProcessSpans callback,
3465 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3467 if (!callback || !outline)
3470 Q_Q(QRasterPaintEngine);
3471 QRasterPaintEngineState *s = q->state();
3473 if (!s->flags.antialiased) {
3474 initializeRasterizer(spanData);
3476 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3480 rasterizer->rasterize(outline, fillRule);
3484 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3488 int q_gray_rendered_spans(QT_FT_Raster raster);
3491 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3493 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3496 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3497 ProcessSpans callback,
3498 void *userData, QRasterBuffer *)
3500 if (!callback || !outline)
3503 Q_Q(QRasterPaintEngine);
3504 QRasterPaintEngineState *s = q->state();
3506 if (!s->flags.antialiased) {
3507 rasterizer->setAntialiased(s->flags.antialiased);
3508 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3509 rasterizer->setClipRect(deviceRect);
3510 rasterizer->initialize(callback, userData);
3512 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3516 rasterizer->rasterize(outline, fillRule);
3520 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3521 // minimize memory reallocations. However if initial size for
3522 // raster pool is changed for lower value, reallocations will
3524 int rasterPoolSize = MINIMUM_POOL_SIZE;
3525 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3526 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3527 uchar *rasterPoolOnHeap = 0;
3529 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3531 void *data = userData;
3533 QT_FT_BBox clip_box = { deviceRect.x(),
3535 deviceRect.x() + deviceRect.width(),
3536 deviceRect.y() + deviceRect.height() };
3538 QT_FT_Raster_Params rasterParams;
3539 rasterParams.target = 0;
3540 rasterParams.source = outline;
3541 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3542 rasterParams.gray_spans = 0;
3543 rasterParams.black_spans = 0;
3544 rasterParams.bit_test = 0;
3545 rasterParams.bit_set = 0;
3546 rasterParams.user = data;
3547 rasterParams.clip_box = clip_box;
3552 int rendered_spans = 0;
3556 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3557 rasterParams.gray_spans = callback;
3558 rasterParams.skip_spans = rendered_spans;
3559 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3561 // Out of memory, reallocate some more and try again...
3562 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3563 rasterPoolSize *= 2;
3564 if (rasterPoolSize > 1024 * 1024) {
3565 qWarning("QPainter: Rasterization of primitive failed");
3569 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3571 free(rasterPoolOnHeap);
3572 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3574 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3576 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3578 qt_ft_grays_raster.raster_done(*grayRaster.data());
3579 qt_ft_grays_raster.raster_new(grayRaster.data());
3580 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3586 free(rasterPoolOnHeap);
3589 void QRasterPaintEnginePrivate::recalculateFastImages()
3591 Q_Q(QRasterPaintEngine);
3592 QRasterPaintEngineState *s = q->state();
3594 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3595 && s->matrix.type() <= QTransform::TxShear;
3598 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3600 Q_Q(const QRasterPaintEngine);
3601 const QRasterPaintEngineState *s = q->state();
3603 return s->flags.fast_images
3604 && (mode == QPainter::CompositionMode_SourceOver
3605 || (mode == QPainter::CompositionMode_Source
3606 && !image.hasAlphaChannel()));
3609 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3611 Q_ASSERT(image.depth() == 1);
3613 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3614 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3616 QRgb fg = PREMUL(color.rgba());
3619 int height = sourceImage.height();
3620 int width = sourceImage.width();
3621 for (int y=0; y<height; ++y) {
3622 uchar *source = sourceImage.scanLine(y);
3623 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3624 if (!source || !target)
3625 QT_THROW(std::bad_alloc()); // we must have run out of memory
3626 for (int x=0; x < width; ++x)
3627 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3632 QRasterBuffer::~QRasterBuffer()
3636 void QRasterBuffer::init()
3638 compositionMode = QPainter::CompositionMode_SourceOver;
3639 monoDestinationWithClut = false;
3644 QImage::Format QRasterBuffer::prepare(QImage *image)
3646 m_buffer = (uchar *)image->bits();
3647 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3648 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3649 bytes_per_pixel = image->depth()/8;
3650 bytes_per_line = image->bytesPerLine();
3652 format = image->format();
3653 drawHelper = qDrawHelper + format;
3654 if (image->depth() == 1 && image->colorTable().size() == 2) {
3655 monoDestinationWithClut = true;
3656 destColor0 = PREMUL(image->colorTable()[0]);
3657 destColor1 = PREMUL(image->colorTable()[1]);
3663 void QRasterBuffer::resetBuffer(int val)
3665 memset(m_buffer, val, m_height*bytes_per_line);
3668 QClipData::QClipData(int height)
3670 clipSpanHeight = height;
3675 xmin = xmax = ymin = ymax = 0;
3679 hasRectClip = hasRegionClip = false;
3682 QClipData::~QClipData()
3690 void QClipData::initialize()
3696 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3698 Q_CHECK_PTR(m_clipLines);
3700 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3701 allocated = clipSpanHeight;
3702 Q_CHECK_PTR(m_spans);
3708 m_clipLines[y].spans = 0;
3709 m_clipLines[y].count = 0;
3713 const int len = clipRect.width();
3716 QSpan *span = m_spans + count;
3720 span->coverage = 255;
3723 m_clipLines[y].spans = span;
3724 m_clipLines[y].count = 1;
3728 while (y < clipSpanHeight) {
3729 m_clipLines[y].spans = 0;
3730 m_clipLines[y].count = 0;
3733 } else if (hasRegionClip) {
3735 const QVector<QRect> rects = clipRegion.rects();
3736 const int numRects = rects.size();
3739 const int maxSpans = (ymax - ymin) * numRects;
3740 if (maxSpans > allocated) {
3741 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3742 allocated = maxSpans;
3747 int firstInBand = 0;
3749 while (firstInBand < numRects) {
3750 const int currMinY = rects.at(firstInBand).y();
3751 const int currMaxY = currMinY + rects.at(firstInBand).height();
3753 while (y < currMinY) {
3754 m_clipLines[y].spans = 0;
3755 m_clipLines[y].count = 0;
3759 int lastInBand = firstInBand;
3760 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3763 while (y < currMaxY) {
3765 m_clipLines[y].spans = m_spans + count;
3766 m_clipLines[y].count = lastInBand - firstInBand + 1;
3768 for (int r = firstInBand; r <= lastInBand; ++r) {
3769 const QRect &currRect = rects.at(r);
3770 QSpan *span = m_spans + count;
3771 span->x = currRect.x();
3772 span->len = currRect.width();
3774 span->coverage = 255;
3780 firstInBand = lastInBand + 1;
3783 Q_ASSERT(count <= allocated);
3785 while (y < clipSpanHeight) {
3786 m_clipLines[y].spans = 0;
3787 m_clipLines[y].count = 0;
3793 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3798 free(m_clipLines); // same for clipLines
3804 void QClipData::fixup()
3809 ymin = ymax = xmin = xmax = 0;
3814 ymin = m_spans[0].y;
3815 ymax = m_spans[count-1].y + 1;
3819 const int firstLeft = m_spans[0].x;
3820 const int firstRight = m_spans[0].x + m_spans[0].len;
3823 for (int i = 0; i < count; ++i) {
3824 QT_FT_Span_& span = m_spans[i];
3827 if (span.y != y + 1 && y != -1)
3830 m_clipLines[y].spans = &span;
3831 m_clipLines[y].count = 1;
3833 ++m_clipLines[y].count;
3835 const int spanLeft = span.x;
3836 const int spanRight = spanLeft + span.len;
3838 if (spanLeft < xmin)
3841 if (spanRight > xmax)
3844 if (spanLeft != firstLeft || spanRight != firstRight)
3850 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3855 Convert \a rect to clip spans.
3857 void QClipData::setClipRect(const QRect &rect)
3859 if (hasRectClip && rect == clipRect)
3862 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3864 hasRegionClip = false;
3868 xmax = rect.x() + rect.width();
3869 ymin = qMin(rect.y(), clipSpanHeight);
3870 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3877 // qDebug() << xmin << xmax << ymin << ymax;
3881 Convert \a region to clip spans.
3883 void QClipData::setClipRegion(const QRegion ®ion)
3885 if (region.rectCount() == 1) {
3886 setClipRect(region.rects().at(0));
3890 hasRegionClip = true;
3891 hasRectClip = false;
3892 clipRegion = region;
3894 { // set bounding rect
3895 const QRect rect = region.boundingRect();
3897 xmax = rect.x() + rect.width();
3899 ymax = rect.y() + rect.height();
3911 spans must be sorted on y
3913 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3914 const QSpan *spans, const QSpan *end,
3915 QSpan **outSpans, int available)
3917 const_cast<QClipData *>(clip)->initialize();
3919 QSpan *out = *outSpans;
3921 const QSpan *clipSpans = clip->m_spans + *currentClip;
3922 const QSpan *clipEnd = clip->m_spans + clip->count;
3924 while (available && spans < end ) {
3925 if (clipSpans >= clipEnd) {
3929 if (clipSpans->y > spans->y) {
3933 if (spans->y != clipSpans->y) {
3934 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3935 clipSpans = clip->m_clipLines[spans->y].spans;
3940 Q_ASSERT(spans->y == clipSpans->y);
3943 int sx2 = sx1 + spans->len;
3944 int cx1 = clipSpans->x;
3945 int cx2 = cx1 + clipSpans->len;
3947 if (cx1 < sx1 && cx2 < sx1) {
3950 } else if (sx1 < cx1 && sx2 < cx1) {
3954 int x = qMax(sx1, cx1);
3955 int len = qMin(sx2, cx2) - x;
3957 out->x = qMax(sx1, cx1);
3958 out->len = qMin(sx2, cx2) - out->x;
3960 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3972 *currentClip = clipSpans - clip->m_spans;
3976 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3978 // qDebug() << "qt_span_fill_clipped" << spanCount;
3979 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3981 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3983 const int NSPANS = 256;
3984 QSpan cspans[NSPANS];
3985 int currentClip = 0;
3986 const QSpan *end = spans + spanCount;
3987 while (spans < end) {
3988 QSpan *clipped = cspans;
3989 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3990 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3991 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3993 if (clipped - cspans)
3994 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4000 Clip spans to \a{clip}-rectangle.
4001 Returns number of unclipped spans
4003 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4006 const short minx = clip.left();
4007 const short miny = clip.top();
4008 const short maxx = clip.right();
4009 const short maxy = clip.bottom();
4012 for (int i = 0; i < numSpans; ++i) {
4013 if (spans[i].y > maxy)
4015 if (spans[i].y < miny
4016 || spans[i].x > maxx
4017 || spans[i].x + spans[i].len <= minx) {
4020 if (spans[i].x < minx) {
4021 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4024 spans[n].x = spans[i].x;
4025 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4027 if (spans[n].len == 0)
4029 spans[n].y = spans[i].y;
4030 spans[n].coverage = spans[i].coverage;
4037 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4040 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4041 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4043 Q_ASSERT(fillData->clip);
4044 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4046 // hw: check if this const_cast<> is safe!!!
4047 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4048 fillData->clip->clipRect);
4050 fillData->unclipped_blend(count, spans, fillData);
4053 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4055 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4057 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4058 // for (int i = 0; i < qMin(count, 10); ++i) {
4059 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4062 switch (clipData->operation) {
4064 case Qt::IntersectClip:
4066 QClipData *newClip = clipData->newClip;
4067 newClip->initialize();
4069 int currentClip = 0;
4070 const QSpan *end = spans + count;
4071 while (spans < end) {
4072 QSpan *newspans = newClip->m_spans + newClip->count;
4073 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4074 &newspans, newClip->allocated - newClip->count);
4075 newClip->count = newspans - newClip->m_spans;
4077 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4078 newClip->allocated *= 2;
4084 case Qt::ReplaceClip:
4085 clipData->newClip->appendSpans(spans, count);
4093 QImage QRasterBuffer::bufferImage() const
4095 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4097 for (int y = 0; y < m_height; ++y) {
4098 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4100 for (int x=0; x<m_width; ++x) {
4101 uint argb = span[x];
4102 image.setPixel(x, y, argb);
4110 void QRasterBuffer::flushToARGBImage(QImage *target) const
4112 int w = qMin(m_width, target->width());
4113 int h = qMin(m_height, target->height());
4115 for (int y=0; y<h; ++y) {
4116 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4117 QRgb *dest = (QRgb *) target->scanLine(y);
4118 for (int x=0; x<w; ++x) {
4119 QRgb pixel = sourceLine[x];
4120 int alpha = qAlpha(pixel);
4124 dest[x] = (alpha << 24)
4125 | ((255*qRed(pixel)/alpha) << 16)
4126 | ((255*qGreen(pixel)/alpha) << 8)
4127 | ((255*qBlue(pixel)/alpha) << 0);
4134 class QGradientCache
4138 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4139 stops(s), opacity(op), interpolationMode(mode) {}
4140 uint buffer[GRADIENT_STOPTABLE_SIZE];
4141 QGradientStops stops;
4143 QGradient::InterpolationMode interpolationMode;
4146 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4149 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4150 quint64 hash_val = 0;
4152 QGradientStops stops = gradient.stops();
4153 for (int i = 0; i < stops.size() && i <= 2; i++)
4154 hash_val += stops[i].second.rgba();
4156 QMutexLocker lock(&mutex);
4157 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4159 if (it == cache.constEnd())
4160 return addCacheElement(hash_val, gradient, opacity);
4163 const CacheInfo &cache_info = it.value();
4164 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4165 return cache_info.buffer;
4167 } while (it != cache.constEnd() && it.key() == hash_val);
4168 // an exact match for these stops and opacity was not found, create new cache
4169 return addCacheElement(hash_val, gradient, opacity);
4173 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4175 inline int maxCacheSize() const { return 60; }
4176 inline void generateGradientColorTable(const QGradient& g,
4178 int size, int opacity) const;
4179 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4180 if (cache.size() == maxCacheSize()) {
4181 // may remove more than 1, but OK
4182 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4184 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4185 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4186 return cache.insert(hash_val, cache_entry).value().buffer;
4189 QGradientColorTableHash cache;
4193 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4195 QGradientStops stops = gradient.stops();
4196 int stopCount = stops.count();
4197 Q_ASSERT(stopCount > 0);
4199 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4201 if (stopCount == 2) {
4202 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4203 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4205 qreal first_stop = stops[0].first;
4206 qreal second_stop = stops[1].first;
4208 if (second_stop < first_stop) {
4209 qSwap(first_color, second_color);
4210 qSwap(first_stop, second_stop);
4213 if (colorInterpolation) {
4214 first_color = PREMUL(first_color);
4215 second_color = PREMUL(second_color);
4218 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4219 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4221 uint red_first = qRed(first_color) << 16;
4222 uint green_first = qGreen(first_color) << 16;
4223 uint blue_first = qBlue(first_color) << 16;
4224 uint alpha_first = qAlpha(first_color) << 16;
4226 uint red_second = qRed(second_color) << 16;
4227 uint green_second = qGreen(second_color) << 16;
4228 uint blue_second = qBlue(second_color) << 16;
4229 uint alpha_second = qAlpha(second_color) << 16;
4232 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4233 if (colorInterpolation)
4234 colorTable[i] = first_color;
4236 colorTable[i] = PREMUL(first_color);
4239 if (i < second_index) {
4240 qreal reciprocal = qreal(1) / (second_index - first_index);
4242 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4243 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4244 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4245 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4248 red_first += 1 << 15;
4249 green_first += 1 << 15;
4250 blue_first += 1 << 15;
4251 alpha_first += 1 << 15;
4253 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4254 red_first += red_delta;
4255 green_first += green_delta;
4256 blue_first += blue_delta;
4257 alpha_first += alpha_delta;
4259 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4260 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4262 if (colorInterpolation)
4263 colorTable[i] = color;
4265 colorTable[i] = PREMUL(color);
4269 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4270 if (colorInterpolation)
4271 colorTable[i] = second_color;
4273 colorTable[i] = PREMUL(second_color);
4279 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4280 if (stopCount == 1) {
4281 current_color = PREMUL(current_color);
4282 for (int i = 0; i < size; ++i)
4283 colorTable[i] = current_color;
4287 // The position where the gradient begins and ends
4288 qreal begin_pos = stops[0].first;
4289 qreal end_pos = stops[stopCount-1].first;
4291 int pos = 0; // The position in the color table.
4294 qreal incr = 1 / qreal(size); // the double increment.
4295 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4297 // Up to first point
4298 colorTable[pos++] = PREMUL(current_color);
4299 while (dpos <= begin_pos) {
4300 colorTable[pos] = colorTable[pos - 1];
4305 int current_stop = 0; // We always interpolate between current and current + 1.
4307 qreal t; // position between current left and right stops
4308 qreal t_delta; // the t increment per entry in the color table
4310 if (dpos < end_pos) {
4312 while (dpos > stops[current_stop+1].first)
4315 if (current_stop != 0)
4316 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4317 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4319 if (colorInterpolation) {
4320 current_color = PREMUL(current_color);
4321 next_color = PREMUL(next_color);
4324 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4325 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4326 t = (dpos - stops[current_stop].first) * c;
4330 Q_ASSERT(current_stop < stopCount);
4332 int dist = qRound(t);
4333 int idist = 256 - dist;
4335 if (colorInterpolation)
4336 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4338 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4343 if (dpos >= end_pos)
4349 while (dpos > stops[current_stop+skip+1].first)
4353 current_stop += skip;
4355 current_color = next_color;
4357 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4358 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4360 if (colorInterpolation) {
4362 current_color = PREMUL(current_color);
4363 next_color = PREMUL(next_color);
4366 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4367 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4368 t = (dpos - stops[current_stop].first) * c;
4375 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4376 while (pos < size - 1) {
4377 colorTable[pos] = current_color;
4381 // Make sure the last color stop is represented at the end of the table
4382 colorTable[size - 1] = current_color;
4385 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4388 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4394 m11 = m22 = m33 = 1.;
4395 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4396 clip = pe ? pe->d_func()->clip() : 0;
4399 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4401 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4403 Qt::BrushStyle brushStyle = qbrush_style(brush);
4404 switch (brushStyle) {
4405 case Qt::SolidPattern: {
4407 QColor c = qbrush_color(brush);
4408 QRgb rgba = c.rgba();
4409 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4410 if ((solid.color & 0xff000000) == 0
4411 && compositionMode == QPainter::CompositionMode_SourceOver) {
4417 case Qt::LinearGradientPattern:
4419 type = LinearGradient;
4420 const QLinearGradient *g = static_cast<const QLinearGradient *>(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 QLinearGradientData &linearData = gradient.linear;
4427 linearData.origin.x = g->start().x();
4428 linearData.origin.y = g->start().y();
4429 linearData.end.x = g->finalStop().x();
4430 linearData.end.y = g->finalStop().y();
4434 case Qt::RadialGradientPattern:
4436 type = RadialGradient;
4437 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4438 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4439 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4440 gradient.spread = g->spread();
4442 QRadialGradientData &radialData = gradient.radial;
4444 QPointF center = g->center();
4445 radialData.center.x = center.x();
4446 radialData.center.y = center.y();
4447 radialData.center.radius = g->centerRadius();
4448 QPointF focal = g->focalPoint();
4449 radialData.focal.x = focal.x();
4450 radialData.focal.y = focal.y();
4451 radialData.focal.radius = g->focalRadius();
4455 case Qt::ConicalGradientPattern:
4457 type = ConicalGradient;
4458 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4459 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4460 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4461 gradient.spread = QGradient::RepeatSpread;
4463 QConicalGradientData &conicalData = gradient.conical;
4465 QPointF center = g->center();
4466 conicalData.center.x = center.x();
4467 conicalData.center.y = center.y();
4468 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4472 case Qt::Dense1Pattern:
4473 case Qt::Dense2Pattern:
4474 case Qt::Dense3Pattern:
4475 case Qt::Dense4Pattern:
4476 case Qt::Dense5Pattern:
4477 case Qt::Dense6Pattern:
4478 case Qt::Dense7Pattern:
4479 case Qt::HorPattern:
4480 case Qt::VerPattern:
4481 case Qt::CrossPattern:
4482 case Qt::BDiagPattern:
4483 case Qt::FDiagPattern:
4484 case Qt::DiagCrossPattern:
4487 tempImage = new QImage();
4488 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4489 initTexture(tempImage, alpha, QTextureData::Tiled);
4491 case Qt::TexturePattern:
4494 tempImage = new QImage();
4496 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4497 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4499 *tempImage = brush.textureImage();
4500 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4508 adjustSpanMethods();
4511 void QSpanData::adjustSpanMethods()
4521 unclipped_blend = 0;
4524 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4525 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4526 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4527 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4528 fillRect = rasterBuffer->drawHelper->fillRect;
4530 case LinearGradient:
4531 case RadialGradient:
4532 case ConicalGradient:
4533 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4536 unclipped_blend = qBlendTexture;
4537 if (!texture.imageData)
4538 unclipped_blend = 0;
4543 if (!unclipped_blend) {
4546 blend = unclipped_blend;
4547 } else if (clip->hasRectClip) {
4548 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4550 blend = qt_span_fill_clipped;
4554 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4557 // make sure we round off correctly in qdrawhelper.cpp
4558 delta.translate(1.0 / 65536, 1.0 / 65536);
4560 QTransform inv = (delta * matrix).inverted();
4573 const bool affine = !m13 && !m23;
4574 fast_matrix = affine
4575 && m11 * m11 + m21 * m21 < 1e4
4576 && m12 * m12 + m22 * m22 < 1e4
4580 adjustSpanMethods();
4583 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4585 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4586 if (!d || d->height == 0) {
4587 texture.imageData = 0;
4594 texture.bytesPerLine = 0;
4595 texture.format = QImage::Format_Invalid;
4596 texture.colorTable = 0;
4597 texture.hasAlpha = alpha != 256;
4599 texture.imageData = d->data;
4600 texture.width = d->width;
4601 texture.height = d->height;
4603 if (sourceRect.isNull()) {
4606 texture.x2 = texture.width;
4607 texture.y2 = texture.height;
4609 texture.x1 = sourceRect.x();
4610 texture.y1 = sourceRect.y();
4611 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4612 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4615 texture.bytesPerLine = d->bytes_per_line;
4617 texture.format = d->format;
4618 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4619 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4621 texture.const_alpha = alpha;
4622 texture.type = _type;
4624 adjustSpanMethods();
4629 \a x and \a y is relative to the midpoint of \a rect.
4631 static inline void drawEllipsePoints(int x, int y, int length,
4634 ProcessSpans pen_func, ProcessSpans brush_func,
4635 QSpanData *pen_data, QSpanData *brush_data)
4640 QT_FT_Span outline[4];
4641 const int midx = rect.x() + (rect.width() + 1) / 2;
4642 const int midy = rect.y() + (rect.height() + 1) / 2;
4648 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4649 outline[0].len = qMin(length, x - outline[0].x);
4651 outline[0].coverage = 255;
4655 outline[1].len = length;
4657 outline[1].coverage = 255;
4660 outline[2].x = outline[0].x;
4661 outline[2].len = outline[0].len;
4662 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4663 outline[2].coverage = 255;
4667 outline[3].len = length;
4668 outline[3].y = outline[2].y;
4669 outline[3].coverage = 255;
4671 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4675 fill[0].x = outline[0].x + outline[0].len - 1;
4676 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4677 fill[0].y = outline[1].y;
4678 fill[0].coverage = 255;
4681 fill[1].x = outline[2].x + outline[2].len - 1;
4682 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4683 fill[1].y = outline[3].y;
4684 fill[1].coverage = 255;
4686 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4687 n = qt_intersect_spans(fill, n, clip);
4689 brush_func(n, fill, brush_data);
4692 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4693 n = qt_intersect_spans(outline, n, clip);
4695 pen_func(n, outline, pen_data);
4701 Draws an ellipse using the integer point midpoint algorithm.
4703 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4704 ProcessSpans pen_func, ProcessSpans brush_func,
4705 QSpanData *pen_data, QSpanData *brush_data)
4707 const qreal a = qreal(rect.width()) / 2;
4708 const qreal b = qreal(rect.height()) / 2;
4709 qreal d = b*b - (a*a*b) + 0.25*a*a;
4712 int y = (rect.height() + 1) / 2;
4716 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4717 if (d < 0) { // select E
4720 } else { // select SE
4721 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4722 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4723 pen_func, brush_func, pen_data, brush_data);
4728 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4729 pen_func, brush_func, pen_data, brush_data);
4732 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4733 const int miny = rect.height() & 0x1;
4735 if (d < 0) { // select SE
4736 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4738 } else { // select S
4739 d += a*a*(-2*y + 3);
4742 drawEllipsePoints(x, y, 1, rect, clip,
4743 pen_func, brush_func, pen_data, brush_data);
4748 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4754 #ifdef QT_DEBUG_DRAW
4755 void dumpClip(int width, int height, const QClipData *clip)
4757 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4758 clipImg.fill(0xffff0000);
4765 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4767 for (int i = 0; i < clip->count; ++i) {
4768 const QSpan *span = ((QClipData *) clip)->spans() + i;
4769 for (int j = 0; j < span->len; ++j)
4770 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4771 x0 = qMin(x0, int(span->x));
4772 x1 = qMax(x1, int(span->x + span->len - 1));
4774 y0 = qMin(y0, int(span->y));
4775 y1 = qMax(y1, int(span->y));
4778 static int counter = 0;
4785 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4786 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));