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 qRound(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 uchar *bits = image.bits();
2822 for (int i=0; i<numGlyphs; ++i) {
2824 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2825 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2826 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2830 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2831 int y = qRound(positions[i].y) - c.baseLineY - margin;
2833 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2836 // c.baseLineX, c.baseLineY,
2839 // positions[i].x.toInt(), positions[i].y.toInt());
2841 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2849 * Returns true if the rectangle is completely within the current clip
2850 * state of the paint engine.
2852 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2854 const QClipData *cl = clip();
2856 // inline contains() for performance (we know the rects are normalized)
2857 const QRect &r1 = deviceRect;
2858 return (r.left() >= r1.left() && r.right() <= r1.right()
2859 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2863 if (cl->hasRectClip) {
2864 // currently all painting functions clips to deviceRect internally
2865 if (cl->clipRect == deviceRect)
2868 // inline contains() for performance (we know the rects are normalized)
2869 const QRect &r1 = cl->clipRect;
2870 return (r.left() >= r1.left() && r.right() <= r1.right()
2871 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2873 return qt_region_strictContains(cl->clipRegion, r);
2877 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2880 Q_Q(const QRasterPaintEngine);
2881 const QRasterPaintEngineState *s = q->state();
2882 const QClipData *cl = clip();
2884 QRect r = rect.normalized();
2885 // inline contains() for performance (we know the rects are normalized)
2886 const QRect &r1 = deviceRect;
2887 return (r.left() >= r1.left() && r.right() <= r1.right()
2888 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2892 // currently all painting functions that call this function clip to deviceRect internally
2893 if (cl->hasRectClip && cl->clipRect == deviceRect)
2896 if (s->flags.antialiased)
2899 QRect r = rect.normalized();
2901 r.setX(r.x() - penWidth);
2902 r.setY(r.y() - penWidth);
2903 r.setWidth(r.width() + 2 * penWidth);
2904 r.setHeight(r.height() + 2 * penWidth);
2907 if (cl->hasRectClip) {
2908 // inline contains() for performance (we know the rects are normalized)
2909 const QRect &r1 = cl->clipRect;
2910 return (r.left() >= r1.left() && r.right() <= r1.right()
2911 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2913 return qt_region_strictContains(cl->clipRegion, r);
2917 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2920 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2924 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2925 const QSpanData *data) const
2927 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2931 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2932 const QSpanData *data) const
2934 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2938 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2939 const QSpanData *data) const
2941 Q_Q(const QRasterPaintEngine);
2942 const QRasterPaintEngineState *s = q->state();
2944 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2946 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2947 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2950 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2951 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2953 QFixed clipLeft = QFixed::fromReal(clip.left());
2954 QFixed clipRight = QFixed::fromReal(clip.right());
2955 QFixed clipTop = QFixed::fromReal(clip.top());
2956 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2959 while (first < numGlyphs) {
2960 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2961 QFixed left = metrics.x + positions[first].x;
2962 QFixed top = metrics.y + positions[first].y;
2963 QFixed right = left + metrics.width;
2964 QFixed bottom = top + metrics.height;
2965 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2969 int last = numGlyphs - 1;
2970 while (last > first) {
2971 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2972 QFixed left = metrics.x + positions[last].x;
2973 QFixed top = metrics.y + positions[last].y;
2974 QFixed right = left + metrics.width;
2975 QFixed bottom = top + metrics.height;
2976 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2980 return QPair<int, int>(first, last + 1);
2986 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2988 if (textItem->numGlyphs == 0)
2992 ensureRasterState();
2994 QFontEngine *fontEngine = textItem->fontEngine();
2995 if (!supportsTransformations(fontEngine)) {
2996 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2998 } else if (state()->matrix.type() < QTransform::TxProject) {
3000 QTransform invMat = state()->matrix.inverted(&invertible);
3004 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3005 textItem->fontEngine(), textItem->glyphs,
3006 textItem->glyphPositions, textItem->numGlyphs);
3007 QStaticTextItem copy = *textItem;
3008 copy.glyphs += range.first;
3009 copy.glyphPositions += range.first;
3010 copy.numGlyphs = range.second - range.first;
3011 QPaintEngineEx::drawStaticTextItem(©);
3013 QPaintEngineEx::drawStaticTextItem(textItem);
3020 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3022 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3024 #ifdef QT_DEBUG_DRAW
3025 Q_D(QRasterPaintEngine);
3026 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3027 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3031 if (ti.glyphs.numGlyphs == 0)
3034 ensureRasterState();
3036 QRasterPaintEngineState *s = state();
3037 QTransform matrix = s->matrix;
3039 if (!supportsTransformations(ti.fontEngine)) {
3040 QVarLengthArray<QFixedPoint> positions;
3041 QVarLengthArray<glyph_t> glyphs;
3043 matrix.translate(p.x(), p.y());
3044 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3046 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3047 } else if (matrix.type() < QTransform::TxProject) {
3049 QTransform invMat = matrix.inverted(&invertible);
3053 QVarLengthArray<QFixedPoint> positions;
3054 QVarLengthArray<glyph_t> glyphs;
3056 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3057 ti.flags, glyphs, positions);
3058 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3059 ti.fontEngine, glyphs.data(), positions.data(),
3062 if (range.first >= range.second)
3065 QStaticTextItem staticTextItem;
3066 staticTextItem.color = s->pen.color();
3067 staticTextItem.font = s->font;
3068 staticTextItem.setFontEngine(ti.fontEngine);
3069 staticTextItem.numGlyphs = range.second - range.first;
3070 staticTextItem.glyphs = glyphs.data() + range.first;
3071 staticTextItem.glyphPositions = positions.data() + range.first;
3072 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3074 QPaintEngineEx::drawTextItem(p, ti);
3081 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3083 Q_D(QRasterPaintEngine);
3084 QRasterPaintEngineState *s = state();
3087 if (!s->penData.blend)
3090 if (!s->flags.fast_pen) {
3091 QPaintEngineEx::drawPoints(points, pointCount);
3095 QCosmeticStroker stroker(s, d->deviceRect);
3096 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3097 stroker.drawPoints(points, pointCount);
3101 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3103 Q_D(QRasterPaintEngine);
3104 QRasterPaintEngineState *s = state();
3107 if (!s->penData.blend)
3110 if (!s->flags.fast_pen) {
3111 QPaintEngineEx::drawPoints(points, pointCount);
3115 QCosmeticStroker stroker(s, d->deviceRect);
3116 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3117 stroker.drawPoints(points, pointCount);
3123 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3125 #ifdef QT_DEBUG_DRAW
3126 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3128 Q_D(QRasterPaintEngine);
3129 QRasterPaintEngineState *s = state();
3132 if (!s->penData.blend)
3135 if (s->flags.fast_pen) {
3136 QCosmeticStroker stroker(s, d->deviceRect);
3137 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3138 for (int i=0; i<lineCount; ++i) {
3139 const QLine &l = lines[i];
3140 stroker.drawLine(l.p1(), l.p2());
3143 QPaintEngineEx::drawLines(lines, lineCount);
3147 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3153 Q_Q(QRasterPaintEngine);
3154 QRasterPaintEngineState *s = q->state();
3156 const QPen &pen = s->lastPen;
3157 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3158 const QVector<qreal> pattern = pen.dashPattern();
3160 qreal patternLength = 0;
3161 for (int i = 0; i < pattern.size(); ++i)
3162 patternLength += pattern.at(i);
3164 if (patternLength <= 0)
3167 qreal length = line.length();
3168 Q_ASSERT(length > 0);
3169 while (length > 0) {
3170 const bool rasterize = *inDash;
3171 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3174 if (dash >= length) {
3176 *dashOffset += dash / width;
3180 *inDash = !(*inDash);
3181 if (++*dashIndex >= pattern.size())
3188 if (rasterize && dash > 0)
3189 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3196 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3198 #ifdef QT_DEBUG_DRAW
3199 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3201 Q_D(QRasterPaintEngine);
3202 QRasterPaintEngineState *s = state();
3205 if (!s->penData.blend)
3207 if (s->flags.fast_pen) {
3208 QCosmeticStroker stroker(s, d->deviceRect);
3209 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3210 for (int i=0; i<lineCount; ++i) {
3211 QLineF line = lines[i];
3212 stroker.drawLine(line.p1(), line.p2());
3215 QPaintEngineEx::drawLines(lines, lineCount);
3223 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3225 Q_D(QRasterPaintEngine);
3226 QRasterPaintEngineState *s = state();
3229 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3230 || (qpen_style(s->lastPen) == Qt::NoPen))
3231 && !s->flags.antialiased
3232 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3234 && s->matrix.type() <= QTransform::TxScale) // no shear
3237 const QRectF r = s->matrix.mapRect(rect);
3238 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3239 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3240 const QRect brect = QRect(int(r.x()), int(r.y()),
3241 int_dim(r.x(), r.width()),
3242 int_dim(r.y(), r.height()));
3244 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3245 &s->penData, &s->brushData);
3249 QPaintEngineEx::drawEllipse(rect);
3257 void QRasterPaintEngine::setDC(HDC hdc) {
3258 Q_D(QRasterPaintEngine);
3265 HDC QRasterPaintEngine::getDC() const
3267 Q_D(const QRasterPaintEngine);
3274 void QRasterPaintEngine::releaseDC(HDC) const
3283 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3285 const QTransform &m = state()->matrix;
3286 return supportsTransformations(fontEngine, m);
3292 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3294 if (fontEngine->supportsTransformations(m))
3297 return !shouldDrawCachedGlyphs(fontEngine, m);
3303 QPoint QRasterPaintEngine::coordinateOffset() const
3305 return QPoint(0, 0);
3308 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3313 Q_D(QRasterPaintEngine);
3315 Q_ASSERT(image.depth() == 1);
3317 const int spanCount = 256;
3318 QT_FT_Span spans[spanCount];
3322 int w = image.width();
3323 int h = image.height();
3324 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3325 int ymin = qMax(qRound(pos.y()), 0);
3326 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3327 int xmin = qMax(qRound(pos.x()), 0);
3329 int x_offset = xmin - qRound(pos.x());
3331 QImage::Format format = image.format();
3332 for (int y = ymin; y < ymax; ++y) {
3333 const uchar *src = image.scanLine(y - qRound(pos.y()));
3334 if (format == QImage::Format_MonoLSB) {
3335 for (int x = 0; x < xmax - xmin; ++x) {
3336 int src_x = x + x_offset;
3337 uchar pixel = src[src_x >> 3];
3342 if (pixel & (0x1 << (src_x & 7))) {
3343 spans[n].x = xmin + x;
3345 spans[n].coverage = 255;
3347 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3351 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3354 if (n == spanCount) {
3355 fg->blend(n, spans, fg);
3361 for (int x = 0; x < xmax - xmin; ++x) {
3362 int src_x = x + x_offset;
3363 uchar pixel = src[src_x >> 3];
3368 if (pixel & (0x80 >> (x & 7))) {
3369 spans[n].x = xmin + x;
3371 spans[n].coverage = 255;
3373 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3377 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3380 if (n == spanCount) {
3381 fg->blend(n, spans, fg);
3389 fg->blend(n, spans, fg);
3395 \enum QRasterPaintEngine::ClipType
3398 \value RectClip Indicates that the currently set clip is a single rectangle.
3399 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3404 Returns the type of the clip currently set.
3406 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3408 Q_D(const QRasterPaintEngine);
3410 const QClipData *clip = d->clip();
3411 if (!clip || clip->hasRectClip)
3419 Returns the bounding rect of the currently set clip.
3421 QRect QRasterPaintEngine::clipBoundingRect() const
3423 Q_D(const QRasterPaintEngine);
3425 const QClipData *clip = d->clip();
3428 return d->deviceRect;
3430 if (clip->hasRectClip)
3431 return clip->clipRect;
3433 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3436 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3438 Q_Q(QRasterPaintEngine);
3439 QRasterPaintEngineState *s = q->state();
3441 rasterizer->setAntialiased(s->flags.antialiased);
3442 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3444 QRect clipRect(deviceRect);
3446 // ### get from optimized rectbased QClipData
3448 const QClipData *c = clip();
3450 const QRect r(QPoint(c->xmin, c->ymin),
3451 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3452 clipRect = clipRect.intersected(r);
3453 blend = data->blend;
3455 blend = data->unclipped_blend;
3458 rasterizer->setClipRect(clipRect);
3459 rasterizer->initialize(blend, data);
3462 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3463 ProcessSpans callback,
3464 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3466 if (!callback || !outline)
3469 Q_Q(QRasterPaintEngine);
3470 QRasterPaintEngineState *s = q->state();
3472 if (!s->flags.antialiased) {
3473 initializeRasterizer(spanData);
3475 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3479 rasterizer->rasterize(outline, fillRule);
3483 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3487 int q_gray_rendered_spans(QT_FT_Raster raster);
3490 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3492 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3495 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3496 ProcessSpans callback,
3497 void *userData, QRasterBuffer *)
3499 if (!callback || !outline)
3502 Q_Q(QRasterPaintEngine);
3503 QRasterPaintEngineState *s = q->state();
3505 if (!s->flags.antialiased) {
3506 rasterizer->setAntialiased(s->flags.antialiased);
3507 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3508 rasterizer->setClipRect(deviceRect);
3509 rasterizer->initialize(callback, userData);
3511 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3515 rasterizer->rasterize(outline, fillRule);
3519 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3520 // minimize memory reallocations. However if initial size for
3521 // raster pool is changed for lower value, reallocations will
3523 int rasterPoolSize = MINIMUM_POOL_SIZE;
3524 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3525 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3526 uchar *rasterPoolOnHeap = 0;
3528 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3530 void *data = userData;
3532 QT_FT_BBox clip_box = { deviceRect.x(),
3534 deviceRect.x() + deviceRect.width(),
3535 deviceRect.y() + deviceRect.height() };
3537 QT_FT_Raster_Params rasterParams;
3538 rasterParams.target = 0;
3539 rasterParams.source = outline;
3540 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3541 rasterParams.gray_spans = 0;
3542 rasterParams.black_spans = 0;
3543 rasterParams.bit_test = 0;
3544 rasterParams.bit_set = 0;
3545 rasterParams.user = data;
3546 rasterParams.clip_box = clip_box;
3551 int rendered_spans = 0;
3555 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3556 rasterParams.gray_spans = callback;
3557 rasterParams.skip_spans = rendered_spans;
3558 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3560 // Out of memory, reallocate some more and try again...
3561 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3562 rasterPoolSize *= 2;
3563 if (rasterPoolSize > 1024 * 1024) {
3564 qWarning("QPainter: Rasterization of primitive failed");
3568 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3570 free(rasterPoolOnHeap);
3571 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3573 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3575 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3577 qt_ft_grays_raster.raster_done(*grayRaster.data());
3578 qt_ft_grays_raster.raster_new(grayRaster.data());
3579 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3585 free(rasterPoolOnHeap);
3588 void QRasterPaintEnginePrivate::recalculateFastImages()
3590 Q_Q(QRasterPaintEngine);
3591 QRasterPaintEngineState *s = q->state();
3593 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3594 && s->matrix.type() <= QTransform::TxShear;
3597 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3599 Q_Q(const QRasterPaintEngine);
3600 const QRasterPaintEngineState *s = q->state();
3602 return s->flags.fast_images
3603 && (mode == QPainter::CompositionMode_SourceOver
3604 || (mode == QPainter::CompositionMode_Source
3605 && !image.hasAlphaChannel()));
3608 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3610 Q_ASSERT(image.depth() == 1);
3612 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3613 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3615 QRgb fg = PREMUL(color.rgba());
3618 int height = sourceImage.height();
3619 int width = sourceImage.width();
3620 for (int y=0; y<height; ++y) {
3621 uchar *source = sourceImage.scanLine(y);
3622 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3623 if (!source || !target)
3624 QT_THROW(std::bad_alloc()); // we must have run out of memory
3625 for (int x=0; x < width; ++x)
3626 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3631 QRasterBuffer::~QRasterBuffer()
3635 void QRasterBuffer::init()
3637 compositionMode = QPainter::CompositionMode_SourceOver;
3638 monoDestinationWithClut = false;
3643 QImage::Format QRasterBuffer::prepare(QImage *image)
3645 m_buffer = (uchar *)image->bits();
3646 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3647 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3648 bytes_per_pixel = image->depth()/8;
3649 bytes_per_line = image->bytesPerLine();
3651 format = image->format();
3652 drawHelper = qDrawHelper + format;
3653 if (image->depth() == 1 && image->colorTable().size() == 2) {
3654 monoDestinationWithClut = true;
3655 destColor0 = PREMUL(image->colorTable()[0]);
3656 destColor1 = PREMUL(image->colorTable()[1]);
3662 void QRasterBuffer::resetBuffer(int val)
3664 memset(m_buffer, val, m_height*bytes_per_line);
3667 QClipData::QClipData(int height)
3669 clipSpanHeight = height;
3674 xmin = xmax = ymin = ymax = 0;
3678 hasRectClip = hasRegionClip = false;
3681 QClipData::~QClipData()
3689 void QClipData::initialize()
3695 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3697 Q_CHECK_PTR(m_clipLines);
3699 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3700 allocated = clipSpanHeight;
3701 Q_CHECK_PTR(m_spans);
3707 m_clipLines[y].spans = 0;
3708 m_clipLines[y].count = 0;
3712 const int len = clipRect.width();
3715 QSpan *span = m_spans + count;
3719 span->coverage = 255;
3722 m_clipLines[y].spans = span;
3723 m_clipLines[y].count = 1;
3727 while (y < clipSpanHeight) {
3728 m_clipLines[y].spans = 0;
3729 m_clipLines[y].count = 0;
3732 } else if (hasRegionClip) {
3734 const QVector<QRect> rects = clipRegion.rects();
3735 const int numRects = rects.size();
3738 const int maxSpans = (ymax - ymin) * numRects;
3739 if (maxSpans > allocated) {
3740 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3741 allocated = maxSpans;
3746 int firstInBand = 0;
3748 while (firstInBand < numRects) {
3749 const int currMinY = rects.at(firstInBand).y();
3750 const int currMaxY = currMinY + rects.at(firstInBand).height();
3752 while (y < currMinY) {
3753 m_clipLines[y].spans = 0;
3754 m_clipLines[y].count = 0;
3758 int lastInBand = firstInBand;
3759 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3762 while (y < currMaxY) {
3764 m_clipLines[y].spans = m_spans + count;
3765 m_clipLines[y].count = lastInBand - firstInBand + 1;
3767 for (int r = firstInBand; r <= lastInBand; ++r) {
3768 const QRect &currRect = rects.at(r);
3769 QSpan *span = m_spans + count;
3770 span->x = currRect.x();
3771 span->len = currRect.width();
3773 span->coverage = 255;
3779 firstInBand = lastInBand + 1;
3782 Q_ASSERT(count <= allocated);
3784 while (y < clipSpanHeight) {
3785 m_clipLines[y].spans = 0;
3786 m_clipLines[y].count = 0;
3792 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3797 free(m_clipLines); // same for clipLines
3803 void QClipData::fixup()
3808 ymin = ymax = xmin = xmax = 0;
3813 ymin = m_spans[0].y;
3814 ymax = m_spans[count-1].y + 1;
3818 const int firstLeft = m_spans[0].x;
3819 const int firstRight = m_spans[0].x + m_spans[0].len;
3822 for (int i = 0; i < count; ++i) {
3823 QT_FT_Span_& span = m_spans[i];
3826 if (span.y != y + 1 && y != -1)
3829 m_clipLines[y].spans = &span;
3830 m_clipLines[y].count = 1;
3832 ++m_clipLines[y].count;
3834 const int spanLeft = span.x;
3835 const int spanRight = spanLeft + span.len;
3837 if (spanLeft < xmin)
3840 if (spanRight > xmax)
3843 if (spanLeft != firstLeft || spanRight != firstRight)
3849 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3854 Convert \a rect to clip spans.
3856 void QClipData::setClipRect(const QRect &rect)
3858 if (hasRectClip && rect == clipRect)
3861 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3863 hasRegionClip = false;
3867 xmax = rect.x() + rect.width();
3868 ymin = qMin(rect.y(), clipSpanHeight);
3869 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3876 // qDebug() << xmin << xmax << ymin << ymax;
3880 Convert \a region to clip spans.
3882 void QClipData::setClipRegion(const QRegion ®ion)
3884 if (region.rectCount() == 1) {
3885 setClipRect(region.rects().at(0));
3889 hasRegionClip = true;
3890 hasRectClip = false;
3891 clipRegion = region;
3893 { // set bounding rect
3894 const QRect rect = region.boundingRect();
3896 xmax = rect.x() + rect.width();
3898 ymax = rect.y() + rect.height();
3910 spans must be sorted on y
3912 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3913 const QSpan *spans, const QSpan *end,
3914 QSpan **outSpans, int available)
3916 const_cast<QClipData *>(clip)->initialize();
3918 QSpan *out = *outSpans;
3920 const QSpan *clipSpans = clip->m_spans + *currentClip;
3921 const QSpan *clipEnd = clip->m_spans + clip->count;
3923 while (available && spans < end ) {
3924 if (clipSpans >= clipEnd) {
3928 if (clipSpans->y > spans->y) {
3932 if (spans->y != clipSpans->y) {
3933 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3934 clipSpans = clip->m_clipLines[spans->y].spans;
3939 Q_ASSERT(spans->y == clipSpans->y);
3942 int sx2 = sx1 + spans->len;
3943 int cx1 = clipSpans->x;
3944 int cx2 = cx1 + clipSpans->len;
3946 if (cx1 < sx1 && cx2 < sx1) {
3949 } else if (sx1 < cx1 && sx2 < cx1) {
3953 int x = qMax(sx1, cx1);
3954 int len = qMin(sx2, cx2) - x;
3956 out->x = qMax(sx1, cx1);
3957 out->len = qMin(sx2, cx2) - out->x;
3959 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3971 *currentClip = clipSpans - clip->m_spans;
3975 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3977 // qDebug() << "qt_span_fill_clipped" << spanCount;
3978 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3980 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3982 const int NSPANS = 256;
3983 QSpan cspans[NSPANS];
3984 int currentClip = 0;
3985 const QSpan *end = spans + spanCount;
3986 while (spans < end) {
3987 QSpan *clipped = cspans;
3988 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3989 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3990 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3992 if (clipped - cspans)
3993 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3999 Clip spans to \a{clip}-rectangle.
4000 Returns number of unclipped spans
4002 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4005 const short minx = clip.left();
4006 const short miny = clip.top();
4007 const short maxx = clip.right();
4008 const short maxy = clip.bottom();
4011 for (int i = 0; i < numSpans; ++i) {
4012 if (spans[i].y > maxy)
4014 if (spans[i].y < miny
4015 || spans[i].x > maxx
4016 || spans[i].x + spans[i].len <= minx) {
4019 if (spans[i].x < minx) {
4020 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4023 spans[n].x = spans[i].x;
4024 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4026 if (spans[n].len == 0)
4028 spans[n].y = spans[i].y;
4029 spans[n].coverage = spans[i].coverage;
4036 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4039 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4040 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4042 Q_ASSERT(fillData->clip);
4043 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4045 // hw: check if this const_cast<> is safe!!!
4046 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4047 fillData->clip->clipRect);
4049 fillData->unclipped_blend(count, spans, fillData);
4052 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4054 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4056 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4057 // for (int i = 0; i < qMin(count, 10); ++i) {
4058 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4061 switch (clipData->operation) {
4063 case Qt::IntersectClip:
4065 QClipData *newClip = clipData->newClip;
4066 newClip->initialize();
4068 int currentClip = 0;
4069 const QSpan *end = spans + count;
4070 while (spans < end) {
4071 QSpan *newspans = newClip->m_spans + newClip->count;
4072 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4073 &newspans, newClip->allocated - newClip->count);
4074 newClip->count = newspans - newClip->m_spans;
4076 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4077 newClip->allocated *= 2;
4083 case Qt::ReplaceClip:
4084 clipData->newClip->appendSpans(spans, count);
4092 QImage QRasterBuffer::bufferImage() const
4094 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4096 for (int y = 0; y < m_height; ++y) {
4097 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4099 for (int x=0; x<m_width; ++x) {
4100 uint argb = span[x];
4101 image.setPixel(x, y, argb);
4109 void QRasterBuffer::flushToARGBImage(QImage *target) const
4111 int w = qMin(m_width, target->width());
4112 int h = qMin(m_height, target->height());
4114 for (int y=0; y<h; ++y) {
4115 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4116 QRgb *dest = (QRgb *) target->scanLine(y);
4117 for (int x=0; x<w; ++x) {
4118 QRgb pixel = sourceLine[x];
4119 int alpha = qAlpha(pixel);
4123 dest[x] = (alpha << 24)
4124 | ((255*qRed(pixel)/alpha) << 16)
4125 | ((255*qGreen(pixel)/alpha) << 8)
4126 | ((255*qBlue(pixel)/alpha) << 0);
4133 class QGradientCache
4137 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4138 stops(s), opacity(op), interpolationMode(mode) {}
4139 uint buffer[GRADIENT_STOPTABLE_SIZE];
4140 QGradientStops stops;
4142 QGradient::InterpolationMode interpolationMode;
4145 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4148 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4149 quint64 hash_val = 0;
4151 QGradientStops stops = gradient.stops();
4152 for (int i = 0; i < stops.size() && i <= 2; i++)
4153 hash_val += stops[i].second.rgba();
4155 QMutexLocker lock(&mutex);
4156 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4158 if (it == cache.constEnd())
4159 return addCacheElement(hash_val, gradient, opacity);
4162 const CacheInfo &cache_info = it.value();
4163 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4164 return cache_info.buffer;
4166 } while (it != cache.constEnd() && it.key() == hash_val);
4167 // an exact match for these stops and opacity was not found, create new cache
4168 return addCacheElement(hash_val, gradient, opacity);
4172 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4174 inline int maxCacheSize() const { return 60; }
4175 inline void generateGradientColorTable(const QGradient& g,
4177 int size, int opacity) const;
4178 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4179 if (cache.size() == maxCacheSize()) {
4180 // may remove more than 1, but OK
4181 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4183 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4184 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4185 return cache.insert(hash_val, cache_entry).value().buffer;
4188 QGradientColorTableHash cache;
4192 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4194 QGradientStops stops = gradient.stops();
4195 int stopCount = stops.count();
4196 Q_ASSERT(stopCount > 0);
4198 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4200 if (stopCount == 2) {
4201 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4202 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4204 qreal first_stop = stops[0].first;
4205 qreal second_stop = stops[1].first;
4207 if (second_stop < first_stop) {
4208 qSwap(first_color, second_color);
4209 qSwap(first_stop, second_stop);
4212 if (colorInterpolation) {
4213 first_color = PREMUL(first_color);
4214 second_color = PREMUL(second_color);
4217 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4218 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4220 uint red_first = qRed(first_color) << 16;
4221 uint green_first = qGreen(first_color) << 16;
4222 uint blue_first = qBlue(first_color) << 16;
4223 uint alpha_first = qAlpha(first_color) << 16;
4225 uint red_second = qRed(second_color) << 16;
4226 uint green_second = qGreen(second_color) << 16;
4227 uint blue_second = qBlue(second_color) << 16;
4228 uint alpha_second = qAlpha(second_color) << 16;
4231 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4232 if (colorInterpolation)
4233 colorTable[i] = first_color;
4235 colorTable[i] = PREMUL(first_color);
4238 if (i < second_index) {
4239 qreal reciprocal = qreal(1) / (second_index - first_index);
4241 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4242 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4243 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4244 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4247 red_first += 1 << 15;
4248 green_first += 1 << 15;
4249 blue_first += 1 << 15;
4250 alpha_first += 1 << 15;
4252 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4253 red_first += red_delta;
4254 green_first += green_delta;
4255 blue_first += blue_delta;
4256 alpha_first += alpha_delta;
4258 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4259 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4261 if (colorInterpolation)
4262 colorTable[i] = color;
4264 colorTable[i] = PREMUL(color);
4268 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4269 if (colorInterpolation)
4270 colorTable[i] = second_color;
4272 colorTable[i] = PREMUL(second_color);
4278 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4279 if (stopCount == 1) {
4280 current_color = PREMUL(current_color);
4281 for (int i = 0; i < size; ++i)
4282 colorTable[i] = current_color;
4286 // The position where the gradient begins and ends
4287 qreal begin_pos = stops[0].first;
4288 qreal end_pos = stops[stopCount-1].first;
4290 int pos = 0; // The position in the color table.
4293 qreal incr = 1 / qreal(size); // the double increment.
4294 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4296 // Up to first point
4297 colorTable[pos++] = PREMUL(current_color);
4298 while (dpos <= begin_pos) {
4299 colorTable[pos] = colorTable[pos - 1];
4304 int current_stop = 0; // We always interpolate between current and current + 1.
4306 qreal t; // position between current left and right stops
4307 qreal t_delta; // the t increment per entry in the color table
4309 if (dpos < end_pos) {
4311 while (dpos > stops[current_stop+1].first)
4314 if (current_stop != 0)
4315 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4316 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4318 if (colorInterpolation) {
4319 current_color = PREMUL(current_color);
4320 next_color = PREMUL(next_color);
4323 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4324 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4325 t = (dpos - stops[current_stop].first) * c;
4329 Q_ASSERT(current_stop < stopCount);
4331 int dist = qRound(t);
4332 int idist = 256 - dist;
4334 if (colorInterpolation)
4335 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4337 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4342 if (dpos >= end_pos)
4348 while (dpos > stops[current_stop+skip+1].first)
4352 current_stop += skip;
4354 current_color = next_color;
4356 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4357 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4359 if (colorInterpolation) {
4361 current_color = PREMUL(current_color);
4362 next_color = PREMUL(next_color);
4365 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4366 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4367 t = (dpos - stops[current_stop].first) * c;
4374 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4375 while (pos < size - 1) {
4376 colorTable[pos] = current_color;
4380 // Make sure the last color stop is represented at the end of the table
4381 colorTable[size - 1] = current_color;
4384 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4387 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4393 m11 = m22 = m33 = 1.;
4394 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4395 clip = pe ? pe->d_func()->clip() : 0;
4398 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4400 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4402 Qt::BrushStyle brushStyle = qbrush_style(brush);
4403 switch (brushStyle) {
4404 case Qt::SolidPattern: {
4406 QColor c = qbrush_color(brush);
4407 QRgb rgba = c.rgba();
4408 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4409 if ((solid.color & 0xff000000) == 0
4410 && compositionMode == QPainter::CompositionMode_SourceOver) {
4416 case Qt::LinearGradientPattern:
4418 type = LinearGradient;
4419 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4420 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4421 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4422 gradient.spread = g->spread();
4424 QLinearGradientData &linearData = gradient.linear;
4426 linearData.origin.x = g->start().x();
4427 linearData.origin.y = g->start().y();
4428 linearData.end.x = g->finalStop().x();
4429 linearData.end.y = g->finalStop().y();
4433 case Qt::RadialGradientPattern:
4435 type = RadialGradient;
4436 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4437 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4438 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4439 gradient.spread = g->spread();
4441 QRadialGradientData &radialData = gradient.radial;
4443 QPointF center = g->center();
4444 radialData.center.x = center.x();
4445 radialData.center.y = center.y();
4446 radialData.center.radius = g->centerRadius();
4447 QPointF focal = g->focalPoint();
4448 radialData.focal.x = focal.x();
4449 radialData.focal.y = focal.y();
4450 radialData.focal.radius = g->focalRadius();
4454 case Qt::ConicalGradientPattern:
4456 type = ConicalGradient;
4457 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4458 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4459 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4460 gradient.spread = QGradient::RepeatSpread;
4462 QConicalGradientData &conicalData = gradient.conical;
4464 QPointF center = g->center();
4465 conicalData.center.x = center.x();
4466 conicalData.center.y = center.y();
4467 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4471 case Qt::Dense1Pattern:
4472 case Qt::Dense2Pattern:
4473 case Qt::Dense3Pattern:
4474 case Qt::Dense4Pattern:
4475 case Qt::Dense5Pattern:
4476 case Qt::Dense6Pattern:
4477 case Qt::Dense7Pattern:
4478 case Qt::HorPattern:
4479 case Qt::VerPattern:
4480 case Qt::CrossPattern:
4481 case Qt::BDiagPattern:
4482 case Qt::FDiagPattern:
4483 case Qt::DiagCrossPattern:
4486 tempImage = new QImage();
4487 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4488 initTexture(tempImage, alpha, QTextureData::Tiled);
4490 case Qt::TexturePattern:
4493 tempImage = new QImage();
4495 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4496 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4498 *tempImage = brush.textureImage();
4499 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4507 adjustSpanMethods();
4510 void QSpanData::adjustSpanMethods()
4520 unclipped_blend = 0;
4523 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4524 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4525 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4526 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4527 fillRect = rasterBuffer->drawHelper->fillRect;
4529 case LinearGradient:
4530 case RadialGradient:
4531 case ConicalGradient:
4532 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4535 unclipped_blend = qBlendTexture;
4536 if (!texture.imageData)
4537 unclipped_blend = 0;
4542 if (!unclipped_blend) {
4545 blend = unclipped_blend;
4546 } else if (clip->hasRectClip) {
4547 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4549 blend = qt_span_fill_clipped;
4553 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4556 // make sure we round off correctly in qdrawhelper.cpp
4557 delta.translate(1.0 / 65536, 1.0 / 65536);
4559 QTransform inv = (delta * matrix).inverted();
4572 const bool affine = !m13 && !m23;
4573 fast_matrix = affine
4574 && m11 * m11 + m21 * m21 < 1e4
4575 && m12 * m12 + m22 * m22 < 1e4
4579 adjustSpanMethods();
4582 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4584 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4585 if (!d || d->height == 0) {
4586 texture.imageData = 0;
4593 texture.bytesPerLine = 0;
4594 texture.format = QImage::Format_Invalid;
4595 texture.colorTable = 0;
4596 texture.hasAlpha = alpha != 256;
4598 texture.imageData = d->data;
4599 texture.width = d->width;
4600 texture.height = d->height;
4602 if (sourceRect.isNull()) {
4605 texture.x2 = texture.width;
4606 texture.y2 = texture.height;
4608 texture.x1 = sourceRect.x();
4609 texture.y1 = sourceRect.y();
4610 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4611 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4614 texture.bytesPerLine = d->bytes_per_line;
4616 texture.format = d->format;
4617 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4618 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4620 texture.const_alpha = alpha;
4621 texture.type = _type;
4623 adjustSpanMethods();
4628 \a x and \a y is relative to the midpoint of \a rect.
4630 static inline void drawEllipsePoints(int x, int y, int length,
4633 ProcessSpans pen_func, ProcessSpans brush_func,
4634 QSpanData *pen_data, QSpanData *brush_data)
4639 QT_FT_Span outline[4];
4640 const int midx = rect.x() + (rect.width() + 1) / 2;
4641 const int midy = rect.y() + (rect.height() + 1) / 2;
4647 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4648 outline[0].len = qMin(length, x - outline[0].x);
4650 outline[0].coverage = 255;
4654 outline[1].len = length;
4656 outline[1].coverage = 255;
4659 outline[2].x = outline[0].x;
4660 outline[2].len = outline[0].len;
4661 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4662 outline[2].coverage = 255;
4666 outline[3].len = length;
4667 outline[3].y = outline[2].y;
4668 outline[3].coverage = 255;
4670 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4674 fill[0].x = outline[0].x + outline[0].len - 1;
4675 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4676 fill[0].y = outline[1].y;
4677 fill[0].coverage = 255;
4680 fill[1].x = outline[2].x + outline[2].len - 1;
4681 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4682 fill[1].y = outline[3].y;
4683 fill[1].coverage = 255;
4685 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4686 n = qt_intersect_spans(fill, n, clip);
4688 brush_func(n, fill, brush_data);
4691 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4692 n = qt_intersect_spans(outline, n, clip);
4694 pen_func(n, outline, pen_data);
4700 Draws an ellipse using the integer point midpoint algorithm.
4702 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4703 ProcessSpans pen_func, ProcessSpans brush_func,
4704 QSpanData *pen_data, QSpanData *brush_data)
4706 const qreal a = qreal(rect.width()) / 2;
4707 const qreal b = qreal(rect.height()) / 2;
4708 qreal d = b*b - (a*a*b) + 0.25*a*a;
4711 int y = (rect.height() + 1) / 2;
4715 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4716 if (d < 0) { // select E
4719 } else { // select SE
4720 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4721 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4722 pen_func, brush_func, pen_data, brush_data);
4727 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4728 pen_func, brush_func, pen_data, brush_data);
4731 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4732 const int miny = rect.height() & 0x1;
4734 if (d < 0) { // select SE
4735 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4737 } else { // select S
4738 d += a*a*(-2*y + 3);
4741 drawEllipsePoints(x, y, 1, rect, clip,
4742 pen_func, brush_func, pen_data, brush_data);
4747 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4753 #ifdef QT_DEBUG_DRAW
4754 void dumpClip(int width, int height, const QClipData *clip)
4756 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4757 clipImg.fill(0xffff0000);
4764 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4766 for (int i = 0; i < clip->count; ++i) {
4767 const QSpan *span = ((QClipData *) clip)->spans() + i;
4768 for (int j = 0; j < span->len; ++j)
4769 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4770 x0 = qMin(x0, int(span->x));
4771 x1 = qMax(x1, int(span->x + span->len - 1));
4773 y0 = qMin(y0, int(span->y));
4774 y1 = qMax(y1, int(span->y));
4777 static int counter = 0;
4784 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4785 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));