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 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
127 return result == FE_FONTSMOOTHINGCLEARTYPE;
130 bool QRasterPaintEngine::clearTypeFontsEnabled()
132 static const bool result = winClearTypeFontsEnabled();
139 extern bool qt_applefontsmoothing_enabled;
143 /********************************************************************************
146 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
147 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
148 static void qt_span_clip(int count, const QSpan *spans, void *userData);
149 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
155 Qt::ClipOperation operation;
161 LineDrawIncludeLastPixel
164 struct QRasterFloatPoint {
170 static const QRectF boundingRect(const QPointF *points, int pointCount)
172 const QPointF *e = points;
173 const QPointF *last = points + pointCount;
174 qreal minx, maxx, miny, maxy;
175 minx = maxx = e->x();
176 miny = maxy = e->y();
180 else if (e->x() > maxx)
184 else if (e->y() > maxy)
187 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
191 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
192 return (elementCount == 5 // 5-point polygon, check for closed rect
193 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
194 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
195 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
196 && pts[0] < pts[4] && pts[1] < pts[5]
198 (elementCount == 4 // 4-point polygon, check for unclosed rect
199 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
200 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
201 && pts[0] < pts[4] && pts[1] < pts[5]
206 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
208 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
211 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
213 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
216 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
217 qfixed c2x, qfixed c2y,
218 qfixed ex, qfixed ey,
221 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
222 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
223 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
227 #if !defined(QT_NO_DEBUG) && 0
228 static void qt_debug_path(const QPainterPath &path)
230 const char *names[] = {
237 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
238 for (int i=0; i<path.elementCount(); ++i) {
239 const QPainterPath::Element &e = path.elementAt(i);
240 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
241 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
246 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
247 QPaintEngineExPrivate(),
254 \class QRasterPaintEngine
259 \brief The QRasterPaintEngine class enables hardware acceleration
260 of painting operations in Qt for Embedded Linux.
262 Note that this functionality is only available in
263 \l{Qt for Embedded Linux}.
265 In \l{Qt for Embedded Linux}, painting is a pure software
266 implementation. But starting with Qt 4.2, it is
267 possible to add an accelerated graphics driver to take advantage
268 of available hardware resources.
270 Hardware acceleration is accomplished by creating a custom screen
271 driver, accelerating the copying from memory to the screen, and
272 implementing a custom paint engine accelerating the various
273 painting operations. Then a custom paint device (derived from the
274 QCustomRasterPaintDevice class) and a custom window surface
275 (derived from QWSWindowSurface) must be implemented to make
276 \l{Qt for Embedded Linux} aware of the accelerated driver.
278 \note The QRasterPaintEngine class does not support 8-bit images.
279 Instead, they need to be converted to a supported format, such as
280 QImage::Format_ARGB32_Premultiplied.
282 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
283 documentation for details.
285 \sa QCustomRasterPaintDevice, QPaintEngine
289 \fn Type QRasterPaintEngine::type() const
295 \relates QRasterPaintEngine
297 A struct equivalent to QT_FT_Span, containing a position (x,
298 y), the span's length in pixels and its color/coverage (a value
299 ranging from 0 to 255).
305 Creates a raster based paint engine for operating on the given
306 \a device, with the complete set of \l
307 {QPaintEngine::PaintEngineFeature}{paint engine features and
310 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
311 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
313 d_func()->device = device;
320 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
323 d_func()->device = device;
327 void QRasterPaintEngine::init()
329 Q_D(QRasterPaintEngine);
336 // The antialiasing raster.
337 d->grayRaster.reset(new QT_FT_Raster);
338 Q_CHECK_PTR(d->grayRaster.data());
339 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
340 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
343 d->rasterizer.reset(new QRasterizer);
344 d->rasterBuffer.reset(new QRasterBuffer());
345 d->outlineMapper.reset(new QOutlineMapper);
346 d->outlinemapper_xform_dirty = true;
348 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
349 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
350 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
352 d->baseClip.reset(new QClipData(d->device->height()));
353 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
355 d->image_filler.init(d->rasterBuffer.data(), this);
356 d->image_filler.type = QSpanData::Texture;
358 d->image_filler_xform.init(d->rasterBuffer.data(), this);
359 d->image_filler_xform.type = QSpanData::Texture;
361 d->solid_color_filler.init(d->rasterBuffer.data(), this);
362 d->solid_color_filler.type = QSpanData::Solid;
364 d->deviceDepth = d->device->depth();
366 d->mono_surface = false;
367 gccaps &= ~PorterDuff;
369 QImage::Format format = QImage::Format_Invalid;
371 switch (d->device->devType()) {
372 case QInternal::Pixmap:
373 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
375 case QInternal::Image:
376 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
379 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
385 case QImage::Format_MonoLSB:
386 case QImage::Format_Mono:
387 d->mono_surface = true;
389 case QImage::Format_ARGB8565_Premultiplied:
390 case QImage::Format_ARGB8555_Premultiplied:
391 case QImage::Format_ARGB6666_Premultiplied:
392 case QImage::Format_ARGB4444_Premultiplied:
393 case QImage::Format_ARGB32_Premultiplied:
394 case QImage::Format_ARGB32:
395 gccaps |= PorterDuff;
397 case QImage::Format_RGB32:
398 case QImage::Format_RGB444:
399 case QImage::Format_RGB555:
400 case QImage::Format_RGB666:
401 case QImage::Format_RGB888:
402 case QImage::Format_RGB16:
413 Destroys this paint engine.
415 QRasterPaintEngine::~QRasterPaintEngine()
417 Q_D(QRasterPaintEngine);
419 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
425 bool QRasterPaintEngine::begin(QPaintDevice *device)
427 Q_D(QRasterPaintEngine);
429 if (device->devType() == QInternal::Pixmap) {
430 QPixmap *pixmap = static_cast<QPixmap *>(device);
431 QPixmapData *pd = pixmap->pixmapData();
432 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
433 d->device = pd->buffer();
438 // Make sure QPaintEngine::paintDevice() returns the proper device.
441 Q_ASSERT(d->device->devType() == QInternal::Image
442 || d->device->devType() == QInternal::CustomRaster);
444 d->systemStateChanged();
446 QRasterPaintEngineState *s = state();
447 ensureOutlineMapper();
448 d->outlineMapper->m_clip_rect = d->deviceRect;
450 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
451 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
452 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
453 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
455 d->rasterizer->setClipRect(d->deviceRect);
457 s->penData.init(d->rasterBuffer.data(), this);
458 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
459 s->stroker = &d->basicStroker;
460 d->basicStroker.setClipRect(d->deviceRect);
462 s->brushData.init(d->rasterBuffer.data(), this);
463 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
465 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
467 setDirty(DirtyBrushOrigin);
470 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
471 << ") devType:" << device->devType()
472 << "devRect:" << d->deviceRect;
474 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
478 #if defined(Q_OS_WIN)
479 d->isPlain45DegreeRotation = true;
483 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
484 #if defined(Q_OS_WIN)
485 else if (clearTypeFontsEnabled())
486 #elif defined (Q_WS_MAC)
487 else if (qt_applefontsmoothing_enabled)
492 QImage::Format format = static_cast<QImage *>(d->device)->format();
493 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
494 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
496 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
507 bool QRasterPaintEngine::end()
510 Q_D(QRasterPaintEngine);
511 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
513 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
523 void QRasterPaintEngine::releaseBuffer()
525 Q_D(QRasterPaintEngine);
526 d->rasterBuffer.reset(new QRasterBuffer);
532 QSize QRasterPaintEngine::size() const
534 Q_D(const QRasterPaintEngine);
535 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
542 void QRasterPaintEngine::saveBuffer(const QString &s) const
544 Q_D(const QRasterPaintEngine);
545 d->rasterBuffer->bufferImage().save(s, "PNG");
552 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
554 QRasterPaintEngineState *s = state();
555 // FALCON: get rid of this line, see drawImage call below.
557 QTransform::TransformationType txop = s->matrix.type();
561 case QTransform::TxNone:
562 s->flags.int_xform = true;
565 case QTransform::TxTranslate:
566 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
567 && qreal(int(s->matrix.dy())) == s->matrix.dy();
570 case QTransform::TxScale:
571 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
572 && qreal(int(s->matrix.dy())) == s->matrix.dy()
573 && qreal(int(s->matrix.m11())) == s->matrix.m11()
574 && qreal(int(s->matrix.m22())) == s->matrix.m22();
577 default: // shear / perspective...
578 s->flags.int_xform = false;
582 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
584 ensureOutlineMapper();
587 Q_D(QRasterPaintEngine);
588 d->isPlain45DegreeRotation = false;
589 if (txop >= QTransform::TxRotate) {
590 d->isPlain45DegreeRotation =
591 (qFuzzyIsNull(matrix.m11())
592 && qFuzzyIsNull(matrix.m12() - qreal(1))
593 && qFuzzyIsNull(matrix.m21() + qreal(1))
594 && qFuzzyIsNull(matrix.m22())
597 (qFuzzyIsNull(matrix.m11() + qreal(1))
598 && qFuzzyIsNull(matrix.m12())
599 && qFuzzyIsNull(matrix.m21())
600 && qFuzzyIsNull(matrix.m22() + qreal(1))
603 (qFuzzyIsNull(matrix.m11())
604 && qFuzzyIsNull(matrix.m12() + qreal(1))
605 && qFuzzyIsNull(matrix.m21() - qreal(1))
606 && qFuzzyIsNull(matrix.m22())
616 QRasterPaintEngineState::~QRasterPaintEngineState()
618 if (flags.has_clip_ownership)
623 QRasterPaintEngineState::QRasterPaintEngineState()
635 flags.fast_pen = true;
636 flags.antialiased = false;
637 flags.bilinear = false;
638 flags.fast_text = true;
639 flags.int_xform = true;
640 flags.tx_noshear = true;
641 flags.fast_images = true;
644 flags.has_clip_ownership = false;
649 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
654 , strokeFlags(s.strokeFlags)
655 , lastBrush(s.lastBrush)
656 , brushData(s.brushData)
657 , fillFlags(s.fillFlags)
658 , pixmapFlags(s.pixmapFlags)
659 , intOpacity(s.intOpacity)
663 , flag_bits(s.flag_bits)
665 brushData.tempImage = 0;
666 penData.tempImage = 0;
667 flags.has_clip_ownership = false;
673 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
675 QRasterPaintEngineState *s;
677 s = new QRasterPaintEngineState();
679 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
687 void QRasterPaintEngine::setState(QPainterState *s)
689 Q_D(QRasterPaintEngine);
690 QPaintEngineEx::setState(s);
691 d->rasterBuffer->compositionMode = s->composition_mode;
695 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
700 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
707 void QRasterPaintEngine::penChanged()
710 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
712 QRasterPaintEngineState *s = state();
713 s->strokeFlags |= DirtyPen;
714 s->dirty |= DirtyPen;
720 void QRasterPaintEngine::updatePen(const QPen &pen)
722 Q_D(QRasterPaintEngine);
723 QRasterPaintEngineState *s = state();
725 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
728 Qt::PenStyle pen_style = qpen_style(pen);
733 s->penData.clip = d->clip();
734 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
736 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
737 || pen.brush().transform().type() >= QTransform::TxNone) {
738 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
741 // Slightly ugly handling of an uncommon case... We need to change
742 // the pen because it is reused in draw_midpoint to decide dashed
744 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
745 pen_style = Qt::SolidLine;
746 s->lastPen.setStyle(Qt::SolidLine);
749 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
750 d->basicStroker.setCapStyle(qpen_capStyle(pen));
751 d->basicStroker.setMiterLimit(pen.miterLimit());
753 qreal penWidth = qpen_widthf(pen);
755 d->basicStroker.setStrokeWidth(1);
757 d->basicStroker.setStrokeWidth(penWidth);
759 if(pen_style == Qt::SolidLine) {
760 s->stroker = &d->basicStroker;
761 } else if (pen_style != Qt::NoPen) {
763 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
764 if (pen.isCosmetic()) {
765 d->dashStroker->setClipRect(d->deviceRect);
767 // ### I've seen this inverted devrect multiple places now...
768 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
769 d->dashStroker->setClipRect(clipRect);
771 d->dashStroker->setDashPattern(pen.dashPattern());
772 d->dashStroker->setDashOffset(pen.dashOffset());
773 s->stroker = d->dashStroker.data();
778 ensureState(); // needed because of tx_noshear...
779 s->flags.fast_pen = pen_style > Qt::NoPen
781 && ((pen.isCosmetic() && penWidth <= 1)
782 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
784 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
794 void QRasterPaintEngine::brushOriginChanged()
796 QRasterPaintEngineState *s = state();
798 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
801 s->fillFlags |= DirtyBrushOrigin;
808 void QRasterPaintEngine::brushChanged()
810 QRasterPaintEngineState *s = state();
812 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
814 s->fillFlags |= DirtyBrush;
823 void QRasterPaintEngine::updateBrush(const QBrush &brush)
826 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
828 Q_D(QRasterPaintEngine);
829 QRasterPaintEngineState *s = state();
830 // must set clip prior to setup, as setup uses it...
831 s->brushData.clip = d->clip();
832 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
833 if (s->fillFlags & DirtyTransform
834 || brush.transform().type() >= QTransform::TxNone)
835 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
836 s->lastBrush = brush;
840 void QRasterPaintEngine::updateOutlineMapper()
842 Q_D(QRasterPaintEngine);
843 d->outlineMapper->setMatrix(state()->matrix);
846 void QRasterPaintEngine::updateState()
848 QRasterPaintEngineState *s = state();
850 if (s->dirty & DirtyTransform)
851 updateMatrix(s->matrix);
853 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
854 const QPainter::CompositionMode mode = s->composition_mode;
855 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
856 && s->intOpacity == 256
857 && (mode == QPainter::CompositionMode_Source
858 || (mode == QPainter::CompositionMode_SourceOver
859 && qAlpha(s->penData.solid.color) == 255));
869 void QRasterPaintEngine::opacityChanged()
871 QRasterPaintEngineState *s = state();
874 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
877 s->fillFlags |= DirtyOpacity;
878 s->strokeFlags |= DirtyOpacity;
879 s->pixmapFlags |= DirtyOpacity;
880 s->dirty |= DirtyOpacity;
881 s->intOpacity = (int) (s->opacity * 256);
887 void QRasterPaintEngine::compositionModeChanged()
889 Q_D(QRasterPaintEngine);
890 QRasterPaintEngineState *s = state();
893 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
896 s->fillFlags |= DirtyCompositionMode;
897 s->dirty |= DirtyCompositionMode;
899 s->strokeFlags |= DirtyCompositionMode;
900 d->rasterBuffer->compositionMode = s->composition_mode;
902 d->recalculateFastImages();
908 void QRasterPaintEngine::renderHintsChanged()
910 QRasterPaintEngineState *s = state();
913 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
916 bool was_aa = s->flags.antialiased;
917 bool was_bilinear = s->flags.bilinear;
919 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
920 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
922 if (was_aa != s->flags.antialiased)
923 s->strokeFlags |= DirtyHints;
925 if (was_bilinear != s->flags.bilinear) {
926 s->strokeFlags |= DirtyPen;
927 s->fillFlags |= DirtyBrush;
930 Q_D(QRasterPaintEngine);
931 d->recalculateFastImages();
937 void QRasterPaintEngine::transformChanged()
939 QRasterPaintEngineState *s = state();
942 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
945 s->fillFlags |= DirtyTransform;
946 s->strokeFlags |= DirtyTransform;
948 s->dirty |= DirtyTransform;
950 Q_D(QRasterPaintEngine);
951 d->recalculateFastImages();
957 void QRasterPaintEngine::clipEnabledChanged()
959 QRasterPaintEngineState *s = state();
962 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
966 s->clip->enabled = s->clipEnabled;
967 s->fillFlags |= DirtyClipEnabled;
968 s->strokeFlags |= DirtyClipEnabled;
969 s->pixmapFlags |= DirtyClipEnabled;
973 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
975 SrcOverBlendFunc func,
980 if (alpha == 0 || !clip.isValid())
983 Q_ASSERT(img.depth() >= 8);
985 int srcBPL = img.bytesPerLine();
986 const uchar *srcBits = img.bits();
987 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
988 int iw = img.width();
989 int ih = img.height();
994 // Adjust the image according to the source offset...
995 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
998 // adapt the x parameters
999 int x = qRound(pt.x());
1001 int cx2 = clip.x() + clip.width();
1004 srcBits += srcSize * d;
1009 int d = x + iw - cx2;
1015 // adapt the y paremeters...
1017 int cy2 = clip.y() + clip.height();
1018 int y = qRound(pt.y());
1021 srcBits += srcBPL * d;
1026 int d = y + ih - cy2;
1032 // call the blend function...
1033 int dstSize = rasterBuffer->bytesPerPixel();
1034 int dstBPL = rasterBuffer->bytesPerLine();
1035 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1042 void QRasterPaintEnginePrivate::systemStateChanged()
1044 QRect clipRect(0, 0,
1045 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1046 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1048 if (!systemClip.isEmpty()) {
1049 QRegion clippedDeviceRgn = systemClip & clipRect;
1050 deviceRect = clippedDeviceRgn.boundingRect();
1051 baseClip->setClipRegion(clippedDeviceRgn);
1053 deviceRect = clipRect;
1054 baseClip->setClipRect(deviceRect);
1056 #ifdef QT_DEBUG_DRAW
1057 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1060 exDeviceRect = deviceRect;
1062 Q_Q(QRasterPaintEngine);
1063 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1064 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1065 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1068 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1070 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1073 Q_Q(QRasterPaintEngine);
1074 bool bilinear = q->state()->flags.bilinear;
1076 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1077 spanData->setupMatrix(b.transform() * m, bilinear);
1079 if (m.type() <= QTransform::TxTranslate) {
1080 // specialize setupMatrix for translation matrices
1081 // to avoid needless matrix inversion
1089 spanData->dx = -m.dx();
1090 spanData->dy = -m.dy();
1091 spanData->txop = m.type();
1092 spanData->bilinear = bilinear;
1093 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1094 spanData->adjustSpanMethods();
1096 spanData->setupMatrix(m, bilinear);
1101 // #define QT_CLIPPING_RATIOS
1103 #ifdef QT_CLIPPING_RATIOS
1108 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1110 if (d->clip()->hasRectClip)
1112 if (d->clip()->hasRegionClip)
1116 if ((totalClips % 5000) == 0) {
1117 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1118 rectClips * 100.0 / (qreal) totalClips,
1119 regionClips * 100.0 / (qreal) totalClips,
1120 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1129 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1131 if (s->flags.has_clip_ownership)
1134 s->flags.has_clip_ownership = false;
1137 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1139 s->fillFlags |= QPaintEngine::DirtyClipPath;
1140 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1141 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1143 d->solid_color_filler.clip = d->clip();
1144 d->solid_color_filler.adjustSpanMethods();
1146 #ifdef QT_DEBUG_DRAW
1147 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1156 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1158 #ifdef QT_DEBUG_DRAW
1159 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1161 if (path.elements()) {
1162 for (int i=0; i<path.elementCount(); ++i) {
1163 qDebug() << " - " << path.elements()[i]
1164 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1167 for (int i=0; i<path.elementCount(); ++i) {
1168 qDebug() << " ---- "
1169 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1174 Q_D(QRasterPaintEngine);
1175 QRasterPaintEngineState *s = state();
1177 const qreal *points = path.points();
1178 const QPainterPath::ElementType *types = path.elements();
1180 // There are some cases that are not supported by clip(QRect)
1181 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1182 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1183 if (s->matrix.type() <= QTransform::TxScale
1184 && ((path.shape() == QVectorPath::RectangleHint)
1185 || (isRect(points, path.elementCount())
1186 && (!types || (types[0] == QPainterPath::MoveToElement
1187 && types[1] == QPainterPath::LineToElement
1188 && types[2] == QPainterPath::LineToElement
1189 && types[3] == QPainterPath::LineToElement))))) {
1190 #ifdef QT_DEBUG_DRAW
1191 qDebug() << " --- optimizing vector clip to rect clip...";
1194 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1195 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1200 if (op == Qt::NoClip) {
1201 qrasterpaintengine_state_setNoClip(s);
1204 QClipData *base = d->baseClip.data();
1206 // Intersect with current clip when available...
1207 if (op == Qt::IntersectClip && s->clip)
1210 // We always intersect, except when there is nothing to
1211 // intersect with, in which case we simplify the operation to
1213 Qt::ClipOperation isectOp = Qt::IntersectClip;
1215 isectOp = Qt::ReplaceClip;
1217 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1218 newClip->initialize();
1219 ClipData clipData = { base, newClip, isectOp };
1220 ensureOutlineMapper();
1221 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1225 if (op == Qt::UniteClip) {
1227 QClipData *result = new QClipData(d->rasterBuffer->height());
1228 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1229 qt_merge_clip(current, newClip, result);
1237 if (s->flags.has_clip_ownership)
1241 s->flags.has_clip_ownership = true;
1243 qrasterpaintengine_dirty_clip(d, s);
1251 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1253 #ifdef QT_DEBUG_DRAW
1254 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1257 QRasterPaintEngineState *s = state();
1259 if (op == Qt::NoClip) {
1260 qrasterpaintengine_state_setNoClip(s);
1262 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1263 QPaintEngineEx::clip(rect, op);
1266 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1267 QPaintEngineEx::clip(rect, op);
1273 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1275 Q_D(QRasterPaintEngine);
1276 QRect clipRect = r & d->deviceRect;
1277 QRasterPaintEngineState *s = state();
1279 if (op == Qt::ReplaceClip || s->clip == 0) {
1281 // No current clip, hence we intersect with sysclip and be
1283 QRegion clipRegion = systemClip();
1284 QClipData *clip = new QClipData(d->rasterBuffer->height());
1286 if (clipRegion.isEmpty())
1287 clip->setClipRect(clipRect);
1289 clip->setClipRegion(clipRegion & clipRect);
1291 if (s->flags.has_clip_ownership)
1295 s->clip->enabled = true;
1296 s->flags.has_clip_ownership = true;
1298 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1299 QClipData *base = s->clip;
1302 if (base->hasRectClip || base->hasRegionClip) {
1303 if (!s->flags.has_clip_ownership) {
1304 s->clip = new QClipData(d->rasterBuffer->height());
1305 s->flags.has_clip_ownership = true;
1307 if (base->hasRectClip)
1308 s->clip->setClipRect(base->clipRect & clipRect);
1310 s->clip->setClipRegion(base->clipRegion & clipRect);
1311 s->clip->enabled = true;
1319 qrasterpaintengine_dirty_clip(d, s);
1327 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1329 #ifdef QT_DEBUG_DRAW
1330 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1333 Q_D(QRasterPaintEngine);
1335 if (region.rectCount() == 1) {
1336 clip(region.boundingRect(), op);
1340 QRasterPaintEngineState *s = state();
1341 const QClipData *clip = d->clip();
1342 const QClipData *baseClip = d->baseClip.data();
1344 if (op == Qt::NoClip) {
1345 qrasterpaintengine_state_setNoClip(s);
1346 } else if (s->matrix.type() > QTransform::TxScale
1347 || op == Qt::UniteClip
1348 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1349 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1350 QPaintEngineEx::clip(region, op);
1352 const QClipData *curClip;
1355 if (op == Qt::IntersectClip)
1360 if (s->flags.has_clip_ownership) {
1364 newClip = new QClipData(d->rasterBuffer->height());
1366 s->flags.has_clip_ownership = true;
1369 QRegion r = s->matrix.map(region);
1370 if (curClip->hasRectClip)
1371 newClip->setClipRegion(r & curClip->clipRect);
1372 else if (curClip->hasRegionClip)
1373 newClip->setClipRegion(r & curClip->clipRegion);
1375 qrasterpaintengine_dirty_clip(d, s);
1382 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1384 #ifdef QT_DEBUG_DRAW
1385 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1388 if (!fillData->blend)
1391 Q_D(QRasterPaintEngine);
1393 const QRectF controlPointRect = path.controlPointRect();
1395 QRasterPaintEngineState *s = state();
1396 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1397 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1398 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1399 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1400 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1401 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1403 if (!s->flags.antialiased && !do_clip) {
1404 d->initializeRasterizer(fillData);
1405 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1409 ensureOutlineMapper();
1410 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1413 static void fillRect_normalized(const QRect &r, QSpanData *data,
1414 QRasterPaintEnginePrivate *pe)
1418 bool rectClipped = true;
1421 x1 = qMax(r.x(), data->clip->xmin);
1422 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1423 y1 = qMax(r.y(), data->clip->ymin);
1424 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1425 rectClipped = data->clip->hasRectClip;
1428 x1 = qMax(r.x(), pe->deviceRect.x());
1429 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1430 y1 = qMax(r.y(), pe->deviceRect.y());
1431 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1433 x1 = qMax(r.x(), 0);
1434 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1435 y1 = qMax(r.y(), 0);
1436 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1439 if (x2 <= x1 || y2 <= y1)
1442 const int width = x2 - x1;
1443 const int height = y2 - y1;
1445 bool isUnclipped = rectClipped
1446 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1448 if (pe && isUnclipped) {
1449 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1451 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1452 || (mode == QPainter::CompositionMode_SourceOver
1453 && qAlpha(data->solid.color) == 255)))
1455 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1461 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1463 const int nspans = 256;
1464 QT_FT_Span spans[nspans];
1466 Q_ASSERT(data->blend);
1469 int n = qMin(nspans, y2 - y);
1473 spans[i].len = width;
1475 spans[i].coverage = 255;
1479 blend(n, spans, data);
1487 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1489 #ifdef QT_DEBUG_DRAW
1490 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1492 Q_D(QRasterPaintEngine);
1494 QRasterPaintEngineState *s = state();
1498 if (s->brushData.blend) {
1499 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1500 const QRect *r = rects;
1501 const QRect *lastRect = rects + rectCount;
1503 int offset_x = int(s->matrix.dx());
1504 int offset_y = int(s->matrix.dy());
1505 while (r < lastRect) {
1506 QRect rect = r->normalized();
1507 QRect rr = rect.translated(offset_x, offset_y);
1508 fillRect_normalized(rr, &s->brushData, d);
1512 QRectVectorPath path;
1513 for (int i=0; i<rectCount; ++i) {
1515 fill(path, s->brush);
1521 if (s->penData.blend) {
1522 QRectVectorPath path;
1523 if (s->flags.fast_pen) {
1524 QCosmeticStroker stroker(s, d->deviceRect);
1525 for (int i = 0; i < rectCount; ++i) {
1527 stroker.drawPath(path);
1530 for (int i = 0; i < rectCount; ++i) {
1532 stroke(path, s->pen);
1541 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1543 #ifdef QT_DEBUG_DRAW
1544 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1546 #ifdef QT_FAST_SPANS
1547 Q_D(QRasterPaintEngine);
1549 QRasterPaintEngineState *s = state();
1552 if (s->flags.tx_noshear) {
1554 if (s->brushData.blend) {
1555 d->initializeRasterizer(&s->brushData);
1556 for (int i = 0; i < rectCount; ++i) {
1557 const QRectF &rect = rects[i].normalized();
1560 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1561 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1562 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1567 if (s->penData.blend) {
1568 QRectVectorPath path;
1569 if (s->flags.fast_pen) {
1570 QCosmeticStroker stroker(s, d->deviceRect);
1571 for (int i = 0; i < rectCount; ++i) {
1573 stroker.drawPath(path);
1576 for (int i = 0; i < rectCount; ++i) {
1578 QPaintEngineEx::stroke(path, s->lastPen);
1585 #endif // QT_FAST_SPANS
1586 QPaintEngineEx::drawRects(rects, rectCount);
1593 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1595 Q_D(QRasterPaintEngine);
1596 QRasterPaintEngineState *s = state();
1599 if (!s->penData.blend)
1602 if (s->flags.fast_pen) {
1603 QCosmeticStroker stroker(s, d->deviceRect);
1604 stroker.drawPath(path);
1605 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1606 qreal width = s->lastPen.isCosmetic()
1607 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1608 : qpen_widthf(s->lastPen) * s->txscale;
1610 qreal dashOffset = s->lastPen.dashOffset();
1612 qreal patternLength = 0;
1613 const QVector<qreal> pattern = s->lastPen.dashPattern();
1614 for (int i = 0; i < pattern.size(); ++i)
1615 patternLength += pattern.at(i);
1617 if (patternLength > 0) {
1618 int n = qFloor(dashOffset / patternLength);
1619 dashOffset -= n * patternLength;
1620 while (dashOffset >= pattern.at(dashIndex)) {
1621 dashOffset -= pattern.at(dashIndex);
1622 if (++dashIndex >= pattern.size())
1628 Q_D(QRasterPaintEngine);
1629 d->initializeRasterizer(&s->penData);
1630 int lineCount = path.elementCount() / 2;
1631 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1633 for (int i = 0; i < lineCount; ++i) {
1634 if (lines[i].p1() == lines[i].p2()) {
1635 if (s->lastPen.capStyle() != Qt::FlatCap) {
1636 QPointF p = lines[i].p1();
1637 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1638 QPointF(p.x() + width*0.5, p.y())));
1639 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1644 const QLineF line = s->matrix.map(lines[i]);
1645 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1646 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1647 width / line.length(),
1648 s->lastPen.capStyle() == Qt::SquareCap);
1650 d->rasterizeLine_dashed(line, width,
1651 &dashIndex, &dashOffset, &inDash);
1656 QPaintEngineEx::stroke(path, pen);
1659 static inline QRect toNormalizedFillRect(const QRectF &rect)
1661 int x1 = qRound(rect.x());
1662 int y1 = qRound(rect.y());
1663 int x2 = qRound(rect.right());
1664 int y2 = qRound(rect.bottom());
1671 return QRect(x1, y1, x2 - x1, y2 - y1);
1677 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1681 #ifdef QT_DEBUG_DRAW
1682 QRectF rf = path.controlPointRect();
1683 qDebug() << "QRasterPaintEngine::fill(): "
1684 << "size=" << path.elementCount()
1685 << ", hints=" << hex << path.hints()
1689 Q_D(QRasterPaintEngine);
1690 QRasterPaintEngineState *s = state();
1693 if (!s->brushData.blend)
1696 if (path.shape() == QVectorPath::RectangleHint) {
1697 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1698 const qreal *p = path.points();
1699 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1700 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1701 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1705 if (s->flags.tx_noshear) {
1706 d->initializeRasterizer(&s->brushData);
1707 // ### Is normalizing really necessary here?
1708 const qreal *p = path.points();
1709 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1711 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1712 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1713 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1719 // ### Optimize for non transformed ellipses and rectangles...
1720 QRectF cpRect = path.controlPointRect();
1721 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1722 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1725 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1726 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1727 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1728 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1730 // ### Falonc: implement....
1731 // if (!s->flags.antialiased && !do_clip) {
1732 // d->initializeRasterizer(&s->brushData);
1733 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1737 ensureOutlineMapper();
1738 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1741 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1743 Q_D(QRasterPaintEngine);
1744 QRasterPaintEngineState *s = state();
1746 if (!s->flags.antialiased) {
1747 uint txop = s->matrix.type();
1748 if (txop == QTransform::TxNone) {
1749 fillRect_normalized(toNormalizedFillRect(r), data, d);
1751 } else if (txop == QTransform::TxTranslate) {
1752 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1753 fillRect_normalized(rr, data, d);
1755 } else if (txop == QTransform::TxScale) {
1756 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1757 fillRect_normalized(rr, data, d);
1762 if (s->flags.tx_noshear) {
1763 d->initializeRasterizer(data);
1764 QRectF nr = r.normalized();
1765 if (!nr.isEmpty()) {
1766 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1767 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1768 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1775 ensureOutlineMapper();
1776 fillPath(path, data);
1782 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1784 #ifdef QT_DEBUG_DRAW
1785 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1787 QRasterPaintEngineState *s = state();
1790 if (!s->brushData.blend)
1793 fillRect(r, &s->brushData);
1799 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1801 #ifdef QT_DEBUG_DRAW
1802 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1804 Q_D(QRasterPaintEngine);
1805 QRasterPaintEngineState *s = state();
1807 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1808 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1809 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1812 d->solid_color_filler.clip = d->clip();
1813 d->solid_color_filler.adjustSpanMethods();
1814 fillRect(r, &d->solid_color_filler);
1817 static inline bool isAbove(const QPointF *a, const QPointF *b)
1819 return a->y() < b->y();
1822 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1827 Q_ASSERT(pointCount >= 2);
1829 QVector<const QPointF *> sorted;
1830 sorted.reserve(pointCount);
1832 upper->reserve(pointCount * 3 / 4);
1833 lower->reserve(pointCount * 3 / 4);
1835 for (int i = 0; i < pointCount; ++i)
1836 sorted << points + i;
1838 qSort(sorted.begin(), sorted.end(), isAbove);
1840 qreal splitY = sorted.at(sorted.size() / 2)->y();
1842 const QPointF *end = points + pointCount;
1843 const QPointF *last = end - 1;
1845 QVector<QPointF> *bin[2] = { upper, lower };
1847 for (const QPointF *p = points; p < end; ++p) {
1848 int side = p->y() < splitY;
1849 int lastSide = last->y() < splitY;
1851 if (side != lastSide) {
1852 if (qFuzzyCompare(p->y(), splitY)) {
1853 bin[!side]->append(*p);
1854 } else if (qFuzzyCompare(last->y(), splitY)) {
1855 bin[side]->append(*last);
1857 QPointF delta = *p - *last;
1858 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1860 bin[0]->append(intersection);
1861 bin[1]->append(intersection);
1865 bin[side]->append(*p);
1870 // give up if we couldn't reduce the point count
1871 return upper->size() < pointCount && lower->size() < pointCount;
1877 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1879 Q_D(QRasterPaintEngine);
1880 QRasterPaintEngineState *s = state();
1882 const int maxPoints = 0xffff;
1884 // max amount of points that raster engine can reliably handle
1885 if (pointCount > maxPoints) {
1886 QVector<QPointF> upper, lower;
1888 if (splitPolygon(points, pointCount, &upper, &lower)) {
1889 fillPolygon(upper.constData(), upper.size(), mode);
1890 fillPolygon(lower.constData(), lower.size(), mode);
1892 qWarning("Polygon too complex for filling.");
1897 // Compose polygon fill..,
1898 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1899 ensureOutlineMapper();
1900 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1903 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1905 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1911 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1913 Q_D(QRasterPaintEngine);
1914 QRasterPaintEngineState *s = state();
1916 #ifdef QT_DEBUG_DRAW
1917 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1918 for (int i=0; i<pointCount; ++i)
1919 qDebug() << " - " << points[i];
1921 Q_ASSERT(pointCount >= 2);
1923 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1924 QRectF r(points[0], points[2]);
1930 if (mode != PolylineMode) {
1933 if (s->brushData.blend) {
1934 fillPolygon(points, pointCount, mode);
1938 // Do the outline...
1939 if (s->penData.blend) {
1940 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1941 if (s->flags.fast_pen) {
1942 QCosmeticStroker stroker(s, d->deviceRect);
1943 stroker.drawPath(vp);
1945 QPaintEngineEx::stroke(vp, s->lastPen);
1953 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1955 Q_D(QRasterPaintEngine);
1956 QRasterPaintEngineState *s = state();
1958 #ifdef QT_DEBUG_DRAW
1959 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1960 for (int i=0; i<pointCount; ++i)
1961 qDebug() << " - " << points[i];
1963 Q_ASSERT(pointCount >= 2);
1964 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1965 QRect r(points[0].x(),
1967 points[2].x() - points[0].x(),
1968 points[2].y() - points[0].y());
1976 if (mode != PolylineMode) {
1978 if (s->brushData.blend) {
1979 // Compose polygon fill..,
1980 ensureOutlineMapper();
1981 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1982 d->outlineMapper->moveTo(*points);
1983 const QPoint *p = points;
1984 const QPoint *ep = points + pointCount - 1;
1986 d->outlineMapper->lineTo(*(++p));
1988 d->outlineMapper->endOutline();
1991 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1993 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1997 // Do the outline...
1998 if (s->penData.blend) {
1999 int count = pointCount * 2;
2000 QVarLengthArray<qreal> fpoints(count);
2002 for (int i=0; i<count; i+=2) {
2003 fpoints[i] = ((int *) points)[i+1];
2004 fpoints[i+1] = ((int *) points)[i];
2007 for (int i=0; i<count; ++i)
2008 fpoints[i] = ((int *) points)[i];
2010 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2012 if (s->flags.fast_pen) {
2013 QCosmeticStroker stroker(s, d->deviceRect);
2014 stroker.drawPath(vp);
2016 QPaintEngineEx::stroke(vp, s->lastPen);
2024 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2026 #ifdef QT_DEBUG_DRAW
2027 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2030 QPixmapData *pd = pixmap.pixmapData();
2031 if (pd->classId() == QPixmapData::RasterClass) {
2032 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2033 if (image.depth() == 1) {
2034 Q_D(QRasterPaintEngine);
2035 QRasterPaintEngineState *s = state();
2036 if (s->matrix.type() <= QTransform::TxTranslate) {
2038 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2040 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2043 QRasterPaintEngine::drawImage(pos, image);
2046 const QImage image = pixmap.toImage();
2047 if (pixmap.depth() == 1) {
2048 Q_D(QRasterPaintEngine);
2049 QRasterPaintEngineState *s = state();
2050 if (s->matrix.type() <= QTransform::TxTranslate) {
2052 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2054 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2057 QRasterPaintEngine::drawImage(pos, image);
2065 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2067 #ifdef QT_DEBUG_DRAW
2068 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2071 QPixmapData* pd = pixmap.pixmapData();
2072 if (pd->classId() == QPixmapData::RasterClass) {
2073 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2074 if (image.depth() == 1) {
2075 Q_D(QRasterPaintEngine);
2076 QRasterPaintEngineState *s = state();
2077 if (s->matrix.type() <= QTransform::TxTranslate
2078 && r.size() == sr.size()
2079 && r.size() == pixmap.size()) {
2081 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2084 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2087 drawImage(r, image, sr);
2090 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2091 const QImage image = pd->toImage(clippedSource);
2092 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2093 if (image.depth() == 1) {
2094 Q_D(QRasterPaintEngine);
2095 QRasterPaintEngineState *s = state();
2096 if (s->matrix.type() <= QTransform::TxTranslate
2097 && r.size() == sr.size()
2098 && r.size() == pixmap.size()) {
2100 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2103 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2106 drawImage(r, image, translatedSource);
2111 // assumes that rect has positive width and height
2112 static inline const QRect toRect_normalized(const QRectF &rect)
2114 const int x = qRound(rect.x());
2115 const int y = qRound(rect.y());
2116 const int w = int(rect.width() + qreal(0.5));
2117 const int h = int(rect.height() + qreal(0.5));
2119 return QRect(x, y, w, h);
2122 static inline int fast_ceil_positive(const qreal &v)
2124 const int iv = int(v);
2131 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2133 const int xmin = int(rect.x());
2134 const int xmax = int(fast_ceil_positive(rect.right()));
2135 const int ymin = int(rect.y());
2136 const int ymax = int(fast_ceil_positive(rect.bottom()));
2137 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2143 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2145 #ifdef QT_DEBUG_DRAW
2146 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2149 Q_D(QRasterPaintEngine);
2150 QRasterPaintEngineState *s = state();
2152 if (s->matrix.type() > QTransform::TxTranslate) {
2153 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2155 QRectF(0, 0, img.width(), img.height()));
2158 const QClipData *clip = d->clip();
2159 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2161 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2162 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2165 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2167 } else if (clip->hasRectClip) {
2168 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2176 d->image_filler.clip = clip;
2177 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2178 if (!d->image_filler.blend)
2180 d->image_filler.dx = -pt.x();
2181 d->image_filler.dy = -pt.y();
2182 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2184 fillRect_normalized(rr, &d->image_filler, d);
2189 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2191 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2202 inline RotationType qRotationType(const QTransform &transform)
2204 QTransform::TransformationType type = transform.type();
2206 if (type > QTransform::TxRotate)
2209 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2210 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2213 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2214 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2217 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2218 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2224 inline bool isPixelAligned(const QRectF &rect) {
2225 return QRectF(rect.toRect()) == rect;
2232 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2233 Qt::ImageConversionFlags)
2235 #ifdef QT_DEBUG_DRAW
2236 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2242 Q_D(QRasterPaintEngine);
2243 QRasterPaintEngineState *s = state();
2244 int sr_l = qFloor(sr.left());
2245 int sr_r = qCeil(sr.right()) - 1;
2246 int sr_t = qFloor(sr.top());
2247 int sr_b = qCeil(sr.bottom()) - 1;
2249 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2250 QTransform old = s->matrix;
2252 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2253 QRgb color = img.pixel(sr_l, sr_t);
2254 switch (img.format()) {
2255 case QImage::Format_ARGB32_Premultiplied:
2256 case QImage::Format_ARGB8565_Premultiplied:
2257 case QImage::Format_ARGB6666_Premultiplied:
2258 case QImage::Format_ARGB8555_Premultiplied:
2259 case QImage::Format_ARGB4444_Premultiplied:
2260 // Combine premultiplied color with the opacity set on the painter.
2261 d->solid_color_filler.solid.color =
2262 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2263 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2266 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2270 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2271 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2275 d->solid_color_filler.clip = d->clip();
2276 d->solid_color_filler.adjustSpanMethods();
2277 fillRect(r, &d->solid_color_filler);
2283 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2285 const QClipData *clip = d->clip();
2287 if (s->matrix.type() > QTransform::TxTranslate
2289 && (!clip || clip->hasRectClip)
2290 && s->intOpacity == 256
2291 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2292 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2293 && d->rasterBuffer->format == img.format()
2294 && (d->rasterBuffer->format == QImage::Format_RGB16
2295 || d->rasterBuffer->format == QImage::Format_RGB32
2296 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2297 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2299 RotationType rotationType = qRotationType(s->matrix);
2301 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2302 QRectF transformedTargetRect = s->matrix.mapRect(r);
2304 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2305 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2307 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2308 if (clippedTransformedTargetRect.isNull())
2311 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2313 QRect clippedSourceRect
2314 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2315 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2317 uint dbpl = d->rasterBuffer->bytesPerLine();
2318 uint sbpl = img.bytesPerLine();
2320 uchar *dst = d->rasterBuffer->buffer();
2321 uint bpp = img.depth() >> 3;
2323 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2324 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2326 uint cw = clippedSourceRect.width();
2327 uint ch = clippedSourceRect.height();
2329 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2336 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2338 QRectF targetBounds = s->matrix.mapRect(r);
2339 bool exceedsPrecision = targetBounds.width() > 0xffff
2340 || targetBounds.height() > 0xffff;
2342 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2343 if (s->matrix.type() > QTransform::TxScale) {
2344 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2345 if (func && (!clip || clip->hasRectClip)) {
2346 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2347 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2348 s->matrix, s->intOpacity);
2352 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2353 if (func && (!clip || clip->hasRectClip)) {
2354 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2355 img.bits(), img.bytesPerLine(),
2356 qt_mapRect_non_normalizing(r, s->matrix), sr,
2357 !clip ? d->deviceRect : clip->clipRect,
2364 QTransform copy = s->matrix;
2365 copy.translate(r.x(), r.y());
2367 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2368 copy.translate(-sr.x(), -sr.y());
2370 d->image_filler_xform.clip = clip;
2371 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2372 if (!d->image_filler_xform.blend)
2374 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2376 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2377 QRectF rr = s->matrix.mapRect(r);
2379 const int x1 = qRound(rr.x());
2380 const int y1 = qRound(rr.y());
2381 const int x2 = qRound(rr.right());
2382 const int y2 = qRound(rr.bottom());
2384 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2388 #ifdef QT_FAST_SPANS
2390 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2391 d->initializeRasterizer(&d->image_filler_xform);
2392 d->rasterizer->setAntialiased(s->flags.antialiased);
2394 const QRectF &rect = r.normalized();
2395 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2396 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2398 if (s->flags.tx_noshear)
2399 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2401 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2407 QTransform m = s->matrix;
2408 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2409 m.m21(), m.m22(), m.m23(),
2410 m.m31(), m.m32(), m.m33());
2411 fillPath(path, &d->image_filler_xform);
2414 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2415 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2417 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2419 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2421 } else if (clip->hasRectClip) {
2422 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2428 d->image_filler.clip = clip;
2429 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2430 if (!d->image_filler.blend)
2432 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2433 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2436 rr.translate(s->matrix.dx(), s->matrix.dy());
2438 const int x1 = qRound(rr.x());
2439 const int y1 = qRound(rr.y());
2440 const int x2 = qRound(rr.right());
2441 const int y2 = qRound(rr.bottom());
2443 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2450 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2452 #ifdef QT_DEBUG_DRAW
2453 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2455 Q_D(QRasterPaintEngine);
2456 QRasterPaintEngineState *s = state();
2460 QPixmapData *pd = pixmap.pixmapData();
2461 if (pd->classId() == QPixmapData::RasterClass) {
2462 image = static_cast<QRasterPixmapData *>(pd)->image;
2464 image = pixmap.toImage();
2467 if (image.depth() == 1)
2468 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2470 if (s->matrix.type() > QTransform::TxTranslate) {
2471 QTransform copy = s->matrix;
2472 copy.translate(r.x(), r.y());
2473 copy.translate(-sr.x(), -sr.y());
2474 d->image_filler_xform.clip = d->clip();
2475 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2476 if (!d->image_filler_xform.blend)
2478 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2480 #ifdef QT_FAST_SPANS
2482 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2483 d->initializeRasterizer(&d->image_filler_xform);
2484 d->rasterizer->setAntialiased(s->flags.antialiased);
2486 const QRectF &rect = r.normalized();
2487 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2488 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2489 if (s->flags.tx_noshear)
2490 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2492 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2498 fillPath(path, &d->image_filler_xform);
2500 d->image_filler.clip = d->clip();
2502 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2503 if (!d->image_filler.blend)
2505 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2506 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2509 rr.translate(s->matrix.dx(), s->matrix.dy());
2510 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2516 static inline bool monoVal(const uchar* s, int x)
2518 return (s[x>>3] << (x&7)) & 0x80;
2524 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2526 Q_D(QRasterPaintEngine);
2527 QRasterPaintEngineState *s = state();
2529 if (!s->penData.blend)
2532 QRasterBuffer *rb = d->rasterBuffer.data();
2534 const QRect rect(rx, ry, w, h);
2535 const QClipData *clip = d->clip();
2536 bool unclipped = false;
2538 // inlined QRect::intersects
2539 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2540 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2542 if (clip->hasRectClip) {
2543 unclipped = rx > clip->xmin
2544 && rx + w < clip->xmax
2546 && ry + h < clip->ymax;
2552 // inlined QRect::intersects
2553 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2554 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2558 // inlined QRect::contains
2559 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2560 && rect.top() >= 0 && rect.bottom() < rb->height();
2562 unclipped = contains && d->isUnclipped_normalized(rect);
2565 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2566 const uchar * scanline = static_cast<const uchar *>(src);
2568 if (s->flags.fast_text) {
2571 if (s->penData.bitmapBlit) {
2572 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2573 scanline, w, h, bpl);
2576 } else if (depth == 8) {
2577 if (s->penData.alphamapBlit) {
2578 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2579 scanline, w, h, bpl, 0);
2582 } else if (depth == 32) {
2583 // (A)RGB Alpha mask where the alpha component is not used.
2584 if (s->penData.alphaRGBBlit) {
2585 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2586 (const uint *) scanline, w, h, bpl / 4, 0);
2590 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2591 // (A)RGB Alpha mask where the alpha component is not used.
2593 int nx = qMax(0, rx);
2594 int ny = qMax(0, ry);
2596 // Move scanline pointer to compensate for moved x and y
2597 int xdiff = nx - rx;
2598 int ydiff = ny - ry;
2599 scanline += ydiff * bpl;
2600 scanline += xdiff * (depth == 32 ? 4 : 1);
2605 if (nx + w > d->rasterBuffer->width())
2606 w = d->rasterBuffer->width() - nx;
2607 if (ny + h > d->rasterBuffer->height())
2608 h = d->rasterBuffer->height() - ny;
2613 if (depth == 8 && s->penData.alphamapBlit) {
2614 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2615 scanline, w, h, bpl, clip);
2616 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2617 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2618 (const uint *) scanline, w, h, bpl / 4, clip);
2633 scanline += bpl * y0;
2637 w = qMin(w, rb->width() - qMax(0, rx));
2638 h = qMin(h, rb->height() - qMax(0, ry));
2640 if (w <= 0 || h <= 0)
2643 const int NSPANS = 256;
2644 QSpan spans[NSPANS];
2647 const int x1 = x0 + w;
2648 const int y1 = y0 + h;
2651 for (int y = y0; y < y1; ++y) {
2652 for (int x = x0; x < x1; ) {
2653 if (!monoVal(scanline, x)) {
2658 if (current == NSPANS) {
2659 blend(current, spans, &s->penData);
2662 spans[current].x = x + rx;
2663 spans[current].y = y + ry;
2664 spans[current].coverage = 255;
2667 // extend span until we find a different one.
2668 while (x < x1 && monoVal(scanline, x)) {
2672 spans[current].len = len;
2677 } else if (depth == 8) {
2678 for (int y = y0; y < y1; ++y) {
2679 for (int x = x0; x < x1; ) {
2680 // Skip those with 0 coverage
2681 if (scanline[x] == 0) {
2686 if (current == NSPANS) {
2687 blend(current, spans, &s->penData);
2690 int coverage = scanline[x];
2691 spans[current].x = x + rx;
2692 spans[current].y = y + ry;
2693 spans[current].coverage = coverage;
2697 // extend span until we find a different one.
2698 while (x < x1 && scanline[x] == coverage) {
2702 spans[current].len = len;
2707 } else { // 32-bit alpha...
2708 uint *sl = (uint *) src;
2709 for (int y = y0; y < y1; ++y) {
2710 for (int x = x0; x < x1; ) {
2711 // Skip those with 0 coverage
2712 if ((sl[x] & 0x00ffffff) == 0) {
2717 if (current == NSPANS) {
2718 blend(current, spans, &s->penData);
2721 uint rgbCoverage = sl[x];
2722 int coverage = qGreen(rgbCoverage);
2723 spans[current].x = x + rx;
2724 spans[current].y = y + ry;
2725 spans[current].coverage = coverage;
2729 // extend span until we find a different one.
2730 while (x < x1 && sl[x] == rgbCoverage) {
2734 spans[current].len = len;
2737 sl += bpl / sizeof(uint);
2740 // qDebug() << "alphaPenBlt: num spans=" << current
2741 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2742 // Call span func for current set of spans.
2744 blend(current, spans, &s->penData);
2747 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2748 const QFixedPoint *positions, QFontEngine *fontEngine)
2750 Q_D(QRasterPaintEngine);
2751 QRasterPaintEngineState *s = state();
2753 #if !defined(QT_NO_FREETYPE)
2754 if (fontEngine->type() == QFontEngine::Freetype) {
2755 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2756 QFontEngineFT::GlyphFormat neededFormat =
2757 painter()->device()->devType() == QInternal::Widget
2758 ? fe->defaultGlyphFormat()
2759 : QFontEngineFT::Format_A8;
2761 if (d_func()->mono_surface
2762 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2764 neededFormat = QFontEngineFT::Format_Mono;
2766 if (neededFormat == QFontEngineFT::Format_None)
2767 neededFormat = QFontEngineFT::Format_A8;
2769 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2770 if (s->matrix.type() >= QTransform::TxScale) {
2771 if (s->matrix.isAffine())
2772 gset = fe->loadTransformedGlyphSet(s->matrix);
2777 if (!gset || gset->outline_drawing
2778 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2781 FT_Face lockedFace = 0;
2784 switch (neededFormat) {
2785 case QFontEngineFT::Format_Mono:
2788 case QFontEngineFT::Format_A8:
2791 case QFontEngineFT::Format_A32:
2799 for (int i = 0; i < numGlyphs; i++) {
2800 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2801 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2803 if (!glyph || glyph->format != neededFormat) {
2805 lockedFace = fe->lockFace();
2806 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2809 if (!glyph || !glyph->data)
2813 switch (neededFormat) {
2814 case QFontEngineFT::Format_Mono:
2815 pitch = ((glyph->width + 31) & ~31) >> 3;
2817 case QFontEngineFT::Format_A8:
2818 pitch = (glyph->width + 3) & ~3;
2820 case QFontEngineFT::Format_A32:
2821 pitch = glyph->width * 4;
2828 alphaPenBlt(glyph->data, pitch, depth,
2829 qFloor(positions[i].x) + glyph->x,
2830 qFloor(positions[i].y) - glyph->y,
2831 glyph->width, glyph->height);
2838 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2840 QImageTextureGlyphCache *cache =
2841 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2843 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2844 fontEngine->setGlyphCache(0, cache);
2847 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2848 cache->fillInPendingGlyphs();
2850 const QImage &image = cache->image();
2851 int bpl = image.bytesPerLine();
2853 int depth = image.depth();
2857 leftShift = 2; // multiply by 4
2858 else if (depth == 1)
2859 rightShift = 3; // divide by 8
2861 int margin = cache->glyphMargin();
2862 const uchar *bits = image.bits();
2863 for (int i=0; i<numGlyphs; ++i) {
2865 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2866 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2867 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2871 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2872 int y = qFloor(positions[i].y) - c.baseLineY - margin;
2874 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2877 // c.baseLineX, c.baseLineY,
2880 // positions[i].x.toInt(), positions[i].y.toInt());
2882 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2888 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2889 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2891 Q_D(QRasterPaintEngine);
2892 QRasterPaintEngineState *s = state();
2894 QFontEngine *fontEngine = ti.fontEngine;
2895 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2896 QPaintEngineEx::drawTextItem(p, ti);
2900 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2902 QVarLengthArray<QFixedPoint> positions;
2903 QVarLengthArray<glyph_t> glyphs;
2904 QTransform matrix = s->matrix;
2905 matrix.translate(p.x(), p.y());
2906 if (matrix.type() == QTransform::TxScale)
2907 fe->setFontScale(matrix.m11());
2908 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2910 for (int i=0; i<glyphs.size(); ++i) {
2911 TOpenFontCharMetrics tmetrics;
2912 const TUint8 *glyphBitmapBytes;
2913 TSize glyphBitmapSize;
2914 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2915 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
2916 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
2917 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2920 if (matrix.type() == QTransform::TxScale)
2921 fe->setFontScale(1.0);
2925 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2928 * Returns true if the rectangle is completely within the current clip
2929 * state of the paint engine.
2931 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2933 const QClipData *cl = clip();
2935 // inline contains() for performance (we know the rects are normalized)
2936 const QRect &r1 = deviceRect;
2937 return (r.left() >= r1.left() && r.right() <= r1.right()
2938 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2942 if (cl->hasRectClip) {
2943 // currently all painting functions clips to deviceRect internally
2944 if (cl->clipRect == deviceRect)
2947 // inline contains() for performance (we know the rects are normalized)
2948 const QRect &r1 = cl->clipRect;
2949 return (r.left() >= r1.left() && r.right() <= r1.right()
2950 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2952 return qt_region_strictContains(cl->clipRegion, r);
2956 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2959 Q_Q(const QRasterPaintEngine);
2960 const QRasterPaintEngineState *s = q->state();
2961 const QClipData *cl = clip();
2963 QRect r = rect.normalized();
2964 // inline contains() for performance (we know the rects are normalized)
2965 const QRect &r1 = deviceRect;
2966 return (r.left() >= r1.left() && r.right() <= r1.right()
2967 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2971 // currently all painting functions that call this function clip to deviceRect internally
2972 if (cl->hasRectClip && cl->clipRect == deviceRect)
2975 if (s->flags.antialiased)
2978 QRect r = rect.normalized();
2980 r.setX(r.x() - penWidth);
2981 r.setY(r.y() - penWidth);
2982 r.setWidth(r.width() + 2 * penWidth);
2983 r.setHeight(r.height() + 2 * penWidth);
2986 if (cl->hasRectClip) {
2987 // inline contains() for performance (we know the rects are normalized)
2988 const QRect &r1 = cl->clipRect;
2989 return (r.left() >= r1.left() && r.right() <= r1.right()
2990 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2992 return qt_region_strictContains(cl->clipRegion, r);
2996 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2999 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3003 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3004 const QSpanData *data) const
3006 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3010 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3011 const QSpanData *data) const
3013 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3019 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3024 QFontEngine *fontEngine = textItem->fontEngine();
3025 if (!supportsTransformations(fontEngine)) {
3026 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3029 QPaintEngineEx::drawStaticTextItem(textItem);
3036 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3038 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3039 QRasterPaintEngineState *s = state();
3041 #ifdef QT_DEBUG_DRAW
3042 Q_D(QRasterPaintEngine);
3043 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3044 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3051 #if defined (Q_OS_WIN) || defined(Q_WS_MAC)
3053 if (!supportsTransformations(ti.fontEngine)) {
3054 QVarLengthArray<QFixedPoint> positions;
3055 QVarLengthArray<glyph_t> glyphs;
3057 QTransform matrix = s->matrix;
3058 matrix.translate(p.x(), p.y());
3060 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3062 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3066 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC
3067 if (s->matrix.type() <= QTransform::TxTranslate
3068 || (s->matrix.type() == QTransform::TxScale
3069 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3070 drawGlyphsS60(p, ti);
3073 #else // Q_OS_WIN || Q_WS_MAC
3075 QFontEngine *fontEngine = ti.fontEngine;
3078 if (s->matrix.type() < QTransform::TxScale) {
3080 QVarLengthArray<QFixedPoint> positions;
3081 QVarLengthArray<glyph_t> glyphs;
3082 QTransform matrix = state()->transform();
3084 qreal _x = qFloor(p.x());
3085 qreal _y = qFloor(p.y());
3086 matrix.translate(_x, _y);
3088 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3089 if (glyphs.size() == 0)
3092 for(int i = 0; i < glyphs.size(); i++) {
3093 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3094 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3095 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3096 qRound(positions[i].x + metrics.x),
3097 qRound(positions[i].y + metrics.y),
3098 img.width(), img.height());
3104 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3106 if (fontEngine->type() != QFontEngine::Freetype) {
3107 QPaintEngineEx::drawTextItem(p, ti);
3111 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3113 QTransform matrix = s->matrix;
3114 matrix.translate(p.x(), p.y());
3116 QVarLengthArray<QFixedPoint> positions;
3117 QVarLengthArray<glyph_t> glyphs;
3118 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3119 if (glyphs.size() == 0)
3122 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3123 QPaintEngine::drawTextItem(p, ti);
3129 QPaintEngineEx::drawTextItem(p, ti);
3135 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3137 Q_D(QRasterPaintEngine);
3138 QRasterPaintEngineState *s = state();
3141 if (!s->penData.blend)
3144 if (!s->flags.fast_pen) {
3145 QPaintEngineEx::drawPoints(points, pointCount);
3149 QCosmeticStroker stroker(s, d->deviceRect);
3150 stroker.drawPoints(points, pointCount);
3154 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3156 Q_D(QRasterPaintEngine);
3157 QRasterPaintEngineState *s = state();
3160 if (!s->penData.blend)
3163 if (!s->flags.fast_pen) {
3164 QPaintEngineEx::drawPoints(points, pointCount);
3168 QCosmeticStroker stroker(s, d->deviceRect);
3169 stroker.drawPoints(points, pointCount);
3175 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3177 #ifdef QT_DEBUG_DRAW
3178 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3180 Q_D(QRasterPaintEngine);
3181 QRasterPaintEngineState *s = state();
3184 if (!s->penData.blend)
3187 if (s->flags.fast_pen) {
3188 QCosmeticStroker stroker(s, d->deviceRect);
3189 for (int i=0; i<lineCount; ++i) {
3190 const QLine &l = lines[i];
3191 stroker.drawLine(l.p1(), l.p2());
3194 QPaintEngineEx::drawLines(lines, lineCount);
3198 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3204 Q_Q(QRasterPaintEngine);
3205 QRasterPaintEngineState *s = q->state();
3207 const QPen &pen = s->lastPen;
3208 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3209 const QVector<qreal> pattern = pen.dashPattern();
3211 qreal patternLength = 0;
3212 for (int i = 0; i < pattern.size(); ++i)
3213 patternLength += pattern.at(i);
3215 if (patternLength <= 0)
3218 qreal length = line.length();
3219 Q_ASSERT(length > 0);
3220 while (length > 0) {
3221 const bool rasterize = *inDash;
3222 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3225 if (dash >= length) {
3227 *dashOffset += dash / width;
3231 *inDash = !(*inDash);
3232 if (++*dashIndex >= pattern.size())
3239 if (rasterize && dash > 0)
3240 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3247 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3249 #ifdef QT_DEBUG_DRAW
3250 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3252 Q_D(QRasterPaintEngine);
3253 QRasterPaintEngineState *s = state();
3256 if (!s->penData.blend)
3258 if (s->flags.fast_pen) {
3259 QCosmeticStroker stroker(s, d->deviceRect);
3260 for (int i=0; i<lineCount; ++i) {
3261 QLineF line = lines[i];
3262 stroker.drawLine(line.p1(), line.p2());
3265 QPaintEngineEx::drawLines(lines, lineCount);
3273 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3275 QPaintEngineEx::drawEllipse(rect);
3282 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3284 Q_D(QRasterPaintEngine);
3291 CGContextRef QRasterPaintEngine::getCGContext() const
3293 Q_D(const QRasterPaintEngine);
3294 return d->cgContext;
3302 void QRasterPaintEngine::setDC(HDC hdc) {
3303 Q_D(QRasterPaintEngine);
3310 HDC QRasterPaintEngine::getDC() const
3312 Q_D(const QRasterPaintEngine);
3319 void QRasterPaintEngine::releaseDC(HDC) const
3325 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3327 const QTransform &m = state()->matrix;
3328 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3329 QFontEngine::Type fontEngineType = fontEngine->type();
3330 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3331 || (m.type() <= QTransform::TxTranslate
3332 && (fontEngineType == QFontEngine::TestFontEngine
3333 || fontEngineType == QFontEngine::Box))) {
3337 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3340 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3342 #if defined(Q_WS_MAC)
3343 // Mac font engines don't support scaling and rotation
3344 if (m.type() > QTransform::TxTranslate)
3346 if (m.type() >= QTransform::TxProject)
3350 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3359 QPoint QRasterPaintEngine::coordinateOffset() const
3361 return QPoint(0, 0);
3364 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3369 Q_D(QRasterPaintEngine);
3371 Q_ASSERT(image.depth() == 1);
3373 const int spanCount = 256;
3374 QT_FT_Span spans[spanCount];
3378 int w = image.width();
3379 int h = image.height();
3380 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3381 int ymin = qMax(qRound(pos.y()), 0);
3382 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3383 int xmin = qMax(qRound(pos.x()), 0);
3385 int x_offset = xmin - qRound(pos.x());
3387 QImage::Format format = image.format();
3388 for (int y = ymin; y < ymax; ++y) {
3389 const uchar *src = image.scanLine(y - qRound(pos.y()));
3390 if (format == QImage::Format_MonoLSB) {
3391 for (int x = 0; x < xmax - xmin; ++x) {
3392 int src_x = x + x_offset;
3393 uchar pixel = src[src_x >> 3];
3398 if (pixel & (0x1 << (src_x & 7))) {
3399 spans[n].x = xmin + x;
3401 spans[n].coverage = 255;
3403 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3407 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3410 if (n == spanCount) {
3411 fg->blend(n, spans, fg);
3417 for (int x = 0; x < xmax - xmin; ++x) {
3418 int src_x = x + x_offset;
3419 uchar pixel = src[src_x >> 3];
3424 if (pixel & (0x80 >> (x & 7))) {
3425 spans[n].x = xmin + x;
3427 spans[n].coverage = 255;
3429 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3433 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3436 if (n == spanCount) {
3437 fg->blend(n, spans, fg);
3445 fg->blend(n, spans, fg);
3451 \enum QRasterPaintEngine::ClipType
3454 \value RectClip Indicates that the currently set clip is a single rectangle.
3455 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3460 Returns the type of the clip currently set.
3462 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3464 Q_D(const QRasterPaintEngine);
3466 const QClipData *clip = d->clip();
3467 if (!clip || clip->hasRectClip)
3475 Returns the bounding rect of the currently set clip.
3477 QRect QRasterPaintEngine::clipBoundingRect() const
3479 Q_D(const QRasterPaintEngine);
3481 const QClipData *clip = d->clip();
3484 return d->deviceRect;
3486 if (clip->hasRectClip)
3487 return clip->clipRect;
3489 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3492 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3494 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3496 QVarLengthArray<short, 4096> buffer;
3498 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3499 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3500 result->initialize();
3502 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3503 const QSpan *c1_spans = c1ClipLines[y].spans;
3504 int c1_count = c1ClipLines[y].count;
3505 const QSpan *c2_spans = c2ClipLines[y].spans;
3506 int c2_count = c2ClipLines[y].count;
3508 if (c1_count == 0 && c2_count == 0)
3510 if (c1_count == 0) {
3511 result->appendSpans(c2_spans, c2_count);
3513 } else if (c2_count == 0) {
3514 result->appendSpans(c1_spans, c1_count);
3518 // we need to merge the two
3520 // find required length
3521 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3522 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3524 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3526 // Fill with old spans.
3527 for (int i = 0; i < c1_count; ++i) {
3528 const QSpan *cs = c1_spans + i;
3529 for (int j=cs->x; j<cs->x + cs->len; ++j)
3530 buffer[j] = cs->coverage;
3533 // Fill with new spans
3534 for (int i = 0; i < c2_count; ++i) {
3535 const QSpan *cs = c2_spans + i;
3536 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3537 buffer[j] += cs->coverage;
3538 if (buffer[j] > 255)
3546 // Skip to next span
3547 while (x < max && buffer[x] == 0) ++x;
3548 if (x >= max) break;
3551 int coverage = buffer[x];
3553 // Find length of span
3554 while (x < max && buffer[x] == coverage)
3557 result->appendSpan(sx, x - sx, y, coverage);
3562 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3564 Q_Q(QRasterPaintEngine);
3565 QRasterPaintEngineState *s = q->state();
3567 rasterizer->setAntialiased(s->flags.antialiased);
3569 QRect clipRect(deviceRect);
3571 // ### get from optimized rectbased QClipData
3573 const QClipData *c = clip();
3575 const QRect r(QPoint(c->xmin, c->ymin),
3576 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3577 clipRect = clipRect.intersected(r);
3578 blend = data->blend;
3580 blend = data->unclipped_blend;
3583 rasterizer->setClipRect(clipRect);
3584 rasterizer->initialize(blend, data);
3587 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3588 ProcessSpans callback,
3589 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3591 if (!callback || !outline)
3594 Q_Q(QRasterPaintEngine);
3595 QRasterPaintEngineState *s = q->state();
3597 if (!s->flags.antialiased) {
3598 initializeRasterizer(spanData);
3600 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3604 rasterizer->rasterize(outline, fillRule);
3608 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3612 int q_gray_rendered_spans(QT_FT_Raster raster);
3615 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3616 ProcessSpans callback,
3617 void *userData, QRasterBuffer *)
3619 if (!callback || !outline)
3622 Q_Q(QRasterPaintEngine);
3623 QRasterPaintEngineState *s = q->state();
3625 if (!s->flags.antialiased) {
3626 rasterizer->setAntialiased(s->flags.antialiased);
3627 rasterizer->setClipRect(deviceRect);
3628 rasterizer->initialize(callback, userData);
3630 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3634 rasterizer->rasterize(outline, fillRule);
3638 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3639 // minimize memory reallocations. However if initial size for
3640 // raster pool is changed for lower value, reallocations will
3642 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3643 int rasterPoolSize = rasterPoolInitialSize;
3644 unsigned char *rasterPoolBase;
3645 #if defined(Q_OS_WIN64)
3647 // We make use of setjmp and longjmp in qgrayraster.c which requires
3648 // 16-byte alignment, hence we hardcode this requirement here..
3649 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3651 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3652 rasterPoolBase = rasterPoolOnStack;
3654 Q_CHECK_PTR(rasterPoolBase);
3656 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3658 void *data = userData;
3660 QT_FT_BBox clip_box = { deviceRect.x(),
3662 deviceRect.x() + deviceRect.width(),
3663 deviceRect.y() + deviceRect.height() };
3665 QT_FT_Raster_Params rasterParams;
3666 rasterParams.target = 0;
3667 rasterParams.source = outline;
3668 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3669 rasterParams.gray_spans = 0;
3670 rasterParams.black_spans = 0;
3671 rasterParams.bit_test = 0;
3672 rasterParams.bit_set = 0;
3673 rasterParams.user = data;
3674 rasterParams.clip_box = clip_box;
3679 int rendered_spans = 0;
3683 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3684 rasterParams.gray_spans = callback;
3685 rasterParams.skip_spans = rendered_spans;
3686 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3688 // Out of memory, reallocate some more and try again...
3689 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3690 int new_size = rasterPoolSize * 2;
3691 if (new_size > 1024 * 1024) {
3692 qWarning("QPainter: Rasterization of primitive failed");
3696 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3698 #if defined(Q_OS_WIN64)
3699 _aligned_free(rasterPoolBase);
3701 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3702 free(rasterPoolBase);
3705 rasterPoolSize = new_size;
3707 #if defined(Q_OS_WIN64)
3708 // We make use of setjmp and longjmp in qgrayraster.c which requires
3709 // 16-byte alignment, hence we hardcode this requirement here..
3710 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3712 (unsigned char *) malloc(rasterPoolSize);
3714 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3716 qt_ft_grays_raster.raster_done(*grayRaster.data());
3717 qt_ft_grays_raster.raster_new(grayRaster.data());
3718 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3724 #if defined(Q_OS_WIN64)
3725 _aligned_free(rasterPoolBase);
3727 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3728 free(rasterPoolBase);
3732 void QRasterPaintEnginePrivate::recalculateFastImages()
3734 Q_Q(QRasterPaintEngine);
3735 QRasterPaintEngineState *s = q->state();
3737 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3738 && s->matrix.type() <= QTransform::TxShear;
3741 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3743 Q_Q(const QRasterPaintEngine);
3744 const QRasterPaintEngineState *s = q->state();
3746 return s->flags.fast_images
3747 && (mode == QPainter::CompositionMode_SourceOver
3748 || (mode == QPainter::CompositionMode_Source
3749 && !image.hasAlphaChannel()));
3752 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3754 Q_ASSERT(image.depth() == 1);
3756 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3757 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3759 QRgb fg = PREMUL(color.rgba());
3762 int height = sourceImage.height();
3763 int width = sourceImage.width();
3764 for (int y=0; y<height; ++y) {
3765 uchar *source = sourceImage.scanLine(y);
3766 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3767 if (!source || !target)
3768 QT_THROW(std::bad_alloc()); // we must have run out of memory
3769 for (int x=0; x < width; ++x)
3770 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3775 QRasterBuffer::~QRasterBuffer()
3779 void QRasterBuffer::init()
3781 compositionMode = QPainter::CompositionMode_SourceOver;
3782 monoDestinationWithClut = false;
3787 QImage::Format QRasterBuffer::prepare(QImage *image)
3789 m_buffer = (uchar *)image->bits();
3790 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3791 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3792 bytes_per_pixel = image->depth()/8;
3793 bytes_per_line = image->bytesPerLine();
3795 format = image->format();
3796 drawHelper = qDrawHelper + format;
3797 if (image->depth() == 1 && image->colorTable().size() == 2) {
3798 monoDestinationWithClut = true;
3799 destColor0 = PREMUL(image->colorTable()[0]);
3800 destColor1 = PREMUL(image->colorTable()[1]);
3806 void QRasterBuffer::resetBuffer(int val)
3808 memset(m_buffer, val, m_height*bytes_per_line);
3811 QClipData::QClipData(int height)
3813 clipSpanHeight = height;
3818 xmin = xmax = ymin = ymax = 0;
3822 hasRectClip = hasRegionClip = false;
3825 QClipData::~QClipData()
3833 void QClipData::initialize()
3839 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3841 Q_CHECK_PTR(m_clipLines);
3843 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3844 allocated = clipSpanHeight;
3845 Q_CHECK_PTR(m_spans);
3851 m_clipLines[y].spans = 0;
3852 m_clipLines[y].count = 0;
3856 const int len = clipRect.width();
3859 QSpan *span = m_spans + count;
3863 span->coverage = 255;
3866 m_clipLines[y].spans = span;
3867 m_clipLines[y].count = 1;
3871 while (y < clipSpanHeight) {
3872 m_clipLines[y].spans = 0;
3873 m_clipLines[y].count = 0;
3876 } else if (hasRegionClip) {
3878 const QVector<QRect> rects = clipRegion.rects();
3879 const int numRects = rects.size();
3882 const int maxSpans = (ymax - ymin) * numRects;
3883 if (maxSpans > allocated) {
3884 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3885 allocated = maxSpans;
3890 int firstInBand = 0;
3892 while (firstInBand < numRects) {
3893 const int currMinY = rects.at(firstInBand).y();
3894 const int currMaxY = currMinY + rects.at(firstInBand).height();
3896 while (y < currMinY) {
3897 m_clipLines[y].spans = 0;
3898 m_clipLines[y].count = 0;
3902 int lastInBand = firstInBand;
3903 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3906 while (y < currMaxY) {
3908 m_clipLines[y].spans = m_spans + count;
3909 m_clipLines[y].count = lastInBand - firstInBand + 1;
3911 for (int r = firstInBand; r <= lastInBand; ++r) {
3912 const QRect &currRect = rects.at(r);
3913 QSpan *span = m_spans + count;
3914 span->x = currRect.x();
3915 span->len = currRect.width();
3917 span->coverage = 255;
3923 firstInBand = lastInBand + 1;
3926 Q_ASSERT(count <= allocated);
3928 while (y < clipSpanHeight) {
3929 m_clipLines[y].spans = 0;
3930 m_clipLines[y].count = 0;
3936 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3941 free(m_clipLines); // same for clipLines
3947 void QClipData::fixup()
3952 ymin = ymax = xmin = xmax = 0;
3957 ymin = m_spans[0].y;
3958 ymax = m_spans[count-1].y + 1;
3962 const int firstLeft = m_spans[0].x;
3963 const int firstRight = m_spans[0].x + m_spans[0].len;
3966 for (int i = 0; i < count; ++i) {
3967 QT_FT_Span_& span = m_spans[i];
3970 if (span.y != y + 1 && y != -1)
3973 m_clipLines[y].spans = &span;
3974 m_clipLines[y].count = 1;
3976 ++m_clipLines[y].count;
3978 const int spanLeft = span.x;
3979 const int spanRight = spanLeft + span.len;
3981 if (spanLeft < xmin)
3984 if (spanRight > xmax)
3987 if (spanLeft != firstLeft || spanRight != firstRight)
3993 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3998 Convert \a rect to clip spans.
4000 void QClipData::setClipRect(const QRect &rect)
4002 if (hasRectClip && rect == clipRect)
4005 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4007 hasRegionClip = false;
4011 xmax = rect.x() + rect.width();
4012 ymin = qMin(rect.y(), clipSpanHeight);
4013 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4020 // qDebug() << xmin << xmax << ymin << ymax;
4024 Convert \a region to clip spans.
4026 void QClipData::setClipRegion(const QRegion ®ion)
4028 if (region.rectCount() == 1) {
4029 setClipRect(region.rects().at(0));
4033 hasRegionClip = true;
4034 hasRectClip = false;
4035 clipRegion = region;
4037 { // set bounding rect
4038 const QRect rect = region.boundingRect();
4040 xmax = rect.x() + rect.width();
4042 ymax = rect.y() + rect.height();
4054 spans must be sorted on y
4056 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4057 const QSpan *spans, const QSpan *end,
4058 QSpan **outSpans, int available)
4060 const_cast<QClipData *>(clip)->initialize();
4062 QSpan *out = *outSpans;
4064 const QSpan *clipSpans = clip->m_spans + *currentClip;
4065 const QSpan *clipEnd = clip->m_spans + clip->count;
4067 while (available && spans < end ) {
4068 if (clipSpans >= clipEnd) {
4072 if (clipSpans->y > spans->y) {
4076 if (spans->y != clipSpans->y) {
4077 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4078 clipSpans = clip->m_clipLines[spans->y].spans;
4083 Q_ASSERT(spans->y == clipSpans->y);
4086 int sx2 = sx1 + spans->len;
4087 int cx1 = clipSpans->x;
4088 int cx2 = cx1 + clipSpans->len;
4090 if (cx1 < sx1 && cx2 < sx1) {
4093 } else if (sx1 < cx1 && sx2 < cx1) {
4097 int x = qMax(sx1, cx1);
4098 int len = qMin(sx2, cx2) - x;
4100 out->x = qMax(sx1, cx1);
4101 out->len = qMin(sx2, cx2) - out->x;
4103 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4115 *currentClip = clipSpans - clip->m_spans;
4119 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4121 // qDebug() << "qt_span_fill_clipped" << spanCount;
4122 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4124 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4126 const int NSPANS = 256;
4127 QSpan cspans[NSPANS];
4128 int currentClip = 0;
4129 const QSpan *end = spans + spanCount;
4130 while (spans < end) {
4131 QSpan *clipped = cspans;
4132 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4133 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4134 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4136 if (clipped - cspans)
4137 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4143 Clip spans to \a{clip}-rectangle.
4144 Returns number of unclipped spans
4146 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4149 const short minx = clip.left();
4150 const short miny = clip.top();
4151 const short maxx = clip.right();
4152 const short maxy = clip.bottom();
4155 for (int i = 0; i < numSpans; ++i) {
4156 if (spans[i].y > maxy)
4158 if (spans[i].y < miny
4159 || spans[i].x > maxx
4160 || spans[i].x + spans[i].len <= minx) {
4163 if (spans[i].x < minx) {
4164 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4167 spans[n].x = spans[i].x;
4168 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4170 if (spans[n].len == 0)
4172 spans[n].y = spans[i].y;
4173 spans[n].coverage = spans[i].coverage;
4180 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4183 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4184 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4186 Q_ASSERT(fillData->clip);
4187 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4189 // hw: check if this const_cast<> is safe!!!
4190 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4191 fillData->clip->clipRect);
4193 fillData->unclipped_blend(count, spans, fillData);
4196 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4198 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4200 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4201 // for (int i = 0; i < qMin(count, 10); ++i) {
4202 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4205 switch (clipData->operation) {
4207 case Qt::IntersectClip:
4209 QClipData *newClip = clipData->newClip;
4210 newClip->initialize();
4212 int currentClip = 0;
4213 const QSpan *end = spans + count;
4214 while (spans < end) {
4215 QSpan *newspans = newClip->m_spans + newClip->count;
4216 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4217 &newspans, newClip->allocated - newClip->count);
4218 newClip->count = newspans - newClip->m_spans;
4220 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4221 newClip->allocated *= 2;
4228 case Qt::ReplaceClip:
4229 clipData->newClip->appendSpans(spans, count);
4237 QImage QRasterBuffer::bufferImage() const
4239 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4241 for (int y = 0; y < m_height; ++y) {
4242 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4244 for (int x=0; x<m_width; ++x) {
4245 uint argb = span[x];
4246 image.setPixel(x, y, argb);
4254 void QRasterBuffer::flushToARGBImage(QImage *target) const
4256 int w = qMin(m_width, target->width());
4257 int h = qMin(m_height, target->height());
4259 for (int y=0; y<h; ++y) {
4260 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4261 QRgb *dest = (QRgb *) target->scanLine(y);
4262 for (int x=0; x<w; ++x) {
4263 QRgb pixel = sourceLine[x];
4264 int alpha = qAlpha(pixel);
4268 dest[x] = (alpha << 24)
4269 | ((255*qRed(pixel)/alpha) << 16)
4270 | ((255*qGreen(pixel)/alpha) << 8)
4271 | ((255*qBlue(pixel)/alpha) << 0);
4278 class QGradientCache
4282 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4283 stops(s), opacity(op), interpolationMode(mode) {}
4284 uint buffer[GRADIENT_STOPTABLE_SIZE];
4285 QGradientStops stops;
4287 QGradient::InterpolationMode interpolationMode;
4290 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4293 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4294 quint64 hash_val = 0;
4296 QGradientStops stops = gradient.stops();
4297 for (int i = 0; i < stops.size() && i <= 2; i++)
4298 hash_val += stops[i].second.rgba();
4300 QMutexLocker lock(&mutex);
4301 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4303 if (it == cache.constEnd())
4304 return addCacheElement(hash_val, gradient, opacity);
4307 const CacheInfo &cache_info = it.value();
4308 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4309 return cache_info.buffer;
4311 } while (it != cache.constEnd() && it.key() == hash_val);
4312 // an exact match for these stops and opacity was not found, create new cache
4313 return addCacheElement(hash_val, gradient, opacity);
4317 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4319 inline int maxCacheSize() const { return 60; }
4320 inline void generateGradientColorTable(const QGradient& g,
4322 int size, int opacity) const;
4323 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4324 if (cache.size() == maxCacheSize()) {
4325 // may remove more than 1, but OK
4326 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4328 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4329 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4330 return cache.insert(hash_val, cache_entry).value().buffer;
4333 QGradientColorTableHash cache;
4337 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4339 QGradientStops stops = gradient.stops();
4340 int stopCount = stops.count();
4341 Q_ASSERT(stopCount > 0);
4343 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4345 if (stopCount == 2) {
4346 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4347 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4349 qreal first_stop = stops[0].first;
4350 qreal second_stop = stops[1].first;
4352 if (second_stop < first_stop) {
4353 qSwap(first_color, second_color);
4354 qSwap(first_stop, second_stop);
4357 if (colorInterpolation) {
4358 first_color = PREMUL(first_color);
4359 second_color = PREMUL(second_color);
4362 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4363 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4365 uint red_first = qRed(first_color) << 16;
4366 uint green_first = qGreen(first_color) << 16;
4367 uint blue_first = qBlue(first_color) << 16;
4368 uint alpha_first = qAlpha(first_color) << 16;
4370 uint red_second = qRed(second_color) << 16;
4371 uint green_second = qGreen(second_color) << 16;
4372 uint blue_second = qBlue(second_color) << 16;
4373 uint alpha_second = qAlpha(second_color) << 16;
4376 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4377 if (colorInterpolation)
4378 colorTable[i] = first_color;
4380 colorTable[i] = PREMUL(first_color);
4383 if (i < second_index) {
4384 qreal reciprocal = qreal(1) / (second_index - first_index);
4386 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4387 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4388 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4389 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4392 red_first += 1 << 15;
4393 green_first += 1 << 15;
4394 blue_first += 1 << 15;
4395 alpha_first += 1 << 15;
4397 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4398 red_first += red_delta;
4399 green_first += green_delta;
4400 blue_first += blue_delta;
4401 alpha_first += alpha_delta;
4403 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4404 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4406 if (colorInterpolation)
4407 colorTable[i] = color;
4409 colorTable[i] = PREMUL(color);
4413 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4414 if (colorInterpolation)
4415 colorTable[i] = second_color;
4417 colorTable[i] = PREMUL(second_color);
4423 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4424 if (stopCount == 1) {
4425 current_color = PREMUL(current_color);
4426 for (int i = 0; i < size; ++i)
4427 colorTable[i] = current_color;
4431 // The position where the gradient begins and ends
4432 qreal begin_pos = stops[0].first;
4433 qreal end_pos = stops[stopCount-1].first;
4435 int pos = 0; // The position in the color table.
4438 qreal incr = 1 / qreal(size); // the double increment.
4439 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4441 // Up to first point
4442 colorTable[pos++] = PREMUL(current_color);
4443 while (dpos <= begin_pos) {
4444 colorTable[pos] = colorTable[pos - 1];
4449 int current_stop = 0; // We always interpolate between current and current + 1.
4451 qreal t; // position between current left and right stops
4452 qreal t_delta; // the t increment per entry in the color table
4454 if (dpos < end_pos) {
4456 while (dpos > stops[current_stop+1].first)
4459 if (current_stop != 0)
4460 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4461 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4463 if (colorInterpolation) {
4464 current_color = PREMUL(current_color);
4465 next_color = PREMUL(next_color);
4468 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4469 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4470 t = (dpos - stops[current_stop].first) * c;
4474 Q_ASSERT(current_stop < stopCount);
4476 int dist = qRound(t);
4477 int idist = 256 - dist;
4479 if (colorInterpolation)
4480 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4482 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4487 if (dpos >= end_pos)
4493 while (dpos > stops[current_stop+skip+1].first)
4497 current_stop += skip;
4499 current_color = next_color;
4501 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4502 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4504 if (colorInterpolation) {
4506 current_color = PREMUL(current_color);
4507 next_color = PREMUL(next_color);
4510 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4511 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4512 t = (dpos - stops[current_stop].first) * c;
4519 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4520 while (pos < size - 1) {
4521 colorTable[pos] = current_color;
4525 // Make sure the last color stop is represented at the end of the table
4526 colorTable[size - 1] = current_color;
4529 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4532 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4538 m11 = m22 = m33 = 1.;
4539 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4540 clip = pe ? pe->d_func()->clip() : 0;
4543 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4545 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4547 Qt::BrushStyle brushStyle = qbrush_style(brush);
4548 switch (brushStyle) {
4549 case Qt::SolidPattern: {
4551 QColor c = qbrush_color(brush);
4552 QRgb rgba = c.rgba();
4553 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4554 if ((solid.color & 0xff000000) == 0
4555 && compositionMode == QPainter::CompositionMode_SourceOver) {
4561 case Qt::LinearGradientPattern:
4563 type = LinearGradient;
4564 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4565 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4566 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4567 gradient.spread = g->spread();
4569 QLinearGradientData &linearData = gradient.linear;
4571 linearData.origin.x = g->start().x();
4572 linearData.origin.y = g->start().y();
4573 linearData.end.x = g->finalStop().x();
4574 linearData.end.y = g->finalStop().y();
4578 case Qt::RadialGradientPattern:
4580 type = RadialGradient;
4581 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4582 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4583 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4584 gradient.spread = g->spread();
4586 QRadialGradientData &radialData = gradient.radial;
4588 QPointF center = g->center();
4589 radialData.center.x = center.x();
4590 radialData.center.y = center.y();
4591 radialData.center.radius = g->centerRadius();
4592 QPointF focal = g->focalPoint();
4593 radialData.focal.x = focal.x();
4594 radialData.focal.y = focal.y();
4595 radialData.focal.radius = g->focalRadius();
4599 case Qt::ConicalGradientPattern:
4601 type = ConicalGradient;
4602 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4603 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4604 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4605 gradient.spread = QGradient::RepeatSpread;
4607 QConicalGradientData &conicalData = gradient.conical;
4609 QPointF center = g->center();
4610 conicalData.center.x = center.x();
4611 conicalData.center.y = center.y();
4612 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4616 case Qt::Dense1Pattern:
4617 case Qt::Dense2Pattern:
4618 case Qt::Dense3Pattern:
4619 case Qt::Dense4Pattern:
4620 case Qt::Dense5Pattern:
4621 case Qt::Dense6Pattern:
4622 case Qt::Dense7Pattern:
4623 case Qt::HorPattern:
4624 case Qt::VerPattern:
4625 case Qt::CrossPattern:
4626 case Qt::BDiagPattern:
4627 case Qt::FDiagPattern:
4628 case Qt::DiagCrossPattern:
4631 tempImage = new QImage();
4632 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4633 initTexture(tempImage, alpha, QTextureData::Tiled);
4635 case Qt::TexturePattern:
4638 tempImage = new QImage();
4640 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4641 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4643 *tempImage = brush.textureImage();
4644 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4652 adjustSpanMethods();
4655 void QSpanData::adjustSpanMethods()
4665 unclipped_blend = 0;
4668 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4669 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4670 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4671 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4672 fillRect = rasterBuffer->drawHelper->fillRect;
4674 case LinearGradient:
4675 case RadialGradient:
4676 case ConicalGradient:
4677 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4680 unclipped_blend = qBlendTexture;
4681 if (!texture.imageData)
4682 unclipped_blend = 0;
4687 if (!unclipped_blend) {
4690 blend = unclipped_blend;
4691 } else if (clip->hasRectClip) {
4692 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4694 blend = qt_span_fill_clipped;
4698 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4701 // make sure we round off correctly in qdrawhelper.cpp
4702 delta.translate(1.0 / 65536, 1.0 / 65536);
4704 QTransform inv = (delta * matrix).inverted();
4717 const bool affine = !m13 && !m23;
4718 fast_matrix = affine
4719 && m11 * m11 + m21 * m21 < 1e4
4720 && m12 * m12 + m22 * m22 < 1e4
4724 adjustSpanMethods();
4727 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4729 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4731 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4732 if (!d || d->height == 0) {
4733 texture.imageData = 0;
4740 texture.bytesPerLine = 0;
4741 texture.format = QImage::Format_Invalid;
4742 texture.colorTable = 0;
4743 texture.hasAlpha = alpha != 256;
4745 texture.imageData = d->data;
4746 texture.width = d->width;
4747 texture.height = d->height;
4749 if (sourceRect.isNull()) {
4752 texture.x2 = texture.width;
4753 texture.y2 = texture.height;
4755 texture.x1 = sourceRect.x();
4756 texture.y1 = sourceRect.y();
4757 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4758 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4761 texture.bytesPerLine = d->bytes_per_line;
4763 texture.format = d->format;
4764 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4765 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4767 texture.const_alpha = alpha;
4768 texture.type = _type;
4770 adjustSpanMethods();
4775 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4778 Draws the first \a pointCount points in the buffer \a points
4780 The default implementation converts the first \a pointCount QPoints in \a points
4781 to QPointFs and calls the floating point version of drawPoints.
4785 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4788 Reimplement this function to draw the largest ellipse that can be
4789 contained within rectangle \a rect.
4792 #ifdef QT_DEBUG_DRAW
4793 void dumpClip(int width, int height, const QClipData *clip)
4795 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4796 clipImg.fill(0xffff0000);
4803 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4805 for (int i = 0; i < clip->count; ++i) {
4806 const QSpan *span = ((QClipData *) clip)->spans() + i;
4807 for (int j = 0; j < span->len; ++j)
4808 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4809 x0 = qMin(x0, int(span->x));
4810 x1 = qMax(x1, int(span->x + span->len - 1));
4812 y0 = qMin(y0, int(span->y));
4813 y1 = qMax(y1, int(span->y));
4816 static int counter = 0;
4823 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4824 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));