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 QRasterPaintEngineState *s = state();
3026 QFontEngine *fontEngine = textItem->fontEngine();
3027 const qreal pixelSize = fontEngine->fontDef.pixelSize;
3028 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) < 64 * 64) {
3029 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3032 QPaintEngineEx::drawStaticTextItem(textItem);
3039 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3041 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3042 QRasterPaintEngineState *s = state();
3044 #ifdef QT_DEBUG_DRAW
3045 Q_D(QRasterPaintEngine);
3046 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3047 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3054 #if defined (Q_OS_WIN) || defined(Q_WS_MAC)
3056 bool drawCached = true;
3058 if (s->matrix.type() >= QTransform::TxProject)
3061 // don't try to cache huge fonts
3062 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3063 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3066 // ### Remove the TestFontEngine and Box engine crap, in these
3067 // ### cases we should delegate painting to the font engine
3071 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
3072 conQFontEngine::Type fontEngineType = ti.fontEngine->type();
3073 // qDebug() << "type" << fontEngineType << s->matrix.type();
3074 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3075 || (s->matrix.type() <= QTransform::TxTranslate
3076 && (fontEngineType == QFontEngine::TestFontEngine
3077 || fontEngineType == QFontEngine::Box))) {
3082 if (s->matrix.type() > QTransform::TxTranslate)
3086 QRasterPaintEngineState *s = state();
3088 QVarLengthArray<QFixedPoint> positions;
3089 QVarLengthArray<glyph_t> glyphs;
3091 QTransform matrix = s->matrix;
3092 matrix.translate(p.x(), p.y());
3094 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3096 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3100 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC
3101 if (s->matrix.type() <= QTransform::TxTranslate
3102 || (s->matrix.type() == QTransform::TxScale
3103 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3104 drawGlyphsS60(p, ti);
3107 #else // Q_OS_WIN || Q_WS_MAC
3109 QFontEngine *fontEngine = ti.fontEngine;
3112 if (s->matrix.type() < QTransform::TxScale) {
3114 QVarLengthArray<QFixedPoint> positions;
3115 QVarLengthArray<glyph_t> glyphs;
3116 QTransform matrix = state()->transform();
3118 qreal _x = qFloor(p.x());
3119 qreal _y = qFloor(p.y());
3120 matrix.translate(_x, _y);
3122 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3123 if (glyphs.size() == 0)
3126 for(int i = 0; i < glyphs.size(); i++) {
3127 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3128 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3129 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3130 qRound(positions[i].x + metrics.x),
3131 qRound(positions[i].y + metrics.y),
3132 img.width(), img.height());
3138 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3140 if (fontEngine->type() != QFontEngine::Freetype) {
3141 QPaintEngineEx::drawTextItem(p, ti);
3145 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3147 QTransform matrix = s->matrix;
3148 matrix.translate(p.x(), p.y());
3150 QVarLengthArray<QFixedPoint> positions;
3151 QVarLengthArray<glyph_t> glyphs;
3152 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3153 if (glyphs.size() == 0)
3156 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3157 QPaintEngine::drawTextItem(p, ti);
3163 QPaintEngineEx::drawTextItem(p, ti);
3169 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3171 Q_D(QRasterPaintEngine);
3172 QRasterPaintEngineState *s = state();
3175 if (!s->penData.blend)
3178 if (!s->flags.fast_pen) {
3179 QPaintEngineEx::drawPoints(points, pointCount);
3183 QCosmeticStroker stroker(s, d->deviceRect);
3184 stroker.drawPoints(points, pointCount);
3188 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3190 Q_D(QRasterPaintEngine);
3191 QRasterPaintEngineState *s = state();
3194 if (!s->penData.blend)
3197 if (!s->flags.fast_pen) {
3198 QPaintEngineEx::drawPoints(points, pointCount);
3202 QCosmeticStroker stroker(s, d->deviceRect);
3203 stroker.drawPoints(points, pointCount);
3209 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3211 #ifdef QT_DEBUG_DRAW
3212 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3214 Q_D(QRasterPaintEngine);
3215 QRasterPaintEngineState *s = state();
3218 if (!s->penData.blend)
3221 if (s->flags.fast_pen) {
3222 QCosmeticStroker stroker(s, d->deviceRect);
3223 for (int i=0; i<lineCount; ++i) {
3224 const QLine &l = lines[i];
3225 stroker.drawLine(l.p1(), l.p2());
3228 QPaintEngineEx::drawLines(lines, lineCount);
3232 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3238 Q_Q(QRasterPaintEngine);
3239 QRasterPaintEngineState *s = q->state();
3241 const QPen &pen = s->lastPen;
3242 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3243 const QVector<qreal> pattern = pen.dashPattern();
3245 qreal patternLength = 0;
3246 for (int i = 0; i < pattern.size(); ++i)
3247 patternLength += pattern.at(i);
3249 if (patternLength <= 0)
3252 qreal length = line.length();
3253 Q_ASSERT(length > 0);
3254 while (length > 0) {
3255 const bool rasterize = *inDash;
3256 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3259 if (dash >= length) {
3261 *dashOffset += dash / width;
3265 *inDash = !(*inDash);
3266 if (++*dashIndex >= pattern.size())
3273 if (rasterize && dash > 0)
3274 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3281 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3283 #ifdef QT_DEBUG_DRAW
3284 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3286 Q_D(QRasterPaintEngine);
3287 QRasterPaintEngineState *s = state();
3290 if (!s->penData.blend)
3292 if (s->flags.fast_pen) {
3293 QCosmeticStroker stroker(s, d->deviceRect);
3294 for (int i=0; i<lineCount; ++i) {
3295 QLineF line = lines[i];
3296 stroker.drawLine(line.p1(), line.p2());
3299 QPaintEngineEx::drawLines(lines, lineCount);
3307 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3309 QPaintEngineEx::drawEllipse(rect);
3316 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3318 Q_D(QRasterPaintEngine);
3325 CGContextRef QRasterPaintEngine::getCGContext() const
3327 Q_D(const QRasterPaintEngine);
3328 return d->cgContext;
3336 void QRasterPaintEngine::setDC(HDC hdc) {
3337 Q_D(QRasterPaintEngine);
3344 HDC QRasterPaintEngine::getDC() const
3346 Q_D(const QRasterPaintEngine);
3353 void QRasterPaintEngine::releaseDC(HDC) const
3362 QPoint QRasterPaintEngine::coordinateOffset() const
3364 return QPoint(0, 0);
3367 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3372 Q_D(QRasterPaintEngine);
3374 Q_ASSERT(image.depth() == 1);
3376 const int spanCount = 256;
3377 QT_FT_Span spans[spanCount];
3381 int w = image.width();
3382 int h = image.height();
3383 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3384 int ymin = qMax(qRound(pos.y()), 0);
3385 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3386 int xmin = qMax(qRound(pos.x()), 0);
3388 int x_offset = xmin - qRound(pos.x());
3390 QImage::Format format = image.format();
3391 for (int y = ymin; y < ymax; ++y) {
3392 const uchar *src = image.scanLine(y - qRound(pos.y()));
3393 if (format == QImage::Format_MonoLSB) {
3394 for (int x = 0; x < xmax - xmin; ++x) {
3395 int src_x = x + x_offset;
3396 uchar pixel = src[src_x >> 3];
3401 if (pixel & (0x1 << (src_x & 7))) {
3402 spans[n].x = xmin + x;
3404 spans[n].coverage = 255;
3406 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3410 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3413 if (n == spanCount) {
3414 fg->blend(n, spans, fg);
3420 for (int x = 0; x < xmax - xmin; ++x) {
3421 int src_x = x + x_offset;
3422 uchar pixel = src[src_x >> 3];
3427 if (pixel & (0x80 >> (x & 7))) {
3428 spans[n].x = xmin + x;
3430 spans[n].coverage = 255;
3432 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3436 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3439 if (n == spanCount) {
3440 fg->blend(n, spans, fg);
3448 fg->blend(n, spans, fg);
3454 \enum QRasterPaintEngine::ClipType
3457 \value RectClip Indicates that the currently set clip is a single rectangle.
3458 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3463 Returns the type of the clip currently set.
3465 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3467 Q_D(const QRasterPaintEngine);
3469 const QClipData *clip = d->clip();
3470 if (!clip || clip->hasRectClip)
3478 Returns the bounding rect of the currently set clip.
3480 QRect QRasterPaintEngine::clipBoundingRect() const
3482 Q_D(const QRasterPaintEngine);
3484 const QClipData *clip = d->clip();
3487 return d->deviceRect;
3489 if (clip->hasRectClip)
3490 return clip->clipRect;
3492 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3495 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3497 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3499 QVarLengthArray<short, 4096> buffer;
3501 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3502 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3503 result->initialize();
3505 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3506 const QSpan *c1_spans = c1ClipLines[y].spans;
3507 int c1_count = c1ClipLines[y].count;
3508 const QSpan *c2_spans = c2ClipLines[y].spans;
3509 int c2_count = c2ClipLines[y].count;
3511 if (c1_count == 0 && c2_count == 0)
3513 if (c1_count == 0) {
3514 result->appendSpans(c2_spans, c2_count);
3516 } else if (c2_count == 0) {
3517 result->appendSpans(c1_spans, c1_count);
3521 // we need to merge the two
3523 // find required length
3524 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3525 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3527 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3529 // Fill with old spans.
3530 for (int i = 0; i < c1_count; ++i) {
3531 const QSpan *cs = c1_spans + i;
3532 for (int j=cs->x; j<cs->x + cs->len; ++j)
3533 buffer[j] = cs->coverage;
3536 // Fill with new spans
3537 for (int i = 0; i < c2_count; ++i) {
3538 const QSpan *cs = c2_spans + i;
3539 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3540 buffer[j] += cs->coverage;
3541 if (buffer[j] > 255)
3549 // Skip to next span
3550 while (x < max && buffer[x] == 0) ++x;
3551 if (x >= max) break;
3554 int coverage = buffer[x];
3556 // Find length of span
3557 while (x < max && buffer[x] == coverage)
3560 result->appendSpan(sx, x - sx, y, coverage);
3565 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3567 Q_Q(QRasterPaintEngine);
3568 QRasterPaintEngineState *s = q->state();
3570 rasterizer->setAntialiased(s->flags.antialiased);
3572 QRect clipRect(deviceRect);
3574 // ### get from optimized rectbased QClipData
3576 const QClipData *c = clip();
3578 const QRect r(QPoint(c->xmin, c->ymin),
3579 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3580 clipRect = clipRect.intersected(r);
3581 blend = data->blend;
3583 blend = data->unclipped_blend;
3586 rasterizer->setClipRect(clipRect);
3587 rasterizer->initialize(blend, data);
3590 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3591 ProcessSpans callback,
3592 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3594 if (!callback || !outline)
3597 Q_Q(QRasterPaintEngine);
3598 QRasterPaintEngineState *s = q->state();
3600 if (!s->flags.antialiased) {
3601 initializeRasterizer(spanData);
3603 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3607 rasterizer->rasterize(outline, fillRule);
3611 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3615 int q_gray_rendered_spans(QT_FT_Raster raster);
3618 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3619 ProcessSpans callback,
3620 void *userData, QRasterBuffer *)
3622 if (!callback || !outline)
3625 Q_Q(QRasterPaintEngine);
3626 QRasterPaintEngineState *s = q->state();
3628 if (!s->flags.antialiased) {
3629 rasterizer->setAntialiased(s->flags.antialiased);
3630 rasterizer->setClipRect(deviceRect);
3631 rasterizer->initialize(callback, userData);
3633 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3637 rasterizer->rasterize(outline, fillRule);
3641 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3642 // minimize memory reallocations. However if initial size for
3643 // raster pool is changed for lower value, reallocations will
3645 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3646 int rasterPoolSize = rasterPoolInitialSize;
3647 unsigned char *rasterPoolBase;
3648 #if defined(Q_OS_WIN64)
3650 // We make use of setjmp and longjmp in qgrayraster.c which requires
3651 // 16-byte alignment, hence we hardcode this requirement here..
3652 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3654 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3655 rasterPoolBase = rasterPoolOnStack;
3657 Q_CHECK_PTR(rasterPoolBase);
3659 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3661 void *data = userData;
3663 QT_FT_BBox clip_box = { deviceRect.x(),
3665 deviceRect.x() + deviceRect.width(),
3666 deviceRect.y() + deviceRect.height() };
3668 QT_FT_Raster_Params rasterParams;
3669 rasterParams.target = 0;
3670 rasterParams.source = outline;
3671 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3672 rasterParams.gray_spans = 0;
3673 rasterParams.black_spans = 0;
3674 rasterParams.bit_test = 0;
3675 rasterParams.bit_set = 0;
3676 rasterParams.user = data;
3677 rasterParams.clip_box = clip_box;
3682 int rendered_spans = 0;
3686 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3687 rasterParams.gray_spans = callback;
3688 rasterParams.skip_spans = rendered_spans;
3689 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3691 // Out of memory, reallocate some more and try again...
3692 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3693 int new_size = rasterPoolSize * 2;
3694 if (new_size > 1024 * 1024) {
3695 qWarning("QPainter: Rasterization of primitive failed");
3699 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3701 #if defined(Q_OS_WIN64)
3702 _aligned_free(rasterPoolBase);
3704 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3705 free(rasterPoolBase);
3708 rasterPoolSize = new_size;
3710 #if defined(Q_OS_WIN64)
3711 // We make use of setjmp and longjmp in qgrayraster.c which requires
3712 // 16-byte alignment, hence we hardcode this requirement here..
3713 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3715 (unsigned char *) malloc(rasterPoolSize);
3717 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3719 qt_ft_grays_raster.raster_done(*grayRaster.data());
3720 qt_ft_grays_raster.raster_new(grayRaster.data());
3721 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3727 #if defined(Q_OS_WIN64)
3728 _aligned_free(rasterPoolBase);
3730 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3731 free(rasterPoolBase);
3735 void QRasterPaintEnginePrivate::recalculateFastImages()
3737 Q_Q(QRasterPaintEngine);
3738 QRasterPaintEngineState *s = q->state();
3740 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3741 && s->matrix.type() <= QTransform::TxShear;
3744 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3746 Q_Q(const QRasterPaintEngine);
3747 const QRasterPaintEngineState *s = q->state();
3749 return s->flags.fast_images
3750 && (mode == QPainter::CompositionMode_SourceOver
3751 || (mode == QPainter::CompositionMode_Source
3752 && !image.hasAlphaChannel()));
3755 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3757 Q_ASSERT(image.depth() == 1);
3759 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3760 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3762 QRgb fg = PREMUL(color.rgba());
3765 int height = sourceImage.height();
3766 int width = sourceImage.width();
3767 for (int y=0; y<height; ++y) {
3768 uchar *source = sourceImage.scanLine(y);
3769 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3770 if (!source || !target)
3771 QT_THROW(std::bad_alloc()); // we must have run out of memory
3772 for (int x=0; x < width; ++x)
3773 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3778 QRasterBuffer::~QRasterBuffer()
3782 void QRasterBuffer::init()
3784 compositionMode = QPainter::CompositionMode_SourceOver;
3785 monoDestinationWithClut = false;
3790 QImage::Format QRasterBuffer::prepare(QImage *image)
3792 m_buffer = (uchar *)image->bits();
3793 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3794 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3795 bytes_per_pixel = image->depth()/8;
3796 bytes_per_line = image->bytesPerLine();
3798 format = image->format();
3799 drawHelper = qDrawHelper + format;
3800 if (image->depth() == 1 && image->colorTable().size() == 2) {
3801 monoDestinationWithClut = true;
3802 destColor0 = PREMUL(image->colorTable()[0]);
3803 destColor1 = PREMUL(image->colorTable()[1]);
3809 void QRasterBuffer::resetBuffer(int val)
3811 memset(m_buffer, val, m_height*bytes_per_line);
3814 QClipData::QClipData(int height)
3816 clipSpanHeight = height;
3821 xmin = xmax = ymin = ymax = 0;
3825 hasRectClip = hasRegionClip = false;
3828 QClipData::~QClipData()
3836 void QClipData::initialize()
3842 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3844 Q_CHECK_PTR(m_clipLines);
3846 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3847 allocated = clipSpanHeight;
3848 Q_CHECK_PTR(m_spans);
3854 m_clipLines[y].spans = 0;
3855 m_clipLines[y].count = 0;
3859 const int len = clipRect.width();
3862 QSpan *span = m_spans + count;
3866 span->coverage = 255;
3869 m_clipLines[y].spans = span;
3870 m_clipLines[y].count = 1;
3874 while (y < clipSpanHeight) {
3875 m_clipLines[y].spans = 0;
3876 m_clipLines[y].count = 0;
3879 } else if (hasRegionClip) {
3881 const QVector<QRect> rects = clipRegion.rects();
3882 const int numRects = rects.size();
3885 const int maxSpans = (ymax - ymin) * numRects;
3886 if (maxSpans > allocated) {
3887 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3888 allocated = maxSpans;
3893 int firstInBand = 0;
3895 while (firstInBand < numRects) {
3896 const int currMinY = rects.at(firstInBand).y();
3897 const int currMaxY = currMinY + rects.at(firstInBand).height();
3899 while (y < currMinY) {
3900 m_clipLines[y].spans = 0;
3901 m_clipLines[y].count = 0;
3905 int lastInBand = firstInBand;
3906 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3909 while (y < currMaxY) {
3911 m_clipLines[y].spans = m_spans + count;
3912 m_clipLines[y].count = lastInBand - firstInBand + 1;
3914 for (int r = firstInBand; r <= lastInBand; ++r) {
3915 const QRect &currRect = rects.at(r);
3916 QSpan *span = m_spans + count;
3917 span->x = currRect.x();
3918 span->len = currRect.width();
3920 span->coverage = 255;
3926 firstInBand = lastInBand + 1;
3929 Q_ASSERT(count <= allocated);
3931 while (y < clipSpanHeight) {
3932 m_clipLines[y].spans = 0;
3933 m_clipLines[y].count = 0;
3939 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3944 free(m_clipLines); // same for clipLines
3950 void QClipData::fixup()
3955 ymin = ymax = xmin = xmax = 0;
3960 ymin = m_spans[0].y;
3961 ymax = m_spans[count-1].y + 1;
3965 const int firstLeft = m_spans[0].x;
3966 const int firstRight = m_spans[0].x + m_spans[0].len;
3969 for (int i = 0; i < count; ++i) {
3970 QT_FT_Span_& span = m_spans[i];
3973 if (span.y != y + 1 && y != -1)
3976 m_clipLines[y].spans = &span;
3977 m_clipLines[y].count = 1;
3979 ++m_clipLines[y].count;
3981 const int spanLeft = span.x;
3982 const int spanRight = spanLeft + span.len;
3984 if (spanLeft < xmin)
3987 if (spanRight > xmax)
3990 if (spanLeft != firstLeft || spanRight != firstRight)
3996 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4001 Convert \a rect to clip spans.
4003 void QClipData::setClipRect(const QRect &rect)
4005 if (hasRectClip && rect == clipRect)
4008 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4010 hasRegionClip = false;
4014 xmax = rect.x() + rect.width();
4015 ymin = qMin(rect.y(), clipSpanHeight);
4016 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4023 // qDebug() << xmin << xmax << ymin << ymax;
4027 Convert \a region to clip spans.
4029 void QClipData::setClipRegion(const QRegion ®ion)
4031 if (region.rectCount() == 1) {
4032 setClipRect(region.rects().at(0));
4036 hasRegionClip = true;
4037 hasRectClip = false;
4038 clipRegion = region;
4040 { // set bounding rect
4041 const QRect rect = region.boundingRect();
4043 xmax = rect.x() + rect.width();
4045 ymax = rect.y() + rect.height();
4057 spans must be sorted on y
4059 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4060 const QSpan *spans, const QSpan *end,
4061 QSpan **outSpans, int available)
4063 const_cast<QClipData *>(clip)->initialize();
4065 QSpan *out = *outSpans;
4067 const QSpan *clipSpans = clip->m_spans + *currentClip;
4068 const QSpan *clipEnd = clip->m_spans + clip->count;
4070 while (available && spans < end ) {
4071 if (clipSpans >= clipEnd) {
4075 if (clipSpans->y > spans->y) {
4079 if (spans->y != clipSpans->y) {
4080 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4081 clipSpans = clip->m_clipLines[spans->y].spans;
4086 Q_ASSERT(spans->y == clipSpans->y);
4089 int sx2 = sx1 + spans->len;
4090 int cx1 = clipSpans->x;
4091 int cx2 = cx1 + clipSpans->len;
4093 if (cx1 < sx1 && cx2 < sx1) {
4096 } else if (sx1 < cx1 && sx2 < cx1) {
4100 int x = qMax(sx1, cx1);
4101 int len = qMin(sx2, cx2) - x;
4103 out->x = qMax(sx1, cx1);
4104 out->len = qMin(sx2, cx2) - out->x;
4106 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4118 *currentClip = clipSpans - clip->m_spans;
4122 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4124 // qDebug() << "qt_span_fill_clipped" << spanCount;
4125 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4127 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4129 const int NSPANS = 256;
4130 QSpan cspans[NSPANS];
4131 int currentClip = 0;
4132 const QSpan *end = spans + spanCount;
4133 while (spans < end) {
4134 QSpan *clipped = cspans;
4135 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4136 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4137 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4139 if (clipped - cspans)
4140 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4146 Clip spans to \a{clip}-rectangle.
4147 Returns number of unclipped spans
4149 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4152 const short minx = clip.left();
4153 const short miny = clip.top();
4154 const short maxx = clip.right();
4155 const short maxy = clip.bottom();
4158 for (int i = 0; i < numSpans; ++i) {
4159 if (spans[i].y > maxy)
4161 if (spans[i].y < miny
4162 || spans[i].x > maxx
4163 || spans[i].x + spans[i].len <= minx) {
4166 if (spans[i].x < minx) {
4167 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4170 spans[n].x = spans[i].x;
4171 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4173 if (spans[n].len == 0)
4175 spans[n].y = spans[i].y;
4176 spans[n].coverage = spans[i].coverage;
4183 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4186 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4187 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4189 Q_ASSERT(fillData->clip);
4190 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4192 // hw: check if this const_cast<> is safe!!!
4193 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4194 fillData->clip->clipRect);
4196 fillData->unclipped_blend(count, spans, fillData);
4199 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4201 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4203 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4204 // for (int i = 0; i < qMin(count, 10); ++i) {
4205 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4208 switch (clipData->operation) {
4210 case Qt::IntersectClip:
4212 QClipData *newClip = clipData->newClip;
4213 newClip->initialize();
4215 int currentClip = 0;
4216 const QSpan *end = spans + count;
4217 while (spans < end) {
4218 QSpan *newspans = newClip->m_spans + newClip->count;
4219 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4220 &newspans, newClip->allocated - newClip->count);
4221 newClip->count = newspans - newClip->m_spans;
4223 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4224 newClip->allocated *= 2;
4231 case Qt::ReplaceClip:
4232 clipData->newClip->appendSpans(spans, count);
4240 QImage QRasterBuffer::bufferImage() const
4242 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4244 for (int y = 0; y < m_height; ++y) {
4245 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4247 for (int x=0; x<m_width; ++x) {
4248 uint argb = span[x];
4249 image.setPixel(x, y, argb);
4257 void QRasterBuffer::flushToARGBImage(QImage *target) const
4259 int w = qMin(m_width, target->width());
4260 int h = qMin(m_height, target->height());
4262 for (int y=0; y<h; ++y) {
4263 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4264 QRgb *dest = (QRgb *) target->scanLine(y);
4265 for (int x=0; x<w; ++x) {
4266 QRgb pixel = sourceLine[x];
4267 int alpha = qAlpha(pixel);
4271 dest[x] = (alpha << 24)
4272 | ((255*qRed(pixel)/alpha) << 16)
4273 | ((255*qGreen(pixel)/alpha) << 8)
4274 | ((255*qBlue(pixel)/alpha) << 0);
4281 class QGradientCache
4285 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4286 stops(s), opacity(op), interpolationMode(mode) {}
4287 uint buffer[GRADIENT_STOPTABLE_SIZE];
4288 QGradientStops stops;
4290 QGradient::InterpolationMode interpolationMode;
4293 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4296 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4297 quint64 hash_val = 0;
4299 QGradientStops stops = gradient.stops();
4300 for (int i = 0; i < stops.size() && i <= 2; i++)
4301 hash_val += stops[i].second.rgba();
4303 QMutexLocker lock(&mutex);
4304 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4306 if (it == cache.constEnd())
4307 return addCacheElement(hash_val, gradient, opacity);
4310 const CacheInfo &cache_info = it.value();
4311 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4312 return cache_info.buffer;
4314 } while (it != cache.constEnd() && it.key() == hash_val);
4315 // an exact match for these stops and opacity was not found, create new cache
4316 return addCacheElement(hash_val, gradient, opacity);
4320 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4322 inline int maxCacheSize() const { return 60; }
4323 inline void generateGradientColorTable(const QGradient& g,
4325 int size, int opacity) const;
4326 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4327 if (cache.size() == maxCacheSize()) {
4328 // may remove more than 1, but OK
4329 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4331 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4332 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4333 return cache.insert(hash_val, cache_entry).value().buffer;
4336 QGradientColorTableHash cache;
4340 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4342 QGradientStops stops = gradient.stops();
4343 int stopCount = stops.count();
4344 Q_ASSERT(stopCount > 0);
4346 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4348 if (stopCount == 2) {
4349 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4350 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4352 qreal first_stop = stops[0].first;
4353 qreal second_stop = stops[1].first;
4355 if (second_stop < first_stop) {
4356 qSwap(first_color, second_color);
4357 qSwap(first_stop, second_stop);
4360 if (colorInterpolation) {
4361 first_color = PREMUL(first_color);
4362 second_color = PREMUL(second_color);
4365 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4366 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4368 uint red_first = qRed(first_color) << 16;
4369 uint green_first = qGreen(first_color) << 16;
4370 uint blue_first = qBlue(first_color) << 16;
4371 uint alpha_first = qAlpha(first_color) << 16;
4373 uint red_second = qRed(second_color) << 16;
4374 uint green_second = qGreen(second_color) << 16;
4375 uint blue_second = qBlue(second_color) << 16;
4376 uint alpha_second = qAlpha(second_color) << 16;
4379 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4380 if (colorInterpolation)
4381 colorTable[i] = first_color;
4383 colorTable[i] = PREMUL(first_color);
4386 if (i < second_index) {
4387 qreal reciprocal = qreal(1) / (second_index - first_index);
4389 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4390 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4391 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4392 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4395 red_first += 1 << 15;
4396 green_first += 1 << 15;
4397 blue_first += 1 << 15;
4398 alpha_first += 1 << 15;
4400 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4401 red_first += red_delta;
4402 green_first += green_delta;
4403 blue_first += blue_delta;
4404 alpha_first += alpha_delta;
4406 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4407 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4409 if (colorInterpolation)
4410 colorTable[i] = color;
4412 colorTable[i] = PREMUL(color);
4416 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4417 if (colorInterpolation)
4418 colorTable[i] = second_color;
4420 colorTable[i] = PREMUL(second_color);
4426 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4427 if (stopCount == 1) {
4428 current_color = PREMUL(current_color);
4429 for (int i = 0; i < size; ++i)
4430 colorTable[i] = current_color;
4434 // The position where the gradient begins and ends
4435 qreal begin_pos = stops[0].first;
4436 qreal end_pos = stops[stopCount-1].first;
4438 int pos = 0; // The position in the color table.
4441 qreal incr = 1 / qreal(size); // the double increment.
4442 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4444 // Up to first point
4445 colorTable[pos++] = PREMUL(current_color);
4446 while (dpos <= begin_pos) {
4447 colorTable[pos] = colorTable[pos - 1];
4452 int current_stop = 0; // We always interpolate between current and current + 1.
4454 qreal t; // position between current left and right stops
4455 qreal t_delta; // the t increment per entry in the color table
4457 if (dpos < end_pos) {
4459 while (dpos > stops[current_stop+1].first)
4462 if (current_stop != 0)
4463 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4464 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4466 if (colorInterpolation) {
4467 current_color = PREMUL(current_color);
4468 next_color = PREMUL(next_color);
4471 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4472 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4473 t = (dpos - stops[current_stop].first) * c;
4477 Q_ASSERT(current_stop < stopCount);
4479 int dist = qRound(t);
4480 int idist = 256 - dist;
4482 if (colorInterpolation)
4483 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4485 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4490 if (dpos >= end_pos)
4496 while (dpos > stops[current_stop+skip+1].first)
4500 current_stop += skip;
4502 current_color = next_color;
4504 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4505 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4507 if (colorInterpolation) {
4509 current_color = PREMUL(current_color);
4510 next_color = PREMUL(next_color);
4513 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4514 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4515 t = (dpos - stops[current_stop].first) * c;
4522 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4523 while (pos < size - 1) {
4524 colorTable[pos] = current_color;
4528 // Make sure the last color stop is represented at the end of the table
4529 colorTable[size - 1] = current_color;
4532 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4535 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4541 m11 = m22 = m33 = 1.;
4542 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4543 clip = pe ? pe->d_func()->clip() : 0;
4546 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4548 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4550 Qt::BrushStyle brushStyle = qbrush_style(brush);
4551 switch (brushStyle) {
4552 case Qt::SolidPattern: {
4554 QColor c = qbrush_color(brush);
4555 QRgb rgba = c.rgba();
4556 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4557 if ((solid.color & 0xff000000) == 0
4558 && compositionMode == QPainter::CompositionMode_SourceOver) {
4564 case Qt::LinearGradientPattern:
4566 type = LinearGradient;
4567 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4568 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4569 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4570 gradient.spread = g->spread();
4572 QLinearGradientData &linearData = gradient.linear;
4574 linearData.origin.x = g->start().x();
4575 linearData.origin.y = g->start().y();
4576 linearData.end.x = g->finalStop().x();
4577 linearData.end.y = g->finalStop().y();
4581 case Qt::RadialGradientPattern:
4583 type = RadialGradient;
4584 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4585 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4586 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4587 gradient.spread = g->spread();
4589 QRadialGradientData &radialData = gradient.radial;
4591 QPointF center = g->center();
4592 radialData.center.x = center.x();
4593 radialData.center.y = center.y();
4594 radialData.center.radius = g->centerRadius();
4595 QPointF focal = g->focalPoint();
4596 radialData.focal.x = focal.x();
4597 radialData.focal.y = focal.y();
4598 radialData.focal.radius = g->focalRadius();
4602 case Qt::ConicalGradientPattern:
4604 type = ConicalGradient;
4605 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4606 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4607 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4608 gradient.spread = QGradient::RepeatSpread;
4610 QConicalGradientData &conicalData = gradient.conical;
4612 QPointF center = g->center();
4613 conicalData.center.x = center.x();
4614 conicalData.center.y = center.y();
4615 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4619 case Qt::Dense1Pattern:
4620 case Qt::Dense2Pattern:
4621 case Qt::Dense3Pattern:
4622 case Qt::Dense4Pattern:
4623 case Qt::Dense5Pattern:
4624 case Qt::Dense6Pattern:
4625 case Qt::Dense7Pattern:
4626 case Qt::HorPattern:
4627 case Qt::VerPattern:
4628 case Qt::CrossPattern:
4629 case Qt::BDiagPattern:
4630 case Qt::FDiagPattern:
4631 case Qt::DiagCrossPattern:
4634 tempImage = new QImage();
4635 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4636 initTexture(tempImage, alpha, QTextureData::Tiled);
4638 case Qt::TexturePattern:
4641 tempImage = new QImage();
4643 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4644 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4646 *tempImage = brush.textureImage();
4647 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4655 adjustSpanMethods();
4658 void QSpanData::adjustSpanMethods()
4668 unclipped_blend = 0;
4671 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4672 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4673 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4674 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4675 fillRect = rasterBuffer->drawHelper->fillRect;
4677 case LinearGradient:
4678 case RadialGradient:
4679 case ConicalGradient:
4680 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4683 unclipped_blend = qBlendTexture;
4684 if (!texture.imageData)
4685 unclipped_blend = 0;
4690 if (!unclipped_blend) {
4693 blend = unclipped_blend;
4694 } else if (clip->hasRectClip) {
4695 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4697 blend = qt_span_fill_clipped;
4701 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4704 // make sure we round off correctly in qdrawhelper.cpp
4705 delta.translate(1.0 / 65536, 1.0 / 65536);
4707 QTransform inv = (delta * matrix).inverted();
4720 const bool affine = !m13 && !m23;
4721 fast_matrix = affine
4722 && m11 * m11 + m21 * m21 < 1e4
4723 && m12 * m12 + m22 * m22 < 1e4
4727 adjustSpanMethods();
4730 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4732 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4734 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4735 if (!d || d->height == 0) {
4736 texture.imageData = 0;
4743 texture.bytesPerLine = 0;
4744 texture.format = QImage::Format_Invalid;
4745 texture.colorTable = 0;
4746 texture.hasAlpha = alpha != 256;
4748 texture.imageData = d->data;
4749 texture.width = d->width;
4750 texture.height = d->height;
4752 if (sourceRect.isNull()) {
4755 texture.x2 = texture.width;
4756 texture.y2 = texture.height;
4758 texture.x1 = sourceRect.x();
4759 texture.y1 = sourceRect.y();
4760 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4761 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4764 texture.bytesPerLine = d->bytes_per_line;
4766 texture.format = d->format;
4767 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4768 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4770 texture.const_alpha = alpha;
4771 texture.type = _type;
4773 adjustSpanMethods();
4778 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4781 Draws the first \a pointCount points in the buffer \a points
4783 The default implementation converts the first \a pointCount QPoints in \a points
4784 to QPointFs and calls the floating point version of drawPoints.
4788 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4791 Reimplement this function to draw the largest ellipse that can be
4792 contained within rectangle \a rect.
4795 #ifdef QT_DEBUG_DRAW
4796 void dumpClip(int width, int height, const QClipData *clip)
4798 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4799 clipImg.fill(0xffff0000);
4806 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4808 for (int i = 0; i < clip->count; ++i) {
4809 const QSpan *span = ((QClipData *) clip)->spans() + i;
4810 for (int j = 0; j < span->len; ++j)
4811 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4812 x0 = qMin(x0, int(span->x));
4813 x1 = qMax(x1, int(span->x + span->len - 1));
4815 y0 = qMin(y0, int(span->y));
4816 y1 = qMax(y1, int(span->y));
4819 static int counter = 0;
4826 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4827 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));