1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
57 #if defined (Q_WS_X11)
58 # include <private/qfontengine_ft_p.h>
61 // #include <private/qdatabuffer_p.h>
62 // #include <private/qpainter_p.h>
63 #include <private/qmath_p.h>
64 #include <private/qtextengine_p.h>
65 #include <private/qfontengine_p.h>
66 #include <private/qpixmap_raster_p.h>
67 // #include <private/qpolygonclipper_p.h>
68 // #include <private/qrasterizer_p.h>
69 #include <private/qimage_p.h>
70 #include <private/qstatictext_p.h>
71 #include <private/qcosmeticstroker_p.h>
72 #include "qmemrotate_p.h"
74 #include "qpaintengine_raster_p.h"
75 // #include "qbezier_p.h"
76 #include "qoutlinemapper_p.h"
79 # include <qt_windows.h>
80 # include <qvarlengtharray.h>
81 # include <private/qfontengine_p.h>
82 # if defined(Q_OS_WINCE)
83 # include "qguifunctions_wince.h"
85 #elif defined(Q_WS_MAC)
86 # include <private/qt_mac_p.h>
87 # include <private/qpixmap_mac_p.h>
88 # include <private/qpaintengine_mac_p.h>
89 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
90 # include <private/qfontengine_s60_p.h>
91 #elif defined(Q_WS_QPA)
92 # include <private/qfontengine_ft_p.h>
95 #if defined(Q_OS_WIN64)
102 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
104 #define qreal_to_fixed_26_6(f) (int(f * 64))
105 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
106 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
108 // #define QT_DEBUG_DRAW
110 void dumpClip(int width, int height, const QClipData *clip);
113 #define QT_FAST_SPANS
116 // A little helper macro to get a better approximation of dimensions.
117 // If we have a rect that starting at 0.5 of width 3.5 it should span
119 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
123 static inline bool winClearTypeFontsEnabled()
126 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
127 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
128 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
130 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
131 return result == FE_FONTSMOOTHINGCLEARTYPE;
134 bool QRasterPaintEngine::clearTypeFontsEnabled()
136 static const bool result = winClearTypeFontsEnabled();
143 extern bool qt_applefontsmoothing_enabled;
147 /********************************************************************************
150 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
151 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
152 static void qt_span_clip(int count, const QSpan *spans, void *userData);
153 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
159 Qt::ClipOperation operation;
165 LineDrawIncludeLastPixel
168 struct QRasterFloatPoint {
174 static const QRectF boundingRect(const QPointF *points, int pointCount)
176 const QPointF *e = points;
177 const QPointF *last = points + pointCount;
178 qreal minx, maxx, miny, maxy;
179 minx = maxx = e->x();
180 miny = maxy = e->y();
184 else if (e->x() > maxx)
188 else if (e->y() > maxy)
191 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
195 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
196 return (elementCount == 5 // 5-point polygon, check for closed rect
197 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
198 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
199 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
200 && pts[0] < pts[4] && pts[1] < pts[5]
202 (elementCount == 4 // 4-point polygon, check for unclosed rect
203 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
204 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
205 && pts[0] < pts[4] && pts[1] < pts[5]
210 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
212 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
215 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
217 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
220 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
221 qfixed c2x, qfixed c2y,
222 qfixed ex, qfixed ey,
225 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
226 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
227 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
231 #if !defined(QT_NO_DEBUG) && 0
232 static void qt_debug_path(const QPainterPath &path)
234 const char *names[] = {
241 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
242 for (int i=0; i<path.elementCount(); ++i) {
243 const QPainterPath::Element &e = path.elementAt(i);
244 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
245 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
250 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
251 QPaintEngineExPrivate(),
258 \class QRasterPaintEngine
263 \brief The QRasterPaintEngine class enables hardware acceleration
264 of painting operations in Qt for Embedded Linux.
266 Note that this functionality is only available in
267 \l{Qt for Embedded Linux}.
269 In \l{Qt for Embedded Linux}, painting is a pure software
270 implementation. But starting with Qt 4.2, it is
271 possible to add an accelerated graphics driver to take advantage
272 of available hardware resources.
274 Hardware acceleration is accomplished by creating a custom screen
275 driver, accelerating the copying from memory to the screen, and
276 implementing a custom paint engine accelerating the various
277 painting operations. Then a custom paint device (derived from the
278 QCustomRasterPaintDevice class) and a custom window surface
279 (derived from QWSWindowSurface) must be implemented to make
280 \l{Qt for Embedded Linux} aware of the accelerated driver.
282 \note The QRasterPaintEngine class does not support 8-bit images.
283 Instead, they need to be converted to a supported format, such as
284 QImage::Format_ARGB32_Premultiplied.
286 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
287 documentation for details.
289 \sa QCustomRasterPaintDevice, QPaintEngine
293 \fn Type QRasterPaintEngine::type() const
299 \relates QRasterPaintEngine
301 A struct equivalent to QT_FT_Span, containing a position (x,
302 y), the span's length in pixels and its color/coverage (a value
303 ranging from 0 to 255).
309 Creates a raster based paint engine for operating on the given
310 \a device, with the complete set of \l
311 {QPaintEngine::PaintEngineFeature}{paint engine features and
314 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
315 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
317 d_func()->device = device;
324 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
327 d_func()->device = device;
331 void QRasterPaintEngine::init()
333 Q_D(QRasterPaintEngine);
340 // The antialiasing raster.
341 d->grayRaster.reset(new QT_FT_Raster);
342 Q_CHECK_PTR(d->grayRaster.data());
343 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
344 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
347 d->rasterizer.reset(new QRasterizer);
348 d->rasterBuffer.reset(new QRasterBuffer());
349 d->outlineMapper.reset(new QOutlineMapper);
350 d->outlinemapper_xform_dirty = true;
352 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
353 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
354 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
356 d->baseClip.reset(new QClipData(d->device->height()));
357 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
359 d->image_filler.init(d->rasterBuffer.data(), this);
360 d->image_filler.type = QSpanData::Texture;
362 d->image_filler_xform.init(d->rasterBuffer.data(), this);
363 d->image_filler_xform.type = QSpanData::Texture;
365 d->solid_color_filler.init(d->rasterBuffer.data(), this);
366 d->solid_color_filler.type = QSpanData::Solid;
368 d->deviceDepth = d->device->depth();
370 d->mono_surface = false;
371 gccaps &= ~PorterDuff;
373 QImage::Format format = QImage::Format_Invalid;
375 switch (d->device->devType()) {
376 case QInternal::Pixmap:
377 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
379 case QInternal::Image:
380 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
383 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
389 case QImage::Format_MonoLSB:
390 case QImage::Format_Mono:
391 d->mono_surface = true;
393 case QImage::Format_ARGB8565_Premultiplied:
394 case QImage::Format_ARGB8555_Premultiplied:
395 case QImage::Format_ARGB6666_Premultiplied:
396 case QImage::Format_ARGB4444_Premultiplied:
397 case QImage::Format_ARGB32_Premultiplied:
398 case QImage::Format_ARGB32:
399 gccaps |= PorterDuff;
401 case QImage::Format_RGB32:
402 case QImage::Format_RGB444:
403 case QImage::Format_RGB555:
404 case QImage::Format_RGB666:
405 case QImage::Format_RGB888:
406 case QImage::Format_RGB16:
417 Destroys this paint engine.
419 QRasterPaintEngine::~QRasterPaintEngine()
421 Q_D(QRasterPaintEngine);
423 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
429 bool QRasterPaintEngine::begin(QPaintDevice *device)
431 Q_D(QRasterPaintEngine);
433 if (device->devType() == QInternal::Pixmap) {
434 QPixmap *pixmap = static_cast<QPixmap *>(device);
435 QPixmapData *pd = pixmap->pixmapData();
436 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
437 d->device = pd->buffer();
442 // Make sure QPaintEngine::paintDevice() returns the proper device.
445 Q_ASSERT(d->device->devType() == QInternal::Image
446 || d->device->devType() == QInternal::CustomRaster);
448 d->systemStateChanged();
450 QRasterPaintEngineState *s = state();
451 ensureOutlineMapper();
452 d->outlineMapper->m_clip_rect = d->deviceRect;
454 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
455 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
456 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
457 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
459 d->rasterizer->setClipRect(d->deviceRect);
461 s->penData.init(d->rasterBuffer.data(), this);
462 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
463 s->stroker = &d->basicStroker;
464 d->basicStroker.setClipRect(d->deviceRect);
466 s->brushData.init(d->rasterBuffer.data(), this);
467 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
469 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
471 setDirty(DirtyBrushOrigin);
474 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
475 << ") devType:" << device->devType()
476 << "devRect:" << d->deviceRect;
478 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
482 #if defined(Q_OS_WIN)
483 d->isPlain45DegreeRotation = true;
487 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
488 #if defined(Q_OS_WIN)
489 else if (clearTypeFontsEnabled())
490 #elif defined (Q_WS_MAC)
491 else if (qt_applefontsmoothing_enabled)
496 QImage::Format format = static_cast<QImage *>(d->device)->format();
497 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
500 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
502 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
511 bool QRasterPaintEngine::end()
514 Q_D(QRasterPaintEngine);
515 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
517 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
527 void QRasterPaintEngine::releaseBuffer()
529 Q_D(QRasterPaintEngine);
530 d->rasterBuffer.reset(new QRasterBuffer);
536 QSize QRasterPaintEngine::size() const
538 Q_D(const QRasterPaintEngine);
539 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
546 void QRasterPaintEngine::saveBuffer(const QString &s) const
548 Q_D(const QRasterPaintEngine);
549 d->rasterBuffer->bufferImage().save(s, "PNG");
556 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
558 QRasterPaintEngineState *s = state();
559 // FALCON: get rid of this line, see drawImage call below.
561 QTransform::TransformationType txop = s->matrix.type();
565 case QTransform::TxNone:
566 s->flags.int_xform = true;
569 case QTransform::TxTranslate:
570 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
571 && qreal(int(s->matrix.dy())) == s->matrix.dy();
574 case QTransform::TxScale:
575 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
576 && qreal(int(s->matrix.dy())) == s->matrix.dy()
577 && qreal(int(s->matrix.m11())) == s->matrix.m11()
578 && qreal(int(s->matrix.m22())) == s->matrix.m22();
581 default: // shear / perspective...
582 s->flags.int_xform = false;
586 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
588 ensureOutlineMapper();
591 Q_D(QRasterPaintEngine);
592 d->isPlain45DegreeRotation = false;
593 if (txop >= QTransform::TxRotate) {
594 d->isPlain45DegreeRotation =
595 (qFuzzyIsNull(matrix.m11())
596 && qFuzzyIsNull(matrix.m12() - qreal(1))
597 && qFuzzyIsNull(matrix.m21() + qreal(1))
598 && qFuzzyIsNull(matrix.m22())
601 (qFuzzyIsNull(matrix.m11() + qreal(1))
602 && qFuzzyIsNull(matrix.m12())
603 && qFuzzyIsNull(matrix.m21())
604 && qFuzzyIsNull(matrix.m22() + qreal(1))
607 (qFuzzyIsNull(matrix.m11())
608 && qFuzzyIsNull(matrix.m12() + qreal(1))
609 && qFuzzyIsNull(matrix.m21() - qreal(1))
610 && qFuzzyIsNull(matrix.m22())
620 QRasterPaintEngineState::~QRasterPaintEngineState()
622 if (flags.has_clip_ownership)
627 QRasterPaintEngineState::QRasterPaintEngineState()
639 flags.fast_pen = true;
640 flags.antialiased = false;
641 flags.bilinear = false;
642 flags.fast_text = true;
643 flags.int_xform = true;
644 flags.tx_noshear = true;
645 flags.fast_images = true;
648 flags.has_clip_ownership = false;
653 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
658 , strokeFlags(s.strokeFlags)
659 , lastBrush(s.lastBrush)
660 , brushData(s.brushData)
661 , fillFlags(s.fillFlags)
662 , pixmapFlags(s.pixmapFlags)
663 , intOpacity(s.intOpacity)
667 , flag_bits(s.flag_bits)
669 brushData.tempImage = 0;
670 penData.tempImage = 0;
671 flags.has_clip_ownership = false;
677 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
679 QRasterPaintEngineState *s;
681 s = new QRasterPaintEngineState();
683 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
691 void QRasterPaintEngine::setState(QPainterState *s)
693 Q_D(QRasterPaintEngine);
694 QPaintEngineEx::setState(s);
695 d->rasterBuffer->compositionMode = s->composition_mode;
699 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
704 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
711 void QRasterPaintEngine::penChanged()
714 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
716 QRasterPaintEngineState *s = state();
717 s->strokeFlags |= DirtyPen;
718 s->dirty |= DirtyPen;
724 void QRasterPaintEngine::updatePen(const QPen &pen)
726 Q_D(QRasterPaintEngine);
727 QRasterPaintEngineState *s = state();
729 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
732 Qt::PenStyle pen_style = qpen_style(pen);
737 s->penData.clip = d->clip();
738 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
740 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
741 || pen.brush().transform().type() >= QTransform::TxNone) {
742 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
745 // Slightly ugly handling of an uncommon case... We need to change
746 // the pen because it is reused in draw_midpoint to decide dashed
748 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
749 pen_style = Qt::SolidLine;
750 s->lastPen.setStyle(Qt::SolidLine);
753 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
754 d->basicStroker.setCapStyle(qpen_capStyle(pen));
755 d->basicStroker.setMiterLimit(pen.miterLimit());
757 qreal penWidth = qpen_widthf(pen);
759 d->basicStroker.setStrokeWidth(1);
761 d->basicStroker.setStrokeWidth(penWidth);
763 if(pen_style == Qt::SolidLine) {
764 s->stroker = &d->basicStroker;
765 } else if (pen_style != Qt::NoPen) {
767 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
768 if (pen.isCosmetic()) {
769 d->dashStroker->setClipRect(d->deviceRect);
771 // ### I've seen this inverted devrect multiple places now...
772 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
773 d->dashStroker->setClipRect(clipRect);
775 d->dashStroker->setDashPattern(pen.dashPattern());
776 d->dashStroker->setDashOffset(pen.dashOffset());
777 s->stroker = d->dashStroker.data();
782 ensureState(); // needed because of tx_noshear...
783 s->flags.fast_pen = pen_style > Qt::NoPen
785 && ((pen.isCosmetic() && penWidth <= 1)
786 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
788 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
798 void QRasterPaintEngine::brushOriginChanged()
800 QRasterPaintEngineState *s = state();
802 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
805 s->fillFlags |= DirtyBrushOrigin;
812 void QRasterPaintEngine::brushChanged()
814 QRasterPaintEngineState *s = state();
816 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
818 s->fillFlags |= DirtyBrush;
827 void QRasterPaintEngine::updateBrush(const QBrush &brush)
830 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
832 Q_D(QRasterPaintEngine);
833 QRasterPaintEngineState *s = state();
834 // must set clip prior to setup, as setup uses it...
835 s->brushData.clip = d->clip();
836 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
837 if (s->fillFlags & DirtyTransform
838 || brush.transform().type() >= QTransform::TxNone)
839 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
840 s->lastBrush = brush;
844 void QRasterPaintEngine::updateOutlineMapper()
846 Q_D(QRasterPaintEngine);
847 d->outlineMapper->setMatrix(state()->matrix);
850 void QRasterPaintEngine::updateState()
852 QRasterPaintEngineState *s = state();
854 if (s->dirty & DirtyTransform)
855 updateMatrix(s->matrix);
857 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
858 const QPainter::CompositionMode mode = s->composition_mode;
859 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
860 && s->intOpacity == 256
861 && (mode == QPainter::CompositionMode_Source
862 || (mode == QPainter::CompositionMode_SourceOver
863 && qAlpha(s->penData.solid.color) == 255));
873 void QRasterPaintEngine::opacityChanged()
875 QRasterPaintEngineState *s = state();
878 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
881 s->fillFlags |= DirtyOpacity;
882 s->strokeFlags |= DirtyOpacity;
883 s->pixmapFlags |= DirtyOpacity;
884 s->dirty |= DirtyOpacity;
885 s->intOpacity = (int) (s->opacity * 256);
891 void QRasterPaintEngine::compositionModeChanged()
893 Q_D(QRasterPaintEngine);
894 QRasterPaintEngineState *s = state();
897 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
900 s->fillFlags |= DirtyCompositionMode;
901 s->dirty |= DirtyCompositionMode;
903 s->strokeFlags |= DirtyCompositionMode;
904 d->rasterBuffer->compositionMode = s->composition_mode;
906 d->recalculateFastImages();
912 void QRasterPaintEngine::renderHintsChanged()
914 QRasterPaintEngineState *s = state();
917 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
920 bool was_aa = s->flags.antialiased;
921 bool was_bilinear = s->flags.bilinear;
923 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
924 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
926 if (was_aa != s->flags.antialiased)
927 s->strokeFlags |= DirtyHints;
929 if (was_bilinear != s->flags.bilinear) {
930 s->strokeFlags |= DirtyPen;
931 s->fillFlags |= DirtyBrush;
934 Q_D(QRasterPaintEngine);
935 d->recalculateFastImages();
941 void QRasterPaintEngine::transformChanged()
943 QRasterPaintEngineState *s = state();
946 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
949 s->fillFlags |= DirtyTransform;
950 s->strokeFlags |= DirtyTransform;
952 s->dirty |= DirtyTransform;
954 Q_D(QRasterPaintEngine);
955 d->recalculateFastImages();
961 void QRasterPaintEngine::clipEnabledChanged()
963 QRasterPaintEngineState *s = state();
966 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
970 s->clip->enabled = s->clipEnabled;
971 s->fillFlags |= DirtyClipEnabled;
972 s->strokeFlags |= DirtyClipEnabled;
973 s->pixmapFlags |= DirtyClipEnabled;
977 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
979 SrcOverBlendFunc func,
984 if (alpha == 0 || !clip.isValid())
987 Q_ASSERT(img.depth() >= 8);
989 int srcBPL = img.bytesPerLine();
990 const uchar *srcBits = img.bits();
991 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
992 int iw = img.width();
993 int ih = img.height();
998 // Adjust the image according to the source offset...
999 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1002 // adapt the x parameters
1003 int x = qRound(pt.x());
1005 int cx2 = clip.x() + clip.width();
1008 srcBits += srcSize * d;
1013 int d = x + iw - cx2;
1019 // adapt the y paremeters...
1021 int cy2 = clip.y() + clip.height();
1022 int y = qRound(pt.y());
1025 srcBits += srcBPL * d;
1030 int d = y + ih - cy2;
1036 // call the blend function...
1037 int dstSize = rasterBuffer->bytesPerPixel();
1038 int dstBPL = rasterBuffer->bytesPerLine();
1039 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1046 void QRasterPaintEnginePrivate::systemStateChanged()
1048 QRect clipRect(0, 0,
1049 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1050 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1052 if (!systemClip.isEmpty()) {
1053 QRegion clippedDeviceRgn = systemClip & clipRect;
1054 deviceRect = clippedDeviceRgn.boundingRect();
1055 baseClip->setClipRegion(clippedDeviceRgn);
1057 deviceRect = clipRect;
1058 baseClip->setClipRect(deviceRect);
1060 #ifdef QT_DEBUG_DRAW
1061 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1064 exDeviceRect = deviceRect;
1066 Q_Q(QRasterPaintEngine);
1067 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1068 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1069 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1072 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1074 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1077 Q_Q(QRasterPaintEngine);
1078 bool bilinear = q->state()->flags.bilinear;
1080 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1081 spanData->setupMatrix(b.transform() * m, bilinear);
1083 if (m.type() <= QTransform::TxTranslate) {
1084 // specialize setupMatrix for translation matrices
1085 // to avoid needless matrix inversion
1093 spanData->dx = -m.dx();
1094 spanData->dy = -m.dy();
1095 spanData->txop = m.type();
1096 spanData->bilinear = bilinear;
1097 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1098 spanData->adjustSpanMethods();
1100 spanData->setupMatrix(m, bilinear);
1105 // #define QT_CLIPPING_RATIOS
1107 #ifdef QT_CLIPPING_RATIOS
1112 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1114 if (d->clip()->hasRectClip)
1116 if (d->clip()->hasRegionClip)
1120 if ((totalClips % 5000) == 0) {
1121 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1122 rectClips * 100.0 / (qreal) totalClips,
1123 regionClips * 100.0 / (qreal) totalClips,
1124 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1133 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1135 if (s->flags.has_clip_ownership)
1138 s->flags.has_clip_ownership = false;
1141 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1143 s->fillFlags |= QPaintEngine::DirtyClipPath;
1144 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1145 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1147 d->solid_color_filler.clip = d->clip();
1148 d->solid_color_filler.adjustSpanMethods();
1150 #ifdef QT_DEBUG_DRAW
1151 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1160 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1162 #ifdef QT_DEBUG_DRAW
1163 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1165 if (path.elements()) {
1166 for (int i=0; i<path.elementCount(); ++i) {
1167 qDebug() << " - " << path.elements()[i]
1168 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1171 for (int i=0; i<path.elementCount(); ++i) {
1172 qDebug() << " ---- "
1173 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1178 Q_D(QRasterPaintEngine);
1179 QRasterPaintEngineState *s = state();
1181 const qreal *points = path.points();
1182 const QPainterPath::ElementType *types = path.elements();
1184 // There are some cases that are not supported by clip(QRect)
1185 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1186 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1187 if (s->matrix.type() <= QTransform::TxScale
1188 && ((path.shape() == QVectorPath::RectangleHint)
1189 || (isRect(points, path.elementCount())
1190 && (!types || (types[0] == QPainterPath::MoveToElement
1191 && types[1] == QPainterPath::LineToElement
1192 && types[2] == QPainterPath::LineToElement
1193 && types[3] == QPainterPath::LineToElement))))) {
1194 #ifdef QT_DEBUG_DRAW
1195 qDebug() << " --- optimizing vector clip to rect clip...";
1198 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1199 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1204 if (op == Qt::NoClip) {
1205 qrasterpaintengine_state_setNoClip(s);
1208 QClipData *base = d->baseClip.data();
1210 // Intersect with current clip when available...
1211 if (op == Qt::IntersectClip && s->clip)
1214 // We always intersect, except when there is nothing to
1215 // intersect with, in which case we simplify the operation to
1217 Qt::ClipOperation isectOp = Qt::IntersectClip;
1219 isectOp = Qt::ReplaceClip;
1221 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1222 newClip->initialize();
1223 ClipData clipData = { base, newClip, isectOp };
1224 ensureOutlineMapper();
1225 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1229 if (op == Qt::UniteClip) {
1231 QClipData *result = new QClipData(d->rasterBuffer->height());
1232 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1233 qt_merge_clip(current, newClip, result);
1241 if (s->flags.has_clip_ownership)
1245 s->flags.has_clip_ownership = true;
1247 qrasterpaintengine_dirty_clip(d, s);
1255 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1257 #ifdef QT_DEBUG_DRAW
1258 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1261 QRasterPaintEngineState *s = state();
1263 if (op == Qt::NoClip) {
1264 qrasterpaintengine_state_setNoClip(s);
1266 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1267 QPaintEngineEx::clip(rect, op);
1270 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1271 QPaintEngineEx::clip(rect, op);
1277 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1279 Q_D(QRasterPaintEngine);
1280 QRect clipRect = r & d->deviceRect;
1281 QRasterPaintEngineState *s = state();
1283 if (op == Qt::ReplaceClip || s->clip == 0) {
1285 // No current clip, hence we intersect with sysclip and be
1287 QRegion clipRegion = systemClip();
1288 QClipData *clip = new QClipData(d->rasterBuffer->height());
1290 if (clipRegion.isEmpty())
1291 clip->setClipRect(clipRect);
1293 clip->setClipRegion(clipRegion & clipRect);
1295 if (s->flags.has_clip_ownership)
1299 s->clip->enabled = true;
1300 s->flags.has_clip_ownership = true;
1302 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1303 QClipData *base = s->clip;
1306 if (base->hasRectClip || base->hasRegionClip) {
1307 if (!s->flags.has_clip_ownership) {
1308 s->clip = new QClipData(d->rasterBuffer->height());
1309 s->flags.has_clip_ownership = true;
1311 if (base->hasRectClip)
1312 s->clip->setClipRect(base->clipRect & clipRect);
1314 s->clip->setClipRegion(base->clipRegion & clipRect);
1315 s->clip->enabled = true;
1323 qrasterpaintengine_dirty_clip(d, s);
1331 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1333 #ifdef QT_DEBUG_DRAW
1334 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1337 Q_D(QRasterPaintEngine);
1339 if (region.rectCount() == 1) {
1340 clip(region.boundingRect(), op);
1344 QRasterPaintEngineState *s = state();
1345 const QClipData *clip = d->clip();
1346 const QClipData *baseClip = d->baseClip.data();
1348 if (op == Qt::NoClip) {
1349 qrasterpaintengine_state_setNoClip(s);
1350 } else if (s->matrix.type() > QTransform::TxScale
1351 || op == Qt::UniteClip
1352 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1353 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1354 QPaintEngineEx::clip(region, op);
1356 const QClipData *curClip;
1359 if (op == Qt::IntersectClip)
1364 if (s->flags.has_clip_ownership) {
1368 newClip = new QClipData(d->rasterBuffer->height());
1370 s->flags.has_clip_ownership = true;
1373 QRegion r = s->matrix.map(region);
1374 if (curClip->hasRectClip)
1375 newClip->setClipRegion(r & curClip->clipRect);
1376 else if (curClip->hasRegionClip)
1377 newClip->setClipRegion(r & curClip->clipRegion);
1379 qrasterpaintengine_dirty_clip(d, s);
1386 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1388 #ifdef QT_DEBUG_DRAW
1389 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1392 if (!fillData->blend)
1395 Q_D(QRasterPaintEngine);
1397 const QRectF controlPointRect = path.controlPointRect();
1399 QRasterPaintEngineState *s = state();
1400 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1401 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1402 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1403 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1404 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1405 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1407 if (!s->flags.antialiased && !do_clip) {
1408 d->initializeRasterizer(fillData);
1409 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1413 ensureOutlineMapper();
1414 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1417 static void fillRect_normalized(const QRect &r, QSpanData *data,
1418 QRasterPaintEnginePrivate *pe)
1422 bool rectClipped = true;
1425 x1 = qMax(r.x(), data->clip->xmin);
1426 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1427 y1 = qMax(r.y(), data->clip->ymin);
1428 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1429 rectClipped = data->clip->hasRectClip;
1432 x1 = qMax(r.x(), pe->deviceRect.x());
1433 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1434 y1 = qMax(r.y(), pe->deviceRect.y());
1435 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1437 x1 = qMax(r.x(), 0);
1438 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1439 y1 = qMax(r.y(), 0);
1440 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1443 if (x2 <= x1 || y2 <= y1)
1446 const int width = x2 - x1;
1447 const int height = y2 - y1;
1449 bool isUnclipped = rectClipped
1450 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1452 if (pe && isUnclipped) {
1453 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1455 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1456 || (mode == QPainter::CompositionMode_SourceOver
1457 && qAlpha(data->solid.color) == 255)))
1459 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1465 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1467 const int nspans = 256;
1468 QT_FT_Span spans[nspans];
1470 Q_ASSERT(data->blend);
1473 int n = qMin(nspans, y2 - y);
1477 spans[i].len = width;
1479 spans[i].coverage = 255;
1483 blend(n, spans, data);
1491 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1493 #ifdef QT_DEBUG_DRAW
1494 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1496 Q_D(QRasterPaintEngine);
1498 QRasterPaintEngineState *s = state();
1502 if (s->brushData.blend) {
1503 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1504 const QRect *r = rects;
1505 const QRect *lastRect = rects + rectCount;
1507 int offset_x = int(s->matrix.dx());
1508 int offset_y = int(s->matrix.dy());
1509 while (r < lastRect) {
1510 QRect rect = r->normalized();
1511 QRect rr = rect.translated(offset_x, offset_y);
1512 fillRect_normalized(rr, &s->brushData, d);
1516 QRectVectorPath path;
1517 for (int i=0; i<rectCount; ++i) {
1519 fill(path, s->brush);
1525 if (s->penData.blend) {
1526 QRectVectorPath path;
1527 if (s->flags.fast_pen) {
1528 QCosmeticStroker stroker(s, d->deviceRect);
1529 for (int i = 0; i < rectCount; ++i) {
1531 stroker.drawPath(path);
1534 for (int i = 0; i < rectCount; ++i) {
1536 stroke(path, s->pen);
1545 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1547 #ifdef QT_DEBUG_DRAW
1548 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1550 #ifdef QT_FAST_SPANS
1551 Q_D(QRasterPaintEngine);
1553 QRasterPaintEngineState *s = state();
1556 if (s->flags.tx_noshear) {
1558 if (s->brushData.blend) {
1559 d->initializeRasterizer(&s->brushData);
1560 for (int i = 0; i < rectCount; ++i) {
1561 const QRectF &rect = rects[i].normalized();
1564 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1565 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1566 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1571 if (s->penData.blend) {
1572 QRectVectorPath path;
1573 if (s->flags.fast_pen) {
1574 QCosmeticStroker stroker(s, d->deviceRect);
1575 for (int i = 0; i < rectCount; ++i) {
1577 stroker.drawPath(path);
1580 for (int i = 0; i < rectCount; ++i) {
1582 QPaintEngineEx::stroke(path, s->lastPen);
1589 #endif // QT_FAST_SPANS
1590 QPaintEngineEx::drawRects(rects, rectCount);
1597 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1599 Q_D(QRasterPaintEngine);
1600 QRasterPaintEngineState *s = state();
1603 if (!s->penData.blend)
1606 if (s->flags.fast_pen) {
1607 QCosmeticStroker stroker(s, d->deviceRect);
1608 stroker.drawPath(path);
1609 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1610 qreal width = s->lastPen.isCosmetic()
1611 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1612 : qpen_widthf(s->lastPen) * s->txscale;
1614 qreal dashOffset = s->lastPen.dashOffset();
1616 qreal patternLength = 0;
1617 const QVector<qreal> pattern = s->lastPen.dashPattern();
1618 for (int i = 0; i < pattern.size(); ++i)
1619 patternLength += pattern.at(i);
1621 if (patternLength > 0) {
1622 int n = qFloor(dashOffset / patternLength);
1623 dashOffset -= n * patternLength;
1624 while (dashOffset >= pattern.at(dashIndex)) {
1625 dashOffset -= pattern.at(dashIndex);
1626 if (++dashIndex >= pattern.size())
1632 Q_D(QRasterPaintEngine);
1633 d->initializeRasterizer(&s->penData);
1634 int lineCount = path.elementCount() / 2;
1635 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1637 for (int i = 0; i < lineCount; ++i) {
1638 if (lines[i].p1() == lines[i].p2()) {
1639 if (s->lastPen.capStyle() != Qt::FlatCap) {
1640 QPointF p = lines[i].p1();
1641 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1642 QPointF(p.x() + width*0.5, p.y())));
1643 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1648 const QLineF line = s->matrix.map(lines[i]);
1649 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1650 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1651 width / line.length(),
1652 s->lastPen.capStyle() == Qt::SquareCap);
1654 d->rasterizeLine_dashed(line, width,
1655 &dashIndex, &dashOffset, &inDash);
1660 QPaintEngineEx::stroke(path, pen);
1663 static inline QRect toNormalizedFillRect(const QRectF &rect)
1665 int x1 = qRound(rect.x());
1666 int y1 = qRound(rect.y());
1667 int x2 = qRound(rect.right());
1668 int y2 = qRound(rect.bottom());
1675 return QRect(x1, y1, x2 - x1, y2 - y1);
1681 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1685 #ifdef QT_DEBUG_DRAW
1686 QRectF rf = path.controlPointRect();
1687 qDebug() << "QRasterPaintEngine::fill(): "
1688 << "size=" << path.elementCount()
1689 << ", hints=" << hex << path.hints()
1693 Q_D(QRasterPaintEngine);
1694 QRasterPaintEngineState *s = state();
1697 if (!s->brushData.blend)
1700 if (path.shape() == QVectorPath::RectangleHint) {
1701 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1702 const qreal *p = path.points();
1703 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1704 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1705 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1709 if (s->flags.tx_noshear) {
1710 d->initializeRasterizer(&s->brushData);
1711 // ### Is normalizing really necessary here?
1712 const qreal *p = path.points();
1713 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1715 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1716 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1717 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1723 // ### Optimize for non transformed ellipses and rectangles...
1724 QRectF cpRect = path.controlPointRect();
1725 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1726 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1729 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1730 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1731 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1732 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1734 // ### Falonc: implement....
1735 // if (!s->flags.antialiased && !do_clip) {
1736 // d->initializeRasterizer(&s->brushData);
1737 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1741 ensureOutlineMapper();
1742 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1745 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1747 Q_D(QRasterPaintEngine);
1748 QRasterPaintEngineState *s = state();
1750 if (!s->flags.antialiased) {
1751 uint txop = s->matrix.type();
1752 if (txop == QTransform::TxNone) {
1753 fillRect_normalized(toNormalizedFillRect(r), data, d);
1755 } else if (txop == QTransform::TxTranslate) {
1756 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1757 fillRect_normalized(rr, data, d);
1759 } else if (txop == QTransform::TxScale) {
1760 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1761 fillRect_normalized(rr, data, d);
1766 if (s->flags.tx_noshear) {
1767 d->initializeRasterizer(data);
1768 QRectF nr = r.normalized();
1769 if (!nr.isEmpty()) {
1770 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1771 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1772 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1779 ensureOutlineMapper();
1780 fillPath(path, data);
1786 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1788 #ifdef QT_DEBUG_DRAW
1789 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1791 QRasterPaintEngineState *s = state();
1794 if (!s->brushData.blend)
1797 fillRect(r, &s->brushData);
1803 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1805 #ifdef QT_DEBUG_DRAW
1806 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1808 Q_D(QRasterPaintEngine);
1809 QRasterPaintEngineState *s = state();
1811 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1812 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1813 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1816 d->solid_color_filler.clip = d->clip();
1817 d->solid_color_filler.adjustSpanMethods();
1818 fillRect(r, &d->solid_color_filler);
1821 static inline bool isAbove(const QPointF *a, const QPointF *b)
1823 return a->y() < b->y();
1826 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1831 Q_ASSERT(pointCount >= 2);
1833 QVector<const QPointF *> sorted;
1834 sorted.reserve(pointCount);
1836 upper->reserve(pointCount * 3 / 4);
1837 lower->reserve(pointCount * 3 / 4);
1839 for (int i = 0; i < pointCount; ++i)
1840 sorted << points + i;
1842 qSort(sorted.begin(), sorted.end(), isAbove);
1844 qreal splitY = sorted.at(sorted.size() / 2)->y();
1846 const QPointF *end = points + pointCount;
1847 const QPointF *last = end - 1;
1849 QVector<QPointF> *bin[2] = { upper, lower };
1851 for (const QPointF *p = points; p < end; ++p) {
1852 int side = p->y() < splitY;
1853 int lastSide = last->y() < splitY;
1855 if (side != lastSide) {
1856 if (qFuzzyCompare(p->y(), splitY)) {
1857 bin[!side]->append(*p);
1858 } else if (qFuzzyCompare(last->y(), splitY)) {
1859 bin[side]->append(*last);
1861 QPointF delta = *p - *last;
1862 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1864 bin[0]->append(intersection);
1865 bin[1]->append(intersection);
1869 bin[side]->append(*p);
1874 // give up if we couldn't reduce the point count
1875 return upper->size() < pointCount && lower->size() < pointCount;
1881 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1883 Q_D(QRasterPaintEngine);
1884 QRasterPaintEngineState *s = state();
1886 const int maxPoints = 0xffff;
1888 // max amount of points that raster engine can reliably handle
1889 if (pointCount > maxPoints) {
1890 QVector<QPointF> upper, lower;
1892 if (splitPolygon(points, pointCount, &upper, &lower)) {
1893 fillPolygon(upper.constData(), upper.size(), mode);
1894 fillPolygon(lower.constData(), lower.size(), mode);
1896 qWarning("Polygon too complex for filling.");
1901 // Compose polygon fill..,
1902 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1903 ensureOutlineMapper();
1904 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1907 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1909 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1915 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1917 Q_D(QRasterPaintEngine);
1918 QRasterPaintEngineState *s = state();
1920 #ifdef QT_DEBUG_DRAW
1921 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1922 for (int i=0; i<pointCount; ++i)
1923 qDebug() << " - " << points[i];
1925 Q_ASSERT(pointCount >= 2);
1927 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1928 QRectF r(points[0], points[2]);
1934 if (mode != PolylineMode) {
1937 if (s->brushData.blend) {
1938 fillPolygon(points, pointCount, mode);
1942 // Do the outline...
1943 if (s->penData.blend) {
1944 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1945 if (s->flags.fast_pen) {
1946 QCosmeticStroker stroker(s, d->deviceRect);
1947 stroker.drawPath(vp);
1949 QPaintEngineEx::stroke(vp, s->lastPen);
1957 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1959 Q_D(QRasterPaintEngine);
1960 QRasterPaintEngineState *s = state();
1962 #ifdef QT_DEBUG_DRAW
1963 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1964 for (int i=0; i<pointCount; ++i)
1965 qDebug() << " - " << points[i];
1967 Q_ASSERT(pointCount >= 2);
1968 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1969 QRect r(points[0].x(),
1971 points[2].x() - points[0].x(),
1972 points[2].y() - points[0].y());
1980 if (mode != PolylineMode) {
1982 if (s->brushData.blend) {
1983 // Compose polygon fill..,
1984 ensureOutlineMapper();
1985 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1986 d->outlineMapper->moveTo(*points);
1987 const QPoint *p = points;
1988 const QPoint *ep = points + pointCount - 1;
1990 d->outlineMapper->lineTo(*(++p));
1992 d->outlineMapper->endOutline();
1995 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1997 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2001 // Do the outline...
2002 if (s->penData.blend) {
2003 int count = pointCount * 2;
2004 QVarLengthArray<qreal> fpoints(count);
2005 for (int i=0; i<count; ++i)
2006 fpoints[i] = ((int *) points)[i];
2007 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2009 if (s->flags.fast_pen) {
2010 QCosmeticStroker stroker(s, d->deviceRect);
2011 stroker.drawPath(vp);
2013 QPaintEngineEx::stroke(vp, s->lastPen);
2021 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2023 #ifdef QT_DEBUG_DRAW
2024 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2027 QPixmapData *pd = pixmap.pixmapData();
2028 if (pd->classId() == QPixmapData::RasterClass) {
2029 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2030 if (image.depth() == 1) {
2031 Q_D(QRasterPaintEngine);
2032 QRasterPaintEngineState *s = state();
2033 if (s->matrix.type() <= QTransform::TxTranslate) {
2035 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2037 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2040 QRasterPaintEngine::drawImage(pos, image);
2043 const QImage image = pixmap.toImage();
2044 if (pixmap.depth() == 1) {
2045 Q_D(QRasterPaintEngine);
2046 QRasterPaintEngineState *s = state();
2047 if (s->matrix.type() <= QTransform::TxTranslate) {
2049 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2051 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2054 QRasterPaintEngine::drawImage(pos, image);
2062 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2064 #ifdef QT_DEBUG_DRAW
2065 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2068 QPixmapData* pd = pixmap.pixmapData();
2069 if (pd->classId() == QPixmapData::RasterClass) {
2070 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2071 if (image.depth() == 1) {
2072 Q_D(QRasterPaintEngine);
2073 QRasterPaintEngineState *s = state();
2074 if (s->matrix.type() <= QTransform::TxTranslate
2075 && r.size() == sr.size()
2076 && r.size() == pixmap.size()) {
2078 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2081 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2084 drawImage(r, image, sr);
2087 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2088 const QImage image = pd->toImage(clippedSource);
2089 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2090 if (image.depth() == 1) {
2091 Q_D(QRasterPaintEngine);
2092 QRasterPaintEngineState *s = state();
2093 if (s->matrix.type() <= QTransform::TxTranslate
2094 && r.size() == sr.size()
2095 && r.size() == pixmap.size()) {
2097 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2100 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2103 drawImage(r, image, translatedSource);
2108 // assumes that rect has positive width and height
2109 static inline const QRect toRect_normalized(const QRectF &rect)
2111 const int x = qRound(rect.x());
2112 const int y = qRound(rect.y());
2113 const int w = int(rect.width() + qreal(0.5));
2114 const int h = int(rect.height() + qreal(0.5));
2116 return QRect(x, y, w, h);
2119 static inline int fast_ceil_positive(const qreal &v)
2121 const int iv = int(v);
2128 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2130 const int xmin = int(rect.x());
2131 const int xmax = int(fast_ceil_positive(rect.right()));
2132 const int ymin = int(rect.y());
2133 const int ymax = int(fast_ceil_positive(rect.bottom()));
2134 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2140 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2142 #ifdef QT_DEBUG_DRAW
2143 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2146 Q_D(QRasterPaintEngine);
2147 QRasterPaintEngineState *s = state();
2149 if (s->matrix.type() > QTransform::TxTranslate) {
2150 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2152 QRectF(0, 0, img.width(), img.height()));
2155 const QClipData *clip = d->clip();
2156 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2158 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2159 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2162 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2164 } else if (clip->hasRectClip) {
2165 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2173 d->image_filler.clip = clip;
2174 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2175 if (!d->image_filler.blend)
2177 d->image_filler.dx = -pt.x();
2178 d->image_filler.dy = -pt.y();
2179 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2181 fillRect_normalized(rr, &d->image_filler, d);
2186 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2188 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2199 inline RotationType qRotationType(const QTransform &transform)
2201 QTransform::TransformationType type = transform.type();
2203 if (type > QTransform::TxRotate)
2206 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2207 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2210 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2211 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2214 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2215 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2221 inline bool isPixelAligned(const QRectF &rect) {
2222 return QRectF(rect.toRect()) == rect;
2229 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2230 Qt::ImageConversionFlags)
2232 #ifdef QT_DEBUG_DRAW
2233 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2239 Q_D(QRasterPaintEngine);
2240 QRasterPaintEngineState *s = state();
2241 int sr_l = qFloor(sr.left());
2242 int sr_r = qCeil(sr.right()) - 1;
2243 int sr_t = qFloor(sr.top());
2244 int sr_b = qCeil(sr.bottom()) - 1;
2246 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2247 QTransform old = s->matrix;
2249 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2250 QRgb color = img.pixel(sr_l, sr_t);
2251 switch (img.format()) {
2252 case QImage::Format_ARGB32_Premultiplied:
2253 case QImage::Format_ARGB8565_Premultiplied:
2254 case QImage::Format_ARGB6666_Premultiplied:
2255 case QImage::Format_ARGB8555_Premultiplied:
2256 case QImage::Format_ARGB4444_Premultiplied:
2257 // Combine premultiplied color with the opacity set on the painter.
2258 d->solid_color_filler.solid.color =
2259 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2260 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2263 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2267 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2268 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2272 d->solid_color_filler.clip = d->clip();
2273 d->solid_color_filler.adjustSpanMethods();
2274 fillRect(r, &d->solid_color_filler);
2280 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2282 const QClipData *clip = d->clip();
2284 if (s->matrix.type() > QTransform::TxTranslate
2286 && (!clip || clip->hasRectClip)
2287 && s->intOpacity == 256
2288 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2289 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2290 && d->rasterBuffer->format == img.format()
2291 && (d->rasterBuffer->format == QImage::Format_RGB16
2292 || d->rasterBuffer->format == QImage::Format_RGB32
2293 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2294 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2296 RotationType rotationType = qRotationType(s->matrix);
2298 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2299 QRectF transformedTargetRect = s->matrix.mapRect(r);
2301 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2302 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2304 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2305 if (clippedTransformedTargetRect.isNull())
2308 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2310 QRect clippedSourceRect
2311 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2312 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2314 uint dbpl = d->rasterBuffer->bytesPerLine();
2315 uint sbpl = img.bytesPerLine();
2317 uchar *dst = d->rasterBuffer->buffer();
2318 uint bpp = img.depth() >> 3;
2320 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2321 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2323 uint cw = clippedSourceRect.width();
2324 uint ch = clippedSourceRect.height();
2326 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2333 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2335 QRectF targetBounds = s->matrix.mapRect(r);
2336 bool exceedsPrecision = targetBounds.width() > 0xffff
2337 || targetBounds.height() > 0xffff;
2339 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2340 if (s->matrix.type() > QTransform::TxScale) {
2341 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2342 if (func && (!clip || clip->hasRectClip)) {
2343 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2344 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2345 s->matrix, s->intOpacity);
2349 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2350 if (func && (!clip || clip->hasRectClip)) {
2351 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2352 img.bits(), img.bytesPerLine(),
2353 qt_mapRect_non_normalizing(r, s->matrix), sr,
2354 !clip ? d->deviceRect : clip->clipRect,
2361 QTransform copy = s->matrix;
2362 copy.translate(r.x(), r.y());
2364 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2365 copy.translate(-sr.x(), -sr.y());
2367 d->image_filler_xform.clip = clip;
2368 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2369 if (!d->image_filler_xform.blend)
2371 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2373 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2374 QRectF rr = s->matrix.mapRect(r);
2376 const int x1 = qRound(rr.x());
2377 const int y1 = qRound(rr.y());
2378 const int x2 = qRound(rr.right());
2379 const int y2 = qRound(rr.bottom());
2381 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2385 #ifdef QT_FAST_SPANS
2387 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2388 d->initializeRasterizer(&d->image_filler_xform);
2389 d->rasterizer->setAntialiased(s->flags.antialiased);
2391 const QRectF &rect = r.normalized();
2392 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2393 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2395 if (s->flags.tx_noshear)
2396 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2398 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2404 QTransform m = s->matrix;
2405 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2406 m.m21(), m.m22(), m.m23(),
2407 m.m31(), m.m32(), m.m33());
2408 fillPath(path, &d->image_filler_xform);
2411 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2412 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2414 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2416 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2418 } else if (clip->hasRectClip) {
2419 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2425 d->image_filler.clip = clip;
2426 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2427 if (!d->image_filler.blend)
2429 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2430 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2433 rr.translate(s->matrix.dx(), s->matrix.dy());
2435 const int x1 = qRound(rr.x());
2436 const int y1 = qRound(rr.y());
2437 const int x2 = qRound(rr.right());
2438 const int y2 = qRound(rr.bottom());
2440 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2447 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2449 #ifdef QT_DEBUG_DRAW
2450 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2452 Q_D(QRasterPaintEngine);
2453 QRasterPaintEngineState *s = state();
2457 QPixmapData *pd = pixmap.pixmapData();
2458 if (pd->classId() == QPixmapData::RasterClass) {
2459 image = static_cast<QRasterPixmapData *>(pd)->image;
2461 image = pixmap.toImage();
2464 if (image.depth() == 1)
2465 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2467 if (s->matrix.type() > QTransform::TxTranslate) {
2468 QTransform copy = s->matrix;
2469 copy.translate(r.x(), r.y());
2470 copy.translate(-sr.x(), -sr.y());
2471 d->image_filler_xform.clip = d->clip();
2472 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2473 if (!d->image_filler_xform.blend)
2475 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2477 #ifdef QT_FAST_SPANS
2479 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2480 d->initializeRasterizer(&d->image_filler_xform);
2481 d->rasterizer->setAntialiased(s->flags.antialiased);
2483 const QRectF &rect = r.normalized();
2484 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2485 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2486 if (s->flags.tx_noshear)
2487 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2489 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2495 fillPath(path, &d->image_filler_xform);
2497 d->image_filler.clip = d->clip();
2499 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2500 if (!d->image_filler.blend)
2502 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2503 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2506 rr.translate(s->matrix.dx(), s->matrix.dy());
2507 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2513 static inline bool monoVal(const uchar* s, int x)
2515 return (s[x>>3] << (x&7)) & 0x80;
2521 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2523 Q_D(QRasterPaintEngine);
2524 QRasterPaintEngineState *s = state();
2526 if (!s->penData.blend)
2529 QRasterBuffer *rb = d->rasterBuffer.data();
2531 const QRect rect(rx, ry, w, h);
2532 const QClipData *clip = d->clip();
2533 bool unclipped = false;
2535 // inlined QRect::intersects
2536 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2537 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2539 if (clip->hasRectClip) {
2540 unclipped = rx > clip->xmin
2541 && rx + w < clip->xmax
2543 && ry + h < clip->ymax;
2549 // inlined QRect::intersects
2550 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2551 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2555 // inlined QRect::contains
2556 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2557 && rect.top() >= 0 && rect.bottom() < rb->height();
2559 unclipped = contains && d->isUnclipped_normalized(rect);
2562 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2563 const uchar * scanline = static_cast<const uchar *>(src);
2565 if (s->flags.fast_text) {
2568 if (s->penData.bitmapBlit) {
2569 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2570 scanline, w, h, bpl);
2573 } else if (depth == 8) {
2574 if (s->penData.alphamapBlit) {
2575 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2576 scanline, w, h, bpl, 0);
2579 } else if (depth == 32) {
2580 // (A)RGB Alpha mask where the alpha component is not used.
2581 if (s->penData.alphaRGBBlit) {
2582 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2583 (const uint *) scanline, w, h, bpl / 4, 0);
2587 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2588 // (A)RGB Alpha mask where the alpha component is not used.
2590 int nx = qMax(0, rx);
2591 int ny = qMax(0, ry);
2593 // Move scanline pointer to compensate for moved x and y
2594 int xdiff = nx - rx;
2595 int ydiff = ny - ry;
2596 scanline += ydiff * bpl;
2597 scanline += xdiff * (depth == 32 ? 4 : 1);
2602 if (nx + w > d->rasterBuffer->width())
2603 w = d->rasterBuffer->width() - nx;
2604 if (ny + h > d->rasterBuffer->height())
2605 h = d->rasterBuffer->height() - ny;
2610 if (depth == 8 && s->penData.alphamapBlit) {
2611 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2612 scanline, w, h, bpl, clip);
2613 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2614 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2615 (const uint *) scanline, w, h, bpl / 4, clip);
2630 scanline += bpl * y0;
2634 w = qMin(w, rb->width() - qMax(0, rx));
2635 h = qMin(h, rb->height() - qMax(0, ry));
2637 if (w <= 0 || h <= 0)
2640 const int NSPANS = 256;
2641 QSpan spans[NSPANS];
2644 const int x1 = x0 + w;
2645 const int y1 = y0 + h;
2648 for (int y = y0; y < y1; ++y) {
2649 for (int x = x0; x < x1; ) {
2650 if (!monoVal(scanline, x)) {
2655 if (current == NSPANS) {
2656 blend(current, spans, &s->penData);
2659 spans[current].x = x + rx;
2660 spans[current].y = y + ry;
2661 spans[current].coverage = 255;
2664 // extend span until we find a different one.
2665 while (x < x1 && monoVal(scanline, x)) {
2669 spans[current].len = len;
2674 } else if (depth == 8) {
2675 for (int y = y0; y < y1; ++y) {
2676 for (int x = x0; x < x1; ) {
2677 // Skip those with 0 coverage
2678 if (scanline[x] == 0) {
2683 if (current == NSPANS) {
2684 blend(current, spans, &s->penData);
2687 int coverage = scanline[x];
2688 spans[current].x = x + rx;
2689 spans[current].y = y + ry;
2690 spans[current].coverage = coverage;
2694 // extend span until we find a different one.
2695 while (x < x1 && scanline[x] == coverage) {
2699 spans[current].len = len;
2704 } else { // 32-bit alpha...
2705 uint *sl = (uint *) src;
2706 for (int y = y0; y < y1; ++y) {
2707 for (int x = x0; x < x1; ) {
2708 // Skip those with 0 coverage
2709 if ((sl[x] & 0x00ffffff) == 0) {
2714 if (current == NSPANS) {
2715 blend(current, spans, &s->penData);
2718 uint rgbCoverage = sl[x];
2719 int coverage = qGreen(rgbCoverage);
2720 spans[current].x = x + rx;
2721 spans[current].y = y + ry;
2722 spans[current].coverage = coverage;
2726 // extend span until we find a different one.
2727 while (x < x1 && sl[x] == rgbCoverage) {
2731 spans[current].len = len;
2734 sl += bpl / sizeof(uint);
2737 // qDebug() << "alphaPenBlt: num spans=" << current
2738 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2739 // Call span func for current set of spans.
2741 blend(current, spans, &s->penData);
2744 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2745 const QFixedPoint *positions, QFontEngine *fontEngine)
2747 Q_D(QRasterPaintEngine);
2748 QRasterPaintEngineState *s = state();
2750 #if !defined(QT_NO_FREETYPE)
2751 if (fontEngine->type() == QFontEngine::Freetype) {
2752 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2753 QFontEngineFT::GlyphFormat neededFormat =
2754 painter()->device()->devType() == QInternal::Widget
2755 ? fe->defaultGlyphFormat()
2756 : QFontEngineFT::Format_A8;
2758 if (d_func()->mono_surface
2759 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2761 neededFormat = QFontEngineFT::Format_Mono;
2763 if (neededFormat == QFontEngineFT::Format_None)
2764 neededFormat = QFontEngineFT::Format_A8;
2766 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2767 if (s->matrix.type() >= QTransform::TxScale) {
2768 if (s->matrix.isAffine())
2769 gset = fe->loadTransformedGlyphSet(s->matrix);
2774 if (!gset || gset->outline_drawing
2775 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2778 FT_Face lockedFace = 0;
2781 switch (neededFormat) {
2782 case QFontEngineFT::Format_Mono:
2785 case QFontEngineFT::Format_A8:
2788 case QFontEngineFT::Format_A32:
2796 for (int i = 0; i < numGlyphs; i++) {
2797 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2798 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2800 if (!glyph || glyph->format != neededFormat) {
2802 lockedFace = fe->lockFace();
2803 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2806 if (!glyph || !glyph->data)
2810 switch (neededFormat) {
2811 case QFontEngineFT::Format_Mono:
2812 pitch = ((glyph->width + 31) & ~31) >> 3;
2814 case QFontEngineFT::Format_A8:
2815 pitch = (glyph->width + 3) & ~3;
2817 case QFontEngineFT::Format_A32:
2818 pitch = glyph->width * 4;
2825 alphaPenBlt(glyph->data, pitch, depth,
2826 qFloor(positions[i].x) + glyph->x,
2827 qFloor(positions[i].y) - glyph->y,
2828 glyph->width, glyph->height);
2835 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2837 QImageTextureGlyphCache *cache =
2838 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2840 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2841 fontEngine->setGlyphCache(0, cache);
2844 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2845 cache->fillInPendingGlyphs();
2847 const QImage &image = cache->image();
2848 int bpl = image.bytesPerLine();
2850 int depth = image.depth();
2854 leftShift = 2; // multiply by 4
2855 else if (depth == 1)
2856 rightShift = 3; // divide by 8
2858 int margin = cache->glyphMargin();
2859 const uchar *bits = image.bits();
2860 for (int i=0; i<numGlyphs; ++i) {
2862 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2863 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2864 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2868 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2869 int y = qFloor(positions[i].y) - c.baseLineY - margin;
2871 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2874 // c.baseLineX, c.baseLineY,
2877 // positions[i].x.toInt(), positions[i].y.toInt());
2879 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2885 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2886 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2888 Q_D(QRasterPaintEngine);
2889 QRasterPaintEngineState *s = state();
2891 QFontEngine *fontEngine = ti.fontEngine;
2892 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2893 QPaintEngineEx::drawTextItem(p, ti);
2897 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2899 QVarLengthArray<QFixedPoint> positions;
2900 QVarLengthArray<glyph_t> glyphs;
2901 QTransform matrix = s->matrix;
2902 matrix.translate(p.x(), p.y());
2903 if (matrix.type() == QTransform::TxScale)
2904 fe->setFontScale(matrix.m11());
2905 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2907 for (int i=0; i<glyphs.size(); ++i) {
2908 TOpenFontCharMetrics tmetrics;
2909 const TUint8 *glyphBitmapBytes;
2910 TSize glyphBitmapSize;
2911 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2912 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
2913 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
2914 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2917 if (matrix.type() == QTransform::TxScale)
2918 fe->setFontScale(1.0);
2922 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2925 * Returns true if the rectangle is completely within the current clip
2926 * state of the paint engine.
2928 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2930 const QClipData *cl = clip();
2932 // inline contains() for performance (we know the rects are normalized)
2933 const QRect &r1 = deviceRect;
2934 return (r.left() >= r1.left() && r.right() <= r1.right()
2935 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2939 if (cl->hasRectClip) {
2940 // currently all painting functions clips to deviceRect internally
2941 if (cl->clipRect == deviceRect)
2944 // inline contains() for performance (we know the rects are normalized)
2945 const QRect &r1 = cl->clipRect;
2946 return (r.left() >= r1.left() && r.right() <= r1.right()
2947 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2949 return qt_region_strictContains(cl->clipRegion, r);
2953 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2956 Q_Q(const QRasterPaintEngine);
2957 const QRasterPaintEngineState *s = q->state();
2958 const QClipData *cl = clip();
2960 QRect r = rect.normalized();
2961 // inline contains() for performance (we know the rects are normalized)
2962 const QRect &r1 = deviceRect;
2963 return (r.left() >= r1.left() && r.right() <= r1.right()
2964 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2968 // currently all painting functions that call this function clip to deviceRect internally
2969 if (cl->hasRectClip && cl->clipRect == deviceRect)
2972 if (s->flags.antialiased)
2975 QRect r = rect.normalized();
2977 r.setX(r.x() - penWidth);
2978 r.setY(r.y() - penWidth);
2979 r.setWidth(r.width() + 2 * penWidth);
2980 r.setHeight(r.height() + 2 * penWidth);
2983 if (cl->hasRectClip) {
2984 // inline contains() for performance (we know the rects are normalized)
2985 const QRect &r1 = cl->clipRect;
2986 return (r.left() >= r1.left() && r.right() <= r1.right()
2987 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2989 return qt_region_strictContains(cl->clipRegion, r);
2993 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2996 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3000 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3001 const QSpanData *data) const
3003 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3007 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3008 const QSpanData *data) const
3010 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3016 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3021 QFontEngine *fontEngine = textItem->fontEngine();
3022 if (!supportsTransformations(fontEngine)) {
3023 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3026 QPaintEngineEx::drawStaticTextItem(textItem);
3033 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3035 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3036 QRasterPaintEngineState *s = state();
3038 #ifdef QT_DEBUG_DRAW
3039 Q_D(QRasterPaintEngine);
3040 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3041 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3048 #if defined (Q_OS_WIN) || defined(Q_WS_MAC)
3050 if (!supportsTransformations(ti.fontEngine)) {
3051 QVarLengthArray<QFixedPoint> positions;
3052 QVarLengthArray<glyph_t> glyphs;
3054 QTransform matrix = s->matrix;
3055 matrix.translate(p.x(), p.y());
3057 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3059 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3063 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC
3064 if (s->matrix.type() <= QTransform::TxTranslate
3065 || (s->matrix.type() == QTransform::TxScale
3066 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3067 drawGlyphsS60(p, ti);
3070 #else // Q_OS_WIN || Q_WS_MAC
3072 QFontEngine *fontEngine = ti.fontEngine;
3075 if (s->matrix.type() < QTransform::TxScale) {
3077 QVarLengthArray<QFixedPoint> positions;
3078 QVarLengthArray<glyph_t> glyphs;
3079 QTransform matrix = state()->transform();
3081 qreal _x = qFloor(p.x());
3082 qreal _y = qFloor(p.y());
3083 matrix.translate(_x, _y);
3085 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3086 if (glyphs.size() == 0)
3089 for(int i = 0; i < glyphs.size(); i++) {
3090 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3091 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3092 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3093 qRound(positions[i].x + metrics.x),
3094 qRound(positions[i].y + metrics.y),
3095 img.width(), img.height());
3101 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3103 if (fontEngine->type() != QFontEngine::Freetype) {
3104 QPaintEngineEx::drawTextItem(p, ti);
3108 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3110 QTransform matrix = s->matrix;
3111 matrix.translate(p.x(), p.y());
3113 QVarLengthArray<QFixedPoint> positions;
3114 QVarLengthArray<glyph_t> glyphs;
3115 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3116 if (glyphs.size() == 0)
3119 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3120 QPaintEngine::drawTextItem(p, ti);
3126 QPaintEngineEx::drawTextItem(p, ti);
3132 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3134 Q_D(QRasterPaintEngine);
3135 QRasterPaintEngineState *s = state();
3138 if (!s->penData.blend)
3141 if (!s->flags.fast_pen) {
3142 QPaintEngineEx::drawPoints(points, pointCount);
3146 QCosmeticStroker stroker(s, d->deviceRect);
3147 stroker.drawPoints(points, pointCount);
3151 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3153 Q_D(QRasterPaintEngine);
3154 QRasterPaintEngineState *s = state();
3157 if (!s->penData.blend)
3160 if (!s->flags.fast_pen) {
3161 QPaintEngineEx::drawPoints(points, pointCount);
3165 QCosmeticStroker stroker(s, d->deviceRect);
3166 stroker.drawPoints(points, pointCount);
3172 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3174 #ifdef QT_DEBUG_DRAW
3175 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3177 Q_D(QRasterPaintEngine);
3178 QRasterPaintEngineState *s = state();
3181 if (!s->penData.blend)
3184 if (s->flags.fast_pen) {
3185 QCosmeticStroker stroker(s, d->deviceRect);
3186 for (int i=0; i<lineCount; ++i) {
3187 const QLine &l = lines[i];
3188 stroker.drawLine(l.p1(), l.p2());
3191 QPaintEngineEx::drawLines(lines, lineCount);
3195 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3201 Q_Q(QRasterPaintEngine);
3202 QRasterPaintEngineState *s = q->state();
3204 const QPen &pen = s->lastPen;
3205 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3206 const QVector<qreal> pattern = pen.dashPattern();
3208 qreal patternLength = 0;
3209 for (int i = 0; i < pattern.size(); ++i)
3210 patternLength += pattern.at(i);
3212 if (patternLength <= 0)
3215 qreal length = line.length();
3216 Q_ASSERT(length > 0);
3217 while (length > 0) {
3218 const bool rasterize = *inDash;
3219 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3222 if (dash >= length) {
3224 *dashOffset += dash / width;
3228 *inDash = !(*inDash);
3229 if (++*dashIndex >= pattern.size())
3236 if (rasterize && dash > 0)
3237 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3244 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3246 #ifdef QT_DEBUG_DRAW
3247 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3249 Q_D(QRasterPaintEngine);
3250 QRasterPaintEngineState *s = state();
3253 if (!s->penData.blend)
3255 if (s->flags.fast_pen) {
3256 QCosmeticStroker stroker(s, d->deviceRect);
3257 for (int i=0; i<lineCount; ++i) {
3258 QLineF line = lines[i];
3259 stroker.drawLine(line.p1(), line.p2());
3262 QPaintEngineEx::drawLines(lines, lineCount);
3270 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3272 QPaintEngineEx::drawEllipse(rect);
3279 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3281 Q_D(QRasterPaintEngine);
3288 CGContextRef QRasterPaintEngine::getCGContext() const
3290 Q_D(const QRasterPaintEngine);
3291 return d->cgContext;
3299 void QRasterPaintEngine::setDC(HDC hdc) {
3300 Q_D(QRasterPaintEngine);
3307 HDC QRasterPaintEngine::getDC() const
3309 Q_D(const QRasterPaintEngine);
3316 void QRasterPaintEngine::releaseDC(HDC) const
3322 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3324 const QTransform &m = state()->matrix;
3325 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3326 QFontEngine::Type fontEngineType = fontEngine->type();
3327 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3328 || (m.type() <= QTransform::TxTranslate
3329 && (fontEngineType == QFontEngine::TestFontEngine
3330 || fontEngineType == QFontEngine::Box))) {
3334 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3337 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3339 #if defined(Q_WS_MAC)
3340 // Mac font engines don't support scaling and rotation
3341 if (m.type() > QTransform::TxTranslate)
3343 if (m.type() >= QTransform::TxProject)
3347 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3356 QPoint QRasterPaintEngine::coordinateOffset() const
3358 return QPoint(0, 0);
3361 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3366 Q_D(QRasterPaintEngine);
3368 Q_ASSERT(image.depth() == 1);
3370 const int spanCount = 256;
3371 QT_FT_Span spans[spanCount];
3375 int w = image.width();
3376 int h = image.height();
3377 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3378 int ymin = qMax(qRound(pos.y()), 0);
3379 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3380 int xmin = qMax(qRound(pos.x()), 0);
3382 int x_offset = xmin - qRound(pos.x());
3384 QImage::Format format = image.format();
3385 for (int y = ymin; y < ymax; ++y) {
3386 const uchar *src = image.scanLine(y - qRound(pos.y()));
3387 if (format == QImage::Format_MonoLSB) {
3388 for (int x = 0; x < xmax - xmin; ++x) {
3389 int src_x = x + x_offset;
3390 uchar pixel = src[src_x >> 3];
3395 if (pixel & (0x1 << (src_x & 7))) {
3396 spans[n].x = xmin + x;
3398 spans[n].coverage = 255;
3400 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3404 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3407 if (n == spanCount) {
3408 fg->blend(n, spans, fg);
3414 for (int x = 0; x < xmax - xmin; ++x) {
3415 int src_x = x + x_offset;
3416 uchar pixel = src[src_x >> 3];
3421 if (pixel & (0x80 >> (x & 7))) {
3422 spans[n].x = xmin + x;
3424 spans[n].coverage = 255;
3426 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3430 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3433 if (n == spanCount) {
3434 fg->blend(n, spans, fg);
3442 fg->blend(n, spans, fg);
3448 \enum QRasterPaintEngine::ClipType
3451 \value RectClip Indicates that the currently set clip is a single rectangle.
3452 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3457 Returns the type of the clip currently set.
3459 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3461 Q_D(const QRasterPaintEngine);
3463 const QClipData *clip = d->clip();
3464 if (!clip || clip->hasRectClip)
3472 Returns the bounding rect of the currently set clip.
3474 QRect QRasterPaintEngine::clipBoundingRect() const
3476 Q_D(const QRasterPaintEngine);
3478 const QClipData *clip = d->clip();
3481 return d->deviceRect;
3483 if (clip->hasRectClip)
3484 return clip->clipRect;
3486 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3489 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3491 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3493 QVarLengthArray<short, 4096> buffer;
3495 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3496 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3497 result->initialize();
3499 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3500 const QSpan *c1_spans = c1ClipLines[y].spans;
3501 int c1_count = c1ClipLines[y].count;
3502 const QSpan *c2_spans = c2ClipLines[y].spans;
3503 int c2_count = c2ClipLines[y].count;
3505 if (c1_count == 0 && c2_count == 0)
3507 if (c1_count == 0) {
3508 result->appendSpans(c2_spans, c2_count);
3510 } else if (c2_count == 0) {
3511 result->appendSpans(c1_spans, c1_count);
3515 // we need to merge the two
3517 // find required length
3518 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3519 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3521 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3523 // Fill with old spans.
3524 for (int i = 0; i < c1_count; ++i) {
3525 const QSpan *cs = c1_spans + i;
3526 for (int j=cs->x; j<cs->x + cs->len; ++j)
3527 buffer[j] = cs->coverage;
3530 // Fill with new spans
3531 for (int i = 0; i < c2_count; ++i) {
3532 const QSpan *cs = c2_spans + i;
3533 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3534 buffer[j] += cs->coverage;
3535 if (buffer[j] > 255)
3543 // Skip to next span
3544 while (x < max && buffer[x] == 0) ++x;
3545 if (x >= max) break;
3548 int coverage = buffer[x];
3550 // Find length of span
3551 while (x < max && buffer[x] == coverage)
3554 result->appendSpan(sx, x - sx, y, coverage);
3559 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3561 Q_Q(QRasterPaintEngine);
3562 QRasterPaintEngineState *s = q->state();
3564 rasterizer->setAntialiased(s->flags.antialiased);
3566 QRect clipRect(deviceRect);
3568 // ### get from optimized rectbased QClipData
3570 const QClipData *c = clip();
3572 const QRect r(QPoint(c->xmin, c->ymin),
3573 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3574 clipRect = clipRect.intersected(r);
3575 blend = data->blend;
3577 blend = data->unclipped_blend;
3580 rasterizer->setClipRect(clipRect);
3581 rasterizer->initialize(blend, data);
3584 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3585 ProcessSpans callback,
3586 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3588 if (!callback || !outline)
3591 Q_Q(QRasterPaintEngine);
3592 QRasterPaintEngineState *s = q->state();
3594 if (!s->flags.antialiased) {
3595 initializeRasterizer(spanData);
3597 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3601 rasterizer->rasterize(outline, fillRule);
3605 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3609 int q_gray_rendered_spans(QT_FT_Raster raster);
3612 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3613 ProcessSpans callback,
3614 void *userData, QRasterBuffer *)
3616 if (!callback || !outline)
3619 Q_Q(QRasterPaintEngine);
3620 QRasterPaintEngineState *s = q->state();
3622 if (!s->flags.antialiased) {
3623 rasterizer->setAntialiased(s->flags.antialiased);
3624 rasterizer->setClipRect(deviceRect);
3625 rasterizer->initialize(callback, userData);
3627 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3631 rasterizer->rasterize(outline, fillRule);
3635 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3636 // minimize memory reallocations. However if initial size for
3637 // raster pool is changed for lower value, reallocations will
3639 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3640 int rasterPoolSize = rasterPoolInitialSize;
3641 unsigned char *rasterPoolBase;
3642 #if defined(Q_OS_WIN64)
3644 // We make use of setjmp and longjmp in qgrayraster.c which requires
3645 // 16-byte alignment, hence we hardcode this requirement here..
3646 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3648 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3649 rasterPoolBase = rasterPoolOnStack;
3651 Q_CHECK_PTR(rasterPoolBase);
3653 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3655 void *data = userData;
3657 QT_FT_BBox clip_box = { deviceRect.x(),
3659 deviceRect.x() + deviceRect.width(),
3660 deviceRect.y() + deviceRect.height() };
3662 QT_FT_Raster_Params rasterParams;
3663 rasterParams.target = 0;
3664 rasterParams.source = outline;
3665 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3666 rasterParams.gray_spans = 0;
3667 rasterParams.black_spans = 0;
3668 rasterParams.bit_test = 0;
3669 rasterParams.bit_set = 0;
3670 rasterParams.user = data;
3671 rasterParams.clip_box = clip_box;
3676 int rendered_spans = 0;
3680 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3681 rasterParams.gray_spans = callback;
3682 rasterParams.skip_spans = rendered_spans;
3683 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3685 // Out of memory, reallocate some more and try again...
3686 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3687 int new_size = rasterPoolSize * 2;
3688 if (new_size > 1024 * 1024) {
3689 qWarning("QPainter: Rasterization of primitive failed");
3693 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3695 #if defined(Q_OS_WIN64)
3696 _aligned_free(rasterPoolBase);
3698 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3699 free(rasterPoolBase);
3702 rasterPoolSize = new_size;
3704 #if defined(Q_OS_WIN64)
3705 // We make use of setjmp and longjmp in qgrayraster.c which requires
3706 // 16-byte alignment, hence we hardcode this requirement here..
3707 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3709 (unsigned char *) malloc(rasterPoolSize);
3711 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3713 qt_ft_grays_raster.raster_done(*grayRaster.data());
3714 qt_ft_grays_raster.raster_new(grayRaster.data());
3715 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3721 #if defined(Q_OS_WIN64)
3722 _aligned_free(rasterPoolBase);
3724 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3725 free(rasterPoolBase);
3729 void QRasterPaintEnginePrivate::recalculateFastImages()
3731 Q_Q(QRasterPaintEngine);
3732 QRasterPaintEngineState *s = q->state();
3734 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3735 && s->matrix.type() <= QTransform::TxShear;
3738 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3740 Q_Q(const QRasterPaintEngine);
3741 const QRasterPaintEngineState *s = q->state();
3743 return s->flags.fast_images
3744 && (mode == QPainter::CompositionMode_SourceOver
3745 || (mode == QPainter::CompositionMode_Source
3746 && !image.hasAlphaChannel()));
3749 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3751 Q_ASSERT(image.depth() == 1);
3753 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3754 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3756 QRgb fg = PREMUL(color.rgba());
3759 int height = sourceImage.height();
3760 int width = sourceImage.width();
3761 for (int y=0; y<height; ++y) {
3762 uchar *source = sourceImage.scanLine(y);
3763 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3764 if (!source || !target)
3765 QT_THROW(std::bad_alloc()); // we must have run out of memory
3766 for (int x=0; x < width; ++x)
3767 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3772 QRasterBuffer::~QRasterBuffer()
3776 void QRasterBuffer::init()
3778 compositionMode = QPainter::CompositionMode_SourceOver;
3779 monoDestinationWithClut = false;
3784 QImage::Format QRasterBuffer::prepare(QImage *image)
3786 m_buffer = (uchar *)image->bits();
3787 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3788 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3789 bytes_per_pixel = image->depth()/8;
3790 bytes_per_line = image->bytesPerLine();
3792 format = image->format();
3793 drawHelper = qDrawHelper + format;
3794 if (image->depth() == 1 && image->colorTable().size() == 2) {
3795 monoDestinationWithClut = true;
3796 destColor0 = PREMUL(image->colorTable()[0]);
3797 destColor1 = PREMUL(image->colorTable()[1]);
3803 void QRasterBuffer::resetBuffer(int val)
3805 memset(m_buffer, val, m_height*bytes_per_line);
3808 QClipData::QClipData(int height)
3810 clipSpanHeight = height;
3815 xmin = xmax = ymin = ymax = 0;
3819 hasRectClip = hasRegionClip = false;
3822 QClipData::~QClipData()
3830 void QClipData::initialize()
3836 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3838 Q_CHECK_PTR(m_clipLines);
3840 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3841 allocated = clipSpanHeight;
3842 Q_CHECK_PTR(m_spans);
3848 m_clipLines[y].spans = 0;
3849 m_clipLines[y].count = 0;
3853 const int len = clipRect.width();
3856 QSpan *span = m_spans + count;
3860 span->coverage = 255;
3863 m_clipLines[y].spans = span;
3864 m_clipLines[y].count = 1;
3868 while (y < clipSpanHeight) {
3869 m_clipLines[y].spans = 0;
3870 m_clipLines[y].count = 0;
3873 } else if (hasRegionClip) {
3875 const QVector<QRect> rects = clipRegion.rects();
3876 const int numRects = rects.size();
3879 const int maxSpans = (ymax - ymin) * numRects;
3880 if (maxSpans > allocated) {
3881 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3882 allocated = maxSpans;
3887 int firstInBand = 0;
3889 while (firstInBand < numRects) {
3890 const int currMinY = rects.at(firstInBand).y();
3891 const int currMaxY = currMinY + rects.at(firstInBand).height();
3893 while (y < currMinY) {
3894 m_clipLines[y].spans = 0;
3895 m_clipLines[y].count = 0;
3899 int lastInBand = firstInBand;
3900 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3903 while (y < currMaxY) {
3905 m_clipLines[y].spans = m_spans + count;
3906 m_clipLines[y].count = lastInBand - firstInBand + 1;
3908 for (int r = firstInBand; r <= lastInBand; ++r) {
3909 const QRect &currRect = rects.at(r);
3910 QSpan *span = m_spans + count;
3911 span->x = currRect.x();
3912 span->len = currRect.width();
3914 span->coverage = 255;
3920 firstInBand = lastInBand + 1;
3923 Q_ASSERT(count <= allocated);
3925 while (y < clipSpanHeight) {
3926 m_clipLines[y].spans = 0;
3927 m_clipLines[y].count = 0;
3933 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3938 free(m_clipLines); // same for clipLines
3944 void QClipData::fixup()
3949 ymin = ymax = xmin = xmax = 0;
3954 ymin = m_spans[0].y;
3955 ymax = m_spans[count-1].y + 1;
3959 const int firstLeft = m_spans[0].x;
3960 const int firstRight = m_spans[0].x + m_spans[0].len;
3963 for (int i = 0; i < count; ++i) {
3964 QT_FT_Span_& span = m_spans[i];
3967 if (span.y != y + 1 && y != -1)
3970 m_clipLines[y].spans = &span;
3971 m_clipLines[y].count = 1;
3973 ++m_clipLines[y].count;
3975 const int spanLeft = span.x;
3976 const int spanRight = spanLeft + span.len;
3978 if (spanLeft < xmin)
3981 if (spanRight > xmax)
3984 if (spanLeft != firstLeft || spanRight != firstRight)
3990 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3995 Convert \a rect to clip spans.
3997 void QClipData::setClipRect(const QRect &rect)
3999 if (hasRectClip && rect == clipRect)
4002 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4004 hasRegionClip = false;
4008 xmax = rect.x() + rect.width();
4009 ymin = qMin(rect.y(), clipSpanHeight);
4010 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4017 // qDebug() << xmin << xmax << ymin << ymax;
4021 Convert \a region to clip spans.
4023 void QClipData::setClipRegion(const QRegion ®ion)
4025 if (region.rectCount() == 1) {
4026 setClipRect(region.rects().at(0));
4030 hasRegionClip = true;
4031 hasRectClip = false;
4032 clipRegion = region;
4034 { // set bounding rect
4035 const QRect rect = region.boundingRect();
4037 xmax = rect.x() + rect.width();
4039 ymax = rect.y() + rect.height();
4051 spans must be sorted on y
4053 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4054 const QSpan *spans, const QSpan *end,
4055 QSpan **outSpans, int available)
4057 const_cast<QClipData *>(clip)->initialize();
4059 QSpan *out = *outSpans;
4061 const QSpan *clipSpans = clip->m_spans + *currentClip;
4062 const QSpan *clipEnd = clip->m_spans + clip->count;
4064 while (available && spans < end ) {
4065 if (clipSpans >= clipEnd) {
4069 if (clipSpans->y > spans->y) {
4073 if (spans->y != clipSpans->y) {
4074 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4075 clipSpans = clip->m_clipLines[spans->y].spans;
4080 Q_ASSERT(spans->y == clipSpans->y);
4083 int sx2 = sx1 + spans->len;
4084 int cx1 = clipSpans->x;
4085 int cx2 = cx1 + clipSpans->len;
4087 if (cx1 < sx1 && cx2 < sx1) {
4090 } else if (sx1 < cx1 && sx2 < cx1) {
4094 int x = qMax(sx1, cx1);
4095 int len = qMin(sx2, cx2) - x;
4097 out->x = qMax(sx1, cx1);
4098 out->len = qMin(sx2, cx2) - out->x;
4100 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4112 *currentClip = clipSpans - clip->m_spans;
4116 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4118 // qDebug() << "qt_span_fill_clipped" << spanCount;
4119 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4121 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4123 const int NSPANS = 256;
4124 QSpan cspans[NSPANS];
4125 int currentClip = 0;
4126 const QSpan *end = spans + spanCount;
4127 while (spans < end) {
4128 QSpan *clipped = cspans;
4129 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4130 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4131 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4133 if (clipped - cspans)
4134 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4140 Clip spans to \a{clip}-rectangle.
4141 Returns number of unclipped spans
4143 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4146 const short minx = clip.left();
4147 const short miny = clip.top();
4148 const short maxx = clip.right();
4149 const short maxy = clip.bottom();
4152 for (int i = 0; i < numSpans; ++i) {
4153 if (spans[i].y > maxy)
4155 if (spans[i].y < miny
4156 || spans[i].x > maxx
4157 || spans[i].x + spans[i].len <= minx) {
4160 if (spans[i].x < minx) {
4161 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4164 spans[n].x = spans[i].x;
4165 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4167 if (spans[n].len == 0)
4169 spans[n].y = spans[i].y;
4170 spans[n].coverage = spans[i].coverage;
4177 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4180 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4181 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4183 Q_ASSERT(fillData->clip);
4184 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4186 // hw: check if this const_cast<> is safe!!!
4187 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4188 fillData->clip->clipRect);
4190 fillData->unclipped_blend(count, spans, fillData);
4193 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4195 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4197 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4198 // for (int i = 0; i < qMin(count, 10); ++i) {
4199 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4202 switch (clipData->operation) {
4204 case Qt::IntersectClip:
4206 QClipData *newClip = clipData->newClip;
4207 newClip->initialize();
4209 int currentClip = 0;
4210 const QSpan *end = spans + count;
4211 while (spans < end) {
4212 QSpan *newspans = newClip->m_spans + newClip->count;
4213 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4214 &newspans, newClip->allocated - newClip->count);
4215 newClip->count = newspans - newClip->m_spans;
4217 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4218 newClip->allocated *= 2;
4225 case Qt::ReplaceClip:
4226 clipData->newClip->appendSpans(spans, count);
4234 QImage QRasterBuffer::bufferImage() const
4236 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4238 for (int y = 0; y < m_height; ++y) {
4239 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4241 for (int x=0; x<m_width; ++x) {
4242 uint argb = span[x];
4243 image.setPixel(x, y, argb);
4251 void QRasterBuffer::flushToARGBImage(QImage *target) const
4253 int w = qMin(m_width, target->width());
4254 int h = qMin(m_height, target->height());
4256 for (int y=0; y<h; ++y) {
4257 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4258 QRgb *dest = (QRgb *) target->scanLine(y);
4259 for (int x=0; x<w; ++x) {
4260 QRgb pixel = sourceLine[x];
4261 int alpha = qAlpha(pixel);
4265 dest[x] = (alpha << 24)
4266 | ((255*qRed(pixel)/alpha) << 16)
4267 | ((255*qGreen(pixel)/alpha) << 8)
4268 | ((255*qBlue(pixel)/alpha) << 0);
4275 class QGradientCache
4279 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4280 stops(s), opacity(op), interpolationMode(mode) {}
4281 uint buffer[GRADIENT_STOPTABLE_SIZE];
4282 QGradientStops stops;
4284 QGradient::InterpolationMode interpolationMode;
4287 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4290 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4291 quint64 hash_val = 0;
4293 QGradientStops stops = gradient.stops();
4294 for (int i = 0; i < stops.size() && i <= 2; i++)
4295 hash_val += stops[i].second.rgba();
4297 QMutexLocker lock(&mutex);
4298 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4300 if (it == cache.constEnd())
4301 return addCacheElement(hash_val, gradient, opacity);
4304 const CacheInfo &cache_info = it.value();
4305 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4306 return cache_info.buffer;
4308 } while (it != cache.constEnd() && it.key() == hash_val);
4309 // an exact match for these stops and opacity was not found, create new cache
4310 return addCacheElement(hash_val, gradient, opacity);
4314 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4316 inline int maxCacheSize() const { return 60; }
4317 inline void generateGradientColorTable(const QGradient& g,
4319 int size, int opacity) const;
4320 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4321 if (cache.size() == maxCacheSize()) {
4322 // may remove more than 1, but OK
4323 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4325 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4326 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4327 return cache.insert(hash_val, cache_entry).value().buffer;
4330 QGradientColorTableHash cache;
4334 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4336 QGradientStops stops = gradient.stops();
4337 int stopCount = stops.count();
4338 Q_ASSERT(stopCount > 0);
4340 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4342 if (stopCount == 2) {
4343 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4344 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4346 qreal first_stop = stops[0].first;
4347 qreal second_stop = stops[1].first;
4349 if (second_stop < first_stop) {
4350 qSwap(first_color, second_color);
4351 qSwap(first_stop, second_stop);
4354 if (colorInterpolation) {
4355 first_color = PREMUL(first_color);
4356 second_color = PREMUL(second_color);
4359 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4360 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4362 uint red_first = qRed(first_color) << 16;
4363 uint green_first = qGreen(first_color) << 16;
4364 uint blue_first = qBlue(first_color) << 16;
4365 uint alpha_first = qAlpha(first_color) << 16;
4367 uint red_second = qRed(second_color) << 16;
4368 uint green_second = qGreen(second_color) << 16;
4369 uint blue_second = qBlue(second_color) << 16;
4370 uint alpha_second = qAlpha(second_color) << 16;
4373 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4374 if (colorInterpolation)
4375 colorTable[i] = first_color;
4377 colorTable[i] = PREMUL(first_color);
4380 if (i < second_index) {
4381 qreal reciprocal = qreal(1) / (second_index - first_index);
4383 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4384 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4385 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4386 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4389 red_first += 1 << 15;
4390 green_first += 1 << 15;
4391 blue_first += 1 << 15;
4392 alpha_first += 1 << 15;
4394 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4395 red_first += red_delta;
4396 green_first += green_delta;
4397 blue_first += blue_delta;
4398 alpha_first += alpha_delta;
4400 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4401 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4403 if (colorInterpolation)
4404 colorTable[i] = color;
4406 colorTable[i] = PREMUL(color);
4410 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4411 if (colorInterpolation)
4412 colorTable[i] = second_color;
4414 colorTable[i] = PREMUL(second_color);
4420 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4421 if (stopCount == 1) {
4422 current_color = PREMUL(current_color);
4423 for (int i = 0; i < size; ++i)
4424 colorTable[i] = current_color;
4428 // The position where the gradient begins and ends
4429 qreal begin_pos = stops[0].first;
4430 qreal end_pos = stops[stopCount-1].first;
4432 int pos = 0; // The position in the color table.
4435 qreal incr = 1 / qreal(size); // the double increment.
4436 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4438 // Up to first point
4439 colorTable[pos++] = PREMUL(current_color);
4440 while (dpos <= begin_pos) {
4441 colorTable[pos] = colorTable[pos - 1];
4446 int current_stop = 0; // We always interpolate between current and current + 1.
4448 qreal t; // position between current left and right stops
4449 qreal t_delta; // the t increment per entry in the color table
4451 if (dpos < end_pos) {
4453 while (dpos > stops[current_stop+1].first)
4456 if (current_stop != 0)
4457 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4458 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4460 if (colorInterpolation) {
4461 current_color = PREMUL(current_color);
4462 next_color = PREMUL(next_color);
4465 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4466 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4467 t = (dpos - stops[current_stop].first) * c;
4471 Q_ASSERT(current_stop < stopCount);
4473 int dist = qRound(t);
4474 int idist = 256 - dist;
4476 if (colorInterpolation)
4477 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4479 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4484 if (dpos >= end_pos)
4490 while (dpos > stops[current_stop+skip+1].first)
4494 current_stop += skip;
4496 current_color = next_color;
4498 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4499 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4501 if (colorInterpolation) {
4503 current_color = PREMUL(current_color);
4504 next_color = PREMUL(next_color);
4507 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4508 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4509 t = (dpos - stops[current_stop].first) * c;
4516 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4517 while (pos < size - 1) {
4518 colorTable[pos] = current_color;
4522 // Make sure the last color stop is represented at the end of the table
4523 colorTable[size - 1] = current_color;
4526 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4529 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4535 m11 = m22 = m33 = 1.;
4536 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4537 clip = pe ? pe->d_func()->clip() : 0;
4540 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4542 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4544 Qt::BrushStyle brushStyle = qbrush_style(brush);
4545 switch (brushStyle) {
4546 case Qt::SolidPattern: {
4548 QColor c = qbrush_color(brush);
4549 QRgb rgba = c.rgba();
4550 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4551 if ((solid.color & 0xff000000) == 0
4552 && compositionMode == QPainter::CompositionMode_SourceOver) {
4558 case Qt::LinearGradientPattern:
4560 type = LinearGradient;
4561 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4562 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4563 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4564 gradient.spread = g->spread();
4566 QLinearGradientData &linearData = gradient.linear;
4568 linearData.origin.x = g->start().x();
4569 linearData.origin.y = g->start().y();
4570 linearData.end.x = g->finalStop().x();
4571 linearData.end.y = g->finalStop().y();
4575 case Qt::RadialGradientPattern:
4577 type = RadialGradient;
4578 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4579 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4580 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4581 gradient.spread = g->spread();
4583 QRadialGradientData &radialData = gradient.radial;
4585 QPointF center = g->center();
4586 radialData.center.x = center.x();
4587 radialData.center.y = center.y();
4588 radialData.center.radius = g->centerRadius();
4589 QPointF focal = g->focalPoint();
4590 radialData.focal.x = focal.x();
4591 radialData.focal.y = focal.y();
4592 radialData.focal.radius = g->focalRadius();
4596 case Qt::ConicalGradientPattern:
4598 type = ConicalGradient;
4599 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4600 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4601 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4602 gradient.spread = QGradient::RepeatSpread;
4604 QConicalGradientData &conicalData = gradient.conical;
4606 QPointF center = g->center();
4607 conicalData.center.x = center.x();
4608 conicalData.center.y = center.y();
4609 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4613 case Qt::Dense1Pattern:
4614 case Qt::Dense2Pattern:
4615 case Qt::Dense3Pattern:
4616 case Qt::Dense4Pattern:
4617 case Qt::Dense5Pattern:
4618 case Qt::Dense6Pattern:
4619 case Qt::Dense7Pattern:
4620 case Qt::HorPattern:
4621 case Qt::VerPattern:
4622 case Qt::CrossPattern:
4623 case Qt::BDiagPattern:
4624 case Qt::FDiagPattern:
4625 case Qt::DiagCrossPattern:
4628 tempImage = new QImage();
4629 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4630 initTexture(tempImage, alpha, QTextureData::Tiled);
4632 case Qt::TexturePattern:
4635 tempImage = new QImage();
4637 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4638 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4640 *tempImage = brush.textureImage();
4641 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4649 adjustSpanMethods();
4652 void QSpanData::adjustSpanMethods()
4662 unclipped_blend = 0;
4665 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4666 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4667 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4668 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4669 fillRect = rasterBuffer->drawHelper->fillRect;
4671 case LinearGradient:
4672 case RadialGradient:
4673 case ConicalGradient:
4674 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4677 unclipped_blend = qBlendTexture;
4678 if (!texture.imageData)
4679 unclipped_blend = 0;
4684 if (!unclipped_blend) {
4687 blend = unclipped_blend;
4688 } else if (clip->hasRectClip) {
4689 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4691 blend = qt_span_fill_clipped;
4695 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4698 // make sure we round off correctly in qdrawhelper.cpp
4699 delta.translate(1.0 / 65536, 1.0 / 65536);
4701 QTransform inv = (delta * matrix).inverted();
4714 const bool affine = !m13 && !m23;
4715 fast_matrix = affine
4716 && m11 * m11 + m21 * m21 < 1e4
4717 && m12 * m12 + m22 * m22 < 1e4
4721 adjustSpanMethods();
4724 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4726 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4728 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4729 if (!d || d->height == 0) {
4730 texture.imageData = 0;
4737 texture.bytesPerLine = 0;
4738 texture.format = QImage::Format_Invalid;
4739 texture.colorTable = 0;
4740 texture.hasAlpha = alpha != 256;
4742 texture.imageData = d->data;
4743 texture.width = d->width;
4744 texture.height = d->height;
4746 if (sourceRect.isNull()) {
4749 texture.x2 = texture.width;
4750 texture.y2 = texture.height;
4752 texture.x1 = sourceRect.x();
4753 texture.y1 = sourceRect.y();
4754 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4755 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4758 texture.bytesPerLine = d->bytes_per_line;
4760 texture.format = d->format;
4761 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4762 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4764 texture.const_alpha = alpha;
4765 texture.type = _type;
4767 adjustSpanMethods();
4772 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4775 Draws the first \a pointCount points in the buffer \a points
4777 The default implementation converts the first \a pointCount QPoints in \a points
4778 to QPointFs and calls the floating point version of drawPoints.
4782 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4785 Reimplement this function to draw the largest ellipse that can be
4786 contained within rectangle \a rect.
4789 #ifdef QT_DEBUG_DRAW
4790 void dumpClip(int width, int height, const QClipData *clip)
4792 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4793 clipImg.fill(0xffff0000);
4800 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4802 for (int i = 0; i < clip->count; ++i) {
4803 const QSpan *span = ((QClipData *) clip)->spans() + i;
4804 for (int j = 0; j < span->len; ++j)
4805 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4806 x0 = qMin(x0, int(span->x));
4807 x1 = qMax(x1, int(span->x + span->len - 1));
4809 y0 = qMin(y0, int(span->y));
4810 y1 = qMax(y1, int(span->y));
4813 static int counter = 0;
4820 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4821 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));