1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
57 // #include <private/qdatabuffer_p.h>
58 // #include <private/qpainter_p.h>
59 #include <private/qmath_p.h>
60 #include <private/qtextengine_p.h>
61 #include <private/qfontengine_p.h>
62 #include <private/qpixmap_raster_p.h>
63 // #include <private/qpolygonclipper_p.h>
64 // #include <private/qrasterizer_p.h>
65 #include <private/qimage_p.h>
66 #include <private/qstatictext_p.h>
67 #include <private/qcosmeticstroker_p.h>
68 #include "qmemrotate_p.h"
70 #include "qpaintengine_raster_p.h"
71 // #include "qbezier_p.h"
72 #include "qoutlinemapper_p.h"
75 # include <qt_windows.h>
76 # include <qvarlengtharray.h>
77 # include <private/qfontengine_p.h>
80 #if defined(Q_OS_WIN64)
87 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
89 #define qreal_to_fixed_26_6(f) (int(f * 64))
90 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
91 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
93 // #define QT_DEBUG_DRAW
95 void dumpClip(int width, int height, const QClipData *clip);
101 // A little helper macro to get a better approximation of dimensions.
102 // If we have a rect that starting at 0.5 of width 3.5 it should span
104 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
106 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
110 static inline bool winClearTypeFontsEnabled()
113 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
114 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
115 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
117 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
118 return result == FE_FONTSMOOTHINGCLEARTYPE;
121 bool QRasterPaintEngine::clearTypeFontsEnabled()
123 static const bool result = winClearTypeFontsEnabled();
131 /********************************************************************************
134 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
135 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
136 static void qt_span_clip(int count, const QSpan *spans, void *userData);
142 Qt::ClipOperation operation;
148 LineDrawIncludeLastPixel
151 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
152 ProcessSpans pen_func, ProcessSpans brush_func,
153 QSpanData *pen_data, QSpanData *brush_data);
155 struct QRasterFloatPoint {
161 static const QRectF boundingRect(const QPointF *points, int pointCount)
163 const QPointF *e = points;
164 const QPointF *last = points + pointCount;
165 qreal minx, maxx, miny, maxy;
166 minx = maxx = e->x();
167 miny = maxy = e->y();
171 else if (e->x() > maxx)
175 else if (e->y() > maxy)
178 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
182 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
183 return (elementCount == 5 // 5-point polygon, check for closed rect
184 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
185 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
186 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
187 && pts[0] < pts[4] && pts[1] < pts[5]
189 (elementCount == 4 // 4-point polygon, check for unclosed rect
190 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
191 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
192 && pts[0] < pts[4] && pts[1] < pts[5]
197 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
199 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
202 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
204 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
207 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
208 qfixed c2x, qfixed c2y,
209 qfixed ex, qfixed ey,
212 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
213 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
214 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
218 #if !defined(QT_NO_DEBUG) && 0
219 static void qt_debug_path(const QPainterPath &path)
221 const char *names[] = {
228 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
229 for (int i=0; i<path.elementCount(); ++i) {
230 const QPainterPath::Element &e = path.elementAt(i);
231 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
232 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
237 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
238 QPaintEngineExPrivate(),
245 \class QRasterPaintEngine
250 \brief The QRasterPaintEngine class enables hardware acceleration
251 of painting operations in Qt for Embedded Linux.
253 Note that this functionality is only available in
254 \l{Qt for Embedded Linux}.
256 In \l{Qt for Embedded Linux}, painting is a pure software
257 implementation. But starting with Qt 4.2, it is
258 possible to add an accelerated graphics driver to take advantage
259 of available hardware resources.
261 Hardware acceleration is accomplished by creating a custom screen
262 driver, accelerating the copying from memory to the screen, and
263 implementing a custom paint engine accelerating the various
264 painting operations. Then a custom paint device (derived from the
265 QCustomRasterPaintDevice class) and a custom window surface
266 (derived from QWSWindowSurface) must be implemented to make
267 \l{Qt for Embedded Linux} aware of the accelerated driver.
269 \note The QRasterPaintEngine class does not support 8-bit images.
270 Instead, they need to be converted to a supported format, such as
271 QImage::Format_ARGB32_Premultiplied.
273 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
274 documentation for details.
276 \sa QCustomRasterPaintDevice, QPaintEngine
280 \fn Type QRasterPaintEngine::type() const
286 \relates QRasterPaintEngine
288 A struct equivalent to QT_FT_Span, containing a position (x,
289 y), the span's length in pixels and its color/coverage (a value
290 ranging from 0 to 255).
296 Creates a raster based paint engine for operating on the given
297 \a device, with the complete set of \l
298 {QPaintEngine::PaintEngineFeature}{paint engine features and
301 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
302 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
304 d_func()->device = device;
311 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
314 d_func()->device = device;
318 void QRasterPaintEngine::init()
320 Q_D(QRasterPaintEngine);
327 // The antialiasing raster.
328 d->grayRaster.reset(new QT_FT_Raster);
329 Q_CHECK_PTR(d->grayRaster.data());
330 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
331 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
334 d->rasterizer.reset(new QRasterizer);
335 d->rasterBuffer.reset(new QRasterBuffer());
336 d->outlineMapper.reset(new QOutlineMapper);
337 d->outlinemapper_xform_dirty = true;
339 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
340 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
341 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
343 d->baseClip.reset(new QClipData(d->device->height()));
344 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
346 d->image_filler.init(d->rasterBuffer.data(), this);
347 d->image_filler.type = QSpanData::Texture;
349 d->image_filler_xform.init(d->rasterBuffer.data(), this);
350 d->image_filler_xform.type = QSpanData::Texture;
352 d->solid_color_filler.init(d->rasterBuffer.data(), this);
353 d->solid_color_filler.type = QSpanData::Solid;
355 d->deviceDepth = d->device->depth();
357 d->mono_surface = false;
358 gccaps &= ~PorterDuff;
360 QImage::Format format = QImage::Format_Invalid;
362 switch (d->device->devType()) {
363 case QInternal::Pixmap:
364 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
366 case QInternal::Image:
367 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
370 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
376 case QImage::Format_MonoLSB:
377 case QImage::Format_Mono:
378 d->mono_surface = true;
380 case QImage::Format_ARGB8565_Premultiplied:
381 case QImage::Format_ARGB8555_Premultiplied:
382 case QImage::Format_ARGB6666_Premultiplied:
383 case QImage::Format_ARGB4444_Premultiplied:
384 case QImage::Format_ARGB32_Premultiplied:
385 case QImage::Format_ARGB32:
386 gccaps |= PorterDuff;
388 case QImage::Format_RGB32:
389 case QImage::Format_RGB444:
390 case QImage::Format_RGB555:
391 case QImage::Format_RGB666:
392 case QImage::Format_RGB888:
393 case QImage::Format_RGB16:
404 Destroys this paint engine.
406 QRasterPaintEngine::~QRasterPaintEngine()
408 Q_D(QRasterPaintEngine);
410 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
416 bool QRasterPaintEngine::begin(QPaintDevice *device)
418 Q_D(QRasterPaintEngine);
420 if (device->devType() == QInternal::Pixmap) {
421 QPixmap *pixmap = static_cast<QPixmap *>(device);
422 QPlatformPixmap *pd = pixmap->handle();
423 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
424 d->device = pd->buffer();
429 // Make sure QPaintEngine::paintDevice() returns the proper device.
432 Q_ASSERT(d->device->devType() == QInternal::Image
433 || d->device->devType() == QInternal::CustomRaster);
435 d->systemStateChanged();
437 QRasterPaintEngineState *s = state();
438 ensureOutlineMapper();
439 d->outlineMapper->m_clip_rect = d->deviceRect;
441 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
442 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
443 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
444 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
446 d->rasterizer->setClipRect(d->deviceRect);
448 s->penData.init(d->rasterBuffer.data(), this);
449 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
450 s->stroker = &d->basicStroker;
451 d->basicStroker.setClipRect(d->deviceRect);
453 s->brushData.init(d->rasterBuffer.data(), this);
454 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
456 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
458 setDirty(DirtyBrushOrigin);
461 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
462 << ") devType:" << device->devType()
463 << "devRect:" << d->deviceRect;
465 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
470 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
471 #if defined(Q_OS_WIN)
472 else if (clearTypeFontsEnabled())
477 QImage::Format format = static_cast<QImage *>(d->device)->format();
478 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
479 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
481 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
483 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
492 bool QRasterPaintEngine::end()
495 Q_D(QRasterPaintEngine);
496 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
498 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
508 void QRasterPaintEngine::releaseBuffer()
510 Q_D(QRasterPaintEngine);
511 d->rasterBuffer.reset(new QRasterBuffer);
517 QSize QRasterPaintEngine::size() const
519 Q_D(const QRasterPaintEngine);
520 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
527 void QRasterPaintEngine::saveBuffer(const QString &s) const
529 Q_D(const QRasterPaintEngine);
530 d->rasterBuffer->bufferImage().save(s, "PNG");
537 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
539 QRasterPaintEngineState *s = state();
540 // FALCON: get rid of this line, see drawImage call below.
542 QTransform::TransformationType txop = s->matrix.type();
546 case QTransform::TxNone:
547 s->flags.int_xform = true;
550 case QTransform::TxTranslate:
551 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
552 && qreal(int(s->matrix.dy())) == s->matrix.dy();
555 case QTransform::TxScale:
556 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
557 && qreal(int(s->matrix.dy())) == s->matrix.dy()
558 && qreal(int(s->matrix.m11())) == s->matrix.m11()
559 && qreal(int(s->matrix.m22())) == s->matrix.m22();
562 default: // shear / perspective...
563 s->flags.int_xform = false;
567 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
569 ensureOutlineMapper();
574 QRasterPaintEngineState::~QRasterPaintEngineState()
576 if (flags.has_clip_ownership)
581 QRasterPaintEngineState::QRasterPaintEngineState()
593 flags.fast_pen = true;
594 flags.antialiased = false;
595 flags.bilinear = false;
596 flags.fast_text = true;
597 flags.int_xform = true;
598 flags.tx_noshear = true;
599 flags.fast_images = true;
602 flags.has_clip_ownership = false;
607 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
612 , strokeFlags(s.strokeFlags)
613 , lastBrush(s.lastBrush)
614 , brushData(s.brushData)
615 , fillFlags(s.fillFlags)
616 , pixmapFlags(s.pixmapFlags)
617 , intOpacity(s.intOpacity)
621 , flag_bits(s.flag_bits)
623 brushData.tempImage = 0;
624 penData.tempImage = 0;
625 flags.has_clip_ownership = false;
631 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
633 QRasterPaintEngineState *s;
635 s = new QRasterPaintEngineState();
637 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
645 void QRasterPaintEngine::setState(QPainterState *s)
647 Q_D(QRasterPaintEngine);
648 QPaintEngineEx::setState(s);
649 d->rasterBuffer->compositionMode = s->composition_mode;
653 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
658 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
665 void QRasterPaintEngine::penChanged()
668 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
670 QRasterPaintEngineState *s = state();
671 s->strokeFlags |= DirtyPen;
672 s->dirty |= DirtyPen;
678 void QRasterPaintEngine::updatePen(const QPen &pen)
680 Q_D(QRasterPaintEngine);
681 QRasterPaintEngineState *s = state();
683 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
686 Qt::PenStyle pen_style = qpen_style(pen);
691 s->penData.clip = d->clip();
692 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
694 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
695 || pen.brush().transform().type() >= QTransform::TxNone) {
696 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
699 // Slightly ugly handling of an uncommon case... We need to change
700 // the pen because it is reused in draw_midpoint to decide dashed
702 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
703 pen_style = Qt::SolidLine;
704 s->lastPen.setStyle(Qt::SolidLine);
707 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
708 d->basicStroker.setCapStyle(qpen_capStyle(pen));
709 d->basicStroker.setMiterLimit(pen.miterLimit());
711 qreal penWidth = qpen_widthf(pen);
713 d->basicStroker.setStrokeWidth(1);
715 d->basicStroker.setStrokeWidth(penWidth);
717 if(pen_style == Qt::SolidLine) {
718 s->stroker = &d->basicStroker;
719 } else if (pen_style != Qt::NoPen) {
721 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
722 if (pen.isCosmetic()) {
723 d->dashStroker->setClipRect(d->deviceRect);
725 // ### I've seen this inverted devrect multiple places now...
726 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
727 d->dashStroker->setClipRect(clipRect);
729 d->dashStroker->setDashPattern(pen.dashPattern());
730 d->dashStroker->setDashOffset(pen.dashOffset());
731 s->stroker = d->dashStroker.data();
736 ensureState(); // needed because of tx_noshear...
737 s->flags.fast_pen = pen_style > Qt::NoPen
739 && ((pen.isCosmetic() && penWidth <= 1)
740 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
742 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
752 void QRasterPaintEngine::brushOriginChanged()
754 QRasterPaintEngineState *s = state();
756 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
759 s->fillFlags |= DirtyBrushOrigin;
766 void QRasterPaintEngine::brushChanged()
768 QRasterPaintEngineState *s = state();
770 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
772 s->fillFlags |= DirtyBrush;
781 void QRasterPaintEngine::updateBrush(const QBrush &brush)
784 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
786 Q_D(QRasterPaintEngine);
787 QRasterPaintEngineState *s = state();
788 // must set clip prior to setup, as setup uses it...
789 s->brushData.clip = d->clip();
790 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
791 if (s->fillFlags & DirtyTransform
792 || brush.transform().type() >= QTransform::TxNone)
793 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
794 s->lastBrush = brush;
798 void QRasterPaintEngine::updateOutlineMapper()
800 Q_D(QRasterPaintEngine);
801 d->outlineMapper->setMatrix(state()->matrix);
804 void QRasterPaintEngine::updateState()
806 QRasterPaintEngineState *s = state();
808 if (s->dirty & DirtyTransform)
809 updateMatrix(s->matrix);
811 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
812 const QPainter::CompositionMode mode = s->composition_mode;
813 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
814 && s->intOpacity == 256
815 && (mode == QPainter::CompositionMode_Source
816 || (mode == QPainter::CompositionMode_SourceOver
817 && qAlpha(s->penData.solid.color) == 255));
827 void QRasterPaintEngine::opacityChanged()
829 QRasterPaintEngineState *s = state();
832 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
835 s->fillFlags |= DirtyOpacity;
836 s->strokeFlags |= DirtyOpacity;
837 s->pixmapFlags |= DirtyOpacity;
838 s->dirty |= DirtyOpacity;
839 s->intOpacity = (int) (s->opacity * 256);
845 void QRasterPaintEngine::compositionModeChanged()
847 Q_D(QRasterPaintEngine);
848 QRasterPaintEngineState *s = state();
851 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
854 s->fillFlags |= DirtyCompositionMode;
855 s->dirty |= DirtyCompositionMode;
857 s->strokeFlags |= DirtyCompositionMode;
858 d->rasterBuffer->compositionMode = s->composition_mode;
860 d->recalculateFastImages();
866 void QRasterPaintEngine::renderHintsChanged()
868 QRasterPaintEngineState *s = state();
871 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
874 bool was_aa = s->flags.antialiased;
875 bool was_bilinear = s->flags.bilinear;
877 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
878 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
880 if (was_aa != s->flags.antialiased)
881 s->strokeFlags |= DirtyHints;
883 if (was_bilinear != s->flags.bilinear) {
884 s->strokeFlags |= DirtyPen;
885 s->fillFlags |= DirtyBrush;
888 Q_D(QRasterPaintEngine);
889 d->recalculateFastImages();
895 void QRasterPaintEngine::transformChanged()
897 QRasterPaintEngineState *s = state();
900 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
903 s->fillFlags |= DirtyTransform;
904 s->strokeFlags |= DirtyTransform;
906 s->dirty |= DirtyTransform;
908 Q_D(QRasterPaintEngine);
909 d->recalculateFastImages();
915 void QRasterPaintEngine::clipEnabledChanged()
917 QRasterPaintEngineState *s = state();
920 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
924 s->clip->enabled = s->clipEnabled;
925 s->fillFlags |= DirtyClipEnabled;
926 s->strokeFlags |= DirtyClipEnabled;
927 s->pixmapFlags |= DirtyClipEnabled;
931 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
933 SrcOverBlendFunc func,
938 if (alpha == 0 || !clip.isValid())
941 Q_ASSERT(img.depth() >= 8);
943 int srcBPL = img.bytesPerLine();
944 const uchar *srcBits = img.bits();
945 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
946 int iw = img.width();
947 int ih = img.height();
952 // Adjust the image according to the source offset...
953 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
956 // adapt the x parameters
957 int x = qRound(pt.x());
959 int cx2 = clip.x() + clip.width();
962 srcBits += srcSize * d;
967 int d = x + iw - cx2;
973 // adapt the y paremeters...
975 int cy2 = clip.y() + clip.height();
976 int y = qRound(pt.y());
979 srcBits += srcBPL * d;
984 int d = y + ih - cy2;
990 // call the blend function...
991 int dstSize = rasterBuffer->bytesPerPixel();
992 int dstBPL = rasterBuffer->bytesPerLine();
993 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1000 void QRasterPaintEnginePrivate::systemStateChanged()
1002 QRect clipRect(0, 0,
1003 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1004 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1006 if (!systemClip.isEmpty()) {
1007 QRegion clippedDeviceRgn = systemClip & clipRect;
1008 deviceRect = clippedDeviceRgn.boundingRect();
1009 baseClip->setClipRegion(clippedDeviceRgn);
1011 deviceRect = clipRect;
1012 baseClip->setClipRect(deviceRect);
1014 #ifdef QT_DEBUG_DRAW
1015 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1018 exDeviceRect = deviceRect;
1020 Q_Q(QRasterPaintEngine);
1021 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1022 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1023 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1026 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1028 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1031 Q_Q(QRasterPaintEngine);
1032 bool bilinear = q->state()->flags.bilinear;
1034 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1035 spanData->setupMatrix(b.transform() * m, bilinear);
1037 if (m.type() <= QTransform::TxTranslate) {
1038 // specialize setupMatrix for translation matrices
1039 // to avoid needless matrix inversion
1047 spanData->dx = -m.dx();
1048 spanData->dy = -m.dy();
1049 spanData->txop = m.type();
1050 spanData->bilinear = bilinear;
1051 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1052 spanData->adjustSpanMethods();
1054 spanData->setupMatrix(m, bilinear);
1059 // #define QT_CLIPPING_RATIOS
1061 #ifdef QT_CLIPPING_RATIOS
1066 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1068 if (d->clip()->hasRectClip)
1070 if (d->clip()->hasRegionClip)
1074 if ((totalClips % 5000) == 0) {
1075 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1076 rectClips * 100.0 / (qreal) totalClips,
1077 regionClips * 100.0 / (qreal) totalClips,
1078 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1087 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1089 if (s->flags.has_clip_ownership)
1092 s->flags.has_clip_ownership = false;
1095 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1097 s->fillFlags |= QPaintEngine::DirtyClipPath;
1098 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1099 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1101 d->solid_color_filler.clip = d->clip();
1102 d->solid_color_filler.adjustSpanMethods();
1104 #ifdef QT_DEBUG_DRAW
1105 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1114 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1116 #ifdef QT_DEBUG_DRAW
1117 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1119 if (path.elements()) {
1120 for (int i=0; i<path.elementCount(); ++i) {
1121 qDebug() << " - " << path.elements()[i]
1122 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1125 for (int i=0; i<path.elementCount(); ++i) {
1126 qDebug() << " ---- "
1127 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1132 Q_D(QRasterPaintEngine);
1133 QRasterPaintEngineState *s = state();
1135 const qreal *points = path.points();
1136 const QPainterPath::ElementType *types = path.elements();
1138 // There are some cases that are not supported by clip(QRect)
1139 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1140 if (s->matrix.type() <= QTransform::TxScale
1141 && ((path.shape() == QVectorPath::RectangleHint)
1142 || (isRect(points, path.elementCount())
1143 && (!types || (types[0] == QPainterPath::MoveToElement
1144 && types[1] == QPainterPath::LineToElement
1145 && types[2] == QPainterPath::LineToElement
1146 && types[3] == QPainterPath::LineToElement))))) {
1147 #ifdef QT_DEBUG_DRAW
1148 qDebug() << " --- optimizing vector clip to rect clip...";
1151 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1152 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1157 if (op == Qt::NoClip) {
1158 qrasterpaintengine_state_setNoClip(s);
1161 QClipData *base = d->baseClip.data();
1163 // Intersect with current clip when available...
1164 if (op == Qt::IntersectClip && s->clip)
1167 // We always intersect, except when there is nothing to
1168 // intersect with, in which case we simplify the operation to
1170 Qt::ClipOperation isectOp = Qt::IntersectClip;
1172 isectOp = Qt::ReplaceClip;
1174 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1175 newClip->initialize();
1176 ClipData clipData = { base, newClip, isectOp };
1177 ensureOutlineMapper();
1178 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1182 if (s->flags.has_clip_ownership)
1186 s->flags.has_clip_ownership = true;
1188 qrasterpaintengine_dirty_clip(d, s);
1196 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1198 #ifdef QT_DEBUG_DRAW
1199 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1202 QRasterPaintEngineState *s = state();
1204 if (op == Qt::NoClip) {
1205 qrasterpaintengine_state_setNoClip(s);
1207 } else if (s->matrix.type() > QTransform::TxScale) {
1208 QPaintEngineEx::clip(rect, op);
1211 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1212 QPaintEngineEx::clip(rect, op);
1218 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1220 Q_D(QRasterPaintEngine);
1221 QRect clipRect = r & d->deviceRect;
1222 QRasterPaintEngineState *s = state();
1224 if (op == Qt::ReplaceClip || s->clip == 0) {
1226 // No current clip, hence we intersect with sysclip and be
1228 QRegion clipRegion = systemClip();
1229 QClipData *clip = new QClipData(d->rasterBuffer->height());
1231 if (clipRegion.isEmpty())
1232 clip->setClipRect(clipRect);
1234 clip->setClipRegion(clipRegion & clipRect);
1236 if (s->flags.has_clip_ownership)
1240 s->clip->enabled = true;
1241 s->flags.has_clip_ownership = true;
1243 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1244 QClipData *base = s->clip;
1247 if (base->hasRectClip || base->hasRegionClip) {
1248 if (!s->flags.has_clip_ownership) {
1249 s->clip = new QClipData(d->rasterBuffer->height());
1250 s->flags.has_clip_ownership = true;
1252 if (base->hasRectClip)
1253 s->clip->setClipRect(base->clipRect & clipRect);
1255 s->clip->setClipRegion(base->clipRegion & clipRect);
1256 s->clip->enabled = true;
1264 qrasterpaintengine_dirty_clip(d, s);
1272 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1274 #ifdef QT_DEBUG_DRAW
1275 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1278 Q_D(QRasterPaintEngine);
1280 if (region.rectCount() == 1) {
1281 clip(region.boundingRect(), op);
1285 QRasterPaintEngineState *s = state();
1286 const QClipData *clip = d->clip();
1287 const QClipData *baseClip = d->baseClip.data();
1289 if (op == Qt::NoClip) {
1290 qrasterpaintengine_state_setNoClip(s);
1291 } else if (s->matrix.type() > QTransform::TxScale
1292 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1293 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1294 QPaintEngineEx::clip(region, op);
1296 const QClipData *curClip;
1299 if (op == Qt::IntersectClip)
1304 if (s->flags.has_clip_ownership) {
1308 newClip = new QClipData(d->rasterBuffer->height());
1310 s->flags.has_clip_ownership = true;
1313 QRegion r = s->matrix.map(region);
1314 if (curClip->hasRectClip)
1315 newClip->setClipRegion(r & curClip->clipRect);
1316 else if (curClip->hasRegionClip)
1317 newClip->setClipRegion(r & curClip->clipRegion);
1319 qrasterpaintengine_dirty_clip(d, s);
1326 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1328 #ifdef QT_DEBUG_DRAW
1329 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1332 if (!fillData->blend)
1335 Q_D(QRasterPaintEngine);
1337 const QRectF controlPointRect = path.controlPointRect();
1339 QRasterPaintEngineState *s = state();
1340 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1341 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1342 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1343 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1344 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1345 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1347 if (!s->flags.antialiased && !do_clip) {
1348 d->initializeRasterizer(fillData);
1349 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1353 ensureOutlineMapper();
1354 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1357 static void fillRect_normalized(const QRect &r, QSpanData *data,
1358 QRasterPaintEnginePrivate *pe)
1362 bool rectClipped = true;
1365 x1 = qMax(r.x(), data->clip->xmin);
1366 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1367 y1 = qMax(r.y(), data->clip->ymin);
1368 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1369 rectClipped = data->clip->hasRectClip;
1372 x1 = qMax(r.x(), pe->deviceRect.x());
1373 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1374 y1 = qMax(r.y(), pe->deviceRect.y());
1375 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1377 x1 = qMax(r.x(), 0);
1378 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1379 y1 = qMax(r.y(), 0);
1380 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1383 if (x2 <= x1 || y2 <= y1)
1386 const int width = x2 - x1;
1387 const int height = y2 - y1;
1389 bool isUnclipped = rectClipped
1390 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1392 if (pe && isUnclipped) {
1393 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1395 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1396 || (mode == QPainter::CompositionMode_SourceOver
1397 && qAlpha(data->solid.color) == 255)))
1399 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1405 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1407 const int nspans = 256;
1408 QT_FT_Span spans[nspans];
1410 Q_ASSERT(data->blend);
1413 int n = qMin(nspans, y2 - y);
1417 spans[i].len = width;
1419 spans[i].coverage = 255;
1423 blend(n, spans, data);
1431 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1433 #ifdef QT_DEBUG_DRAW
1434 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1436 Q_D(QRasterPaintEngine);
1438 QRasterPaintEngineState *s = state();
1442 if (s->brushData.blend) {
1443 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1444 const QRect *r = rects;
1445 const QRect *lastRect = rects + rectCount;
1447 int offset_x = int(s->matrix.dx());
1448 int offset_y = int(s->matrix.dy());
1449 while (r < lastRect) {
1450 QRect rect = r->normalized();
1451 QRect rr = rect.translated(offset_x, offset_y);
1452 fillRect_normalized(rr, &s->brushData, d);
1456 QRectVectorPath path;
1457 for (int i=0; i<rectCount; ++i) {
1459 fill(path, s->brush);
1465 if (s->penData.blend) {
1466 QRectVectorPath path;
1467 if (s->flags.fast_pen) {
1468 QCosmeticStroker stroker(s, d->deviceRect);
1469 for (int i = 0; i < rectCount; ++i) {
1471 stroker.drawPath(path);
1474 for (int i = 0; i < rectCount; ++i) {
1476 stroke(path, s->pen);
1485 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1487 #ifdef QT_DEBUG_DRAW
1488 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1490 #ifdef QT_FAST_SPANS
1491 Q_D(QRasterPaintEngine);
1493 QRasterPaintEngineState *s = state();
1496 if (s->flags.tx_noshear) {
1498 if (s->brushData.blend) {
1499 d->initializeRasterizer(&s->brushData);
1500 for (int i = 0; i < rectCount; ++i) {
1501 const QRectF &rect = rects[i].normalized();
1504 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1505 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1506 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1511 if (s->penData.blend) {
1512 QRectVectorPath path;
1513 if (s->flags.fast_pen) {
1514 QCosmeticStroker stroker(s, d->deviceRect);
1515 for (int i = 0; i < rectCount; ++i) {
1517 stroker.drawPath(path);
1520 for (int i = 0; i < rectCount; ++i) {
1522 QPaintEngineEx::stroke(path, s->lastPen);
1529 #endif // QT_FAST_SPANS
1530 QPaintEngineEx::drawRects(rects, rectCount);
1537 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1539 Q_D(QRasterPaintEngine);
1540 QRasterPaintEngineState *s = state();
1543 if (!s->penData.blend)
1546 if (s->flags.fast_pen) {
1547 QCosmeticStroker stroker(s, d->deviceRect);
1548 stroker.drawPath(path);
1549 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1550 qreal width = s->lastPen.isCosmetic()
1551 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1552 : qpen_widthf(s->lastPen) * s->txscale;
1554 qreal dashOffset = s->lastPen.dashOffset();
1556 qreal patternLength = 0;
1557 const QVector<qreal> pattern = s->lastPen.dashPattern();
1558 for (int i = 0; i < pattern.size(); ++i)
1559 patternLength += pattern.at(i);
1561 if (patternLength > 0) {
1562 int n = qFloor(dashOffset / patternLength);
1563 dashOffset -= n * patternLength;
1564 while (dashOffset >= pattern.at(dashIndex)) {
1565 dashOffset -= pattern.at(dashIndex);
1566 if (++dashIndex >= pattern.size())
1572 Q_D(QRasterPaintEngine);
1573 d->initializeRasterizer(&s->penData);
1574 int lineCount = path.elementCount() / 2;
1575 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1577 for (int i = 0; i < lineCount; ++i) {
1578 if (lines[i].p1() == lines[i].p2()) {
1579 if (s->lastPen.capStyle() != Qt::FlatCap) {
1580 QPointF p = lines[i].p1();
1581 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1582 QPointF(p.x() + width*0.5, p.y())));
1583 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1588 const QLineF line = s->matrix.map(lines[i]);
1589 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1590 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1591 width / line.length(),
1592 s->lastPen.capStyle() == Qt::SquareCap);
1594 d->rasterizeLine_dashed(line, width,
1595 &dashIndex, &dashOffset, &inDash);
1600 QPaintEngineEx::stroke(path, pen);
1603 static inline QRect toNormalizedFillRect(const QRectF &rect)
1605 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1606 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1607 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1608 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1615 return QRect(x1, y1, x2 - x1, y2 - y1);
1621 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1625 #ifdef QT_DEBUG_DRAW
1626 QRectF rf = path.controlPointRect();
1627 qDebug() << "QRasterPaintEngine::fill(): "
1628 << "size=" << path.elementCount()
1629 << ", hints=" << hex << path.hints()
1633 Q_D(QRasterPaintEngine);
1634 QRasterPaintEngineState *s = state();
1637 if (!s->brushData.blend)
1640 if (path.shape() == QVectorPath::RectangleHint) {
1641 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1642 const qreal *p = path.points();
1643 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1644 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1645 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1649 if (s->flags.tx_noshear) {
1650 d->initializeRasterizer(&s->brushData);
1651 // ### Is normalizing really necessary here?
1652 const qreal *p = path.points();
1653 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1655 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1656 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1657 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1663 // ### Optimize for non transformed ellipses and rectangles...
1664 QRectF cpRect = path.controlPointRect();
1665 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1666 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1669 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1670 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1671 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1672 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1674 // ### Falonc: implement....
1675 // if (!s->flags.antialiased && !do_clip) {
1676 // d->initializeRasterizer(&s->brushData);
1677 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1681 ensureOutlineMapper();
1682 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1685 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1687 Q_D(QRasterPaintEngine);
1688 QRasterPaintEngineState *s = state();
1690 if (!s->flags.antialiased) {
1691 uint txop = s->matrix.type();
1692 if (txop == QTransform::TxNone) {
1693 fillRect_normalized(toNormalizedFillRect(r), data, d);
1695 } else if (txop == QTransform::TxTranslate) {
1696 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1697 fillRect_normalized(rr, data, d);
1699 } else if (txop == QTransform::TxScale) {
1700 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1701 fillRect_normalized(rr, data, d);
1706 if (s->flags.tx_noshear) {
1707 d->initializeRasterizer(data);
1708 QRectF nr = r.normalized();
1709 if (!nr.isEmpty()) {
1710 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1711 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1712 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1719 ensureOutlineMapper();
1720 fillPath(path, data);
1726 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1728 #ifdef QT_DEBUG_DRAW
1729 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1731 QRasterPaintEngineState *s = state();
1734 if (!s->brushData.blend)
1737 fillRect(r, &s->brushData);
1743 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1745 #ifdef QT_DEBUG_DRAW
1746 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1748 Q_D(QRasterPaintEngine);
1749 QRasterPaintEngineState *s = state();
1751 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1752 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1753 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1756 d->solid_color_filler.clip = d->clip();
1757 d->solid_color_filler.adjustSpanMethods();
1758 fillRect(r, &d->solid_color_filler);
1761 static inline bool isAbove(const QPointF *a, const QPointF *b)
1763 return a->y() < b->y();
1766 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1771 Q_ASSERT(pointCount >= 2);
1773 QVector<const QPointF *> sorted;
1774 sorted.reserve(pointCount);
1776 upper->reserve(pointCount * 3 / 4);
1777 lower->reserve(pointCount * 3 / 4);
1779 for (int i = 0; i < pointCount; ++i)
1780 sorted << points + i;
1782 qSort(sorted.begin(), sorted.end(), isAbove);
1784 qreal splitY = sorted.at(sorted.size() / 2)->y();
1786 const QPointF *end = points + pointCount;
1787 const QPointF *last = end - 1;
1789 QVector<QPointF> *bin[2] = { upper, lower };
1791 for (const QPointF *p = points; p < end; ++p) {
1792 int side = p->y() < splitY;
1793 int lastSide = last->y() < splitY;
1795 if (side != lastSide) {
1796 if (qFuzzyCompare(p->y(), splitY)) {
1797 bin[!side]->append(*p);
1798 } else if (qFuzzyCompare(last->y(), splitY)) {
1799 bin[side]->append(*last);
1801 QPointF delta = *p - *last;
1802 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1804 bin[0]->append(intersection);
1805 bin[1]->append(intersection);
1809 bin[side]->append(*p);
1814 // give up if we couldn't reduce the point count
1815 return upper->size() < pointCount && lower->size() < pointCount;
1821 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1823 Q_D(QRasterPaintEngine);
1824 QRasterPaintEngineState *s = state();
1826 const int maxPoints = 0xffff;
1828 // max amount of points that raster engine can reliably handle
1829 if (pointCount > maxPoints) {
1830 QVector<QPointF> upper, lower;
1832 if (splitPolygon(points, pointCount, &upper, &lower)) {
1833 fillPolygon(upper.constData(), upper.size(), mode);
1834 fillPolygon(lower.constData(), lower.size(), mode);
1836 qWarning("Polygon too complex for filling.");
1841 // Compose polygon fill..,
1842 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1843 ensureOutlineMapper();
1844 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1847 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1849 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1855 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1857 Q_D(QRasterPaintEngine);
1858 QRasterPaintEngineState *s = state();
1860 #ifdef QT_DEBUG_DRAW
1861 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1862 for (int i=0; i<pointCount; ++i)
1863 qDebug() << " - " << points[i];
1865 Q_ASSERT(pointCount >= 2);
1867 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1868 QRectF r(points[0], points[2]);
1874 if (mode != PolylineMode) {
1877 if (s->brushData.blend) {
1878 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1879 fillPolygon(points, pointCount, mode);
1880 d->outlineMapper->setCoordinateRounding(false);
1884 // Do the outline...
1885 if (s->penData.blend) {
1886 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1887 if (s->flags.fast_pen) {
1888 QCosmeticStroker stroker(s, d->deviceRect);
1889 stroker.drawPath(vp);
1891 QPaintEngineEx::stroke(vp, s->lastPen);
1899 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1901 Q_D(QRasterPaintEngine);
1902 QRasterPaintEngineState *s = state();
1904 #ifdef QT_DEBUG_DRAW
1905 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1906 for (int i=0; i<pointCount; ++i)
1907 qDebug() << " - " << points[i];
1909 Q_ASSERT(pointCount >= 2);
1910 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1911 QRect r(points[0].x(),
1913 points[2].x() - points[0].x(),
1914 points[2].y() - points[0].y());
1922 if (mode != PolylineMode) {
1924 if (s->brushData.blend) {
1925 // Compose polygon fill..,
1926 ensureOutlineMapper();
1927 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1928 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1929 d->outlineMapper->moveTo(*points);
1930 const QPoint *p = points;
1931 const QPoint *ep = points + pointCount - 1;
1933 d->outlineMapper->lineTo(*(++p));
1935 d->outlineMapper->endOutline();
1938 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1940 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1941 d->outlineMapper->setCoordinateRounding(false);
1945 // Do the outline...
1946 if (s->penData.blend) {
1947 int count = pointCount * 2;
1948 QVarLengthArray<qreal> fpoints(count);
1949 for (int i=0; i<count; ++i)
1950 fpoints[i] = ((int *) points)[i];
1951 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1953 if (s->flags.fast_pen) {
1954 QCosmeticStroker stroker(s, d->deviceRect);
1955 stroker.drawPath(vp);
1957 QPaintEngineEx::stroke(vp, s->lastPen);
1965 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1967 #ifdef QT_DEBUG_DRAW
1968 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1971 QPlatformPixmap *pd = pixmap.handle();
1972 if (pd->classId() == QPlatformPixmap::RasterClass) {
1973 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
1974 if (image.depth() == 1) {
1975 Q_D(QRasterPaintEngine);
1976 QRasterPaintEngineState *s = state();
1977 if (s->matrix.type() <= QTransform::TxTranslate) {
1979 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1981 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
1984 QRasterPaintEngine::drawImage(pos, image);
1987 const QImage image = pixmap.toImage();
1988 if (pixmap.depth() == 1) {
1989 Q_D(QRasterPaintEngine);
1990 QRasterPaintEngineState *s = state();
1991 if (s->matrix.type() <= QTransform::TxTranslate) {
1993 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
1995 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
1998 QRasterPaintEngine::drawImage(pos, image);
2006 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2008 #ifdef QT_DEBUG_DRAW
2009 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2012 QPlatformPixmap* pd = pixmap.handle();
2013 if (pd->classId() == QPlatformPixmap::RasterClass) {
2014 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2015 if (image.depth() == 1) {
2016 Q_D(QRasterPaintEngine);
2017 QRasterPaintEngineState *s = state();
2018 if (s->matrix.type() <= QTransform::TxTranslate
2019 && r.size() == sr.size()
2020 && r.size() == pixmap.size()) {
2022 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2025 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2028 drawImage(r, image, sr);
2031 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2032 const QImage image = pd->toImage(clippedSource);
2033 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2034 if (image.depth() == 1) {
2035 Q_D(QRasterPaintEngine);
2036 QRasterPaintEngineState *s = state();
2037 if (s->matrix.type() <= QTransform::TxTranslate
2038 && r.size() == sr.size()
2039 && r.size() == pixmap.size()) {
2041 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2044 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2047 drawImage(r, image, translatedSource);
2052 // assumes that rect has positive width and height
2053 static inline const QRect toRect_normalized(const QRectF &rect)
2055 const int x = qRound(rect.x());
2056 const int y = qRound(rect.y());
2057 const int w = int(rect.width() + qreal(0.5));
2058 const int h = int(rect.height() + qreal(0.5));
2060 return QRect(x, y, w, h);
2063 static inline int fast_ceil_positive(const qreal &v)
2065 const int iv = int(v);
2072 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2074 const int xmin = int(rect.x());
2075 const int xmax = int(fast_ceil_positive(rect.right()));
2076 const int ymin = int(rect.y());
2077 const int ymax = int(fast_ceil_positive(rect.bottom()));
2078 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2084 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2086 #ifdef QT_DEBUG_DRAW
2087 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2090 Q_D(QRasterPaintEngine);
2091 QRasterPaintEngineState *s = state();
2093 if (s->matrix.type() > QTransform::TxTranslate) {
2094 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2096 QRectF(0, 0, img.width(), img.height()));
2099 const QClipData *clip = d->clip();
2100 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2102 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2103 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2106 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2108 } else if (clip->hasRectClip) {
2109 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2117 d->image_filler.clip = clip;
2118 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2119 if (!d->image_filler.blend)
2121 d->image_filler.dx = -pt.x();
2122 d->image_filler.dy = -pt.y();
2123 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2125 fillRect_normalized(rr, &d->image_filler, d);
2130 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2132 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2143 inline RotationType qRotationType(const QTransform &transform)
2145 QTransform::TransformationType type = transform.type();
2147 if (type > QTransform::TxRotate)
2150 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2151 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2154 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2155 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2158 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2159 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2165 inline bool isPixelAligned(const QRectF &rect) {
2166 return QRectF(rect.toRect()) == rect;
2173 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2174 Qt::ImageConversionFlags)
2176 #ifdef QT_DEBUG_DRAW
2177 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2183 Q_D(QRasterPaintEngine);
2184 QRasterPaintEngineState *s = state();
2185 int sr_l = qFloor(sr.left());
2186 int sr_r = qCeil(sr.right()) - 1;
2187 int sr_t = qFloor(sr.top());
2188 int sr_b = qCeil(sr.bottom()) - 1;
2190 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2191 // as fillRect will apply the aliased coordinate delta we need to
2192 // subtract it here as we don't use it for image drawing
2193 QTransform old = s->matrix;
2194 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2196 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2197 QRgb color = img.pixel(sr_l, sr_t);
2198 switch (img.format()) {
2199 case QImage::Format_ARGB32_Premultiplied:
2200 case QImage::Format_ARGB8565_Premultiplied:
2201 case QImage::Format_ARGB6666_Premultiplied:
2202 case QImage::Format_ARGB8555_Premultiplied:
2203 case QImage::Format_ARGB4444_Premultiplied:
2204 // Combine premultiplied color with the opacity set on the painter.
2205 d->solid_color_filler.solid.color =
2206 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2207 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2210 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2214 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2215 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2219 d->solid_color_filler.clip = d->clip();
2220 d->solid_color_filler.adjustSpanMethods();
2221 fillRect(r, &d->solid_color_filler);
2227 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2229 const QClipData *clip = d->clip();
2231 if (s->matrix.type() > QTransform::TxTranslate
2233 && (!clip || clip->hasRectClip)
2234 && s->intOpacity == 256
2235 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2236 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2237 && d->rasterBuffer->format == img.format()
2238 && (d->rasterBuffer->format == QImage::Format_RGB16
2239 || d->rasterBuffer->format == QImage::Format_RGB32
2240 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2241 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2243 RotationType rotationType = qRotationType(s->matrix);
2245 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2246 QRectF transformedTargetRect = s->matrix.mapRect(r);
2248 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2249 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2251 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2252 if (clippedTransformedTargetRect.isNull())
2255 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2257 QRect clippedSourceRect
2258 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2259 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2261 uint dbpl = d->rasterBuffer->bytesPerLine();
2262 uint sbpl = img.bytesPerLine();
2264 uchar *dst = d->rasterBuffer->buffer();
2265 uint bpp = img.depth() >> 3;
2267 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2268 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2270 uint cw = clippedSourceRect.width();
2271 uint ch = clippedSourceRect.height();
2273 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2280 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2282 QRectF targetBounds = s->matrix.mapRect(r);
2283 bool exceedsPrecision = targetBounds.width() > 0xffff
2284 || targetBounds.height() > 0xffff;
2286 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2287 if (s->matrix.type() > QTransform::TxScale) {
2288 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2289 if (func && (!clip || clip->hasRectClip)) {
2290 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2291 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2292 s->matrix, s->intOpacity);
2296 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2297 if (func && (!clip || clip->hasRectClip)) {
2298 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2299 img.bits(), img.bytesPerLine(),
2300 qt_mapRect_non_normalizing(r, s->matrix), sr,
2301 !clip ? d->deviceRect : clip->clipRect,
2308 QTransform copy = s->matrix;
2309 copy.translate(r.x(), r.y());
2311 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2312 copy.translate(-sr.x(), -sr.y());
2314 d->image_filler_xform.clip = clip;
2315 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2316 if (!d->image_filler_xform.blend)
2318 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2320 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2321 QRectF rr = s->matrix.mapRect(r);
2323 const int x1 = qRound(rr.x());
2324 const int y1 = qRound(rr.y());
2325 const int x2 = qRound(rr.right());
2326 const int y2 = qRound(rr.bottom());
2328 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2332 #ifdef QT_FAST_SPANS
2334 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2335 d->initializeRasterizer(&d->image_filler_xform);
2336 d->rasterizer->setAntialiased(s->flags.antialiased);
2338 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2340 const QRectF &rect = r.normalized();
2341 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2342 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2344 if (s->flags.tx_noshear)
2345 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2347 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2351 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2354 QTransform m = s->matrix;
2355 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2356 m.m21(), m.m22(), m.m23(),
2357 m.m31() - offs, m.m32() - offs, m.m33());
2358 fillPath(path, &d->image_filler_xform);
2361 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2362 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2364 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2366 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2368 } else if (clip->hasRectClip) {
2369 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2375 d->image_filler.clip = clip;
2376 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2377 if (!d->image_filler.blend)
2379 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2380 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2383 rr.translate(s->matrix.dx(), s->matrix.dy());
2385 const int x1 = qRound(rr.x());
2386 const int y1 = qRound(rr.y());
2387 const int x2 = qRound(rr.right());
2388 const int y2 = qRound(rr.bottom());
2390 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2397 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2399 #ifdef QT_DEBUG_DRAW
2400 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2402 Q_D(QRasterPaintEngine);
2403 QRasterPaintEngineState *s = state();
2407 QPlatformPixmap *pd = pixmap.handle();
2408 if (pd->classId() == QPlatformPixmap::RasterClass) {
2409 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2411 image = pixmap.toImage();
2414 if (image.depth() == 1)
2415 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2417 if (s->matrix.type() > QTransform::TxTranslate) {
2418 QTransform copy = s->matrix;
2419 copy.translate(r.x(), r.y());
2420 copy.translate(-sr.x(), -sr.y());
2421 d->image_filler_xform.clip = d->clip();
2422 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2423 if (!d->image_filler_xform.blend)
2425 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2427 #ifdef QT_FAST_SPANS
2429 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2430 d->initializeRasterizer(&d->image_filler_xform);
2431 d->rasterizer->setAntialiased(s->flags.antialiased);
2433 const QRectF &rect = r.normalized();
2434 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2435 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2436 if (s->flags.tx_noshear)
2437 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2439 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2445 fillPath(path, &d->image_filler_xform);
2447 d->image_filler.clip = d->clip();
2449 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2450 if (!d->image_filler.blend)
2452 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2453 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2456 rr.translate(s->matrix.dx(), s->matrix.dy());
2457 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2463 static inline bool monoVal(const uchar* s, int x)
2465 return (s[x>>3] << (x&7)) & 0x80;
2471 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2473 Q_D(QRasterPaintEngine);
2474 QRasterPaintEngineState *s = state();
2476 if (!s->penData.blend)
2479 QRasterBuffer *rb = d->rasterBuffer.data();
2481 const QRect rect(rx, ry, w, h);
2482 const QClipData *clip = d->clip();
2483 bool unclipped = false;
2485 // inlined QRect::intersects
2486 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2487 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2489 if (clip->hasRectClip) {
2490 unclipped = rx > clip->xmin
2491 && rx + w < clip->xmax
2493 && ry + h < clip->ymax;
2499 // inlined QRect::intersects
2500 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2501 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2505 // inlined QRect::contains
2506 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2507 && rect.top() >= 0 && rect.bottom() < rb->height();
2509 unclipped = contains && d->isUnclipped_normalized(rect);
2512 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2513 const uchar * scanline = static_cast<const uchar *>(src);
2515 if (s->flags.fast_text) {
2518 if (s->penData.bitmapBlit) {
2519 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2520 scanline, w, h, bpl);
2523 } else if (depth == 8) {
2524 if (s->penData.alphamapBlit) {
2525 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2526 scanline, w, h, bpl, 0);
2529 } else if (depth == 32) {
2530 // (A)RGB Alpha mask where the alpha component is not used.
2531 if (s->penData.alphaRGBBlit) {
2532 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2533 (const uint *) scanline, w, h, bpl / 4, 0);
2537 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2538 // (A)RGB Alpha mask where the alpha component is not used.
2540 int nx = qMax(0, rx);
2541 int ny = qMax(0, ry);
2543 // Move scanline pointer to compensate for moved x and y
2544 int xdiff = nx - rx;
2545 int ydiff = ny - ry;
2546 scanline += ydiff * bpl;
2547 scanline += xdiff * (depth == 32 ? 4 : 1);
2552 if (nx + w > d->rasterBuffer->width())
2553 w = d->rasterBuffer->width() - nx;
2554 if (ny + h > d->rasterBuffer->height())
2555 h = d->rasterBuffer->height() - ny;
2560 if (depth == 8 && s->penData.alphamapBlit) {
2561 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2562 scanline, w, h, bpl, clip);
2563 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2564 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2565 (const uint *) scanline, w, h, bpl / 4, clip);
2580 scanline += bpl * y0;
2584 w = qMin(w, rb->width() - qMax(0, rx));
2585 h = qMin(h, rb->height() - qMax(0, ry));
2587 if (w <= 0 || h <= 0)
2590 const int NSPANS = 256;
2591 QSpan spans[NSPANS];
2594 const int x1 = x0 + w;
2595 const int y1 = y0 + h;
2598 for (int y = y0; y < y1; ++y) {
2599 for (int x = x0; x < x1; ) {
2600 if (!monoVal(scanline, x)) {
2605 if (current == NSPANS) {
2606 blend(current, spans, &s->penData);
2609 spans[current].x = x + rx;
2610 spans[current].y = y + ry;
2611 spans[current].coverage = 255;
2614 // extend span until we find a different one.
2615 while (x < x1 && monoVal(scanline, x)) {
2619 spans[current].len = len;
2624 } else if (depth == 8) {
2625 for (int y = y0; y < y1; ++y) {
2626 for (int x = x0; x < x1; ) {
2627 // Skip those with 0 coverage
2628 if (scanline[x] == 0) {
2633 if (current == NSPANS) {
2634 blend(current, spans, &s->penData);
2637 int coverage = scanline[x];
2638 spans[current].x = x + rx;
2639 spans[current].y = y + ry;
2640 spans[current].coverage = coverage;
2644 // extend span until we find a different one.
2645 while (x < x1 && scanline[x] == coverage) {
2649 spans[current].len = len;
2654 } else { // 32-bit alpha...
2655 uint *sl = (uint *) src;
2656 for (int y = y0; y < y1; ++y) {
2657 for (int x = x0; x < x1; ) {
2658 // Skip those with 0 coverage
2659 if ((sl[x] & 0x00ffffff) == 0) {
2664 if (current == NSPANS) {
2665 blend(current, spans, &s->penData);
2668 uint rgbCoverage = sl[x];
2669 int coverage = qGreen(rgbCoverage);
2670 spans[current].x = x + rx;
2671 spans[current].y = y + ry;
2672 spans[current].coverage = coverage;
2676 // extend span until we find a different one.
2677 while (x < x1 && sl[x] == rgbCoverage) {
2681 spans[current].len = len;
2684 sl += bpl / sizeof(uint);
2687 // qDebug() << "alphaPenBlt: num spans=" << current
2688 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2689 // Call span func for current set of spans.
2691 blend(current, spans, &s->penData);
2694 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2695 const QFixedPoint *positions, QFontEngine *fontEngine)
2697 Q_D(QRasterPaintEngine);
2698 QRasterPaintEngineState *s = state();
2700 if (fontEngine->hasInternalCaching()) {
2701 QFontEngine::GlyphFormat neededFormat =
2702 painter()->device()->devType() == QInternal::Widget
2703 ? QFontEngine::Format_None
2704 : QFontEngine::Format_A8;
2706 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2707 neededFormat = QFontEngine::Format_Mono;
2709 for (int i = 0; i < numGlyphs; i++) {
2710 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2713 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2715 if (alphaMap == 0 || alphaMap->isNull())
2718 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2719 qFloor(positions[i].x) + offset.x(),
2720 qFloor(positions[i].y) + offset.y(),
2721 alphaMap->width(), alphaMap->height());
2723 fontEngine->unlockAlphaMapForGlyph();
2727 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2729 QImageTextureGlyphCache *cache =
2730 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2732 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2733 fontEngine->setGlyphCache(0, cache);
2736 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2737 cache->fillInPendingGlyphs();
2739 const QImage &image = cache->image();
2740 int bpl = image.bytesPerLine();
2742 int depth = image.depth();
2746 leftShift = 2; // multiply by 4
2747 else if (depth == 1)
2748 rightShift = 3; // divide by 8
2750 int margin = cache->glyphMargin();
2751 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2752 const uchar *bits = image.bits();
2753 for (int i=0; i<numGlyphs; ++i) {
2755 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2756 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2757 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2761 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2762 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2764 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2767 // c.baseLineX, c.baseLineY,
2770 // positions[i].x.toInt(), positions[i].y.toInt());
2772 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2780 * Returns true if the rectangle is completely within the current clip
2781 * state of the paint engine.
2783 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2785 const QClipData *cl = clip();
2787 // inline contains() for performance (we know the rects are normalized)
2788 const QRect &r1 = deviceRect;
2789 return (r.left() >= r1.left() && r.right() <= r1.right()
2790 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2794 if (cl->hasRectClip) {
2795 // currently all painting functions clips to deviceRect internally
2796 if (cl->clipRect == deviceRect)
2799 // inline contains() for performance (we know the rects are normalized)
2800 const QRect &r1 = cl->clipRect;
2801 return (r.left() >= r1.left() && r.right() <= r1.right()
2802 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2804 return qt_region_strictContains(cl->clipRegion, r);
2808 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2811 Q_Q(const QRasterPaintEngine);
2812 const QRasterPaintEngineState *s = q->state();
2813 const QClipData *cl = clip();
2815 QRect r = rect.normalized();
2816 // inline contains() for performance (we know the rects are normalized)
2817 const QRect &r1 = deviceRect;
2818 return (r.left() >= r1.left() && r.right() <= r1.right()
2819 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2823 // currently all painting functions that call this function clip to deviceRect internally
2824 if (cl->hasRectClip && cl->clipRect == deviceRect)
2827 if (s->flags.antialiased)
2830 QRect r = rect.normalized();
2832 r.setX(r.x() - penWidth);
2833 r.setY(r.y() - penWidth);
2834 r.setWidth(r.width() + 2 * penWidth);
2835 r.setHeight(r.height() + 2 * penWidth);
2838 if (cl->hasRectClip) {
2839 // inline contains() for performance (we know the rects are normalized)
2840 const QRect &r1 = cl->clipRect;
2841 return (r.left() >= r1.left() && r.right() <= r1.right()
2842 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2844 return qt_region_strictContains(cl->clipRegion, r);
2848 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2851 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2855 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2856 const QSpanData *data) const
2858 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2862 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2863 const QSpanData *data) const
2865 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2869 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2870 const QSpanData *data) const
2872 Q_Q(const QRasterPaintEngine);
2873 const QRasterPaintEngineState *s = q->state();
2875 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2877 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2878 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2884 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2889 QFontEngine *fontEngine = textItem->fontEngine();
2890 if (shouldDrawCachedGlyphs(fontEngine->fontDef.pixelSize, state()->matrix)) {
2891 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2894 QPaintEngineEx::drawStaticTextItem(textItem);
2901 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
2903 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
2904 QRasterPaintEngineState *s = state();
2906 #ifdef QT_DEBUG_DRAW
2907 Q_D(QRasterPaintEngine);
2908 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
2909 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
2917 if (!supportsTransformations(ti.fontEngine)) {
2918 QVarLengthArray<QFixedPoint> positions;
2919 QVarLengthArray<glyph_t> glyphs;
2921 QTransform matrix = s->matrix;
2922 matrix.translate(p.x(), p.y());
2924 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2926 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
2931 QPaintEngineEx::drawTextItem(p, ti);
2937 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
2939 Q_D(QRasterPaintEngine);
2940 QRasterPaintEngineState *s = state();
2943 if (!s->penData.blend)
2946 if (!s->flags.fast_pen) {
2947 QPaintEngineEx::drawPoints(points, pointCount);
2951 QCosmeticStroker stroker(s, d->deviceRect);
2952 stroker.drawPoints(points, pointCount);
2956 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
2958 Q_D(QRasterPaintEngine);
2959 QRasterPaintEngineState *s = state();
2962 if (!s->penData.blend)
2965 if (!s->flags.fast_pen) {
2966 QPaintEngineEx::drawPoints(points, pointCount);
2970 QCosmeticStroker stroker(s, d->deviceRect);
2971 stroker.drawPoints(points, pointCount);
2977 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
2979 #ifdef QT_DEBUG_DRAW
2980 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
2982 Q_D(QRasterPaintEngine);
2983 QRasterPaintEngineState *s = state();
2986 if (!s->penData.blend)
2989 if (s->flags.fast_pen) {
2990 QCosmeticStroker stroker(s, d->deviceRect);
2991 for (int i=0; i<lineCount; ++i) {
2992 const QLine &l = lines[i];
2993 stroker.drawLine(l.p1(), l.p2());
2996 QPaintEngineEx::drawLines(lines, lineCount);
3000 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3006 Q_Q(QRasterPaintEngine);
3007 QRasterPaintEngineState *s = q->state();
3009 const QPen &pen = s->lastPen;
3010 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3011 const QVector<qreal> pattern = pen.dashPattern();
3013 qreal patternLength = 0;
3014 for (int i = 0; i < pattern.size(); ++i)
3015 patternLength += pattern.at(i);
3017 if (patternLength <= 0)
3020 qreal length = line.length();
3021 Q_ASSERT(length > 0);
3022 while (length > 0) {
3023 const bool rasterize = *inDash;
3024 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3027 if (dash >= length) {
3029 *dashOffset += dash / width;
3033 *inDash = !(*inDash);
3034 if (++*dashIndex >= pattern.size())
3041 if (rasterize && dash > 0)
3042 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3049 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3051 #ifdef QT_DEBUG_DRAW
3052 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3054 Q_D(QRasterPaintEngine);
3055 QRasterPaintEngineState *s = state();
3058 if (!s->penData.blend)
3060 if (s->flags.fast_pen) {
3061 QCosmeticStroker stroker(s, d->deviceRect);
3062 for (int i=0; i<lineCount; ++i) {
3063 QLineF line = lines[i];
3064 stroker.drawLine(line.p1(), line.p2());
3067 QPaintEngineEx::drawLines(lines, lineCount);
3075 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3077 Q_D(QRasterPaintEngine);
3078 QRasterPaintEngineState *s = state();
3081 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3082 || (qpen_style(s->lastPen) == Qt::NoPen))
3083 && !s->flags.antialiased
3084 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3086 && s->matrix.type() <= QTransform::TxScale) // no shear
3089 const QRectF r = s->matrix.mapRect(rect);
3090 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3091 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3092 const QRect brect = QRect(int(r.x()), int(r.y()),
3093 int_dim(r.x(), r.width()),
3094 int_dim(r.y(), r.height()));
3096 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3097 &s->penData, &s->brushData);
3101 QPaintEngineEx::drawEllipse(rect);
3112 void QRasterPaintEngine::setDC(HDC hdc) {
3113 Q_D(QRasterPaintEngine);
3120 HDC QRasterPaintEngine::getDC() const
3122 Q_D(const QRasterPaintEngine);
3129 void QRasterPaintEngine::releaseDC(HDC) const
3135 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3137 const QTransform &m = state()->matrix;
3138 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3141 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3143 if (m.type() >= QTransform::TxProject)
3146 return !shouldDrawCachedGlyphs(pixelSize, m);
3152 QPoint QRasterPaintEngine::coordinateOffset() const
3154 return QPoint(0, 0);
3157 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3162 Q_D(QRasterPaintEngine);
3164 Q_ASSERT(image.depth() == 1);
3166 const int spanCount = 256;
3167 QT_FT_Span spans[spanCount];
3171 int w = image.width();
3172 int h = image.height();
3173 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3174 int ymin = qMax(qRound(pos.y()), 0);
3175 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3176 int xmin = qMax(qRound(pos.x()), 0);
3178 int x_offset = xmin - qRound(pos.x());
3180 QImage::Format format = image.format();
3181 for (int y = ymin; y < ymax; ++y) {
3182 const uchar *src = image.scanLine(y - qRound(pos.y()));
3183 if (format == QImage::Format_MonoLSB) {
3184 for (int x = 0; x < xmax - xmin; ++x) {
3185 int src_x = x + x_offset;
3186 uchar pixel = src[src_x >> 3];
3191 if (pixel & (0x1 << (src_x & 7))) {
3192 spans[n].x = xmin + x;
3194 spans[n].coverage = 255;
3196 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3200 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3203 if (n == spanCount) {
3204 fg->blend(n, spans, fg);
3210 for (int x = 0; x < xmax - xmin; ++x) {
3211 int src_x = x + x_offset;
3212 uchar pixel = src[src_x >> 3];
3217 if (pixel & (0x80 >> (x & 7))) {
3218 spans[n].x = xmin + x;
3220 spans[n].coverage = 255;
3222 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3226 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3229 if (n == spanCount) {
3230 fg->blend(n, spans, fg);
3238 fg->blend(n, spans, fg);
3244 \enum QRasterPaintEngine::ClipType
3247 \value RectClip Indicates that the currently set clip is a single rectangle.
3248 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3253 Returns the type of the clip currently set.
3255 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3257 Q_D(const QRasterPaintEngine);
3259 const QClipData *clip = d->clip();
3260 if (!clip || clip->hasRectClip)
3268 Returns the bounding rect of the currently set clip.
3270 QRect QRasterPaintEngine::clipBoundingRect() const
3272 Q_D(const QRasterPaintEngine);
3274 const QClipData *clip = d->clip();
3277 return d->deviceRect;
3279 if (clip->hasRectClip)
3280 return clip->clipRect;
3282 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3285 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3287 Q_Q(QRasterPaintEngine);
3288 QRasterPaintEngineState *s = q->state();
3290 rasterizer->setAntialiased(s->flags.antialiased);
3292 QRect clipRect(deviceRect);
3294 // ### get from optimized rectbased QClipData
3296 const QClipData *c = clip();
3298 const QRect r(QPoint(c->xmin, c->ymin),
3299 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3300 clipRect = clipRect.intersected(r);
3301 blend = data->blend;
3303 blend = data->unclipped_blend;
3306 rasterizer->setClipRect(clipRect);
3307 rasterizer->initialize(blend, data);
3310 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3311 ProcessSpans callback,
3312 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3314 if (!callback || !outline)
3317 Q_Q(QRasterPaintEngine);
3318 QRasterPaintEngineState *s = q->state();
3320 if (!s->flags.antialiased) {
3321 initializeRasterizer(spanData);
3323 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3327 rasterizer->rasterize(outline, fillRule);
3331 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3335 int q_gray_rendered_spans(QT_FT_Raster raster);
3338 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3340 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3343 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3344 ProcessSpans callback,
3345 void *userData, QRasterBuffer *)
3347 if (!callback || !outline)
3350 Q_Q(QRasterPaintEngine);
3351 QRasterPaintEngineState *s = q->state();
3353 if (!s->flags.antialiased) {
3354 rasterizer->setAntialiased(s->flags.antialiased);
3355 rasterizer->setClipRect(deviceRect);
3356 rasterizer->initialize(callback, userData);
3358 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3362 rasterizer->rasterize(outline, fillRule);
3366 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3367 // minimize memory reallocations. However if initial size for
3368 // raster pool is changed for lower value, reallocations will
3370 int rasterPoolSize = MINIMUM_POOL_SIZE;
3371 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3372 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3373 uchar *rasterPoolOnHeap = 0;
3375 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3377 void *data = userData;
3379 QT_FT_BBox clip_box = { deviceRect.x(),
3381 deviceRect.x() + deviceRect.width(),
3382 deviceRect.y() + deviceRect.height() };
3384 QT_FT_Raster_Params rasterParams;
3385 rasterParams.target = 0;
3386 rasterParams.source = outline;
3387 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3388 rasterParams.gray_spans = 0;
3389 rasterParams.black_spans = 0;
3390 rasterParams.bit_test = 0;
3391 rasterParams.bit_set = 0;
3392 rasterParams.user = data;
3393 rasterParams.clip_box = clip_box;
3398 int rendered_spans = 0;
3402 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3403 rasterParams.gray_spans = callback;
3404 rasterParams.skip_spans = rendered_spans;
3405 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3407 // Out of memory, reallocate some more and try again...
3408 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3409 rasterPoolSize *= 2;
3410 if (rasterPoolSize > 1024 * 1024) {
3411 qWarning("QPainter: Rasterization of primitive failed");
3415 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3417 free(rasterPoolOnHeap);
3418 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3420 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3422 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3424 qt_ft_grays_raster.raster_done(*grayRaster.data());
3425 qt_ft_grays_raster.raster_new(grayRaster.data());
3426 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3432 free(rasterPoolOnHeap);
3435 void QRasterPaintEnginePrivate::recalculateFastImages()
3437 Q_Q(QRasterPaintEngine);
3438 QRasterPaintEngineState *s = q->state();
3440 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3441 && s->matrix.type() <= QTransform::TxShear;
3444 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3446 Q_Q(const QRasterPaintEngine);
3447 const QRasterPaintEngineState *s = q->state();
3449 return s->flags.fast_images
3450 && (mode == QPainter::CompositionMode_SourceOver
3451 || (mode == QPainter::CompositionMode_Source
3452 && !image.hasAlphaChannel()));
3455 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3457 Q_ASSERT(image.depth() == 1);
3459 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3460 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3462 QRgb fg = PREMUL(color.rgba());
3465 int height = sourceImage.height();
3466 int width = sourceImage.width();
3467 for (int y=0; y<height; ++y) {
3468 uchar *source = sourceImage.scanLine(y);
3469 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3470 if (!source || !target)
3471 QT_THROW(std::bad_alloc()); // we must have run out of memory
3472 for (int x=0; x < width; ++x)
3473 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3478 QRasterBuffer::~QRasterBuffer()
3482 void QRasterBuffer::init()
3484 compositionMode = QPainter::CompositionMode_SourceOver;
3485 monoDestinationWithClut = false;
3490 QImage::Format QRasterBuffer::prepare(QImage *image)
3492 m_buffer = (uchar *)image->bits();
3493 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3494 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3495 bytes_per_pixel = image->depth()/8;
3496 bytes_per_line = image->bytesPerLine();
3498 format = image->format();
3499 drawHelper = qDrawHelper + format;
3500 if (image->depth() == 1 && image->colorTable().size() == 2) {
3501 monoDestinationWithClut = true;
3502 destColor0 = PREMUL(image->colorTable()[0]);
3503 destColor1 = PREMUL(image->colorTable()[1]);
3509 void QRasterBuffer::resetBuffer(int val)
3511 memset(m_buffer, val, m_height*bytes_per_line);
3514 QClipData::QClipData(int height)
3516 clipSpanHeight = height;
3521 xmin = xmax = ymin = ymax = 0;
3525 hasRectClip = hasRegionClip = false;
3528 QClipData::~QClipData()
3536 void QClipData::initialize()
3542 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3544 Q_CHECK_PTR(m_clipLines);
3546 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3547 allocated = clipSpanHeight;
3548 Q_CHECK_PTR(m_spans);
3554 m_clipLines[y].spans = 0;
3555 m_clipLines[y].count = 0;
3559 const int len = clipRect.width();
3562 QSpan *span = m_spans + count;
3566 span->coverage = 255;
3569 m_clipLines[y].spans = span;
3570 m_clipLines[y].count = 1;
3574 while (y < clipSpanHeight) {
3575 m_clipLines[y].spans = 0;
3576 m_clipLines[y].count = 0;
3579 } else if (hasRegionClip) {
3581 const QVector<QRect> rects = clipRegion.rects();
3582 const int numRects = rects.size();
3585 const int maxSpans = (ymax - ymin) * numRects;
3586 if (maxSpans > allocated) {
3587 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3588 allocated = maxSpans;
3593 int firstInBand = 0;
3595 while (firstInBand < numRects) {
3596 const int currMinY = rects.at(firstInBand).y();
3597 const int currMaxY = currMinY + rects.at(firstInBand).height();
3599 while (y < currMinY) {
3600 m_clipLines[y].spans = 0;
3601 m_clipLines[y].count = 0;
3605 int lastInBand = firstInBand;
3606 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3609 while (y < currMaxY) {
3611 m_clipLines[y].spans = m_spans + count;
3612 m_clipLines[y].count = lastInBand - firstInBand + 1;
3614 for (int r = firstInBand; r <= lastInBand; ++r) {
3615 const QRect &currRect = rects.at(r);
3616 QSpan *span = m_spans + count;
3617 span->x = currRect.x();
3618 span->len = currRect.width();
3620 span->coverage = 255;
3626 firstInBand = lastInBand + 1;
3629 Q_ASSERT(count <= allocated);
3631 while (y < clipSpanHeight) {
3632 m_clipLines[y].spans = 0;
3633 m_clipLines[y].count = 0;
3639 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3644 free(m_clipLines); // same for clipLines
3650 void QClipData::fixup()
3655 ymin = ymax = xmin = xmax = 0;
3660 ymin = m_spans[0].y;
3661 ymax = m_spans[count-1].y + 1;
3665 const int firstLeft = m_spans[0].x;
3666 const int firstRight = m_spans[0].x + m_spans[0].len;
3669 for (int i = 0; i < count; ++i) {
3670 QT_FT_Span_& span = m_spans[i];
3673 if (span.y != y + 1 && y != -1)
3676 m_clipLines[y].spans = &span;
3677 m_clipLines[y].count = 1;
3679 ++m_clipLines[y].count;
3681 const int spanLeft = span.x;
3682 const int spanRight = spanLeft + span.len;
3684 if (spanLeft < xmin)
3687 if (spanRight > xmax)
3690 if (spanLeft != firstLeft || spanRight != firstRight)
3696 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3701 Convert \a rect to clip spans.
3703 void QClipData::setClipRect(const QRect &rect)
3705 if (hasRectClip && rect == clipRect)
3708 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3710 hasRegionClip = false;
3714 xmax = rect.x() + rect.width();
3715 ymin = qMin(rect.y(), clipSpanHeight);
3716 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3723 // qDebug() << xmin << xmax << ymin << ymax;
3727 Convert \a region to clip spans.
3729 void QClipData::setClipRegion(const QRegion ®ion)
3731 if (region.rectCount() == 1) {
3732 setClipRect(region.rects().at(0));
3736 hasRegionClip = true;
3737 hasRectClip = false;
3738 clipRegion = region;
3740 { // set bounding rect
3741 const QRect rect = region.boundingRect();
3743 xmax = rect.x() + rect.width();
3745 ymax = rect.y() + rect.height();
3757 spans must be sorted on y
3759 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3760 const QSpan *spans, const QSpan *end,
3761 QSpan **outSpans, int available)
3763 const_cast<QClipData *>(clip)->initialize();
3765 QSpan *out = *outSpans;
3767 const QSpan *clipSpans = clip->m_spans + *currentClip;
3768 const QSpan *clipEnd = clip->m_spans + clip->count;
3770 while (available && spans < end ) {
3771 if (clipSpans >= clipEnd) {
3775 if (clipSpans->y > spans->y) {
3779 if (spans->y != clipSpans->y) {
3780 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3781 clipSpans = clip->m_clipLines[spans->y].spans;
3786 Q_ASSERT(spans->y == clipSpans->y);
3789 int sx2 = sx1 + spans->len;
3790 int cx1 = clipSpans->x;
3791 int cx2 = cx1 + clipSpans->len;
3793 if (cx1 < sx1 && cx2 < sx1) {
3796 } else if (sx1 < cx1 && sx2 < cx1) {
3800 int x = qMax(sx1, cx1);
3801 int len = qMin(sx2, cx2) - x;
3803 out->x = qMax(sx1, cx1);
3804 out->len = qMin(sx2, cx2) - out->x;
3806 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3818 *currentClip = clipSpans - clip->m_spans;
3822 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3824 // qDebug() << "qt_span_fill_clipped" << spanCount;
3825 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3827 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3829 const int NSPANS = 256;
3830 QSpan cspans[NSPANS];
3831 int currentClip = 0;
3832 const QSpan *end = spans + spanCount;
3833 while (spans < end) {
3834 QSpan *clipped = cspans;
3835 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3836 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3837 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3839 if (clipped - cspans)
3840 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3846 Clip spans to \a{clip}-rectangle.
3847 Returns number of unclipped spans
3849 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3852 const short minx = clip.left();
3853 const short miny = clip.top();
3854 const short maxx = clip.right();
3855 const short maxy = clip.bottom();
3858 for (int i = 0; i < numSpans; ++i) {
3859 if (spans[i].y > maxy)
3861 if (spans[i].y < miny
3862 || spans[i].x > maxx
3863 || spans[i].x + spans[i].len <= minx) {
3866 if (spans[i].x < minx) {
3867 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
3870 spans[n].x = spans[i].x;
3871 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
3873 if (spans[n].len == 0)
3875 spans[n].y = spans[i].y;
3876 spans[n].coverage = spans[i].coverage;
3883 static void qt_span_fill_clipRect(int count, const QSpan *spans,
3886 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3887 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3889 Q_ASSERT(fillData->clip);
3890 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
3892 // hw: check if this const_cast<> is safe!!!
3893 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
3894 fillData->clip->clipRect);
3896 fillData->unclipped_blend(count, spans, fillData);
3899 static void qt_span_clip(int count, const QSpan *spans, void *userData)
3901 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
3903 // qDebug() << " qt_span_clip: " << count << clipData->operation;
3904 // for (int i = 0; i < qMin(count, 10); ++i) {
3905 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
3908 switch (clipData->operation) {
3910 case Qt::IntersectClip:
3912 QClipData *newClip = clipData->newClip;
3913 newClip->initialize();
3915 int currentClip = 0;
3916 const QSpan *end = spans + count;
3917 while (spans < end) {
3918 QSpan *newspans = newClip->m_spans + newClip->count;
3919 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
3920 &newspans, newClip->allocated - newClip->count);
3921 newClip->count = newspans - newClip->m_spans;
3923 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
3924 newClip->allocated *= 2;
3930 case Qt::ReplaceClip:
3931 clipData->newClip->appendSpans(spans, count);
3939 QImage QRasterBuffer::bufferImage() const
3941 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
3943 for (int y = 0; y < m_height; ++y) {
3944 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
3946 for (int x=0; x<m_width; ++x) {
3947 uint argb = span[x];
3948 image.setPixel(x, y, argb);
3956 void QRasterBuffer::flushToARGBImage(QImage *target) const
3958 int w = qMin(m_width, target->width());
3959 int h = qMin(m_height, target->height());
3961 for (int y=0; y<h; ++y) {
3962 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
3963 QRgb *dest = (QRgb *) target->scanLine(y);
3964 for (int x=0; x<w; ++x) {
3965 QRgb pixel = sourceLine[x];
3966 int alpha = qAlpha(pixel);
3970 dest[x] = (alpha << 24)
3971 | ((255*qRed(pixel)/alpha) << 16)
3972 | ((255*qGreen(pixel)/alpha) << 8)
3973 | ((255*qBlue(pixel)/alpha) << 0);
3980 class QGradientCache
3984 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
3985 stops(s), opacity(op), interpolationMode(mode) {}
3986 uint buffer[GRADIENT_STOPTABLE_SIZE];
3987 QGradientStops stops;
3989 QGradient::InterpolationMode interpolationMode;
3992 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
3995 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
3996 quint64 hash_val = 0;
3998 QGradientStops stops = gradient.stops();
3999 for (int i = 0; i < stops.size() && i <= 2; i++)
4000 hash_val += stops[i].second.rgba();
4002 QMutexLocker lock(&mutex);
4003 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4005 if (it == cache.constEnd())
4006 return addCacheElement(hash_val, gradient, opacity);
4009 const CacheInfo &cache_info = it.value();
4010 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4011 return cache_info.buffer;
4013 } while (it != cache.constEnd() && it.key() == hash_val);
4014 // an exact match for these stops and opacity was not found, create new cache
4015 return addCacheElement(hash_val, gradient, opacity);
4019 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4021 inline int maxCacheSize() const { return 60; }
4022 inline void generateGradientColorTable(const QGradient& g,
4024 int size, int opacity) const;
4025 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4026 if (cache.size() == maxCacheSize()) {
4027 // may remove more than 1, but OK
4028 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4030 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4031 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4032 return cache.insert(hash_val, cache_entry).value().buffer;
4035 QGradientColorTableHash cache;
4039 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4041 QGradientStops stops = gradient.stops();
4042 int stopCount = stops.count();
4043 Q_ASSERT(stopCount > 0);
4045 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4047 if (stopCount == 2) {
4048 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4049 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4051 qreal first_stop = stops[0].first;
4052 qreal second_stop = stops[1].first;
4054 if (second_stop < first_stop) {
4055 qSwap(first_color, second_color);
4056 qSwap(first_stop, second_stop);
4059 if (colorInterpolation) {
4060 first_color = PREMUL(first_color);
4061 second_color = PREMUL(second_color);
4064 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4065 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4067 uint red_first = qRed(first_color) << 16;
4068 uint green_first = qGreen(first_color) << 16;
4069 uint blue_first = qBlue(first_color) << 16;
4070 uint alpha_first = qAlpha(first_color) << 16;
4072 uint red_second = qRed(second_color) << 16;
4073 uint green_second = qGreen(second_color) << 16;
4074 uint blue_second = qBlue(second_color) << 16;
4075 uint alpha_second = qAlpha(second_color) << 16;
4078 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4079 if (colorInterpolation)
4080 colorTable[i] = first_color;
4082 colorTable[i] = PREMUL(first_color);
4085 if (i < second_index) {
4086 qreal reciprocal = qreal(1) / (second_index - first_index);
4088 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4089 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4090 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4091 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4094 red_first += 1 << 15;
4095 green_first += 1 << 15;
4096 blue_first += 1 << 15;
4097 alpha_first += 1 << 15;
4099 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4100 red_first += red_delta;
4101 green_first += green_delta;
4102 blue_first += blue_delta;
4103 alpha_first += alpha_delta;
4105 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4106 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4108 if (colorInterpolation)
4109 colorTable[i] = color;
4111 colorTable[i] = PREMUL(color);
4115 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4116 if (colorInterpolation)
4117 colorTable[i] = second_color;
4119 colorTable[i] = PREMUL(second_color);
4125 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4126 if (stopCount == 1) {
4127 current_color = PREMUL(current_color);
4128 for (int i = 0; i < size; ++i)
4129 colorTable[i] = current_color;
4133 // The position where the gradient begins and ends
4134 qreal begin_pos = stops[0].first;
4135 qreal end_pos = stops[stopCount-1].first;
4137 int pos = 0; // The position in the color table.
4140 qreal incr = 1 / qreal(size); // the double increment.
4141 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4143 // Up to first point
4144 colorTable[pos++] = PREMUL(current_color);
4145 while (dpos <= begin_pos) {
4146 colorTable[pos] = colorTable[pos - 1];
4151 int current_stop = 0; // We always interpolate between current and current + 1.
4153 qreal t; // position between current left and right stops
4154 qreal t_delta; // the t increment per entry in the color table
4156 if (dpos < end_pos) {
4158 while (dpos > stops[current_stop+1].first)
4161 if (current_stop != 0)
4162 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4163 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4165 if (colorInterpolation) {
4166 current_color = PREMUL(current_color);
4167 next_color = PREMUL(next_color);
4170 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4171 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4172 t = (dpos - stops[current_stop].first) * c;
4176 Q_ASSERT(current_stop < stopCount);
4178 int dist = qRound(t);
4179 int idist = 256 - dist;
4181 if (colorInterpolation)
4182 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4184 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4189 if (dpos >= end_pos)
4195 while (dpos > stops[current_stop+skip+1].first)
4199 current_stop += skip;
4201 current_color = next_color;
4203 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4204 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4206 if (colorInterpolation) {
4208 current_color = PREMUL(current_color);
4209 next_color = PREMUL(next_color);
4212 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4213 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4214 t = (dpos - stops[current_stop].first) * c;
4221 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4222 while (pos < size - 1) {
4223 colorTable[pos] = current_color;
4227 // Make sure the last color stop is represented at the end of the table
4228 colorTable[size - 1] = current_color;
4231 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4234 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4240 m11 = m22 = m33 = 1.;
4241 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4242 clip = pe ? pe->d_func()->clip() : 0;
4245 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4247 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4249 Qt::BrushStyle brushStyle = qbrush_style(brush);
4250 switch (brushStyle) {
4251 case Qt::SolidPattern: {
4253 QColor c = qbrush_color(brush);
4254 QRgb rgba = c.rgba();
4255 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4256 if ((solid.color & 0xff000000) == 0
4257 && compositionMode == QPainter::CompositionMode_SourceOver) {
4263 case Qt::LinearGradientPattern:
4265 type = LinearGradient;
4266 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4267 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4268 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4269 gradient.spread = g->spread();
4271 QLinearGradientData &linearData = gradient.linear;
4273 linearData.origin.x = g->start().x();
4274 linearData.origin.y = g->start().y();
4275 linearData.end.x = g->finalStop().x();
4276 linearData.end.y = g->finalStop().y();
4280 case Qt::RadialGradientPattern:
4282 type = RadialGradient;
4283 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4284 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4285 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4286 gradient.spread = g->spread();
4288 QRadialGradientData &radialData = gradient.radial;
4290 QPointF center = g->center();
4291 radialData.center.x = center.x();
4292 radialData.center.y = center.y();
4293 radialData.center.radius = g->centerRadius();
4294 QPointF focal = g->focalPoint();
4295 radialData.focal.x = focal.x();
4296 radialData.focal.y = focal.y();
4297 radialData.focal.radius = g->focalRadius();
4301 case Qt::ConicalGradientPattern:
4303 type = ConicalGradient;
4304 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4305 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4306 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4307 gradient.spread = QGradient::RepeatSpread;
4309 QConicalGradientData &conicalData = gradient.conical;
4311 QPointF center = g->center();
4312 conicalData.center.x = center.x();
4313 conicalData.center.y = center.y();
4314 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4318 case Qt::Dense1Pattern:
4319 case Qt::Dense2Pattern:
4320 case Qt::Dense3Pattern:
4321 case Qt::Dense4Pattern:
4322 case Qt::Dense5Pattern:
4323 case Qt::Dense6Pattern:
4324 case Qt::Dense7Pattern:
4325 case Qt::HorPattern:
4326 case Qt::VerPattern:
4327 case Qt::CrossPattern:
4328 case Qt::BDiagPattern:
4329 case Qt::FDiagPattern:
4330 case Qt::DiagCrossPattern:
4333 tempImage = new QImage();
4334 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4335 initTexture(tempImage, alpha, QTextureData::Tiled);
4337 case Qt::TexturePattern:
4340 tempImage = new QImage();
4342 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4343 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4345 *tempImage = brush.textureImage();
4346 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4354 adjustSpanMethods();
4357 void QSpanData::adjustSpanMethods()
4367 unclipped_blend = 0;
4370 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4371 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4372 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4373 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4374 fillRect = rasterBuffer->drawHelper->fillRect;
4376 case LinearGradient:
4377 case RadialGradient:
4378 case ConicalGradient:
4379 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4382 unclipped_blend = qBlendTexture;
4383 if (!texture.imageData)
4384 unclipped_blend = 0;
4389 if (!unclipped_blend) {
4392 blend = unclipped_blend;
4393 } else if (clip->hasRectClip) {
4394 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4396 blend = qt_span_fill_clipped;
4400 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4403 // make sure we round off correctly in qdrawhelper.cpp
4404 delta.translate(1.0 / 65536, 1.0 / 65536);
4406 QTransform inv = (delta * matrix).inverted();
4419 const bool affine = !m13 && !m23;
4420 fast_matrix = affine
4421 && m11 * m11 + m21 * m21 < 1e4
4422 && m12 * m12 + m22 * m22 < 1e4
4426 adjustSpanMethods();
4429 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4431 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4433 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4434 if (!d || d->height == 0) {
4435 texture.imageData = 0;
4442 texture.bytesPerLine = 0;
4443 texture.format = QImage::Format_Invalid;
4444 texture.colorTable = 0;
4445 texture.hasAlpha = alpha != 256;
4447 texture.imageData = d->data;
4448 texture.width = d->width;
4449 texture.height = d->height;
4451 if (sourceRect.isNull()) {
4454 texture.x2 = texture.width;
4455 texture.y2 = texture.height;
4457 texture.x1 = sourceRect.x();
4458 texture.y1 = sourceRect.y();
4459 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4460 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4463 texture.bytesPerLine = d->bytes_per_line;
4465 texture.format = d->format;
4466 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4467 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4469 texture.const_alpha = alpha;
4470 texture.type = _type;
4472 adjustSpanMethods();
4477 \a x and \a y is relative to the midpoint of \a rect.
4479 static inline void drawEllipsePoints(int x, int y, int length,
4482 ProcessSpans pen_func, ProcessSpans brush_func,
4483 QSpanData *pen_data, QSpanData *brush_data)
4488 QT_FT_Span outline[4];
4489 const int midx = rect.x() + (rect.width() + 1) / 2;
4490 const int midy = rect.y() + (rect.height() + 1) / 2;
4496 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4497 outline[0].len = qMin(length, x - outline[0].x);
4499 outline[0].coverage = 255;
4503 outline[1].len = length;
4505 outline[1].coverage = 255;
4508 outline[2].x = outline[0].x;
4509 outline[2].len = outline[0].len;
4510 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4511 outline[2].coverage = 255;
4515 outline[3].len = length;
4516 outline[3].y = outline[2].y;
4517 outline[3].coverage = 255;
4519 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4523 fill[0].x = outline[0].x + outline[0].len - 1;
4524 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4525 fill[0].y = outline[1].y;
4526 fill[0].coverage = 255;
4529 fill[1].x = outline[2].x + outline[2].len - 1;
4530 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4531 fill[1].y = outline[3].y;
4532 fill[1].coverage = 255;
4534 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4535 n = qt_intersect_spans(fill, n, clip);
4537 brush_func(n, fill, brush_data);
4540 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4541 n = qt_intersect_spans(outline, n, clip);
4543 pen_func(n, outline, pen_data);
4549 Draws an ellipse using the integer point midpoint algorithm.
4551 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4552 ProcessSpans pen_func, ProcessSpans brush_func,
4553 QSpanData *pen_data, QSpanData *brush_data)
4555 const qreal a = qreal(rect.width()) / 2;
4556 const qreal b = qreal(rect.height()) / 2;
4557 qreal d = b*b - (a*a*b) + 0.25*a*a;
4560 int y = (rect.height() + 1) / 2;
4564 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4565 if (d < 0) { // select E
4568 } else { // select SE
4569 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4570 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4571 pen_func, brush_func, pen_data, brush_data);
4576 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4577 pen_func, brush_func, pen_data, brush_data);
4580 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4581 const int miny = rect.height() & 0x1;
4583 if (d < 0) { // select SE
4584 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4586 } else { // select S
4587 d += a*a*(-2*y + 3);
4590 drawEllipsePoints(x, y, 1, rect, clip,
4591 pen_func, brush_func, pen_data, brush_data);
4596 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4599 Draws the first \a pointCount points in the buffer \a points
4601 The default implementation converts the first \a pointCount QPoints in \a points
4602 to QPointFs and calls the floating point version of drawPoints.
4606 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4609 Reimplement this function to draw the largest ellipse that can be
4610 contained within rectangle \a rect.
4613 #ifdef QT_DEBUG_DRAW
4614 void dumpClip(int width, int height, const QClipData *clip)
4616 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4617 clipImg.fill(0xffff0000);
4624 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4626 for (int i = 0; i < clip->count; ++i) {
4627 const QSpan *span = ((QClipData *) clip)->spans() + i;
4628 for (int j = 0; j < span->len; ++j)
4629 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4630 x0 = qMin(x0, int(span->x));
4631 x1 = qMax(x1, int(span->x + span->len - 1));
4633 y0 = qMin(y0, int(span->y));
4634 y1 = qMax(y1, int(span->y));
4637 static int counter = 0;
4644 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4645 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));