1 /****************************************************************************
3 ** Copyright (C) 2012 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 ensureRasterState(); // 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::updateRasterState()
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);
1437 ensureRasterState();
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);
1492 ensureRasterState();
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);
1648 ensureRasterState();
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);
1705 ensureRasterState();
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
2333 ensureRasterState();
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
2428 ensureRasterState();
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;
2881 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2882 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2884 QFixed clipLeft = QFixed::fromReal(clip.left());
2885 QFixed clipRight = QFixed::fromReal(clip.right());
2886 QFixed clipTop = QFixed::fromReal(clip.top());
2887 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2890 while (first < numGlyphs) {
2891 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2892 QFixed left = metrics.x + positions[first].x;
2893 QFixed top = metrics.y + positions[first].y;
2894 QFixed right = left + metrics.width;
2895 QFixed bottom = top + metrics.height;
2896 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2900 int last = numGlyphs - 1;
2901 while (last > first) {
2902 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2903 QFixed left = metrics.x + positions[last].x;
2904 QFixed top = metrics.y + positions[last].y;
2905 QFixed right = left + metrics.width;
2906 QFixed bottom = top + metrics.height;
2907 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2911 return QPair<int, int>(first, last + 1);
2917 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2919 if (textItem->numGlyphs == 0)
2923 ensureRasterState();
2925 QFontEngine *fontEngine = textItem->fontEngine();
2926 if (shouldDrawCachedGlyphs(fontEngine->fontDef.pixelSize, state()->matrix)) {
2927 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2929 } else if (state()->matrix.type() < QTransform::TxProject) {
2931 QTransform invMat = state()->matrix.inverted(&invertible);
2935 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2936 textItem->fontEngine(), textItem->glyphs,
2937 textItem->glyphPositions, textItem->numGlyphs);
2938 QStaticTextItem copy = *textItem;
2939 copy.glyphs += range.first;
2940 copy.glyphPositions += range.first;
2941 copy.numGlyphs = range.second - range.first;
2942 QPaintEngineEx::drawStaticTextItem(©);
2944 QPaintEngineEx::drawStaticTextItem(textItem);
2951 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
2953 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
2955 #ifdef QT_DEBUG_DRAW
2956 Q_D(QRasterPaintEngine);
2957 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
2958 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
2962 if (ti.glyphs.numGlyphs == 0)
2965 ensureRasterState();
2967 QRasterPaintEngineState *s = state();
2968 QTransform matrix = s->matrix;
2970 if (!supportsTransformations(ti.fontEngine)) {
2971 QVarLengthArray<QFixedPoint> positions;
2972 QVarLengthArray<glyph_t> glyphs;
2974 matrix.translate(p.x(), p.y());
2975 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2977 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
2978 } else if (matrix.type() < QTransform::TxProject) {
2980 QTransform invMat = matrix.inverted(&invertible);
2984 QVarLengthArray<QFixedPoint> positions;
2985 QVarLengthArray<glyph_t> glyphs;
2987 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
2988 ti.flags, glyphs, positions);
2989 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2990 ti.fontEngine, glyphs.data(), positions.data(),
2993 if (range.first >= range.second)
2996 QStaticTextItem staticTextItem;
2997 staticTextItem.color = s->pen.color();
2998 staticTextItem.font = s->font;
2999 staticTextItem.setFontEngine(ti.fontEngine);
3000 staticTextItem.numGlyphs = range.second - range.first;
3001 staticTextItem.glyphs = glyphs.data() + range.first;
3002 staticTextItem.glyphPositions = positions.data() + range.first;
3003 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3005 QPaintEngineEx::drawTextItem(p, ti);
3012 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3014 Q_D(QRasterPaintEngine);
3015 QRasterPaintEngineState *s = state();
3018 if (!s->penData.blend)
3021 if (!s->flags.fast_pen) {
3022 QPaintEngineEx::drawPoints(points, pointCount);
3026 QCosmeticStroker stroker(s, d->deviceRect);
3027 stroker.drawPoints(points, pointCount);
3031 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3033 Q_D(QRasterPaintEngine);
3034 QRasterPaintEngineState *s = state();
3037 if (!s->penData.blend)
3040 if (!s->flags.fast_pen) {
3041 QPaintEngineEx::drawPoints(points, pointCount);
3045 QCosmeticStroker stroker(s, d->deviceRect);
3046 stroker.drawPoints(points, pointCount);
3052 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3054 #ifdef QT_DEBUG_DRAW
3055 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3057 Q_D(QRasterPaintEngine);
3058 QRasterPaintEngineState *s = state();
3061 if (!s->penData.blend)
3064 if (s->flags.fast_pen) {
3065 QCosmeticStroker stroker(s, d->deviceRect);
3066 for (int i=0; i<lineCount; ++i) {
3067 const QLine &l = lines[i];
3068 stroker.drawLine(l.p1(), l.p2());
3071 QPaintEngineEx::drawLines(lines, lineCount);
3075 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3081 Q_Q(QRasterPaintEngine);
3082 QRasterPaintEngineState *s = q->state();
3084 const QPen &pen = s->lastPen;
3085 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3086 const QVector<qreal> pattern = pen.dashPattern();
3088 qreal patternLength = 0;
3089 for (int i = 0; i < pattern.size(); ++i)
3090 patternLength += pattern.at(i);
3092 if (patternLength <= 0)
3095 qreal length = line.length();
3096 Q_ASSERT(length > 0);
3097 while (length > 0) {
3098 const bool rasterize = *inDash;
3099 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3102 if (dash >= length) {
3104 *dashOffset += dash / width;
3108 *inDash = !(*inDash);
3109 if (++*dashIndex >= pattern.size())
3116 if (rasterize && dash > 0)
3117 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3124 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3126 #ifdef QT_DEBUG_DRAW
3127 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3129 Q_D(QRasterPaintEngine);
3130 QRasterPaintEngineState *s = state();
3133 if (!s->penData.blend)
3135 if (s->flags.fast_pen) {
3136 QCosmeticStroker stroker(s, d->deviceRect);
3137 for (int i=0; i<lineCount; ++i) {
3138 QLineF line = lines[i];
3139 stroker.drawLine(line.p1(), line.p2());
3142 QPaintEngineEx::drawLines(lines, lineCount);
3150 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3152 Q_D(QRasterPaintEngine);
3153 QRasterPaintEngineState *s = state();
3156 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3157 || (qpen_style(s->lastPen) == Qt::NoPen))
3158 && !s->flags.antialiased
3159 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3161 && s->matrix.type() <= QTransform::TxScale) // no shear
3164 const QRectF r = s->matrix.mapRect(rect);
3165 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3166 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3167 const QRect brect = QRect(int(r.x()), int(r.y()),
3168 int_dim(r.x(), r.width()),
3169 int_dim(r.y(), r.height()));
3171 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3172 &s->penData, &s->brushData);
3176 QPaintEngineEx::drawEllipse(rect);
3187 void QRasterPaintEngine::setDC(HDC hdc) {
3188 Q_D(QRasterPaintEngine);
3195 HDC QRasterPaintEngine::getDC() const
3197 Q_D(const QRasterPaintEngine);
3204 void QRasterPaintEngine::releaseDC(HDC) const
3210 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3212 const QTransform &m = state()->matrix;
3213 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3216 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3218 if (m.type() >= QTransform::TxProject)
3221 return !shouldDrawCachedGlyphs(pixelSize, m);
3227 QPoint QRasterPaintEngine::coordinateOffset() const
3229 return QPoint(0, 0);
3232 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3237 Q_D(QRasterPaintEngine);
3239 Q_ASSERT(image.depth() == 1);
3241 const int spanCount = 256;
3242 QT_FT_Span spans[spanCount];
3246 int w = image.width();
3247 int h = image.height();
3248 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3249 int ymin = qMax(qRound(pos.y()), 0);
3250 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3251 int xmin = qMax(qRound(pos.x()), 0);
3253 int x_offset = xmin - qRound(pos.x());
3255 QImage::Format format = image.format();
3256 for (int y = ymin; y < ymax; ++y) {
3257 const uchar *src = image.scanLine(y - qRound(pos.y()));
3258 if (format == QImage::Format_MonoLSB) {
3259 for (int x = 0; x < xmax - xmin; ++x) {
3260 int src_x = x + x_offset;
3261 uchar pixel = src[src_x >> 3];
3266 if (pixel & (0x1 << (src_x & 7))) {
3267 spans[n].x = xmin + x;
3269 spans[n].coverage = 255;
3271 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3275 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3278 if (n == spanCount) {
3279 fg->blend(n, spans, fg);
3285 for (int x = 0; x < xmax - xmin; ++x) {
3286 int src_x = x + x_offset;
3287 uchar pixel = src[src_x >> 3];
3292 if (pixel & (0x80 >> (x & 7))) {
3293 spans[n].x = xmin + x;
3295 spans[n].coverage = 255;
3297 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3301 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3304 if (n == spanCount) {
3305 fg->blend(n, spans, fg);
3313 fg->blend(n, spans, fg);
3319 \enum QRasterPaintEngine::ClipType
3322 \value RectClip Indicates that the currently set clip is a single rectangle.
3323 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3328 Returns the type of the clip currently set.
3330 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3332 Q_D(const QRasterPaintEngine);
3334 const QClipData *clip = d->clip();
3335 if (!clip || clip->hasRectClip)
3343 Returns the bounding rect of the currently set clip.
3345 QRect QRasterPaintEngine::clipBoundingRect() const
3347 Q_D(const QRasterPaintEngine);
3349 const QClipData *clip = d->clip();
3352 return d->deviceRect;
3354 if (clip->hasRectClip)
3355 return clip->clipRect;
3357 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3360 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3362 Q_Q(QRasterPaintEngine);
3363 QRasterPaintEngineState *s = q->state();
3365 rasterizer->setAntialiased(s->flags.antialiased);
3367 QRect clipRect(deviceRect);
3369 // ### get from optimized rectbased QClipData
3371 const QClipData *c = clip();
3373 const QRect r(QPoint(c->xmin, c->ymin),
3374 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3375 clipRect = clipRect.intersected(r);
3376 blend = data->blend;
3378 blend = data->unclipped_blend;
3381 rasterizer->setClipRect(clipRect);
3382 rasterizer->initialize(blend, data);
3385 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3386 ProcessSpans callback,
3387 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3389 if (!callback || !outline)
3392 Q_Q(QRasterPaintEngine);
3393 QRasterPaintEngineState *s = q->state();
3395 if (!s->flags.antialiased) {
3396 initializeRasterizer(spanData);
3398 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3402 rasterizer->rasterize(outline, fillRule);
3406 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3410 int q_gray_rendered_spans(QT_FT_Raster raster);
3413 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3415 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3418 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3419 ProcessSpans callback,
3420 void *userData, QRasterBuffer *)
3422 if (!callback || !outline)
3425 Q_Q(QRasterPaintEngine);
3426 QRasterPaintEngineState *s = q->state();
3428 if (!s->flags.antialiased) {
3429 rasterizer->setAntialiased(s->flags.antialiased);
3430 rasterizer->setClipRect(deviceRect);
3431 rasterizer->initialize(callback, userData);
3433 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3437 rasterizer->rasterize(outline, fillRule);
3441 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3442 // minimize memory reallocations. However if initial size for
3443 // raster pool is changed for lower value, reallocations will
3445 int rasterPoolSize = MINIMUM_POOL_SIZE;
3446 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3447 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3448 uchar *rasterPoolOnHeap = 0;
3450 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3452 void *data = userData;
3454 QT_FT_BBox clip_box = { deviceRect.x(),
3456 deviceRect.x() + deviceRect.width(),
3457 deviceRect.y() + deviceRect.height() };
3459 QT_FT_Raster_Params rasterParams;
3460 rasterParams.target = 0;
3461 rasterParams.source = outline;
3462 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3463 rasterParams.gray_spans = 0;
3464 rasterParams.black_spans = 0;
3465 rasterParams.bit_test = 0;
3466 rasterParams.bit_set = 0;
3467 rasterParams.user = data;
3468 rasterParams.clip_box = clip_box;
3473 int rendered_spans = 0;
3477 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3478 rasterParams.gray_spans = callback;
3479 rasterParams.skip_spans = rendered_spans;
3480 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3482 // Out of memory, reallocate some more and try again...
3483 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3484 rasterPoolSize *= 2;
3485 if (rasterPoolSize > 1024 * 1024) {
3486 qWarning("QPainter: Rasterization of primitive failed");
3490 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3492 free(rasterPoolOnHeap);
3493 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3495 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3497 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3499 qt_ft_grays_raster.raster_done(*grayRaster.data());
3500 qt_ft_grays_raster.raster_new(grayRaster.data());
3501 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3507 free(rasterPoolOnHeap);
3510 void QRasterPaintEnginePrivate::recalculateFastImages()
3512 Q_Q(QRasterPaintEngine);
3513 QRasterPaintEngineState *s = q->state();
3515 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3516 && s->matrix.type() <= QTransform::TxShear;
3519 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3521 Q_Q(const QRasterPaintEngine);
3522 const QRasterPaintEngineState *s = q->state();
3524 return s->flags.fast_images
3525 && (mode == QPainter::CompositionMode_SourceOver
3526 || (mode == QPainter::CompositionMode_Source
3527 && !image.hasAlphaChannel()));
3530 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3532 Q_ASSERT(image.depth() == 1);
3534 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3535 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3537 QRgb fg = PREMUL(color.rgba());
3540 int height = sourceImage.height();
3541 int width = sourceImage.width();
3542 for (int y=0; y<height; ++y) {
3543 uchar *source = sourceImage.scanLine(y);
3544 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3545 if (!source || !target)
3546 QT_THROW(std::bad_alloc()); // we must have run out of memory
3547 for (int x=0; x < width; ++x)
3548 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3553 QRasterBuffer::~QRasterBuffer()
3557 void QRasterBuffer::init()
3559 compositionMode = QPainter::CompositionMode_SourceOver;
3560 monoDestinationWithClut = false;
3565 QImage::Format QRasterBuffer::prepare(QImage *image)
3567 m_buffer = (uchar *)image->bits();
3568 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3569 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3570 bytes_per_pixel = image->depth()/8;
3571 bytes_per_line = image->bytesPerLine();
3573 format = image->format();
3574 drawHelper = qDrawHelper + format;
3575 if (image->depth() == 1 && image->colorTable().size() == 2) {
3576 monoDestinationWithClut = true;
3577 destColor0 = PREMUL(image->colorTable()[0]);
3578 destColor1 = PREMUL(image->colorTable()[1]);
3584 void QRasterBuffer::resetBuffer(int val)
3586 memset(m_buffer, val, m_height*bytes_per_line);
3589 QClipData::QClipData(int height)
3591 clipSpanHeight = height;
3596 xmin = xmax = ymin = ymax = 0;
3600 hasRectClip = hasRegionClip = false;
3603 QClipData::~QClipData()
3611 void QClipData::initialize()
3617 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3619 Q_CHECK_PTR(m_clipLines);
3621 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3622 allocated = clipSpanHeight;
3623 Q_CHECK_PTR(m_spans);
3629 m_clipLines[y].spans = 0;
3630 m_clipLines[y].count = 0;
3634 const int len = clipRect.width();
3637 QSpan *span = m_spans + count;
3641 span->coverage = 255;
3644 m_clipLines[y].spans = span;
3645 m_clipLines[y].count = 1;
3649 while (y < clipSpanHeight) {
3650 m_clipLines[y].spans = 0;
3651 m_clipLines[y].count = 0;
3654 } else if (hasRegionClip) {
3656 const QVector<QRect> rects = clipRegion.rects();
3657 const int numRects = rects.size();
3660 const int maxSpans = (ymax - ymin) * numRects;
3661 if (maxSpans > allocated) {
3662 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3663 allocated = maxSpans;
3668 int firstInBand = 0;
3670 while (firstInBand < numRects) {
3671 const int currMinY = rects.at(firstInBand).y();
3672 const int currMaxY = currMinY + rects.at(firstInBand).height();
3674 while (y < currMinY) {
3675 m_clipLines[y].spans = 0;
3676 m_clipLines[y].count = 0;
3680 int lastInBand = firstInBand;
3681 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3684 while (y < currMaxY) {
3686 m_clipLines[y].spans = m_spans + count;
3687 m_clipLines[y].count = lastInBand - firstInBand + 1;
3689 for (int r = firstInBand; r <= lastInBand; ++r) {
3690 const QRect &currRect = rects.at(r);
3691 QSpan *span = m_spans + count;
3692 span->x = currRect.x();
3693 span->len = currRect.width();
3695 span->coverage = 255;
3701 firstInBand = lastInBand + 1;
3704 Q_ASSERT(count <= allocated);
3706 while (y < clipSpanHeight) {
3707 m_clipLines[y].spans = 0;
3708 m_clipLines[y].count = 0;
3714 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3719 free(m_clipLines); // same for clipLines
3725 void QClipData::fixup()
3730 ymin = ymax = xmin = xmax = 0;
3735 ymin = m_spans[0].y;
3736 ymax = m_spans[count-1].y + 1;
3740 const int firstLeft = m_spans[0].x;
3741 const int firstRight = m_spans[0].x + m_spans[0].len;
3744 for (int i = 0; i < count; ++i) {
3745 QT_FT_Span_& span = m_spans[i];
3748 if (span.y != y + 1 && y != -1)
3751 m_clipLines[y].spans = &span;
3752 m_clipLines[y].count = 1;
3754 ++m_clipLines[y].count;
3756 const int spanLeft = span.x;
3757 const int spanRight = spanLeft + span.len;
3759 if (spanLeft < xmin)
3762 if (spanRight > xmax)
3765 if (spanLeft != firstLeft || spanRight != firstRight)
3771 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3776 Convert \a rect to clip spans.
3778 void QClipData::setClipRect(const QRect &rect)
3780 if (hasRectClip && rect == clipRect)
3783 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3785 hasRegionClip = false;
3789 xmax = rect.x() + rect.width();
3790 ymin = qMin(rect.y(), clipSpanHeight);
3791 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3798 // qDebug() << xmin << xmax << ymin << ymax;
3802 Convert \a region to clip spans.
3804 void QClipData::setClipRegion(const QRegion ®ion)
3806 if (region.rectCount() == 1) {
3807 setClipRect(region.rects().at(0));
3811 hasRegionClip = true;
3812 hasRectClip = false;
3813 clipRegion = region;
3815 { // set bounding rect
3816 const QRect rect = region.boundingRect();
3818 xmax = rect.x() + rect.width();
3820 ymax = rect.y() + rect.height();
3832 spans must be sorted on y
3834 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3835 const QSpan *spans, const QSpan *end,
3836 QSpan **outSpans, int available)
3838 const_cast<QClipData *>(clip)->initialize();
3840 QSpan *out = *outSpans;
3842 const QSpan *clipSpans = clip->m_spans + *currentClip;
3843 const QSpan *clipEnd = clip->m_spans + clip->count;
3845 while (available && spans < end ) {
3846 if (clipSpans >= clipEnd) {
3850 if (clipSpans->y > spans->y) {
3854 if (spans->y != clipSpans->y) {
3855 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3856 clipSpans = clip->m_clipLines[spans->y].spans;
3861 Q_ASSERT(spans->y == clipSpans->y);
3864 int sx2 = sx1 + spans->len;
3865 int cx1 = clipSpans->x;
3866 int cx2 = cx1 + clipSpans->len;
3868 if (cx1 < sx1 && cx2 < sx1) {
3871 } else if (sx1 < cx1 && sx2 < cx1) {
3875 int x = qMax(sx1, cx1);
3876 int len = qMin(sx2, cx2) - x;
3878 out->x = qMax(sx1, cx1);
3879 out->len = qMin(sx2, cx2) - out->x;
3881 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3893 *currentClip = clipSpans - clip->m_spans;
3897 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3899 // qDebug() << "qt_span_fill_clipped" << spanCount;
3900 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3902 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3904 const int NSPANS = 256;
3905 QSpan cspans[NSPANS];
3906 int currentClip = 0;
3907 const QSpan *end = spans + spanCount;
3908 while (spans < end) {
3909 QSpan *clipped = cspans;
3910 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3911 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3912 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3914 if (clipped - cspans)
3915 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3921 Clip spans to \a{clip}-rectangle.
3922 Returns number of unclipped spans
3924 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3927 const short minx = clip.left();
3928 const short miny = clip.top();
3929 const short maxx = clip.right();
3930 const short maxy = clip.bottom();
3933 for (int i = 0; i < numSpans; ++i) {
3934 if (spans[i].y > maxy)
3936 if (spans[i].y < miny
3937 || spans[i].x > maxx
3938 || spans[i].x + spans[i].len <= minx) {
3941 if (spans[i].x < minx) {
3942 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
3945 spans[n].x = spans[i].x;
3946 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
3948 if (spans[n].len == 0)
3950 spans[n].y = spans[i].y;
3951 spans[n].coverage = spans[i].coverage;
3958 static void qt_span_fill_clipRect(int count, const QSpan *spans,
3961 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3962 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3964 Q_ASSERT(fillData->clip);
3965 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
3967 // hw: check if this const_cast<> is safe!!!
3968 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
3969 fillData->clip->clipRect);
3971 fillData->unclipped_blend(count, spans, fillData);
3974 static void qt_span_clip(int count, const QSpan *spans, void *userData)
3976 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
3978 // qDebug() << " qt_span_clip: " << count << clipData->operation;
3979 // for (int i = 0; i < qMin(count, 10); ++i) {
3980 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
3983 switch (clipData->operation) {
3985 case Qt::IntersectClip:
3987 QClipData *newClip = clipData->newClip;
3988 newClip->initialize();
3990 int currentClip = 0;
3991 const QSpan *end = spans + count;
3992 while (spans < end) {
3993 QSpan *newspans = newClip->m_spans + newClip->count;
3994 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
3995 &newspans, newClip->allocated - newClip->count);
3996 newClip->count = newspans - newClip->m_spans;
3998 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
3999 newClip->allocated *= 2;
4005 case Qt::ReplaceClip:
4006 clipData->newClip->appendSpans(spans, count);
4014 QImage QRasterBuffer::bufferImage() const
4016 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4018 for (int y = 0; y < m_height; ++y) {
4019 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4021 for (int x=0; x<m_width; ++x) {
4022 uint argb = span[x];
4023 image.setPixel(x, y, argb);
4031 void QRasterBuffer::flushToARGBImage(QImage *target) const
4033 int w = qMin(m_width, target->width());
4034 int h = qMin(m_height, target->height());
4036 for (int y=0; y<h; ++y) {
4037 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4038 QRgb *dest = (QRgb *) target->scanLine(y);
4039 for (int x=0; x<w; ++x) {
4040 QRgb pixel = sourceLine[x];
4041 int alpha = qAlpha(pixel);
4045 dest[x] = (alpha << 24)
4046 | ((255*qRed(pixel)/alpha) << 16)
4047 | ((255*qGreen(pixel)/alpha) << 8)
4048 | ((255*qBlue(pixel)/alpha) << 0);
4055 class QGradientCache
4059 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4060 stops(s), opacity(op), interpolationMode(mode) {}
4061 uint buffer[GRADIENT_STOPTABLE_SIZE];
4062 QGradientStops stops;
4064 QGradient::InterpolationMode interpolationMode;
4067 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4070 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4071 quint64 hash_val = 0;
4073 QGradientStops stops = gradient.stops();
4074 for (int i = 0; i < stops.size() && i <= 2; i++)
4075 hash_val += stops[i].second.rgba();
4077 QMutexLocker lock(&mutex);
4078 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4080 if (it == cache.constEnd())
4081 return addCacheElement(hash_val, gradient, opacity);
4084 const CacheInfo &cache_info = it.value();
4085 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4086 return cache_info.buffer;
4088 } while (it != cache.constEnd() && it.key() == hash_val);
4089 // an exact match for these stops and opacity was not found, create new cache
4090 return addCacheElement(hash_val, gradient, opacity);
4094 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4096 inline int maxCacheSize() const { return 60; }
4097 inline void generateGradientColorTable(const QGradient& g,
4099 int size, int opacity) const;
4100 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4101 if (cache.size() == maxCacheSize()) {
4102 // may remove more than 1, but OK
4103 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4105 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4106 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4107 return cache.insert(hash_val, cache_entry).value().buffer;
4110 QGradientColorTableHash cache;
4114 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4116 QGradientStops stops = gradient.stops();
4117 int stopCount = stops.count();
4118 Q_ASSERT(stopCount > 0);
4120 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4122 if (stopCount == 2) {
4123 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4124 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4126 qreal first_stop = stops[0].first;
4127 qreal second_stop = stops[1].first;
4129 if (second_stop < first_stop) {
4130 qSwap(first_color, second_color);
4131 qSwap(first_stop, second_stop);
4134 if (colorInterpolation) {
4135 first_color = PREMUL(first_color);
4136 second_color = PREMUL(second_color);
4139 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4140 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4142 uint red_first = qRed(first_color) << 16;
4143 uint green_first = qGreen(first_color) << 16;
4144 uint blue_first = qBlue(first_color) << 16;
4145 uint alpha_first = qAlpha(first_color) << 16;
4147 uint red_second = qRed(second_color) << 16;
4148 uint green_second = qGreen(second_color) << 16;
4149 uint blue_second = qBlue(second_color) << 16;
4150 uint alpha_second = qAlpha(second_color) << 16;
4153 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4154 if (colorInterpolation)
4155 colorTable[i] = first_color;
4157 colorTable[i] = PREMUL(first_color);
4160 if (i < second_index) {
4161 qreal reciprocal = qreal(1) / (second_index - first_index);
4163 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4164 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4165 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4166 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4169 red_first += 1 << 15;
4170 green_first += 1 << 15;
4171 blue_first += 1 << 15;
4172 alpha_first += 1 << 15;
4174 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4175 red_first += red_delta;
4176 green_first += green_delta;
4177 blue_first += blue_delta;
4178 alpha_first += alpha_delta;
4180 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4181 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4183 if (colorInterpolation)
4184 colorTable[i] = color;
4186 colorTable[i] = PREMUL(color);
4190 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4191 if (colorInterpolation)
4192 colorTable[i] = second_color;
4194 colorTable[i] = PREMUL(second_color);
4200 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4201 if (stopCount == 1) {
4202 current_color = PREMUL(current_color);
4203 for (int i = 0; i < size; ++i)
4204 colorTable[i] = current_color;
4208 // The position where the gradient begins and ends
4209 qreal begin_pos = stops[0].first;
4210 qreal end_pos = stops[stopCount-1].first;
4212 int pos = 0; // The position in the color table.
4215 qreal incr = 1 / qreal(size); // the double increment.
4216 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4218 // Up to first point
4219 colorTable[pos++] = PREMUL(current_color);
4220 while (dpos <= begin_pos) {
4221 colorTable[pos] = colorTable[pos - 1];
4226 int current_stop = 0; // We always interpolate between current and current + 1.
4228 qreal t; // position between current left and right stops
4229 qreal t_delta; // the t increment per entry in the color table
4231 if (dpos < end_pos) {
4233 while (dpos > stops[current_stop+1].first)
4236 if (current_stop != 0)
4237 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4238 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4240 if (colorInterpolation) {
4241 current_color = PREMUL(current_color);
4242 next_color = PREMUL(next_color);
4245 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4246 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4247 t = (dpos - stops[current_stop].first) * c;
4251 Q_ASSERT(current_stop < stopCount);
4253 int dist = qRound(t);
4254 int idist = 256 - dist;
4256 if (colorInterpolation)
4257 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4259 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4264 if (dpos >= end_pos)
4270 while (dpos > stops[current_stop+skip+1].first)
4274 current_stop += skip;
4276 current_color = next_color;
4278 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4279 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4281 if (colorInterpolation) {
4283 current_color = PREMUL(current_color);
4284 next_color = PREMUL(next_color);
4287 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4288 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4289 t = (dpos - stops[current_stop].first) * c;
4296 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4297 while (pos < size - 1) {
4298 colorTable[pos] = current_color;
4302 // Make sure the last color stop is represented at the end of the table
4303 colorTable[size - 1] = current_color;
4306 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4309 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4315 m11 = m22 = m33 = 1.;
4316 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4317 clip = pe ? pe->d_func()->clip() : 0;
4320 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4322 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4324 Qt::BrushStyle brushStyle = qbrush_style(brush);
4325 switch (brushStyle) {
4326 case Qt::SolidPattern: {
4328 QColor c = qbrush_color(brush);
4329 QRgb rgba = c.rgba();
4330 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4331 if ((solid.color & 0xff000000) == 0
4332 && compositionMode == QPainter::CompositionMode_SourceOver) {
4338 case Qt::LinearGradientPattern:
4340 type = LinearGradient;
4341 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4342 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4343 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4344 gradient.spread = g->spread();
4346 QLinearGradientData &linearData = gradient.linear;
4348 linearData.origin.x = g->start().x();
4349 linearData.origin.y = g->start().y();
4350 linearData.end.x = g->finalStop().x();
4351 linearData.end.y = g->finalStop().y();
4355 case Qt::RadialGradientPattern:
4357 type = RadialGradient;
4358 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4359 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4360 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4361 gradient.spread = g->spread();
4363 QRadialGradientData &radialData = gradient.radial;
4365 QPointF center = g->center();
4366 radialData.center.x = center.x();
4367 radialData.center.y = center.y();
4368 radialData.center.radius = g->centerRadius();
4369 QPointF focal = g->focalPoint();
4370 radialData.focal.x = focal.x();
4371 radialData.focal.y = focal.y();
4372 radialData.focal.radius = g->focalRadius();
4376 case Qt::ConicalGradientPattern:
4378 type = ConicalGradient;
4379 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4380 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4381 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4382 gradient.spread = QGradient::RepeatSpread;
4384 QConicalGradientData &conicalData = gradient.conical;
4386 QPointF center = g->center();
4387 conicalData.center.x = center.x();
4388 conicalData.center.y = center.y();
4389 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4393 case Qt::Dense1Pattern:
4394 case Qt::Dense2Pattern:
4395 case Qt::Dense3Pattern:
4396 case Qt::Dense4Pattern:
4397 case Qt::Dense5Pattern:
4398 case Qt::Dense6Pattern:
4399 case Qt::Dense7Pattern:
4400 case Qt::HorPattern:
4401 case Qt::VerPattern:
4402 case Qt::CrossPattern:
4403 case Qt::BDiagPattern:
4404 case Qt::FDiagPattern:
4405 case Qt::DiagCrossPattern:
4408 tempImage = new QImage();
4409 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4410 initTexture(tempImage, alpha, QTextureData::Tiled);
4412 case Qt::TexturePattern:
4415 tempImage = new QImage();
4417 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4418 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4420 *tempImage = brush.textureImage();
4421 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4429 adjustSpanMethods();
4432 void QSpanData::adjustSpanMethods()
4442 unclipped_blend = 0;
4445 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4446 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4447 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4448 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4449 fillRect = rasterBuffer->drawHelper->fillRect;
4451 case LinearGradient:
4452 case RadialGradient:
4453 case ConicalGradient:
4454 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4457 unclipped_blend = qBlendTexture;
4458 if (!texture.imageData)
4459 unclipped_blend = 0;
4464 if (!unclipped_blend) {
4467 blend = unclipped_blend;
4468 } else if (clip->hasRectClip) {
4469 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4471 blend = qt_span_fill_clipped;
4475 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4478 // make sure we round off correctly in qdrawhelper.cpp
4479 delta.translate(1.0 / 65536, 1.0 / 65536);
4481 QTransform inv = (delta * matrix).inverted();
4494 const bool affine = !m13 && !m23;
4495 fast_matrix = affine
4496 && m11 * m11 + m21 * m21 < 1e4
4497 && m12 * m12 + m22 * m22 < 1e4
4501 adjustSpanMethods();
4504 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4506 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4507 if (!d || d->height == 0) {
4508 texture.imageData = 0;
4515 texture.bytesPerLine = 0;
4516 texture.format = QImage::Format_Invalid;
4517 texture.colorTable = 0;
4518 texture.hasAlpha = alpha != 256;
4520 texture.imageData = d->data;
4521 texture.width = d->width;
4522 texture.height = d->height;
4524 if (sourceRect.isNull()) {
4527 texture.x2 = texture.width;
4528 texture.y2 = texture.height;
4530 texture.x1 = sourceRect.x();
4531 texture.y1 = sourceRect.y();
4532 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4533 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4536 texture.bytesPerLine = d->bytes_per_line;
4538 texture.format = d->format;
4539 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4540 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4542 texture.const_alpha = alpha;
4543 texture.type = _type;
4545 adjustSpanMethods();
4550 \a x and \a y is relative to the midpoint of \a rect.
4552 static inline void drawEllipsePoints(int x, int y, int length,
4555 ProcessSpans pen_func, ProcessSpans brush_func,
4556 QSpanData *pen_data, QSpanData *brush_data)
4561 QT_FT_Span outline[4];
4562 const int midx = rect.x() + (rect.width() + 1) / 2;
4563 const int midy = rect.y() + (rect.height() + 1) / 2;
4569 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4570 outline[0].len = qMin(length, x - outline[0].x);
4572 outline[0].coverage = 255;
4576 outline[1].len = length;
4578 outline[1].coverage = 255;
4581 outline[2].x = outline[0].x;
4582 outline[2].len = outline[0].len;
4583 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4584 outline[2].coverage = 255;
4588 outline[3].len = length;
4589 outline[3].y = outline[2].y;
4590 outline[3].coverage = 255;
4592 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4596 fill[0].x = outline[0].x + outline[0].len - 1;
4597 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4598 fill[0].y = outline[1].y;
4599 fill[0].coverage = 255;
4602 fill[1].x = outline[2].x + outline[2].len - 1;
4603 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4604 fill[1].y = outline[3].y;
4605 fill[1].coverage = 255;
4607 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4608 n = qt_intersect_spans(fill, n, clip);
4610 brush_func(n, fill, brush_data);
4613 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4614 n = qt_intersect_spans(outline, n, clip);
4616 pen_func(n, outline, pen_data);
4622 Draws an ellipse using the integer point midpoint algorithm.
4624 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4625 ProcessSpans pen_func, ProcessSpans brush_func,
4626 QSpanData *pen_data, QSpanData *brush_data)
4628 const qreal a = qreal(rect.width()) / 2;
4629 const qreal b = qreal(rect.height()) / 2;
4630 qreal d = b*b - (a*a*b) + 0.25*a*a;
4633 int y = (rect.height() + 1) / 2;
4637 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4638 if (d < 0) { // select E
4641 } else { // select SE
4642 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4643 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4644 pen_func, brush_func, pen_data, brush_data);
4649 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4650 pen_func, brush_func, pen_data, brush_data);
4653 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4654 const int miny = rect.height() & 0x1;
4656 if (d < 0) { // select SE
4657 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4659 } else { // select S
4660 d += a*a*(-2*y + 3);
4663 drawEllipsePoints(x, y, 1, rect, clip,
4664 pen_func, brush_func, pen_data, brush_data);
4669 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4672 Draws the first \a pointCount points in the buffer \a points
4674 The default implementation converts the first \a pointCount QPoints in \a points
4675 to QPointFs and calls the floating point version of drawPoints.
4679 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4682 Reimplement this function to draw the largest ellipse that can be
4683 contained within rectangle \a rect.
4686 #ifdef QT_DEBUG_DRAW
4687 void dumpClip(int width, int height, const QClipData *clip)
4689 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4690 clipImg.fill(0xffff0000);
4697 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4699 for (int i = 0; i < clip->count; ++i) {
4700 const QSpan *span = ((QClipData *) clip)->spans() + i;
4701 for (int j = 0; j < span->len; ++j)
4702 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4703 x0 = qMin(x0, int(span->x));
4704 x1 = qMax(x1, int(span->x + span->len - 1));
4706 y0 = qMin(y0, int(span->y));
4707 y1 = qMax(y1, int(span->y));
4710 static int counter = 0;
4717 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4718 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));