1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
57 // #include <private/qdatabuffer_p.h>
58 // #include <private/qpainter_p.h>
59 #include <private/qmath_p.h>
60 #include <private/qtextengine_p.h>
61 #include <private/qfontengine_p.h>
62 #include <private/qpixmap_raster_p.h>
63 // #include <private/qpolygonclipper_p.h>
64 // #include <private/qrasterizer_p.h>
65 #include <private/qimage_p.h>
66 #include <private/qstatictext_p.h>
67 #include <private/qcosmeticstroker_p.h>
68 #include "qmemrotate_p.h"
70 #include "qpaintengine_raster_p.h"
71 // #include "qbezier_p.h"
72 #include "qoutlinemapper_p.h"
77 # include <qvarlengtharray.h>
78 # include <private/qfontengine_p.h>
79 # include <qt_windows.h>
87 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 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2473 Q_D(QRasterPaintEngine);
2474 return d->rasterBuffer.data();
2480 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2482 Q_D(QRasterPaintEngine);
2483 QRasterPaintEngineState *s = state();
2485 if (!s->penData.blend)
2488 QRasterBuffer *rb = d->rasterBuffer.data();
2490 const QRect rect(rx, ry, w, h);
2491 const QClipData *clip = d->clip();
2492 bool unclipped = false;
2494 // inlined QRect::intersects
2495 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2496 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2498 if (clip->hasRectClip) {
2499 unclipped = rx > clip->xmin
2500 && rx + w < clip->xmax
2502 && ry + h < clip->ymax;
2508 // inlined QRect::intersects
2509 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2510 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2514 // inlined QRect::contains
2515 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2516 && rect.top() >= 0 && rect.bottom() < rb->height();
2518 unclipped = contains && d->isUnclipped_normalized(rect);
2521 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2522 const uchar * scanline = static_cast<const uchar *>(src);
2524 if (s->flags.fast_text) {
2527 if (s->penData.bitmapBlit) {
2528 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2529 scanline, w, h, bpl);
2532 } else if (depth == 8) {
2533 if (s->penData.alphamapBlit) {
2534 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2535 scanline, w, h, bpl, 0);
2538 } else if (depth == 32) {
2539 // (A)RGB Alpha mask where the alpha component is not used.
2540 if (s->penData.alphaRGBBlit) {
2541 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2542 (const uint *) scanline, w, h, bpl / 4, 0);
2546 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2547 // (A)RGB Alpha mask where the alpha component is not used.
2549 int nx = qMax(0, rx);
2550 int ny = qMax(0, ry);
2552 // Move scanline pointer to compensate for moved x and y
2553 int xdiff = nx - rx;
2554 int ydiff = ny - ry;
2555 scanline += ydiff * bpl;
2556 scanline += xdiff * (depth == 32 ? 4 : 1);
2561 if (nx + w > d->rasterBuffer->width())
2562 w = d->rasterBuffer->width() - nx;
2563 if (ny + h > d->rasterBuffer->height())
2564 h = d->rasterBuffer->height() - ny;
2569 if (depth == 8 && s->penData.alphamapBlit) {
2570 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2571 scanline, w, h, bpl, clip);
2572 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2573 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2574 (const uint *) scanline, w, h, bpl / 4, clip);
2589 scanline += bpl * y0;
2593 w = qMin(w, rb->width() - qMax(0, rx));
2594 h = qMin(h, rb->height() - qMax(0, ry));
2596 if (w <= 0 || h <= 0)
2599 const int NSPANS = 256;
2600 QSpan spans[NSPANS];
2603 const int x1 = x0 + w;
2604 const int y1 = y0 + h;
2607 for (int y = y0; y < y1; ++y) {
2608 for (int x = x0; x < x1; ) {
2609 if (!monoVal(scanline, x)) {
2614 if (current == NSPANS) {
2615 blend(current, spans, &s->penData);
2618 spans[current].x = x + rx;
2619 spans[current].y = y + ry;
2620 spans[current].coverage = 255;
2623 // extend span until we find a different one.
2624 while (x < x1 && monoVal(scanline, x)) {
2628 spans[current].len = len;
2633 } else if (depth == 8) {
2634 for (int y = y0; y < y1; ++y) {
2635 for (int x = x0; x < x1; ) {
2636 // Skip those with 0 coverage
2637 if (scanline[x] == 0) {
2642 if (current == NSPANS) {
2643 blend(current, spans, &s->penData);
2646 int coverage = scanline[x];
2647 spans[current].x = x + rx;
2648 spans[current].y = y + ry;
2649 spans[current].coverage = coverage;
2653 // extend span until we find a different one.
2654 while (x < x1 && scanline[x] == coverage) {
2658 spans[current].len = len;
2663 } else { // 32-bit alpha...
2664 uint *sl = (uint *) src;
2665 for (int y = y0; y < y1; ++y) {
2666 for (int x = x0; x < x1; ) {
2667 // Skip those with 0 coverage
2668 if ((sl[x] & 0x00ffffff) == 0) {
2673 if (current == NSPANS) {
2674 blend(current, spans, &s->penData);
2677 uint rgbCoverage = sl[x];
2678 int coverage = qGreen(rgbCoverage);
2679 spans[current].x = x + rx;
2680 spans[current].y = y + ry;
2681 spans[current].coverage = coverage;
2685 // extend span until we find a different one.
2686 while (x < x1 && sl[x] == rgbCoverage) {
2690 spans[current].len = len;
2693 sl += bpl / sizeof(uint);
2696 // qDebug() << "alphaPenBlt: num spans=" << current
2697 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2698 // Call span func for current set of spans.
2700 blend(current, spans, &s->penData);
2703 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2704 const QFixedPoint *positions, QFontEngine *fontEngine)
2706 Q_D(QRasterPaintEngine);
2707 QRasterPaintEngineState *s = state();
2709 if (fontEngine->hasInternalCaching()) {
2710 QFontEngine::GlyphFormat neededFormat =
2711 painter()->device()->devType() == QInternal::Widget
2712 ? QFontEngine::Format_None
2713 : QFontEngine::Format_A8;
2715 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2716 neededFormat = QFontEngine::Format_Mono;
2718 for (int i = 0; i < numGlyphs; i++) {
2719 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2722 QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
2724 if (alphaMap == 0 || alphaMap->isNull())
2727 alphaPenBlt(alphaMap->bits(), alphaMap->bytesPerLine(), alphaMap->depth(),
2728 qFloor(positions[i].x) + offset.x(),
2729 qFloor(positions[i].y) + offset.y(),
2730 alphaMap->width(), alphaMap->height());
2732 fontEngine->unlockAlphaMapForGlyph();
2736 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2738 QImageTextureGlyphCache *cache =
2739 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2741 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2742 fontEngine->setGlyphCache(0, cache);
2745 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2746 cache->fillInPendingGlyphs();
2748 const QImage &image = cache->image();
2749 int bpl = image.bytesPerLine();
2751 int depth = image.depth();
2755 leftShift = 2; // multiply by 4
2756 else if (depth == 1)
2757 rightShift = 3; // divide by 8
2759 int margin = fontEngine->glyphMargin(glyphType);
2760 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2761 const uchar *bits = image.bits();
2762 for (int i=0; i<numGlyphs; ++i) {
2764 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2765 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2766 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2770 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2771 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2773 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2776 // c.baseLineX, c.baseLineY,
2779 // positions[i].x.toInt(), positions[i].y.toInt());
2781 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2789 * Returns true if the rectangle is completely within the current clip
2790 * state of the paint engine.
2792 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2794 const QClipData *cl = clip();
2796 // inline contains() for performance (we know the rects are normalized)
2797 const QRect &r1 = deviceRect;
2798 return (r.left() >= r1.left() && r.right() <= r1.right()
2799 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2803 if (cl->hasRectClip) {
2804 // currently all painting functions clips to deviceRect internally
2805 if (cl->clipRect == deviceRect)
2808 // inline contains() for performance (we know the rects are normalized)
2809 const QRect &r1 = cl->clipRect;
2810 return (r.left() >= r1.left() && r.right() <= r1.right()
2811 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2813 return qt_region_strictContains(cl->clipRegion, r);
2817 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2820 Q_Q(const QRasterPaintEngine);
2821 const QRasterPaintEngineState *s = q->state();
2822 const QClipData *cl = clip();
2824 QRect r = rect.normalized();
2825 // inline contains() for performance (we know the rects are normalized)
2826 const QRect &r1 = deviceRect;
2827 return (r.left() >= r1.left() && r.right() <= r1.right()
2828 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2832 // currently all painting functions that call this function clip to deviceRect internally
2833 if (cl->hasRectClip && cl->clipRect == deviceRect)
2836 if (s->flags.antialiased)
2839 QRect r = rect.normalized();
2841 r.setX(r.x() - penWidth);
2842 r.setY(r.y() - penWidth);
2843 r.setWidth(r.width() + 2 * penWidth);
2844 r.setHeight(r.height() + 2 * penWidth);
2847 if (cl->hasRectClip) {
2848 // inline contains() for performance (we know the rects are normalized)
2849 const QRect &r1 = cl->clipRect;
2850 return (r.left() >= r1.left() && r.right() <= r1.right()
2851 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2853 return qt_region_strictContains(cl->clipRegion, r);
2857 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2860 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2864 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2865 const QSpanData *data) const
2867 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2871 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2872 const QSpanData *data) const
2874 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2878 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2879 const QSpanData *data) const
2881 Q_Q(const QRasterPaintEngine);
2882 const QRasterPaintEngineState *s = q->state();
2884 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2886 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2887 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2890 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2891 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2893 QFixed clipLeft = QFixed::fromReal(clip.left());
2894 QFixed clipRight = QFixed::fromReal(clip.right());
2895 QFixed clipTop = QFixed::fromReal(clip.top());
2896 QFixed clipBottom = QFixed::fromReal(clip.bottom());
2899 while (first < numGlyphs) {
2900 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
2901 QFixed left = metrics.x + positions[first].x;
2902 QFixed top = metrics.y + positions[first].y;
2903 QFixed right = left + metrics.width;
2904 QFixed bottom = top + metrics.height;
2905 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2909 int last = numGlyphs - 1;
2910 while (last > first) {
2911 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
2912 QFixed left = metrics.x + positions[last].x;
2913 QFixed top = metrics.y + positions[last].y;
2914 QFixed right = left + metrics.width;
2915 QFixed bottom = top + metrics.height;
2916 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
2920 return QPair<int, int>(first, last + 1);
2926 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
2928 if (textItem->numGlyphs == 0)
2932 ensureRasterState();
2934 QFontEngine *fontEngine = textItem->fontEngine();
2935 if (shouldDrawCachedGlyphs(fontEngine, state()->matrix)) {
2936 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
2938 } else if (state()->matrix.type() < QTransform::TxProject) {
2940 QTransform invMat = state()->matrix.inverted(&invertible);
2944 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2945 textItem->fontEngine(), textItem->glyphs,
2946 textItem->glyphPositions, textItem->numGlyphs);
2947 QStaticTextItem copy = *textItem;
2948 copy.glyphs += range.first;
2949 copy.glyphPositions += range.first;
2950 copy.numGlyphs = range.second - range.first;
2951 QPaintEngineEx::drawStaticTextItem(©);
2953 QPaintEngineEx::drawStaticTextItem(textItem);
2960 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
2962 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
2964 #ifdef QT_DEBUG_DRAW
2965 Q_D(QRasterPaintEngine);
2966 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
2967 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
2971 if (ti.glyphs.numGlyphs == 0)
2974 ensureRasterState();
2976 QRasterPaintEngineState *s = state();
2977 QTransform matrix = s->matrix;
2979 if (!supportsTransformations(ti.fontEngine)) {
2980 QVarLengthArray<QFixedPoint> positions;
2981 QVarLengthArray<glyph_t> glyphs;
2983 matrix.translate(p.x(), p.y());
2984 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2986 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
2987 } else if (matrix.type() < QTransform::TxProject) {
2989 QTransform invMat = matrix.inverted(&invertible);
2993 QVarLengthArray<QFixedPoint> positions;
2994 QVarLengthArray<glyph_t> glyphs;
2996 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
2997 ti.flags, glyphs, positions);
2998 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
2999 ti.fontEngine, glyphs.data(), positions.data(),
3002 if (range.first >= range.second)
3005 QStaticTextItem staticTextItem;
3006 staticTextItem.color = s->pen.color();
3007 staticTextItem.font = s->font;
3008 staticTextItem.setFontEngine(ti.fontEngine);
3009 staticTextItem.numGlyphs = range.second - range.first;
3010 staticTextItem.glyphs = glyphs.data() + range.first;
3011 staticTextItem.glyphPositions = positions.data() + range.first;
3012 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3014 QPaintEngineEx::drawTextItem(p, ti);
3021 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3023 Q_D(QRasterPaintEngine);
3024 QRasterPaintEngineState *s = state();
3027 if (!s->penData.blend)
3030 if (!s->flags.fast_pen) {
3031 QPaintEngineEx::drawPoints(points, pointCount);
3035 QCosmeticStroker stroker(s, d->deviceRect);
3036 stroker.drawPoints(points, pointCount);
3040 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3042 Q_D(QRasterPaintEngine);
3043 QRasterPaintEngineState *s = state();
3046 if (!s->penData.blend)
3049 if (!s->flags.fast_pen) {
3050 QPaintEngineEx::drawPoints(points, pointCount);
3054 QCosmeticStroker stroker(s, d->deviceRect);
3055 stroker.drawPoints(points, pointCount);
3061 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3063 #ifdef QT_DEBUG_DRAW
3064 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3066 Q_D(QRasterPaintEngine);
3067 QRasterPaintEngineState *s = state();
3070 if (!s->penData.blend)
3073 if (s->flags.fast_pen) {
3074 QCosmeticStroker stroker(s, d->deviceRect);
3075 for (int i=0; i<lineCount; ++i) {
3076 const QLine &l = lines[i];
3077 stroker.drawLine(l.p1(), l.p2());
3080 QPaintEngineEx::drawLines(lines, lineCount);
3084 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3090 Q_Q(QRasterPaintEngine);
3091 QRasterPaintEngineState *s = q->state();
3093 const QPen &pen = s->lastPen;
3094 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3095 const QVector<qreal> pattern = pen.dashPattern();
3097 qreal patternLength = 0;
3098 for (int i = 0; i < pattern.size(); ++i)
3099 patternLength += pattern.at(i);
3101 if (patternLength <= 0)
3104 qreal length = line.length();
3105 Q_ASSERT(length > 0);
3106 while (length > 0) {
3107 const bool rasterize = *inDash;
3108 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3111 if (dash >= length) {
3113 *dashOffset += dash / width;
3117 *inDash = !(*inDash);
3118 if (++*dashIndex >= pattern.size())
3125 if (rasterize && dash > 0)
3126 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3133 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3135 #ifdef QT_DEBUG_DRAW
3136 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3138 Q_D(QRasterPaintEngine);
3139 QRasterPaintEngineState *s = state();
3142 if (!s->penData.blend)
3144 if (s->flags.fast_pen) {
3145 QCosmeticStroker stroker(s, d->deviceRect);
3146 for (int i=0; i<lineCount; ++i) {
3147 QLineF line = lines[i];
3148 stroker.drawLine(line.p1(), line.p2());
3151 QPaintEngineEx::drawLines(lines, lineCount);
3159 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3161 Q_D(QRasterPaintEngine);
3162 QRasterPaintEngineState *s = state();
3165 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3166 || (qpen_style(s->lastPen) == Qt::NoPen))
3167 && !s->flags.antialiased
3168 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3170 && s->matrix.type() <= QTransform::TxScale) // no shear
3173 const QRectF r = s->matrix.mapRect(rect);
3174 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3175 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3176 const QRect brect = QRect(int(r.x()), int(r.y()),
3177 int_dim(r.x(), r.width()),
3178 int_dim(r.y(), r.height()));
3180 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3181 &s->penData, &s->brushData);
3185 QPaintEngineEx::drawEllipse(rect);
3196 void QRasterPaintEngine::setDC(HDC hdc) {
3197 Q_D(QRasterPaintEngine);
3204 HDC QRasterPaintEngine::getDC() const
3206 Q_D(const QRasterPaintEngine);
3213 void QRasterPaintEngine::releaseDC(HDC) const
3219 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine) const
3221 const QTransform &m = state()->matrix;
3222 return supportsTransformations(fontEngine, m);
3225 bool QRasterPaintEngine::supportsTransformations(QFontEngine *fontEngine, const QTransform &m) const
3227 if (m.type() >= QTransform::TxProject)
3230 return !shouldDrawCachedGlyphs(fontEngine, m);
3236 QPoint QRasterPaintEngine::coordinateOffset() const
3238 return QPoint(0, 0);
3241 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3246 Q_D(QRasterPaintEngine);
3248 Q_ASSERT(image.depth() == 1);
3250 const int spanCount = 256;
3251 QT_FT_Span spans[spanCount];
3255 int w = image.width();
3256 int h = image.height();
3257 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3258 int ymin = qMax(qRound(pos.y()), 0);
3259 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3260 int xmin = qMax(qRound(pos.x()), 0);
3262 int x_offset = xmin - qRound(pos.x());
3264 QImage::Format format = image.format();
3265 for (int y = ymin; y < ymax; ++y) {
3266 const uchar *src = image.scanLine(y - qRound(pos.y()));
3267 if (format == QImage::Format_MonoLSB) {
3268 for (int x = 0; x < xmax - xmin; ++x) {
3269 int src_x = x + x_offset;
3270 uchar pixel = src[src_x >> 3];
3275 if (pixel & (0x1 << (src_x & 7))) {
3276 spans[n].x = xmin + x;
3278 spans[n].coverage = 255;
3280 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3284 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3287 if (n == spanCount) {
3288 fg->blend(n, spans, fg);
3294 for (int x = 0; x < xmax - xmin; ++x) {
3295 int src_x = x + x_offset;
3296 uchar pixel = src[src_x >> 3];
3301 if (pixel & (0x80 >> (x & 7))) {
3302 spans[n].x = xmin + x;
3304 spans[n].coverage = 255;
3306 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3310 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3313 if (n == spanCount) {
3314 fg->blend(n, spans, fg);
3322 fg->blend(n, spans, fg);
3328 \enum QRasterPaintEngine::ClipType
3331 \value RectClip Indicates that the currently set clip is a single rectangle.
3332 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3337 Returns the type of the clip currently set.
3339 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3341 Q_D(const QRasterPaintEngine);
3343 const QClipData *clip = d->clip();
3344 if (!clip || clip->hasRectClip)
3352 Returns the bounding rect of the currently set clip.
3354 QRect QRasterPaintEngine::clipBoundingRect() const
3356 Q_D(const QRasterPaintEngine);
3358 const QClipData *clip = d->clip();
3361 return d->deviceRect;
3363 if (clip->hasRectClip)
3364 return clip->clipRect;
3366 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3369 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3371 Q_Q(QRasterPaintEngine);
3372 QRasterPaintEngineState *s = q->state();
3374 rasterizer->setAntialiased(s->flags.antialiased);
3376 QRect clipRect(deviceRect);
3378 // ### get from optimized rectbased QClipData
3380 const QClipData *c = clip();
3382 const QRect r(QPoint(c->xmin, c->ymin),
3383 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3384 clipRect = clipRect.intersected(r);
3385 blend = data->blend;
3387 blend = data->unclipped_blend;
3390 rasterizer->setClipRect(clipRect);
3391 rasterizer->initialize(blend, data);
3394 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3395 ProcessSpans callback,
3396 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3398 if (!callback || !outline)
3401 Q_Q(QRasterPaintEngine);
3402 QRasterPaintEngineState *s = q->state();
3404 if (!s->flags.antialiased) {
3405 initializeRasterizer(spanData);
3407 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3411 rasterizer->rasterize(outline, fillRule);
3415 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3419 int q_gray_rendered_spans(QT_FT_Raster raster);
3422 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3424 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3427 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3428 ProcessSpans callback,
3429 void *userData, QRasterBuffer *)
3431 if (!callback || !outline)
3434 Q_Q(QRasterPaintEngine);
3435 QRasterPaintEngineState *s = q->state();
3437 if (!s->flags.antialiased) {
3438 rasterizer->setAntialiased(s->flags.antialiased);
3439 rasterizer->setClipRect(deviceRect);
3440 rasterizer->initialize(callback, userData);
3442 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3446 rasterizer->rasterize(outline, fillRule);
3450 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3451 // minimize memory reallocations. However if initial size for
3452 // raster pool is changed for lower value, reallocations will
3454 int rasterPoolSize = MINIMUM_POOL_SIZE;
3455 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3456 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3457 uchar *rasterPoolOnHeap = 0;
3459 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3461 void *data = userData;
3463 QT_FT_BBox clip_box = { deviceRect.x(),
3465 deviceRect.x() + deviceRect.width(),
3466 deviceRect.y() + deviceRect.height() };
3468 QT_FT_Raster_Params rasterParams;
3469 rasterParams.target = 0;
3470 rasterParams.source = outline;
3471 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3472 rasterParams.gray_spans = 0;
3473 rasterParams.black_spans = 0;
3474 rasterParams.bit_test = 0;
3475 rasterParams.bit_set = 0;
3476 rasterParams.user = data;
3477 rasterParams.clip_box = clip_box;
3482 int rendered_spans = 0;
3486 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3487 rasterParams.gray_spans = callback;
3488 rasterParams.skip_spans = rendered_spans;
3489 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3491 // Out of memory, reallocate some more and try again...
3492 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3493 rasterPoolSize *= 2;
3494 if (rasterPoolSize > 1024 * 1024) {
3495 qWarning("QPainter: Rasterization of primitive failed");
3499 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3501 free(rasterPoolOnHeap);
3502 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3504 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3506 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3508 qt_ft_grays_raster.raster_done(*grayRaster.data());
3509 qt_ft_grays_raster.raster_new(grayRaster.data());
3510 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3516 free(rasterPoolOnHeap);
3519 void QRasterPaintEnginePrivate::recalculateFastImages()
3521 Q_Q(QRasterPaintEngine);
3522 QRasterPaintEngineState *s = q->state();
3524 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3525 && s->matrix.type() <= QTransform::TxShear;
3528 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3530 Q_Q(const QRasterPaintEngine);
3531 const QRasterPaintEngineState *s = q->state();
3533 return s->flags.fast_images
3534 && (mode == QPainter::CompositionMode_SourceOver
3535 || (mode == QPainter::CompositionMode_Source
3536 && !image.hasAlphaChannel()));
3539 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3541 Q_ASSERT(image.depth() == 1);
3543 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3544 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3546 QRgb fg = PREMUL(color.rgba());
3549 int height = sourceImage.height();
3550 int width = sourceImage.width();
3551 for (int y=0; y<height; ++y) {
3552 uchar *source = sourceImage.scanLine(y);
3553 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3554 if (!source || !target)
3555 QT_THROW(std::bad_alloc()); // we must have run out of memory
3556 for (int x=0; x < width; ++x)
3557 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3562 QRasterBuffer::~QRasterBuffer()
3566 void QRasterBuffer::init()
3568 compositionMode = QPainter::CompositionMode_SourceOver;
3569 monoDestinationWithClut = false;
3574 QImage::Format QRasterBuffer::prepare(QImage *image)
3576 m_buffer = (uchar *)image->bits();
3577 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3578 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3579 bytes_per_pixel = image->depth()/8;
3580 bytes_per_line = image->bytesPerLine();
3582 format = image->format();
3583 drawHelper = qDrawHelper + format;
3584 if (image->depth() == 1 && image->colorTable().size() == 2) {
3585 monoDestinationWithClut = true;
3586 destColor0 = PREMUL(image->colorTable()[0]);
3587 destColor1 = PREMUL(image->colorTable()[1]);
3593 void QRasterBuffer::resetBuffer(int val)
3595 memset(m_buffer, val, m_height*bytes_per_line);
3598 QClipData::QClipData(int height)
3600 clipSpanHeight = height;
3605 xmin = xmax = ymin = ymax = 0;
3609 hasRectClip = hasRegionClip = false;
3612 QClipData::~QClipData()
3620 void QClipData::initialize()
3626 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3628 Q_CHECK_PTR(m_clipLines);
3630 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3631 allocated = clipSpanHeight;
3632 Q_CHECK_PTR(m_spans);
3638 m_clipLines[y].spans = 0;
3639 m_clipLines[y].count = 0;
3643 const int len = clipRect.width();
3646 QSpan *span = m_spans + count;
3650 span->coverage = 255;
3653 m_clipLines[y].spans = span;
3654 m_clipLines[y].count = 1;
3658 while (y < clipSpanHeight) {
3659 m_clipLines[y].spans = 0;
3660 m_clipLines[y].count = 0;
3663 } else if (hasRegionClip) {
3665 const QVector<QRect> rects = clipRegion.rects();
3666 const int numRects = rects.size();
3669 const int maxSpans = (ymax - ymin) * numRects;
3670 if (maxSpans > allocated) {
3671 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3672 allocated = maxSpans;
3677 int firstInBand = 0;
3679 while (firstInBand < numRects) {
3680 const int currMinY = rects.at(firstInBand).y();
3681 const int currMaxY = currMinY + rects.at(firstInBand).height();
3683 while (y < currMinY) {
3684 m_clipLines[y].spans = 0;
3685 m_clipLines[y].count = 0;
3689 int lastInBand = firstInBand;
3690 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3693 while (y < currMaxY) {
3695 m_clipLines[y].spans = m_spans + count;
3696 m_clipLines[y].count = lastInBand - firstInBand + 1;
3698 for (int r = firstInBand; r <= lastInBand; ++r) {
3699 const QRect &currRect = rects.at(r);
3700 QSpan *span = m_spans + count;
3701 span->x = currRect.x();
3702 span->len = currRect.width();
3704 span->coverage = 255;
3710 firstInBand = lastInBand + 1;
3713 Q_ASSERT(count <= allocated);
3715 while (y < clipSpanHeight) {
3716 m_clipLines[y].spans = 0;
3717 m_clipLines[y].count = 0;
3723 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3728 free(m_clipLines); // same for clipLines
3734 void QClipData::fixup()
3739 ymin = ymax = xmin = xmax = 0;
3744 ymin = m_spans[0].y;
3745 ymax = m_spans[count-1].y + 1;
3749 const int firstLeft = m_spans[0].x;
3750 const int firstRight = m_spans[0].x + m_spans[0].len;
3753 for (int i = 0; i < count; ++i) {
3754 QT_FT_Span_& span = m_spans[i];
3757 if (span.y != y + 1 && y != -1)
3760 m_clipLines[y].spans = &span;
3761 m_clipLines[y].count = 1;
3763 ++m_clipLines[y].count;
3765 const int spanLeft = span.x;
3766 const int spanRight = spanLeft + span.len;
3768 if (spanLeft < xmin)
3771 if (spanRight > xmax)
3774 if (spanLeft != firstLeft || spanRight != firstRight)
3780 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3785 Convert \a rect to clip spans.
3787 void QClipData::setClipRect(const QRect &rect)
3789 if (hasRectClip && rect == clipRect)
3792 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3794 hasRegionClip = false;
3798 xmax = rect.x() + rect.width();
3799 ymin = qMin(rect.y(), clipSpanHeight);
3800 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3807 // qDebug() << xmin << xmax << ymin << ymax;
3811 Convert \a region to clip spans.
3813 void QClipData::setClipRegion(const QRegion ®ion)
3815 if (region.rectCount() == 1) {
3816 setClipRect(region.rects().at(0));
3820 hasRegionClip = true;
3821 hasRectClip = false;
3822 clipRegion = region;
3824 { // set bounding rect
3825 const QRect rect = region.boundingRect();
3827 xmax = rect.x() + rect.width();
3829 ymax = rect.y() + rect.height();
3841 spans must be sorted on y
3843 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3844 const QSpan *spans, const QSpan *end,
3845 QSpan **outSpans, int available)
3847 const_cast<QClipData *>(clip)->initialize();
3849 QSpan *out = *outSpans;
3851 const QSpan *clipSpans = clip->m_spans + *currentClip;
3852 const QSpan *clipEnd = clip->m_spans + clip->count;
3854 while (available && spans < end ) {
3855 if (clipSpans >= clipEnd) {
3859 if (clipSpans->y > spans->y) {
3863 if (spans->y != clipSpans->y) {
3864 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3865 clipSpans = clip->m_clipLines[spans->y].spans;
3870 Q_ASSERT(spans->y == clipSpans->y);
3873 int sx2 = sx1 + spans->len;
3874 int cx1 = clipSpans->x;
3875 int cx2 = cx1 + clipSpans->len;
3877 if (cx1 < sx1 && cx2 < sx1) {
3880 } else if (sx1 < cx1 && sx2 < cx1) {
3884 int x = qMax(sx1, cx1);
3885 int len = qMin(sx2, cx2) - x;
3887 out->x = qMax(sx1, cx1);
3888 out->len = qMin(sx2, cx2) - out->x;
3890 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
3902 *currentClip = clipSpans - clip->m_spans;
3906 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
3908 // qDebug() << "qt_span_fill_clipped" << spanCount;
3909 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3911 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3913 const int NSPANS = 256;
3914 QSpan cspans[NSPANS];
3915 int currentClip = 0;
3916 const QSpan *end = spans + spanCount;
3917 while (spans < end) {
3918 QSpan *clipped = cspans;
3919 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
3920 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
3921 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
3923 if (clipped - cspans)
3924 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
3930 Clip spans to \a{clip}-rectangle.
3931 Returns number of unclipped spans
3933 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
3936 const short minx = clip.left();
3937 const short miny = clip.top();
3938 const short maxx = clip.right();
3939 const short maxy = clip.bottom();
3942 for (int i = 0; i < numSpans; ++i) {
3943 if (spans[i].y > maxy)
3945 if (spans[i].y < miny
3946 || spans[i].x > maxx
3947 || spans[i].x + spans[i].len <= minx) {
3950 if (spans[i].x < minx) {
3951 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
3954 spans[n].x = spans[i].x;
3955 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
3957 if (spans[n].len == 0)
3959 spans[n].y = spans[i].y;
3960 spans[n].coverage = spans[i].coverage;
3967 static void qt_span_fill_clipRect(int count, const QSpan *spans,
3970 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
3971 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
3973 Q_ASSERT(fillData->clip);
3974 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
3976 // hw: check if this const_cast<> is safe!!!
3977 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
3978 fillData->clip->clipRect);
3980 fillData->unclipped_blend(count, spans, fillData);
3983 static void qt_span_clip(int count, const QSpan *spans, void *userData)
3985 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
3987 // qDebug() << " qt_span_clip: " << count << clipData->operation;
3988 // for (int i = 0; i < qMin(count, 10); ++i) {
3989 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
3992 switch (clipData->operation) {
3994 case Qt::IntersectClip:
3996 QClipData *newClip = clipData->newClip;
3997 newClip->initialize();
3999 int currentClip = 0;
4000 const QSpan *end = spans + count;
4001 while (spans < end) {
4002 QSpan *newspans = newClip->m_spans + newClip->count;
4003 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4004 &newspans, newClip->allocated - newClip->count);
4005 newClip->count = newspans - newClip->m_spans;
4007 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4008 newClip->allocated *= 2;
4014 case Qt::ReplaceClip:
4015 clipData->newClip->appendSpans(spans, count);
4023 QImage QRasterBuffer::bufferImage() const
4025 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4027 for (int y = 0; y < m_height; ++y) {
4028 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4030 for (int x=0; x<m_width; ++x) {
4031 uint argb = span[x];
4032 image.setPixel(x, y, argb);
4040 void QRasterBuffer::flushToARGBImage(QImage *target) const
4042 int w = qMin(m_width, target->width());
4043 int h = qMin(m_height, target->height());
4045 for (int y=0; y<h; ++y) {
4046 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4047 QRgb *dest = (QRgb *) target->scanLine(y);
4048 for (int x=0; x<w; ++x) {
4049 QRgb pixel = sourceLine[x];
4050 int alpha = qAlpha(pixel);
4054 dest[x] = (alpha << 24)
4055 | ((255*qRed(pixel)/alpha) << 16)
4056 | ((255*qGreen(pixel)/alpha) << 8)
4057 | ((255*qBlue(pixel)/alpha) << 0);
4064 class QGradientCache
4068 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4069 stops(s), opacity(op), interpolationMode(mode) {}
4070 uint buffer[GRADIENT_STOPTABLE_SIZE];
4071 QGradientStops stops;
4073 QGradient::InterpolationMode interpolationMode;
4076 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4079 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4080 quint64 hash_val = 0;
4082 QGradientStops stops = gradient.stops();
4083 for (int i = 0; i < stops.size() && i <= 2; i++)
4084 hash_val += stops[i].second.rgba();
4086 QMutexLocker lock(&mutex);
4087 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4089 if (it == cache.constEnd())
4090 return addCacheElement(hash_val, gradient, opacity);
4093 const CacheInfo &cache_info = it.value();
4094 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4095 return cache_info.buffer;
4097 } while (it != cache.constEnd() && it.key() == hash_val);
4098 // an exact match for these stops and opacity was not found, create new cache
4099 return addCacheElement(hash_val, gradient, opacity);
4103 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4105 inline int maxCacheSize() const { return 60; }
4106 inline void generateGradientColorTable(const QGradient& g,
4108 int size, int opacity) const;
4109 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4110 if (cache.size() == maxCacheSize()) {
4111 // may remove more than 1, but OK
4112 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4114 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4115 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4116 return cache.insert(hash_val, cache_entry).value().buffer;
4119 QGradientColorTableHash cache;
4123 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4125 QGradientStops stops = gradient.stops();
4126 int stopCount = stops.count();
4127 Q_ASSERT(stopCount > 0);
4129 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4131 if (stopCount == 2) {
4132 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4133 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4135 qreal first_stop = stops[0].first;
4136 qreal second_stop = stops[1].first;
4138 if (second_stop < first_stop) {
4139 qSwap(first_color, second_color);
4140 qSwap(first_stop, second_stop);
4143 if (colorInterpolation) {
4144 first_color = PREMUL(first_color);
4145 second_color = PREMUL(second_color);
4148 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4149 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4151 uint red_first = qRed(first_color) << 16;
4152 uint green_first = qGreen(first_color) << 16;
4153 uint blue_first = qBlue(first_color) << 16;
4154 uint alpha_first = qAlpha(first_color) << 16;
4156 uint red_second = qRed(second_color) << 16;
4157 uint green_second = qGreen(second_color) << 16;
4158 uint blue_second = qBlue(second_color) << 16;
4159 uint alpha_second = qAlpha(second_color) << 16;
4162 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4163 if (colorInterpolation)
4164 colorTable[i] = first_color;
4166 colorTable[i] = PREMUL(first_color);
4169 if (i < second_index) {
4170 qreal reciprocal = qreal(1) / (second_index - first_index);
4172 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4173 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4174 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4175 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4178 red_first += 1 << 15;
4179 green_first += 1 << 15;
4180 blue_first += 1 << 15;
4181 alpha_first += 1 << 15;
4183 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4184 red_first += red_delta;
4185 green_first += green_delta;
4186 blue_first += blue_delta;
4187 alpha_first += alpha_delta;
4189 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4190 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4192 if (colorInterpolation)
4193 colorTable[i] = color;
4195 colorTable[i] = PREMUL(color);
4199 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4200 if (colorInterpolation)
4201 colorTable[i] = second_color;
4203 colorTable[i] = PREMUL(second_color);
4209 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4210 if (stopCount == 1) {
4211 current_color = PREMUL(current_color);
4212 for (int i = 0; i < size; ++i)
4213 colorTable[i] = current_color;
4217 // The position where the gradient begins and ends
4218 qreal begin_pos = stops[0].first;
4219 qreal end_pos = stops[stopCount-1].first;
4221 int pos = 0; // The position in the color table.
4224 qreal incr = 1 / qreal(size); // the double increment.
4225 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4227 // Up to first point
4228 colorTable[pos++] = PREMUL(current_color);
4229 while (dpos <= begin_pos) {
4230 colorTable[pos] = colorTable[pos - 1];
4235 int current_stop = 0; // We always interpolate between current and current + 1.
4237 qreal t; // position between current left and right stops
4238 qreal t_delta; // the t increment per entry in the color table
4240 if (dpos < end_pos) {
4242 while (dpos > stops[current_stop+1].first)
4245 if (current_stop != 0)
4246 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4247 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4249 if (colorInterpolation) {
4250 current_color = PREMUL(current_color);
4251 next_color = PREMUL(next_color);
4254 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4255 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4256 t = (dpos - stops[current_stop].first) * c;
4260 Q_ASSERT(current_stop < stopCount);
4262 int dist = qRound(t);
4263 int idist = 256 - dist;
4265 if (colorInterpolation)
4266 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4268 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4273 if (dpos >= end_pos)
4279 while (dpos > stops[current_stop+skip+1].first)
4283 current_stop += skip;
4285 current_color = next_color;
4287 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4288 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4290 if (colorInterpolation) {
4292 current_color = PREMUL(current_color);
4293 next_color = PREMUL(next_color);
4296 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4297 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4298 t = (dpos - stops[current_stop].first) * c;
4305 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4306 while (pos < size - 1) {
4307 colorTable[pos] = current_color;
4311 // Make sure the last color stop is represented at the end of the table
4312 colorTable[size - 1] = current_color;
4315 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4318 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4324 m11 = m22 = m33 = 1.;
4325 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4326 clip = pe ? pe->d_func()->clip() : 0;
4329 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4331 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4333 Qt::BrushStyle brushStyle = qbrush_style(brush);
4334 switch (brushStyle) {
4335 case Qt::SolidPattern: {
4337 QColor c = qbrush_color(brush);
4338 QRgb rgba = c.rgba();
4339 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4340 if ((solid.color & 0xff000000) == 0
4341 && compositionMode == QPainter::CompositionMode_SourceOver) {
4347 case Qt::LinearGradientPattern:
4349 type = LinearGradient;
4350 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4351 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4352 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4353 gradient.spread = g->spread();
4355 QLinearGradientData &linearData = gradient.linear;
4357 linearData.origin.x = g->start().x();
4358 linearData.origin.y = g->start().y();
4359 linearData.end.x = g->finalStop().x();
4360 linearData.end.y = g->finalStop().y();
4364 case Qt::RadialGradientPattern:
4366 type = RadialGradient;
4367 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4368 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4369 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4370 gradient.spread = g->spread();
4372 QRadialGradientData &radialData = gradient.radial;
4374 QPointF center = g->center();
4375 radialData.center.x = center.x();
4376 radialData.center.y = center.y();
4377 radialData.center.radius = g->centerRadius();
4378 QPointF focal = g->focalPoint();
4379 radialData.focal.x = focal.x();
4380 radialData.focal.y = focal.y();
4381 radialData.focal.radius = g->focalRadius();
4385 case Qt::ConicalGradientPattern:
4387 type = ConicalGradient;
4388 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4389 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4390 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4391 gradient.spread = QGradient::RepeatSpread;
4393 QConicalGradientData &conicalData = gradient.conical;
4395 QPointF center = g->center();
4396 conicalData.center.x = center.x();
4397 conicalData.center.y = center.y();
4398 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4402 case Qt::Dense1Pattern:
4403 case Qt::Dense2Pattern:
4404 case Qt::Dense3Pattern:
4405 case Qt::Dense4Pattern:
4406 case Qt::Dense5Pattern:
4407 case Qt::Dense6Pattern:
4408 case Qt::Dense7Pattern:
4409 case Qt::HorPattern:
4410 case Qt::VerPattern:
4411 case Qt::CrossPattern:
4412 case Qt::BDiagPattern:
4413 case Qt::FDiagPattern:
4414 case Qt::DiagCrossPattern:
4417 tempImage = new QImage();
4418 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4419 initTexture(tempImage, alpha, QTextureData::Tiled);
4421 case Qt::TexturePattern:
4424 tempImage = new QImage();
4426 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4427 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4429 *tempImage = brush.textureImage();
4430 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4438 adjustSpanMethods();
4441 void QSpanData::adjustSpanMethods()
4451 unclipped_blend = 0;
4454 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4455 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4456 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4457 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4458 fillRect = rasterBuffer->drawHelper->fillRect;
4460 case LinearGradient:
4461 case RadialGradient:
4462 case ConicalGradient:
4463 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4466 unclipped_blend = qBlendTexture;
4467 if (!texture.imageData)
4468 unclipped_blend = 0;
4473 if (!unclipped_blend) {
4476 blend = unclipped_blend;
4477 } else if (clip->hasRectClip) {
4478 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4480 blend = qt_span_fill_clipped;
4484 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4487 // make sure we round off correctly in qdrawhelper.cpp
4488 delta.translate(1.0 / 65536, 1.0 / 65536);
4490 QTransform inv = (delta * matrix).inverted();
4503 const bool affine = !m13 && !m23;
4504 fast_matrix = affine
4505 && m11 * m11 + m21 * m21 < 1e4
4506 && m12 * m12 + m22 * m22 < 1e4
4510 adjustSpanMethods();
4513 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4515 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4516 if (!d || d->height == 0) {
4517 texture.imageData = 0;
4524 texture.bytesPerLine = 0;
4525 texture.format = QImage::Format_Invalid;
4526 texture.colorTable = 0;
4527 texture.hasAlpha = alpha != 256;
4529 texture.imageData = d->data;
4530 texture.width = d->width;
4531 texture.height = d->height;
4533 if (sourceRect.isNull()) {
4536 texture.x2 = texture.width;
4537 texture.y2 = texture.height;
4539 texture.x1 = sourceRect.x();
4540 texture.y1 = sourceRect.y();
4541 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4542 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4545 texture.bytesPerLine = d->bytes_per_line;
4547 texture.format = d->format;
4548 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4549 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4551 texture.const_alpha = alpha;
4552 texture.type = _type;
4554 adjustSpanMethods();
4559 \a x and \a y is relative to the midpoint of \a rect.
4561 static inline void drawEllipsePoints(int x, int y, int length,
4564 ProcessSpans pen_func, ProcessSpans brush_func,
4565 QSpanData *pen_data, QSpanData *brush_data)
4570 QT_FT_Span outline[4];
4571 const int midx = rect.x() + (rect.width() + 1) / 2;
4572 const int midy = rect.y() + (rect.height() + 1) / 2;
4578 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4579 outline[0].len = qMin(length, x - outline[0].x);
4581 outline[0].coverage = 255;
4585 outline[1].len = length;
4587 outline[1].coverage = 255;
4590 outline[2].x = outline[0].x;
4591 outline[2].len = outline[0].len;
4592 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4593 outline[2].coverage = 255;
4597 outline[3].len = length;
4598 outline[3].y = outline[2].y;
4599 outline[3].coverage = 255;
4601 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4605 fill[0].x = outline[0].x + outline[0].len - 1;
4606 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4607 fill[0].y = outline[1].y;
4608 fill[0].coverage = 255;
4611 fill[1].x = outline[2].x + outline[2].len - 1;
4612 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4613 fill[1].y = outline[3].y;
4614 fill[1].coverage = 255;
4616 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4617 n = qt_intersect_spans(fill, n, clip);
4619 brush_func(n, fill, brush_data);
4622 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4623 n = qt_intersect_spans(outline, n, clip);
4625 pen_func(n, outline, pen_data);
4631 Draws an ellipse using the integer point midpoint algorithm.
4633 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4634 ProcessSpans pen_func, ProcessSpans brush_func,
4635 QSpanData *pen_data, QSpanData *brush_data)
4637 const qreal a = qreal(rect.width()) / 2;
4638 const qreal b = qreal(rect.height()) / 2;
4639 qreal d = b*b - (a*a*b) + 0.25*a*a;
4642 int y = (rect.height() + 1) / 2;
4646 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4647 if (d < 0) { // select E
4650 } else { // select SE
4651 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4652 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4653 pen_func, brush_func, pen_data, brush_data);
4658 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4659 pen_func, brush_func, pen_data, brush_data);
4662 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4663 const int miny = rect.height() & 0x1;
4665 if (d < 0) { // select SE
4666 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4668 } else { // select S
4669 d += a*a*(-2*y + 3);
4672 drawEllipsePoints(x, y, 1, rect, clip,
4673 pen_func, brush_func, pen_data, brush_data);
4678 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4681 Draws the first \a pointCount points in the buffer \a points
4683 The default implementation converts the first \a pointCount QPoints in \a points
4684 to QPointFs and calls the floating point version of drawPoints.
4688 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4691 Reimplement this function to draw the largest ellipse that can be
4692 contained within rectangle \a rect.
4695 #ifdef QT_DEBUG_DRAW
4696 void dumpClip(int width, int height, const QClipData *clip)
4698 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4699 clipImg.fill(0xffff0000);
4706 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4708 for (int i = 0; i < clip->count; ++i) {
4709 const QSpan *span = ((QClipData *) clip)->spans() + i;
4710 for (int j = 0; j < span->len; ++j)
4711 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4712 x0 = qMin(x0, int(span->x));
4713 x1 = qMax(x1, int(span->x + span->len - 1));
4715 y0 = qMin(y0, int(span->y));
4716 y1 = qMax(y1, int(span->y));
4719 static int counter = 0;
4726 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4727 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));