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))
121 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
125 static inline bool winClearTypeFontsEnabled()
128 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
129 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
130 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
132 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
133 return result == FE_FONTSMOOTHINGCLEARTYPE;
136 bool QRasterPaintEngine::clearTypeFontsEnabled()
138 static const bool result = winClearTypeFontsEnabled();
145 extern bool qt_applefontsmoothing_enabled;
149 /********************************************************************************
152 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
153 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
154 static void qt_span_clip(int count, const QSpan *spans, void *userData);
155 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
161 Qt::ClipOperation operation;
167 LineDrawIncludeLastPixel
170 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
171 ProcessSpans pen_func, ProcessSpans brush_func,
172 QSpanData *pen_data, QSpanData *brush_data);
174 struct QRasterFloatPoint {
180 static const QRectF boundingRect(const QPointF *points, int pointCount)
182 const QPointF *e = points;
183 const QPointF *last = points + pointCount;
184 qreal minx, maxx, miny, maxy;
185 minx = maxx = e->x();
186 miny = maxy = e->y();
190 else if (e->x() > maxx)
194 else if (e->y() > maxy)
197 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
201 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
202 return (elementCount == 5 // 5-point polygon, check for closed rect
203 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
204 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
205 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
206 && pts[0] < pts[4] && pts[1] < pts[5]
208 (elementCount == 4 // 4-point polygon, check for unclosed rect
209 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
210 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
211 && pts[0] < pts[4] && pts[1] < pts[5]
216 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
218 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
221 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
223 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
226 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
227 qfixed c2x, qfixed c2y,
228 qfixed ex, qfixed ey,
231 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
232 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
233 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
237 #if !defined(QT_NO_DEBUG) && 0
238 static void qt_debug_path(const QPainterPath &path)
240 const char *names[] = {
247 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
248 for (int i=0; i<path.elementCount(); ++i) {
249 const QPainterPath::Element &e = path.elementAt(i);
250 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
251 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
256 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
257 QPaintEngineExPrivate(),
264 \class QRasterPaintEngine
269 \brief The QRasterPaintEngine class enables hardware acceleration
270 of painting operations in Qt for Embedded Linux.
272 Note that this functionality is only available in
273 \l{Qt for Embedded Linux}.
275 In \l{Qt for Embedded Linux}, painting is a pure software
276 implementation. But starting with Qt 4.2, it is
277 possible to add an accelerated graphics driver to take advantage
278 of available hardware resources.
280 Hardware acceleration is accomplished by creating a custom screen
281 driver, accelerating the copying from memory to the screen, and
282 implementing a custom paint engine accelerating the various
283 painting operations. Then a custom paint device (derived from the
284 QCustomRasterPaintDevice class) and a custom window surface
285 (derived from QWSWindowSurface) must be implemented to make
286 \l{Qt for Embedded Linux} aware of the accelerated driver.
288 \note The QRasterPaintEngine class does not support 8-bit images.
289 Instead, they need to be converted to a supported format, such as
290 QImage::Format_ARGB32_Premultiplied.
292 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
293 documentation for details.
295 \sa QCustomRasterPaintDevice, QPaintEngine
299 \fn Type QRasterPaintEngine::type() const
305 \relates QRasterPaintEngine
307 A struct equivalent to QT_FT_Span, containing a position (x,
308 y), the span's length in pixels and its color/coverage (a value
309 ranging from 0 to 255).
315 Creates a raster based paint engine for operating on the given
316 \a device, with the complete set of \l
317 {QPaintEngine::PaintEngineFeature}{paint engine features and
320 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
321 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
323 d_func()->device = device;
330 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
333 d_func()->device = device;
337 void QRasterPaintEngine::init()
339 Q_D(QRasterPaintEngine);
346 // The antialiasing raster.
347 d->grayRaster.reset(new QT_FT_Raster);
348 Q_CHECK_PTR(d->grayRaster.data());
349 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
350 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
353 d->rasterizer.reset(new QRasterizer);
354 d->rasterBuffer.reset(new QRasterBuffer());
355 d->outlineMapper.reset(new QOutlineMapper);
356 d->outlinemapper_xform_dirty = true;
358 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
359 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
360 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
362 d->baseClip.reset(new QClipData(d->device->height()));
363 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
365 d->image_filler.init(d->rasterBuffer.data(), this);
366 d->image_filler.type = QSpanData::Texture;
368 d->image_filler_xform.init(d->rasterBuffer.data(), this);
369 d->image_filler_xform.type = QSpanData::Texture;
371 d->solid_color_filler.init(d->rasterBuffer.data(), this);
372 d->solid_color_filler.type = QSpanData::Solid;
374 d->deviceDepth = d->device->depth();
376 d->mono_surface = false;
377 gccaps &= ~PorterDuff;
379 QImage::Format format = QImage::Format_Invalid;
381 switch (d->device->devType()) {
382 case QInternal::Pixmap:
383 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
385 case QInternal::Image:
386 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
389 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
395 case QImage::Format_MonoLSB:
396 case QImage::Format_Mono:
397 d->mono_surface = true;
399 case QImage::Format_ARGB8565_Premultiplied:
400 case QImage::Format_ARGB8555_Premultiplied:
401 case QImage::Format_ARGB6666_Premultiplied:
402 case QImage::Format_ARGB4444_Premultiplied:
403 case QImage::Format_ARGB32_Premultiplied:
404 case QImage::Format_ARGB32:
405 gccaps |= PorterDuff;
407 case QImage::Format_RGB32:
408 case QImage::Format_RGB444:
409 case QImage::Format_RGB555:
410 case QImage::Format_RGB666:
411 case QImage::Format_RGB888:
412 case QImage::Format_RGB16:
423 Destroys this paint engine.
425 QRasterPaintEngine::~QRasterPaintEngine()
427 Q_D(QRasterPaintEngine);
429 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
435 bool QRasterPaintEngine::begin(QPaintDevice *device)
437 Q_D(QRasterPaintEngine);
439 if (device->devType() == QInternal::Pixmap) {
440 QPixmap *pixmap = static_cast<QPixmap *>(device);
441 QPixmapData *pd = pixmap->pixmapData();
442 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
443 d->device = pd->buffer();
448 // Make sure QPaintEngine::paintDevice() returns the proper device.
451 Q_ASSERT(d->device->devType() == QInternal::Image
452 || d->device->devType() == QInternal::CustomRaster);
454 d->systemStateChanged();
456 QRasterPaintEngineState *s = state();
457 ensureOutlineMapper();
458 d->outlineMapper->m_clip_rect = d->deviceRect;
460 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
461 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
462 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
463 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
465 d->rasterizer->setClipRect(d->deviceRect);
467 s->penData.init(d->rasterBuffer.data(), this);
468 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
469 s->stroker = &d->basicStroker;
470 d->basicStroker.setClipRect(d->deviceRect);
472 s->brushData.init(d->rasterBuffer.data(), this);
473 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
475 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
477 setDirty(DirtyBrushOrigin);
480 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
481 << ") devType:" << device->devType()
482 << "devRect:" << d->deviceRect;
484 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
489 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
490 #if defined(Q_OS_WIN)
491 else if (clearTypeFontsEnabled())
492 #elif defined (Q_WS_MAC)
493 else if (qt_applefontsmoothing_enabled)
498 QImage::Format format = static_cast<QImage *>(d->device)->format();
499 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
500 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
502 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
504 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
513 bool QRasterPaintEngine::end()
516 Q_D(QRasterPaintEngine);
517 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
519 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
529 void QRasterPaintEngine::releaseBuffer()
531 Q_D(QRasterPaintEngine);
532 d->rasterBuffer.reset(new QRasterBuffer);
538 QSize QRasterPaintEngine::size() const
540 Q_D(const QRasterPaintEngine);
541 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
548 void QRasterPaintEngine::saveBuffer(const QString &s) const
550 Q_D(const QRasterPaintEngine);
551 d->rasterBuffer->bufferImage().save(s, "PNG");
558 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
560 QRasterPaintEngineState *s = state();
561 // FALCON: get rid of this line, see drawImage call below.
563 QTransform::TransformationType txop = s->matrix.type();
567 case QTransform::TxNone:
568 s->flags.int_xform = true;
571 case QTransform::TxTranslate:
572 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
573 && qreal(int(s->matrix.dy())) == s->matrix.dy();
576 case QTransform::TxScale:
577 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
578 && qreal(int(s->matrix.dy())) == s->matrix.dy()
579 && qreal(int(s->matrix.m11())) == s->matrix.m11()
580 && qreal(int(s->matrix.m22())) == s->matrix.m22();
583 default: // shear / perspective...
584 s->flags.int_xform = false;
588 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
590 ensureOutlineMapper();
595 QRasterPaintEngineState::~QRasterPaintEngineState()
597 if (flags.has_clip_ownership)
602 QRasterPaintEngineState::QRasterPaintEngineState()
614 flags.fast_pen = true;
615 flags.antialiased = false;
616 flags.bilinear = false;
617 flags.fast_text = true;
618 flags.int_xform = true;
619 flags.tx_noshear = true;
620 flags.fast_images = true;
623 flags.has_clip_ownership = false;
628 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
633 , strokeFlags(s.strokeFlags)
634 , lastBrush(s.lastBrush)
635 , brushData(s.brushData)
636 , fillFlags(s.fillFlags)
637 , pixmapFlags(s.pixmapFlags)
638 , intOpacity(s.intOpacity)
642 , flag_bits(s.flag_bits)
644 brushData.tempImage = 0;
645 penData.tempImage = 0;
646 flags.has_clip_ownership = false;
652 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
654 QRasterPaintEngineState *s;
656 s = new QRasterPaintEngineState();
658 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
666 void QRasterPaintEngine::setState(QPainterState *s)
668 Q_D(QRasterPaintEngine);
669 QPaintEngineEx::setState(s);
670 d->rasterBuffer->compositionMode = s->composition_mode;
674 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
679 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
686 void QRasterPaintEngine::penChanged()
689 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
691 QRasterPaintEngineState *s = state();
692 s->strokeFlags |= DirtyPen;
693 s->dirty |= DirtyPen;
699 void QRasterPaintEngine::updatePen(const QPen &pen)
701 Q_D(QRasterPaintEngine);
702 QRasterPaintEngineState *s = state();
704 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
707 Qt::PenStyle pen_style = qpen_style(pen);
712 s->penData.clip = d->clip();
713 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
715 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
716 || pen.brush().transform().type() >= QTransform::TxNone) {
717 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
720 // Slightly ugly handling of an uncommon case... We need to change
721 // the pen because it is reused in draw_midpoint to decide dashed
723 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
724 pen_style = Qt::SolidLine;
725 s->lastPen.setStyle(Qt::SolidLine);
728 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
729 d->basicStroker.setCapStyle(qpen_capStyle(pen));
730 d->basicStroker.setMiterLimit(pen.miterLimit());
732 qreal penWidth = qpen_widthf(pen);
734 d->basicStroker.setStrokeWidth(1);
736 d->basicStroker.setStrokeWidth(penWidth);
738 if(pen_style == Qt::SolidLine) {
739 s->stroker = &d->basicStroker;
740 } else if (pen_style != Qt::NoPen) {
742 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
743 if (pen.isCosmetic()) {
744 d->dashStroker->setClipRect(d->deviceRect);
746 // ### I've seen this inverted devrect multiple places now...
747 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
748 d->dashStroker->setClipRect(clipRect);
750 d->dashStroker->setDashPattern(pen.dashPattern());
751 d->dashStroker->setDashOffset(pen.dashOffset());
752 s->stroker = d->dashStroker.data();
757 ensureState(); // needed because of tx_noshear...
758 s->flags.fast_pen = pen_style > Qt::NoPen
760 && ((pen.isCosmetic() && penWidth <= 1)
761 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
763 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
773 void QRasterPaintEngine::brushOriginChanged()
775 QRasterPaintEngineState *s = state();
777 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
780 s->fillFlags |= DirtyBrushOrigin;
787 void QRasterPaintEngine::brushChanged()
789 QRasterPaintEngineState *s = state();
791 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
793 s->fillFlags |= DirtyBrush;
802 void QRasterPaintEngine::updateBrush(const QBrush &brush)
805 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
807 Q_D(QRasterPaintEngine);
808 QRasterPaintEngineState *s = state();
809 // must set clip prior to setup, as setup uses it...
810 s->brushData.clip = d->clip();
811 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
812 if (s->fillFlags & DirtyTransform
813 || brush.transform().type() >= QTransform::TxNone)
814 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
815 s->lastBrush = brush;
819 void QRasterPaintEngine::updateOutlineMapper()
821 Q_D(QRasterPaintEngine);
822 d->outlineMapper->setMatrix(state()->matrix);
825 void QRasterPaintEngine::updateState()
827 QRasterPaintEngineState *s = state();
829 if (s->dirty & DirtyTransform)
830 updateMatrix(s->matrix);
832 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
833 const QPainter::CompositionMode mode = s->composition_mode;
834 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
835 && s->intOpacity == 256
836 && (mode == QPainter::CompositionMode_Source
837 || (mode == QPainter::CompositionMode_SourceOver
838 && qAlpha(s->penData.solid.color) == 255));
848 void QRasterPaintEngine::opacityChanged()
850 QRasterPaintEngineState *s = state();
853 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
856 s->fillFlags |= DirtyOpacity;
857 s->strokeFlags |= DirtyOpacity;
858 s->pixmapFlags |= DirtyOpacity;
859 s->dirty |= DirtyOpacity;
860 s->intOpacity = (int) (s->opacity * 256);
866 void QRasterPaintEngine::compositionModeChanged()
868 Q_D(QRasterPaintEngine);
869 QRasterPaintEngineState *s = state();
872 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
875 s->fillFlags |= DirtyCompositionMode;
876 s->dirty |= DirtyCompositionMode;
878 s->strokeFlags |= DirtyCompositionMode;
879 d->rasterBuffer->compositionMode = s->composition_mode;
881 d->recalculateFastImages();
887 void QRasterPaintEngine::renderHintsChanged()
889 QRasterPaintEngineState *s = state();
892 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
895 bool was_aa = s->flags.antialiased;
896 bool was_bilinear = s->flags.bilinear;
898 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
899 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
901 if (was_aa != s->flags.antialiased)
902 s->strokeFlags |= DirtyHints;
904 if (was_bilinear != s->flags.bilinear) {
905 s->strokeFlags |= DirtyPen;
906 s->fillFlags |= DirtyBrush;
909 Q_D(QRasterPaintEngine);
910 d->recalculateFastImages();
916 void QRasterPaintEngine::transformChanged()
918 QRasterPaintEngineState *s = state();
921 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
924 s->fillFlags |= DirtyTransform;
925 s->strokeFlags |= DirtyTransform;
927 s->dirty |= DirtyTransform;
929 Q_D(QRasterPaintEngine);
930 d->recalculateFastImages();
936 void QRasterPaintEngine::clipEnabledChanged()
938 QRasterPaintEngineState *s = state();
941 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
945 s->clip->enabled = s->clipEnabled;
946 s->fillFlags |= DirtyClipEnabled;
947 s->strokeFlags |= DirtyClipEnabled;
948 s->pixmapFlags |= DirtyClipEnabled;
952 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
954 SrcOverBlendFunc func,
959 if (alpha == 0 || !clip.isValid())
962 Q_ASSERT(img.depth() >= 8);
964 int srcBPL = img.bytesPerLine();
965 const uchar *srcBits = img.bits();
966 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
967 int iw = img.width();
968 int ih = img.height();
973 // Adjust the image according to the source offset...
974 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
977 // adapt the x parameters
978 int x = qRound(pt.x());
980 int cx2 = clip.x() + clip.width();
983 srcBits += srcSize * d;
988 int d = x + iw - cx2;
994 // adapt the y paremeters...
996 int cy2 = clip.y() + clip.height();
997 int y = qRound(pt.y());
1000 srcBits += srcBPL * d;
1005 int d = y + ih - cy2;
1011 // call the blend function...
1012 int dstSize = rasterBuffer->bytesPerPixel();
1013 int dstBPL = rasterBuffer->bytesPerLine();
1014 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1021 void QRasterPaintEnginePrivate::systemStateChanged()
1023 QRect clipRect(0, 0,
1024 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1025 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1027 if (!systemClip.isEmpty()) {
1028 QRegion clippedDeviceRgn = systemClip & clipRect;
1029 deviceRect = clippedDeviceRgn.boundingRect();
1030 baseClip->setClipRegion(clippedDeviceRgn);
1032 deviceRect = clipRect;
1033 baseClip->setClipRect(deviceRect);
1035 #ifdef QT_DEBUG_DRAW
1036 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1039 exDeviceRect = deviceRect;
1041 Q_Q(QRasterPaintEngine);
1042 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1043 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1044 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1047 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1049 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1052 Q_Q(QRasterPaintEngine);
1053 bool bilinear = q->state()->flags.bilinear;
1055 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1056 spanData->setupMatrix(b.transform() * m, bilinear);
1058 if (m.type() <= QTransform::TxTranslate) {
1059 // specialize setupMatrix for translation matrices
1060 // to avoid needless matrix inversion
1068 spanData->dx = -m.dx();
1069 spanData->dy = -m.dy();
1070 spanData->txop = m.type();
1071 spanData->bilinear = bilinear;
1072 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1073 spanData->adjustSpanMethods();
1075 spanData->setupMatrix(m, bilinear);
1080 // #define QT_CLIPPING_RATIOS
1082 #ifdef QT_CLIPPING_RATIOS
1087 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1089 if (d->clip()->hasRectClip)
1091 if (d->clip()->hasRegionClip)
1095 if ((totalClips % 5000) == 0) {
1096 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1097 rectClips * 100.0 / (qreal) totalClips,
1098 regionClips * 100.0 / (qreal) totalClips,
1099 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1108 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1110 if (s->flags.has_clip_ownership)
1113 s->flags.has_clip_ownership = false;
1116 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1118 s->fillFlags |= QPaintEngine::DirtyClipPath;
1119 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1120 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1122 d->solid_color_filler.clip = d->clip();
1123 d->solid_color_filler.adjustSpanMethods();
1125 #ifdef QT_DEBUG_DRAW
1126 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1135 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1137 #ifdef QT_DEBUG_DRAW
1138 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1140 if (path.elements()) {
1141 for (int i=0; i<path.elementCount(); ++i) {
1142 qDebug() << " - " << path.elements()[i]
1143 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1146 for (int i=0; i<path.elementCount(); ++i) {
1147 qDebug() << " ---- "
1148 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1153 Q_D(QRasterPaintEngine);
1154 QRasterPaintEngineState *s = state();
1156 const qreal *points = path.points();
1157 const QPainterPath::ElementType *types = path.elements();
1159 // There are some cases that are not supported by clip(QRect)
1160 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1161 if (s->matrix.type() <= QTransform::TxScale
1162 && ((path.shape() == QVectorPath::RectangleHint)
1163 || (isRect(points, path.elementCount())
1164 && (!types || (types[0] == QPainterPath::MoveToElement
1165 && types[1] == QPainterPath::LineToElement
1166 && types[2] == QPainterPath::LineToElement
1167 && types[3] == QPainterPath::LineToElement))))) {
1168 #ifdef QT_DEBUG_DRAW
1169 qDebug() << " --- optimizing vector clip to rect clip...";
1172 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1173 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1178 if (op == Qt::NoClip) {
1179 qrasterpaintengine_state_setNoClip(s);
1182 QClipData *base = d->baseClip.data();
1184 // Intersect with current clip when available...
1185 if (op == Qt::IntersectClip && s->clip)
1188 // We always intersect, except when there is nothing to
1189 // intersect with, in which case we simplify the operation to
1191 Qt::ClipOperation isectOp = Qt::IntersectClip;
1193 isectOp = Qt::ReplaceClip;
1195 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1196 newClip->initialize();
1197 ClipData clipData = { base, newClip, isectOp };
1198 ensureOutlineMapper();
1199 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1203 if (s->flags.has_clip_ownership)
1207 s->flags.has_clip_ownership = true;
1209 qrasterpaintengine_dirty_clip(d, s);
1217 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1219 #ifdef QT_DEBUG_DRAW
1220 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1223 QRasterPaintEngineState *s = state();
1225 if (op == Qt::NoClip) {
1226 qrasterpaintengine_state_setNoClip(s);
1228 } else if (s->matrix.type() > QTransform::TxScale) {
1229 QPaintEngineEx::clip(rect, op);
1232 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1233 QPaintEngineEx::clip(rect, op);
1239 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1241 Q_D(QRasterPaintEngine);
1242 QRect clipRect = r & d->deviceRect;
1243 QRasterPaintEngineState *s = state();
1245 if (op == Qt::ReplaceClip || s->clip == 0) {
1247 // No current clip, hence we intersect with sysclip and be
1249 QRegion clipRegion = systemClip();
1250 QClipData *clip = new QClipData(d->rasterBuffer->height());
1252 if (clipRegion.isEmpty())
1253 clip->setClipRect(clipRect);
1255 clip->setClipRegion(clipRegion & clipRect);
1257 if (s->flags.has_clip_ownership)
1261 s->clip->enabled = true;
1262 s->flags.has_clip_ownership = true;
1264 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1265 QClipData *base = s->clip;
1268 if (base->hasRectClip || base->hasRegionClip) {
1269 if (!s->flags.has_clip_ownership) {
1270 s->clip = new QClipData(d->rasterBuffer->height());
1271 s->flags.has_clip_ownership = true;
1273 if (base->hasRectClip)
1274 s->clip->setClipRect(base->clipRect & clipRect);
1276 s->clip->setClipRegion(base->clipRegion & clipRect);
1277 s->clip->enabled = true;
1285 qrasterpaintengine_dirty_clip(d, s);
1293 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1295 #ifdef QT_DEBUG_DRAW
1296 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1299 Q_D(QRasterPaintEngine);
1301 if (region.rectCount() == 1) {
1302 clip(region.boundingRect(), op);
1306 QRasterPaintEngineState *s = state();
1307 const QClipData *clip = d->clip();
1308 const QClipData *baseClip = d->baseClip.data();
1310 if (op == Qt::NoClip) {
1311 qrasterpaintengine_state_setNoClip(s);
1312 } else if (s->matrix.type() > QTransform::TxScale
1313 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1314 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1315 QPaintEngineEx::clip(region, op);
1317 const QClipData *curClip;
1320 if (op == Qt::IntersectClip)
1325 if (s->flags.has_clip_ownership) {
1329 newClip = new QClipData(d->rasterBuffer->height());
1331 s->flags.has_clip_ownership = true;
1334 QRegion r = s->matrix.map(region);
1335 if (curClip->hasRectClip)
1336 newClip->setClipRegion(r & curClip->clipRect);
1337 else if (curClip->hasRegionClip)
1338 newClip->setClipRegion(r & curClip->clipRegion);
1340 qrasterpaintengine_dirty_clip(d, s);
1347 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1349 #ifdef QT_DEBUG_DRAW
1350 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1353 if (!fillData->blend)
1356 Q_D(QRasterPaintEngine);
1358 const QRectF controlPointRect = path.controlPointRect();
1360 QRasterPaintEngineState *s = state();
1361 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1362 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1363 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1364 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1365 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1366 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1368 if (!s->flags.antialiased && !do_clip) {
1369 d->initializeRasterizer(fillData);
1370 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1374 ensureOutlineMapper();
1375 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1378 static void fillRect_normalized(const QRect &r, QSpanData *data,
1379 QRasterPaintEnginePrivate *pe)
1383 bool rectClipped = true;
1386 x1 = qMax(r.x(), data->clip->xmin);
1387 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1388 y1 = qMax(r.y(), data->clip->ymin);
1389 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1390 rectClipped = data->clip->hasRectClip;
1393 x1 = qMax(r.x(), pe->deviceRect.x());
1394 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1395 y1 = qMax(r.y(), pe->deviceRect.y());
1396 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1398 x1 = qMax(r.x(), 0);
1399 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1400 y1 = qMax(r.y(), 0);
1401 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1404 if (x2 <= x1 || y2 <= y1)
1407 const int width = x2 - x1;
1408 const int height = y2 - y1;
1410 bool isUnclipped = rectClipped
1411 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1413 if (pe && isUnclipped) {
1414 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1416 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1417 || (mode == QPainter::CompositionMode_SourceOver
1418 && qAlpha(data->solid.color) == 255)))
1420 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1426 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1428 const int nspans = 256;
1429 QT_FT_Span spans[nspans];
1431 Q_ASSERT(data->blend);
1434 int n = qMin(nspans, y2 - y);
1438 spans[i].len = width;
1440 spans[i].coverage = 255;
1444 blend(n, spans, data);
1452 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1454 #ifdef QT_DEBUG_DRAW
1455 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1457 Q_D(QRasterPaintEngine);
1459 QRasterPaintEngineState *s = state();
1463 if (s->brushData.blend) {
1464 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1465 const QRect *r = rects;
1466 const QRect *lastRect = rects + rectCount;
1468 int offset_x = int(s->matrix.dx());
1469 int offset_y = int(s->matrix.dy());
1470 while (r < lastRect) {
1471 QRect rect = r->normalized();
1472 QRect rr = rect.translated(offset_x, offset_y);
1473 fillRect_normalized(rr, &s->brushData, d);
1477 QRectVectorPath path;
1478 for (int i=0; i<rectCount; ++i) {
1480 fill(path, s->brush);
1486 if (s->penData.blend) {
1487 QRectVectorPath path;
1488 if (s->flags.fast_pen) {
1489 QCosmeticStroker stroker(s, d->deviceRect);
1490 for (int i = 0; i < rectCount; ++i) {
1492 stroker.drawPath(path);
1495 for (int i = 0; i < rectCount; ++i) {
1497 stroke(path, s->pen);
1506 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1508 #ifdef QT_DEBUG_DRAW
1509 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1511 #ifdef QT_FAST_SPANS
1512 Q_D(QRasterPaintEngine);
1514 QRasterPaintEngineState *s = state();
1517 if (s->flags.tx_noshear) {
1519 if (s->brushData.blend) {
1520 d->initializeRasterizer(&s->brushData);
1521 for (int i = 0; i < rectCount; ++i) {
1522 const QRectF &rect = rects[i].normalized();
1525 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1526 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1527 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1532 if (s->penData.blend) {
1533 QRectVectorPath path;
1534 if (s->flags.fast_pen) {
1535 QCosmeticStroker stroker(s, d->deviceRect);
1536 for (int i = 0; i < rectCount; ++i) {
1538 stroker.drawPath(path);
1541 for (int i = 0; i < rectCount; ++i) {
1543 QPaintEngineEx::stroke(path, s->lastPen);
1550 #endif // QT_FAST_SPANS
1551 QPaintEngineEx::drawRects(rects, rectCount);
1558 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1560 Q_D(QRasterPaintEngine);
1561 QRasterPaintEngineState *s = state();
1564 if (!s->penData.blend)
1567 if (s->flags.fast_pen) {
1568 QCosmeticStroker stroker(s, d->deviceRect);
1569 stroker.drawPath(path);
1570 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1571 qreal width = s->lastPen.isCosmetic()
1572 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1573 : qpen_widthf(s->lastPen) * s->txscale;
1575 qreal dashOffset = s->lastPen.dashOffset();
1577 qreal patternLength = 0;
1578 const QVector<qreal> pattern = s->lastPen.dashPattern();
1579 for (int i = 0; i < pattern.size(); ++i)
1580 patternLength += pattern.at(i);
1582 if (patternLength > 0) {
1583 int n = qFloor(dashOffset / patternLength);
1584 dashOffset -= n * patternLength;
1585 while (dashOffset >= pattern.at(dashIndex)) {
1586 dashOffset -= pattern.at(dashIndex);
1587 if (++dashIndex >= pattern.size())
1593 Q_D(QRasterPaintEngine);
1594 d->initializeRasterizer(&s->penData);
1595 int lineCount = path.elementCount() / 2;
1596 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1598 for (int i = 0; i < lineCount; ++i) {
1599 if (lines[i].p1() == lines[i].p2()) {
1600 if (s->lastPen.capStyle() != Qt::FlatCap) {
1601 QPointF p = lines[i].p1();
1602 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1603 QPointF(p.x() + width*0.5, p.y())));
1604 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1609 const QLineF line = s->matrix.map(lines[i]);
1610 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1611 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1612 width / line.length(),
1613 s->lastPen.capStyle() == Qt::SquareCap);
1615 d->rasterizeLine_dashed(line, width,
1616 &dashIndex, &dashOffset, &inDash);
1621 QPaintEngineEx::stroke(path, pen);
1624 static inline QRect toNormalizedFillRect(const QRectF &rect)
1626 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1627 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1628 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1629 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1636 return QRect(x1, y1, x2 - x1, y2 - y1);
1642 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1646 #ifdef QT_DEBUG_DRAW
1647 QRectF rf = path.controlPointRect();
1648 qDebug() << "QRasterPaintEngine::fill(): "
1649 << "size=" << path.elementCount()
1650 << ", hints=" << hex << path.hints()
1654 Q_D(QRasterPaintEngine);
1655 QRasterPaintEngineState *s = state();
1658 if (!s->brushData.blend)
1661 if (path.shape() == QVectorPath::RectangleHint) {
1662 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1663 const qreal *p = path.points();
1664 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1665 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1666 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1670 if (s->flags.tx_noshear) {
1671 d->initializeRasterizer(&s->brushData);
1672 // ### Is normalizing really necessary here?
1673 const qreal *p = path.points();
1674 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1676 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1677 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1678 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1684 // ### Optimize for non transformed ellipses and rectangles...
1685 QRectF cpRect = path.controlPointRect();
1686 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1687 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1690 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1691 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1692 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1693 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1695 // ### Falonc: implement....
1696 // if (!s->flags.antialiased && !do_clip) {
1697 // d->initializeRasterizer(&s->brushData);
1698 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1702 ensureOutlineMapper();
1703 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1706 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1708 Q_D(QRasterPaintEngine);
1709 QRasterPaintEngineState *s = state();
1711 if (!s->flags.antialiased) {
1712 uint txop = s->matrix.type();
1713 if (txop == QTransform::TxNone) {
1714 fillRect_normalized(toNormalizedFillRect(r), data, d);
1716 } else if (txop == QTransform::TxTranslate) {
1717 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1718 fillRect_normalized(rr, data, d);
1720 } else if (txop == QTransform::TxScale) {
1721 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1722 fillRect_normalized(rr, data, d);
1727 if (s->flags.tx_noshear) {
1728 d->initializeRasterizer(data);
1729 QRectF nr = r.normalized();
1730 if (!nr.isEmpty()) {
1731 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1732 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1733 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1740 ensureOutlineMapper();
1741 fillPath(path, data);
1747 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1749 #ifdef QT_DEBUG_DRAW
1750 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1752 QRasterPaintEngineState *s = state();
1755 if (!s->brushData.blend)
1758 fillRect(r, &s->brushData);
1764 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1766 #ifdef QT_DEBUG_DRAW
1767 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1769 Q_D(QRasterPaintEngine);
1770 QRasterPaintEngineState *s = state();
1772 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1773 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1774 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1777 d->solid_color_filler.clip = d->clip();
1778 d->solid_color_filler.adjustSpanMethods();
1779 fillRect(r, &d->solid_color_filler);
1782 static inline bool isAbove(const QPointF *a, const QPointF *b)
1784 return a->y() < b->y();
1787 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1792 Q_ASSERT(pointCount >= 2);
1794 QVector<const QPointF *> sorted;
1795 sorted.reserve(pointCount);
1797 upper->reserve(pointCount * 3 / 4);
1798 lower->reserve(pointCount * 3 / 4);
1800 for (int i = 0; i < pointCount; ++i)
1801 sorted << points + i;
1803 qSort(sorted.begin(), sorted.end(), isAbove);
1805 qreal splitY = sorted.at(sorted.size() / 2)->y();
1807 const QPointF *end = points + pointCount;
1808 const QPointF *last = end - 1;
1810 QVector<QPointF> *bin[2] = { upper, lower };
1812 for (const QPointF *p = points; p < end; ++p) {
1813 int side = p->y() < splitY;
1814 int lastSide = last->y() < splitY;
1816 if (side != lastSide) {
1817 if (qFuzzyCompare(p->y(), splitY)) {
1818 bin[!side]->append(*p);
1819 } else if (qFuzzyCompare(last->y(), splitY)) {
1820 bin[side]->append(*last);
1822 QPointF delta = *p - *last;
1823 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1825 bin[0]->append(intersection);
1826 bin[1]->append(intersection);
1830 bin[side]->append(*p);
1835 // give up if we couldn't reduce the point count
1836 return upper->size() < pointCount && lower->size() < pointCount;
1842 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1844 Q_D(QRasterPaintEngine);
1845 QRasterPaintEngineState *s = state();
1847 const int maxPoints = 0xffff;
1849 // max amount of points that raster engine can reliably handle
1850 if (pointCount > maxPoints) {
1851 QVector<QPointF> upper, lower;
1853 if (splitPolygon(points, pointCount, &upper, &lower)) {
1854 fillPolygon(upper.constData(), upper.size(), mode);
1855 fillPolygon(lower.constData(), lower.size(), mode);
1857 qWarning("Polygon too complex for filling.");
1862 // Compose polygon fill..,
1863 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1864 ensureOutlineMapper();
1865 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1868 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1870 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1876 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1878 Q_D(QRasterPaintEngine);
1879 QRasterPaintEngineState *s = state();
1881 #ifdef QT_DEBUG_DRAW
1882 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1883 for (int i=0; i<pointCount; ++i)
1884 qDebug() << " - " << points[i];
1886 Q_ASSERT(pointCount >= 2);
1888 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1889 QRectF r(points[0], points[2]);
1895 if (mode != PolylineMode) {
1898 if (s->brushData.blend) {
1899 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1900 fillPolygon(points, pointCount, mode);
1901 d->outlineMapper->setCoordinateRounding(false);
1905 // Do the outline...
1906 if (s->penData.blend) {
1907 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1908 if (s->flags.fast_pen) {
1909 QCosmeticStroker stroker(s, d->deviceRect);
1910 stroker.drawPath(vp);
1912 QPaintEngineEx::stroke(vp, s->lastPen);
1920 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1922 Q_D(QRasterPaintEngine);
1923 QRasterPaintEngineState *s = state();
1925 #ifdef QT_DEBUG_DRAW
1926 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1927 for (int i=0; i<pointCount; ++i)
1928 qDebug() << " - " << points[i];
1930 Q_ASSERT(pointCount >= 2);
1931 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1932 QRect r(points[0].x(),
1934 points[2].x() - points[0].x(),
1935 points[2].y() - points[0].y());
1943 if (mode != PolylineMode) {
1945 if (s->brushData.blend) {
1946 // Compose polygon fill..,
1947 ensureOutlineMapper();
1948 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1949 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1950 d->outlineMapper->moveTo(*points);
1951 const QPoint *p = points;
1952 const QPoint *ep = points + pointCount - 1;
1954 d->outlineMapper->lineTo(*(++p));
1956 d->outlineMapper->endOutline();
1959 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1961 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1962 d->outlineMapper->setCoordinateRounding(false);
1966 // Do the outline...
1967 if (s->penData.blend) {
1968 int count = pointCount * 2;
1969 QVarLengthArray<qreal> fpoints(count);
1970 for (int i=0; i<count; ++i)
1971 fpoints[i] = ((int *) points)[i];
1972 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1974 if (s->flags.fast_pen) {
1975 QCosmeticStroker stroker(s, d->deviceRect);
1976 stroker.drawPath(vp);
1978 QPaintEngineEx::stroke(vp, s->lastPen);
1986 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1988 #ifdef QT_DEBUG_DRAW
1989 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1992 QPixmapData *pd = pixmap.pixmapData();
1993 if (pd->classId() == QPixmapData::RasterClass) {
1994 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
1995 if (image.depth() == 1) {
1996 Q_D(QRasterPaintEngine);
1997 QRasterPaintEngineState *s = state();
1998 if (s->matrix.type() <= QTransform::TxTranslate) {
2000 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2002 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2005 QRasterPaintEngine::drawImage(pos, image);
2008 const QImage image = pixmap.toImage();
2009 if (pixmap.depth() == 1) {
2010 Q_D(QRasterPaintEngine);
2011 QRasterPaintEngineState *s = state();
2012 if (s->matrix.type() <= QTransform::TxTranslate) {
2014 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2016 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2019 QRasterPaintEngine::drawImage(pos, image);
2027 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2029 #ifdef QT_DEBUG_DRAW
2030 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2033 QPixmapData* pd = pixmap.pixmapData();
2034 if (pd->classId() == QPixmapData::RasterClass) {
2035 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2036 if (image.depth() == 1) {
2037 Q_D(QRasterPaintEngine);
2038 QRasterPaintEngineState *s = state();
2039 if (s->matrix.type() <= QTransform::TxTranslate
2040 && r.size() == sr.size()
2041 && r.size() == pixmap.size()) {
2043 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2046 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2049 drawImage(r, image, sr);
2052 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2053 const QImage image = pd->toImage(clippedSource);
2054 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2055 if (image.depth() == 1) {
2056 Q_D(QRasterPaintEngine);
2057 QRasterPaintEngineState *s = state();
2058 if (s->matrix.type() <= QTransform::TxTranslate
2059 && r.size() == sr.size()
2060 && r.size() == pixmap.size()) {
2062 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2065 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2068 drawImage(r, image, translatedSource);
2073 // assumes that rect has positive width and height
2074 static inline const QRect toRect_normalized(const QRectF &rect)
2076 const int x = qRound(rect.x());
2077 const int y = qRound(rect.y());
2078 const int w = int(rect.width() + qreal(0.5));
2079 const int h = int(rect.height() + qreal(0.5));
2081 return QRect(x, y, w, h);
2084 static inline int fast_ceil_positive(const qreal &v)
2086 const int iv = int(v);
2093 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2095 const int xmin = int(rect.x());
2096 const int xmax = int(fast_ceil_positive(rect.right()));
2097 const int ymin = int(rect.y());
2098 const int ymax = int(fast_ceil_positive(rect.bottom()));
2099 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2105 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2107 #ifdef QT_DEBUG_DRAW
2108 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2111 Q_D(QRasterPaintEngine);
2112 QRasterPaintEngineState *s = state();
2114 if (s->matrix.type() > QTransform::TxTranslate) {
2115 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2117 QRectF(0, 0, img.width(), img.height()));
2120 const QClipData *clip = d->clip();
2121 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2123 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2124 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2127 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2129 } else if (clip->hasRectClip) {
2130 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2138 d->image_filler.clip = clip;
2139 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2140 if (!d->image_filler.blend)
2142 d->image_filler.dx = -pt.x();
2143 d->image_filler.dy = -pt.y();
2144 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2146 fillRect_normalized(rr, &d->image_filler, d);
2151 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2153 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2164 inline RotationType qRotationType(const QTransform &transform)
2166 QTransform::TransformationType type = transform.type();
2168 if (type > QTransform::TxRotate)
2171 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2172 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2175 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2176 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2179 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2180 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2186 inline bool isPixelAligned(const QRectF &rect) {
2187 return QRectF(rect.toRect()) == rect;
2194 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2195 Qt::ImageConversionFlags)
2197 #ifdef QT_DEBUG_DRAW
2198 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2204 Q_D(QRasterPaintEngine);
2205 QRasterPaintEngineState *s = state();
2206 int sr_l = qFloor(sr.left());
2207 int sr_r = qCeil(sr.right()) - 1;
2208 int sr_t = qFloor(sr.top());
2209 int sr_b = qCeil(sr.bottom()) - 1;
2211 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2212 // as fillRect will apply the aliased coordinate delta we need to
2213 // subtract it here as we don't use it for image drawing
2214 QTransform old = s->matrix;
2215 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2217 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2218 QRgb color = img.pixel(sr_l, sr_t);
2219 switch (img.format()) {
2220 case QImage::Format_ARGB32_Premultiplied:
2221 case QImage::Format_ARGB8565_Premultiplied:
2222 case QImage::Format_ARGB6666_Premultiplied:
2223 case QImage::Format_ARGB8555_Premultiplied:
2224 case QImage::Format_ARGB4444_Premultiplied:
2225 // Combine premultiplied color with the opacity set on the painter.
2226 d->solid_color_filler.solid.color =
2227 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2228 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2231 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2235 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2236 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2240 d->solid_color_filler.clip = d->clip();
2241 d->solid_color_filler.adjustSpanMethods();
2242 fillRect(r, &d->solid_color_filler);
2248 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2250 const QClipData *clip = d->clip();
2252 if (s->matrix.type() > QTransform::TxTranslate
2254 && (!clip || clip->hasRectClip)
2255 && s->intOpacity == 256
2256 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2257 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2258 && d->rasterBuffer->format == img.format()
2259 && (d->rasterBuffer->format == QImage::Format_RGB16
2260 || d->rasterBuffer->format == QImage::Format_RGB32
2261 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2262 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2264 RotationType rotationType = qRotationType(s->matrix);
2266 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2267 QRectF transformedTargetRect = s->matrix.mapRect(r);
2269 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2270 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2272 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2273 if (clippedTransformedTargetRect.isNull())
2276 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2278 QRect clippedSourceRect
2279 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2280 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2282 uint dbpl = d->rasterBuffer->bytesPerLine();
2283 uint sbpl = img.bytesPerLine();
2285 uchar *dst = d->rasterBuffer->buffer();
2286 uint bpp = img.depth() >> 3;
2288 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2289 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2291 uint cw = clippedSourceRect.width();
2292 uint ch = clippedSourceRect.height();
2294 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2301 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2303 QRectF targetBounds = s->matrix.mapRect(r);
2304 bool exceedsPrecision = targetBounds.width() > 0xffff
2305 || targetBounds.height() > 0xffff;
2307 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2308 if (s->matrix.type() > QTransform::TxScale) {
2309 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2310 if (func && (!clip || clip->hasRectClip)) {
2311 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2312 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2313 s->matrix, s->intOpacity);
2317 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2318 if (func && (!clip || clip->hasRectClip)) {
2319 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2320 img.bits(), img.bytesPerLine(),
2321 qt_mapRect_non_normalizing(r, s->matrix), sr,
2322 !clip ? d->deviceRect : clip->clipRect,
2329 QTransform copy = s->matrix;
2330 copy.translate(r.x(), r.y());
2332 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2333 copy.translate(-sr.x(), -sr.y());
2335 d->image_filler_xform.clip = clip;
2336 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2337 if (!d->image_filler_xform.blend)
2339 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2341 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2342 QRectF rr = s->matrix.mapRect(r);
2344 const int x1 = qRound(rr.x());
2345 const int y1 = qRound(rr.y());
2346 const int x2 = qRound(rr.right());
2347 const int y2 = qRound(rr.bottom());
2349 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2353 #ifdef QT_FAST_SPANS
2355 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2356 d->initializeRasterizer(&d->image_filler_xform);
2357 d->rasterizer->setAntialiased(s->flags.antialiased);
2359 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2361 const QRectF &rect = r.normalized();
2362 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2363 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2365 if (s->flags.tx_noshear)
2366 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2368 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2372 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2375 QTransform m = s->matrix;
2376 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2377 m.m21(), m.m22(), m.m23(),
2378 m.m31() - offs, m.m32() - offs, m.m33());
2379 fillPath(path, &d->image_filler_xform);
2382 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2383 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2385 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2387 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2389 } else if (clip->hasRectClip) {
2390 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2396 d->image_filler.clip = clip;
2397 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2398 if (!d->image_filler.blend)
2400 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2401 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2404 rr.translate(s->matrix.dx(), s->matrix.dy());
2406 const int x1 = qRound(rr.x());
2407 const int y1 = qRound(rr.y());
2408 const int x2 = qRound(rr.right());
2409 const int y2 = qRound(rr.bottom());
2411 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2418 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2420 #ifdef QT_DEBUG_DRAW
2421 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2423 Q_D(QRasterPaintEngine);
2424 QRasterPaintEngineState *s = state();
2428 QPixmapData *pd = pixmap.pixmapData();
2429 if (pd->classId() == QPixmapData::RasterClass) {
2430 image = static_cast<QRasterPixmapData *>(pd)->image;
2432 image = pixmap.toImage();
2435 if (image.depth() == 1)
2436 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2438 if (s->matrix.type() > QTransform::TxTranslate) {
2439 QTransform copy = s->matrix;
2440 copy.translate(r.x(), r.y());
2441 copy.translate(-sr.x(), -sr.y());
2442 d->image_filler_xform.clip = d->clip();
2443 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2444 if (!d->image_filler_xform.blend)
2446 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2448 #ifdef QT_FAST_SPANS
2450 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2451 d->initializeRasterizer(&d->image_filler_xform);
2452 d->rasterizer->setAntialiased(s->flags.antialiased);
2454 const QRectF &rect = r.normalized();
2455 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2456 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2457 if (s->flags.tx_noshear)
2458 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2460 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2466 fillPath(path, &d->image_filler_xform);
2468 d->image_filler.clip = d->clip();
2470 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2471 if (!d->image_filler.blend)
2473 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2474 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2477 rr.translate(s->matrix.dx(), s->matrix.dy());
2478 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2484 static inline bool monoVal(const uchar* s, int x)
2486 return (s[x>>3] << (x&7)) & 0x80;
2492 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2494 Q_D(QRasterPaintEngine);
2495 QRasterPaintEngineState *s = state();
2497 if (!s->penData.blend)
2500 QRasterBuffer *rb = d->rasterBuffer.data();
2502 const QRect rect(rx, ry, w, h);
2503 const QClipData *clip = d->clip();
2504 bool unclipped = false;
2506 // inlined QRect::intersects
2507 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2508 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2510 if (clip->hasRectClip) {
2511 unclipped = rx > clip->xmin
2512 && rx + w < clip->xmax
2514 && ry + h < clip->ymax;
2520 // inlined QRect::intersects
2521 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2522 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2526 // inlined QRect::contains
2527 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2528 && rect.top() >= 0 && rect.bottom() < rb->height();
2530 unclipped = contains && d->isUnclipped_normalized(rect);
2533 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2534 const uchar * scanline = static_cast<const uchar *>(src);
2536 if (s->flags.fast_text) {
2539 if (s->penData.bitmapBlit) {
2540 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2541 scanline, w, h, bpl);
2544 } else if (depth == 8) {
2545 if (s->penData.alphamapBlit) {
2546 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2547 scanline, w, h, bpl, 0);
2550 } else if (depth == 32) {
2551 // (A)RGB Alpha mask where the alpha component is not used.
2552 if (s->penData.alphaRGBBlit) {
2553 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2554 (const uint *) scanline, w, h, bpl / 4, 0);
2558 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2559 // (A)RGB Alpha mask where the alpha component is not used.
2561 int nx = qMax(0, rx);
2562 int ny = qMax(0, ry);
2564 // Move scanline pointer to compensate for moved x and y
2565 int xdiff = nx - rx;
2566 int ydiff = ny - ry;
2567 scanline += ydiff * bpl;
2568 scanline += xdiff * (depth == 32 ? 4 : 1);
2573 if (nx + w > d->rasterBuffer->width())
2574 w = d->rasterBuffer->width() - nx;
2575 if (ny + h > d->rasterBuffer->height())
2576 h = d->rasterBuffer->height() - ny;
2581 if (depth == 8 && s->penData.alphamapBlit) {
2582 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2583 scanline, w, h, bpl, clip);
2584 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2585 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2586 (const uint *) scanline, w, h, bpl / 4, clip);
2601 scanline += bpl * y0;
2605 w = qMin(w, rb->width() - qMax(0, rx));
2606 h = qMin(h, rb->height() - qMax(0, ry));
2608 if (w <= 0 || h <= 0)
2611 const int NSPANS = 256;
2612 QSpan spans[NSPANS];
2615 const int x1 = x0 + w;
2616 const int y1 = y0 + h;
2619 for (int y = y0; y < y1; ++y) {
2620 for (int x = x0; x < x1; ) {
2621 if (!monoVal(scanline, x)) {
2626 if (current == NSPANS) {
2627 blend(current, spans, &s->penData);
2630 spans[current].x = x + rx;
2631 spans[current].y = y + ry;
2632 spans[current].coverage = 255;
2635 // extend span until we find a different one.
2636 while (x < x1 && monoVal(scanline, x)) {
2640 spans[current].len = len;
2645 } else if (depth == 8) {
2646 for (int y = y0; y < y1; ++y) {
2647 for (int x = x0; x < x1; ) {
2648 // Skip those with 0 coverage
2649 if (scanline[x] == 0) {
2654 if (current == NSPANS) {
2655 blend(current, spans, &s->penData);
2658 int coverage = scanline[x];
2659 spans[current].x = x + rx;
2660 spans[current].y = y + ry;
2661 spans[current].coverage = coverage;
2665 // extend span until we find a different one.
2666 while (x < x1 && scanline[x] == coverage) {
2670 spans[current].len = len;
2675 } else { // 32-bit alpha...
2676 uint *sl = (uint *) src;
2677 for (int y = y0; y < y1; ++y) {
2678 for (int x = x0; x < x1; ) {
2679 // Skip those with 0 coverage
2680 if ((sl[x] & 0x00ffffff) == 0) {
2685 if (current == NSPANS) {
2686 blend(current, spans, &s->penData);
2689 uint rgbCoverage = sl[x];
2690 int coverage = qGreen(rgbCoverage);
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 && sl[x] == rgbCoverage) {
2702 spans[current].len = len;
2705 sl += bpl / sizeof(uint);
2708 // qDebug() << "alphaPenBlt: num spans=" << current
2709 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2710 // Call span func for current set of spans.
2712 blend(current, spans, &s->penData);
2715 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2716 const QFixedPoint *positions, QFontEngine *fontEngine)
2718 Q_D(QRasterPaintEngine);
2719 QRasterPaintEngineState *s = state();
2721 #if !defined(QT_NO_FREETYPE)
2722 if (fontEngine->type() == QFontEngine::Freetype) {
2723 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2724 QFontEngineFT::GlyphFormat neededFormat =
2725 painter()->device()->devType() == QInternal::Widget
2726 ? fe->defaultGlyphFormat()
2727 : QFontEngineFT::Format_A8;
2729 if (d_func()->mono_surface
2730 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2732 neededFormat = QFontEngineFT::Format_Mono;
2734 if (neededFormat == QFontEngineFT::Format_None)
2735 neededFormat = QFontEngineFT::Format_A8;
2737 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2738 if (s->matrix.type() >= QTransform::TxScale) {
2739 if (s->matrix.isAffine())
2740 gset = fe->loadTransformedGlyphSet(s->matrix);
2745 if (!gset || gset->outline_drawing
2746 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2749 FT_Face lockedFace = 0;
2752 switch (neededFormat) {
2753 case QFontEngineFT::Format_Mono:
2756 case QFontEngineFT::Format_A8:
2759 case QFontEngineFT::Format_A32:
2767 for (int i = 0; i < numGlyphs; i++) {
2768 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2769 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2771 if (!glyph || glyph->format != neededFormat) {
2773 lockedFace = fe->lockFace();
2774 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2777 if (!glyph || !glyph->data)
2781 switch (neededFormat) {
2782 case QFontEngineFT::Format_Mono:
2783 pitch = ((glyph->width + 31) & ~31) >> 3;
2785 case QFontEngineFT::Format_A8:
2786 pitch = (glyph->width + 3) & ~3;
2788 case QFontEngineFT::Format_A32:
2789 pitch = glyph->width * 4;
2796 alphaPenBlt(glyph->data, pitch, depth,
2797 qFloor(positions[i].x) + glyph->x,
2798 qFloor(positions[i].y) - glyph->y,
2799 glyph->width, glyph->height);
2806 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2808 QImageTextureGlyphCache *cache =
2809 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2811 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2812 fontEngine->setGlyphCache(0, cache);
2815 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2816 cache->fillInPendingGlyphs();
2818 const QImage &image = cache->image();
2819 int bpl = image.bytesPerLine();
2821 int depth = image.depth();
2825 leftShift = 2; // multiply by 4
2826 else if (depth == 1)
2827 rightShift = 3; // divide by 8
2829 int margin = cache->glyphMargin();
2830 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2831 const uchar *bits = image.bits();
2832 for (int i=0; i<numGlyphs; ++i) {
2834 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2835 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2836 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2840 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2841 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2843 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2846 // c.baseLineX, c.baseLineY,
2849 // positions[i].x.toInt(), positions[i].y.toInt());
2851 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2857 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2858 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2860 Q_D(QRasterPaintEngine);
2861 QRasterPaintEngineState *s = state();
2863 QFontEngine *fontEngine = ti.fontEngine;
2864 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2865 QPaintEngineEx::drawTextItem(p, ti);
2869 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2871 QVarLengthArray<QFixedPoint> positions;
2872 QVarLengthArray<glyph_t> glyphs;
2873 QTransform matrix = s->matrix;
2874 matrix.translate(p.x(), p.y());
2875 if (matrix.type() == QTransform::TxScale)
2876 fe->setFontScale(matrix.m11());
2877 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2879 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2881 for (int i=0; i<glyphs.size(); ++i) {
2882 TOpenFontCharMetrics tmetrics;
2883 const TUint8 *glyphBitmapBytes;
2884 TSize glyphBitmapSize;
2885 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2886 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2887 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2888 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2891 if (matrix.type() == QTransform::TxScale)
2892 fe->setFontScale(1.0);
2896 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2899 * Returns true if the rectangle is completely within the current clip
2900 * state of the paint engine.
2902 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2904 const QClipData *cl = clip();
2906 // inline contains() for performance (we know the rects are normalized)
2907 const QRect &r1 = deviceRect;
2908 return (r.left() >= r1.left() && r.right() <= r1.right()
2909 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2913 if (cl->hasRectClip) {
2914 // currently all painting functions clips to deviceRect internally
2915 if (cl->clipRect == deviceRect)
2918 // inline contains() for performance (we know the rects are normalized)
2919 const QRect &r1 = cl->clipRect;
2920 return (r.left() >= r1.left() && r.right() <= r1.right()
2921 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2923 return qt_region_strictContains(cl->clipRegion, r);
2927 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2930 Q_Q(const QRasterPaintEngine);
2931 const QRasterPaintEngineState *s = q->state();
2932 const QClipData *cl = clip();
2934 QRect r = rect.normalized();
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 // currently all painting functions that call this function clip to deviceRect internally
2943 if (cl->hasRectClip && cl->clipRect == deviceRect)
2946 if (s->flags.antialiased)
2949 QRect r = rect.normalized();
2951 r.setX(r.x() - penWidth);
2952 r.setY(r.y() - penWidth);
2953 r.setWidth(r.width() + 2 * penWidth);
2954 r.setHeight(r.height() + 2 * penWidth);
2957 if (cl->hasRectClip) {
2958 // inline contains() for performance (we know the rects are normalized)
2959 const QRect &r1 = cl->clipRect;
2960 return (r.left() >= r1.left() && r.right() <= r1.right()
2961 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2963 return qt_region_strictContains(cl->clipRegion, r);
2967 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2970 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2974 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2975 const QSpanData *data) const
2977 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2981 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2982 const QSpanData *data) const
2984 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2988 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2989 const QSpanData *data) const
2991 Q_Q(const QRasterPaintEngine);
2992 const QRasterPaintEngineState *s = q->state();
2994 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2996 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2997 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3003 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3008 QFontEngine *fontEngine = textItem->fontEngine();
3009 if (!supportsTransformations(fontEngine)) {
3010 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3013 QPaintEngineEx::drawStaticTextItem(textItem);
3020 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3022 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3023 QRasterPaintEngineState *s = state();
3025 #ifdef QT_DEBUG_DRAW
3026 Q_D(QRasterPaintEngine);
3027 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3028 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3035 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3037 if (!supportsTransformations(ti.fontEngine)) {
3038 QVarLengthArray<QFixedPoint> positions;
3039 QVarLengthArray<glyph_t> glyphs;
3041 QTransform matrix = s->matrix;
3042 matrix.translate(p.x(), p.y());
3044 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3046 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3050 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC
3051 if (s->matrix.type() <= QTransform::TxTranslate
3052 || (s->matrix.type() == QTransform::TxScale
3053 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3054 drawGlyphsS60(p, ti);
3057 #else // Q_OS_WIN || Q_WS_MAC
3059 QFontEngine *fontEngine = ti.fontEngine;
3062 if (s->matrix.type() < QTransform::TxScale) {
3064 QVarLengthArray<QFixedPoint> positions;
3065 QVarLengthArray<glyph_t> glyphs;
3066 QTransform matrix = state()->transform();
3068 qreal _x = qFloor(p.x());
3069 qreal _y = qFloor(p.y());
3070 matrix.translate(_x, _y);
3072 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3073 if (glyphs.size() == 0)
3076 for(int i = 0; i < glyphs.size(); i++) {
3077 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3078 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3079 // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here?
3080 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3081 qRound(positions[i].x + metrics.x),
3082 qRound(positions[i].y + metrics.y),
3083 img.width(), img.height());
3089 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3091 if (fontEngine->type() != QFontEngine::Freetype) {
3092 QPaintEngineEx::drawTextItem(p, ti);
3096 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3098 QTransform matrix = s->matrix;
3099 matrix.translate(p.x(), p.y());
3101 QVarLengthArray<QFixedPoint> positions;
3102 QVarLengthArray<glyph_t> glyphs;
3103 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3104 if (glyphs.size() == 0)
3107 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3108 QPaintEngine::drawTextItem(p, ti);
3114 QPaintEngineEx::drawTextItem(p, ti);
3120 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3122 Q_D(QRasterPaintEngine);
3123 QRasterPaintEngineState *s = state();
3126 if (!s->penData.blend)
3129 if (!s->flags.fast_pen) {
3130 QPaintEngineEx::drawPoints(points, pointCount);
3134 QCosmeticStroker stroker(s, d->deviceRect);
3135 stroker.drawPoints(points, pointCount);
3139 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3141 Q_D(QRasterPaintEngine);
3142 QRasterPaintEngineState *s = state();
3145 if (!s->penData.blend)
3148 if (!s->flags.fast_pen) {
3149 QPaintEngineEx::drawPoints(points, pointCount);
3153 QCosmeticStroker stroker(s, d->deviceRect);
3154 stroker.drawPoints(points, pointCount);
3160 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3162 #ifdef QT_DEBUG_DRAW
3163 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3165 Q_D(QRasterPaintEngine);
3166 QRasterPaintEngineState *s = state();
3169 if (!s->penData.blend)
3172 if (s->flags.fast_pen) {
3173 QCosmeticStroker stroker(s, d->deviceRect);
3174 for (int i=0; i<lineCount; ++i) {
3175 const QLine &l = lines[i];
3176 stroker.drawLine(l.p1(), l.p2());
3179 QPaintEngineEx::drawLines(lines, lineCount);
3183 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3189 Q_Q(QRasterPaintEngine);
3190 QRasterPaintEngineState *s = q->state();
3192 const QPen &pen = s->lastPen;
3193 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3194 const QVector<qreal> pattern = pen.dashPattern();
3196 qreal patternLength = 0;
3197 for (int i = 0; i < pattern.size(); ++i)
3198 patternLength += pattern.at(i);
3200 if (patternLength <= 0)
3203 qreal length = line.length();
3204 Q_ASSERT(length > 0);
3205 while (length > 0) {
3206 const bool rasterize = *inDash;
3207 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3210 if (dash >= length) {
3212 *dashOffset += dash / width;
3216 *inDash = !(*inDash);
3217 if (++*dashIndex >= pattern.size())
3224 if (rasterize && dash > 0)
3225 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3232 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3234 #ifdef QT_DEBUG_DRAW
3235 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3237 Q_D(QRasterPaintEngine);
3238 QRasterPaintEngineState *s = state();
3241 if (!s->penData.blend)
3243 if (s->flags.fast_pen) {
3244 QCosmeticStroker stroker(s, d->deviceRect);
3245 for (int i=0; i<lineCount; ++i) {
3246 QLineF line = lines[i];
3247 stroker.drawLine(line.p1(), line.p2());
3250 QPaintEngineEx::drawLines(lines, lineCount);
3258 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3260 Q_D(QRasterPaintEngine);
3261 QRasterPaintEngineState *s = state();
3264 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3265 || (qpen_style(s->lastPen) == Qt::NoPen))
3266 && !s->flags.antialiased
3267 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3269 && s->matrix.type() <= QTransform::TxScale) // no shear
3272 const QRectF r = s->matrix.mapRect(rect);
3273 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3274 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3275 const QRect brect = QRect(int(r.x()), int(r.y()),
3276 int_dim(r.x(), r.width()),
3277 int_dim(r.y(), r.height()));
3279 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3280 &s->penData, &s->brushData);
3284 QPaintEngineEx::drawEllipse(rect);
3291 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3293 Q_D(QRasterPaintEngine);
3300 CGContextRef QRasterPaintEngine::getCGContext() const
3302 Q_D(const QRasterPaintEngine);
3303 return d->cgContext;
3311 void QRasterPaintEngine::setDC(HDC hdc) {
3312 Q_D(QRasterPaintEngine);
3319 HDC QRasterPaintEngine::getDC() const
3321 Q_D(const QRasterPaintEngine);
3328 void QRasterPaintEngine::releaseDC(HDC) const
3334 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3336 const QTransform &m = state()->matrix;
3337 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3338 QFontEngine::Type fontEngineType = fontEngine->type();
3339 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3340 || (m.type() <= QTransform::TxTranslate
3341 && (fontEngineType == QFontEngine::TestFontEngine
3342 || fontEngineType == QFontEngine::Box))) {
3346 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3349 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3351 #if defined(Q_WS_MAC)
3352 // Mac font engines don't support scaling and rotation
3353 if (m.type() > QTransform::TxTranslate)
3355 if (m.type() >= QTransform::TxProject)
3359 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3368 QPoint QRasterPaintEngine::coordinateOffset() const
3370 return QPoint(0, 0);
3373 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3378 Q_D(QRasterPaintEngine);
3380 Q_ASSERT(image.depth() == 1);
3382 const int spanCount = 256;
3383 QT_FT_Span spans[spanCount];
3387 int w = image.width();
3388 int h = image.height();
3389 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3390 int ymin = qMax(qRound(pos.y()), 0);
3391 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3392 int xmin = qMax(qRound(pos.x()), 0);
3394 int x_offset = xmin - qRound(pos.x());
3396 QImage::Format format = image.format();
3397 for (int y = ymin; y < ymax; ++y) {
3398 const uchar *src = image.scanLine(y - qRound(pos.y()));
3399 if (format == QImage::Format_MonoLSB) {
3400 for (int x = 0; x < xmax - xmin; ++x) {
3401 int src_x = x + x_offset;
3402 uchar pixel = src[src_x >> 3];
3407 if (pixel & (0x1 << (src_x & 7))) {
3408 spans[n].x = xmin + x;
3410 spans[n].coverage = 255;
3412 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3416 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3419 if (n == spanCount) {
3420 fg->blend(n, spans, fg);
3426 for (int x = 0; x < xmax - xmin; ++x) {
3427 int src_x = x + x_offset;
3428 uchar pixel = src[src_x >> 3];
3433 if (pixel & (0x80 >> (x & 7))) {
3434 spans[n].x = xmin + x;
3436 spans[n].coverage = 255;
3438 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3442 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3445 if (n == spanCount) {
3446 fg->blend(n, spans, fg);
3454 fg->blend(n, spans, fg);
3460 \enum QRasterPaintEngine::ClipType
3463 \value RectClip Indicates that the currently set clip is a single rectangle.
3464 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3469 Returns the type of the clip currently set.
3471 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3473 Q_D(const QRasterPaintEngine);
3475 const QClipData *clip = d->clip();
3476 if (!clip || clip->hasRectClip)
3484 Returns the bounding rect of the currently set clip.
3486 QRect QRasterPaintEngine::clipBoundingRect() const
3488 Q_D(const QRasterPaintEngine);
3490 const QClipData *clip = d->clip();
3493 return d->deviceRect;
3495 if (clip->hasRectClip)
3496 return clip->clipRect;
3498 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3501 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3503 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3505 QVarLengthArray<short, 4096> buffer;
3507 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3508 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3509 result->initialize();
3511 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3512 const QSpan *c1_spans = c1ClipLines[y].spans;
3513 int c1_count = c1ClipLines[y].count;
3514 const QSpan *c2_spans = c2ClipLines[y].spans;
3515 int c2_count = c2ClipLines[y].count;
3517 if (c1_count == 0 && c2_count == 0)
3519 if (c1_count == 0) {
3520 result->appendSpans(c2_spans, c2_count);
3522 } else if (c2_count == 0) {
3523 result->appendSpans(c1_spans, c1_count);
3527 // we need to merge the two
3529 // find required length
3530 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3531 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3533 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3535 // Fill with old spans.
3536 for (int i = 0; i < c1_count; ++i) {
3537 const QSpan *cs = c1_spans + i;
3538 for (int j=cs->x; j<cs->x + cs->len; ++j)
3539 buffer[j] = cs->coverage;
3542 // Fill with new spans
3543 for (int i = 0; i < c2_count; ++i) {
3544 const QSpan *cs = c2_spans + i;
3545 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3546 buffer[j] += cs->coverage;
3547 if (buffer[j] > 255)
3555 // Skip to next span
3556 while (x < max && buffer[x] == 0) ++x;
3557 if (x >= max) break;
3560 int coverage = buffer[x];
3562 // Find length of span
3563 while (x < max && buffer[x] == coverage)
3566 result->appendSpan(sx, x - sx, y, coverage);
3571 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3573 Q_Q(QRasterPaintEngine);
3574 QRasterPaintEngineState *s = q->state();
3576 rasterizer->setAntialiased(s->flags.antialiased);
3578 QRect clipRect(deviceRect);
3580 // ### get from optimized rectbased QClipData
3582 const QClipData *c = clip();
3584 const QRect r(QPoint(c->xmin, c->ymin),
3585 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3586 clipRect = clipRect.intersected(r);
3587 blend = data->blend;
3589 blend = data->unclipped_blend;
3592 rasterizer->setClipRect(clipRect);
3593 rasterizer->initialize(blend, data);
3596 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3597 ProcessSpans callback,
3598 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3600 if (!callback || !outline)
3603 Q_Q(QRasterPaintEngine);
3604 QRasterPaintEngineState *s = q->state();
3606 if (!s->flags.antialiased) {
3607 initializeRasterizer(spanData);
3609 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3613 rasterizer->rasterize(outline, fillRule);
3617 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3621 int q_gray_rendered_spans(QT_FT_Raster raster);
3624 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3625 ProcessSpans callback,
3626 void *userData, QRasterBuffer *)
3628 if (!callback || !outline)
3631 Q_Q(QRasterPaintEngine);
3632 QRasterPaintEngineState *s = q->state();
3634 if (!s->flags.antialiased) {
3635 rasterizer->setAntialiased(s->flags.antialiased);
3636 rasterizer->setClipRect(deviceRect);
3637 rasterizer->initialize(callback, userData);
3639 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3643 rasterizer->rasterize(outline, fillRule);
3647 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3648 // minimize memory reallocations. However if initial size for
3649 // raster pool is changed for lower value, reallocations will
3651 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3652 int rasterPoolSize = rasterPoolInitialSize;
3653 unsigned char *rasterPoolBase;
3654 #if defined(Q_OS_WIN64)
3656 // We make use of setjmp and longjmp in qgrayraster.c which requires
3657 // 16-byte alignment, hence we hardcode this requirement here..
3658 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3660 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3661 rasterPoolBase = rasterPoolOnStack;
3663 Q_CHECK_PTR(rasterPoolBase);
3665 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3667 void *data = userData;
3669 QT_FT_BBox clip_box = { deviceRect.x(),
3671 deviceRect.x() + deviceRect.width(),
3672 deviceRect.y() + deviceRect.height() };
3674 QT_FT_Raster_Params rasterParams;
3675 rasterParams.target = 0;
3676 rasterParams.source = outline;
3677 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3678 rasterParams.gray_spans = 0;
3679 rasterParams.black_spans = 0;
3680 rasterParams.bit_test = 0;
3681 rasterParams.bit_set = 0;
3682 rasterParams.user = data;
3683 rasterParams.clip_box = clip_box;
3688 int rendered_spans = 0;
3692 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3693 rasterParams.gray_spans = callback;
3694 rasterParams.skip_spans = rendered_spans;
3695 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3697 // Out of memory, reallocate some more and try again...
3698 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3699 int new_size = rasterPoolSize * 2;
3700 if (new_size > 1024 * 1024) {
3701 qWarning("QPainter: Rasterization of primitive failed");
3705 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3707 #if defined(Q_OS_WIN64)
3708 _aligned_free(rasterPoolBase);
3710 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3711 free(rasterPoolBase);
3714 rasterPoolSize = new_size;
3716 #if defined(Q_OS_WIN64)
3717 // We make use of setjmp and longjmp in qgrayraster.c which requires
3718 // 16-byte alignment, hence we hardcode this requirement here..
3719 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3721 (unsigned char *) malloc(rasterPoolSize);
3723 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3725 qt_ft_grays_raster.raster_done(*grayRaster.data());
3726 qt_ft_grays_raster.raster_new(grayRaster.data());
3727 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3733 #if defined(Q_OS_WIN64)
3734 _aligned_free(rasterPoolBase);
3736 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3737 free(rasterPoolBase);
3741 void QRasterPaintEnginePrivate::recalculateFastImages()
3743 Q_Q(QRasterPaintEngine);
3744 QRasterPaintEngineState *s = q->state();
3746 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3747 && s->matrix.type() <= QTransform::TxShear;
3750 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3752 Q_Q(const QRasterPaintEngine);
3753 const QRasterPaintEngineState *s = q->state();
3755 return s->flags.fast_images
3756 && (mode == QPainter::CompositionMode_SourceOver
3757 || (mode == QPainter::CompositionMode_Source
3758 && !image.hasAlphaChannel()));
3761 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3763 Q_ASSERT(image.depth() == 1);
3765 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3766 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3768 QRgb fg = PREMUL(color.rgba());
3771 int height = sourceImage.height();
3772 int width = sourceImage.width();
3773 for (int y=0; y<height; ++y) {
3774 uchar *source = sourceImage.scanLine(y);
3775 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3776 if (!source || !target)
3777 QT_THROW(std::bad_alloc()); // we must have run out of memory
3778 for (int x=0; x < width; ++x)
3779 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3784 QRasterBuffer::~QRasterBuffer()
3788 void QRasterBuffer::init()
3790 compositionMode = QPainter::CompositionMode_SourceOver;
3791 monoDestinationWithClut = false;
3796 QImage::Format QRasterBuffer::prepare(QImage *image)
3798 m_buffer = (uchar *)image->bits();
3799 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3800 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3801 bytes_per_pixel = image->depth()/8;
3802 bytes_per_line = image->bytesPerLine();
3804 format = image->format();
3805 drawHelper = qDrawHelper + format;
3806 if (image->depth() == 1 && image->colorTable().size() == 2) {
3807 monoDestinationWithClut = true;
3808 destColor0 = PREMUL(image->colorTable()[0]);
3809 destColor1 = PREMUL(image->colorTable()[1]);
3815 void QRasterBuffer::resetBuffer(int val)
3817 memset(m_buffer, val, m_height*bytes_per_line);
3820 QClipData::QClipData(int height)
3822 clipSpanHeight = height;
3827 xmin = xmax = ymin = ymax = 0;
3831 hasRectClip = hasRegionClip = false;
3834 QClipData::~QClipData()
3842 void QClipData::initialize()
3848 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3850 Q_CHECK_PTR(m_clipLines);
3852 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3853 allocated = clipSpanHeight;
3854 Q_CHECK_PTR(m_spans);
3860 m_clipLines[y].spans = 0;
3861 m_clipLines[y].count = 0;
3865 const int len = clipRect.width();
3868 QSpan *span = m_spans + count;
3872 span->coverage = 255;
3875 m_clipLines[y].spans = span;
3876 m_clipLines[y].count = 1;
3880 while (y < clipSpanHeight) {
3881 m_clipLines[y].spans = 0;
3882 m_clipLines[y].count = 0;
3885 } else if (hasRegionClip) {
3887 const QVector<QRect> rects = clipRegion.rects();
3888 const int numRects = rects.size();
3891 const int maxSpans = (ymax - ymin) * numRects;
3892 if (maxSpans > allocated) {
3893 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3894 allocated = maxSpans;
3899 int firstInBand = 0;
3901 while (firstInBand < numRects) {
3902 const int currMinY = rects.at(firstInBand).y();
3903 const int currMaxY = currMinY + rects.at(firstInBand).height();
3905 while (y < currMinY) {
3906 m_clipLines[y].spans = 0;
3907 m_clipLines[y].count = 0;
3911 int lastInBand = firstInBand;
3912 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3915 while (y < currMaxY) {
3917 m_clipLines[y].spans = m_spans + count;
3918 m_clipLines[y].count = lastInBand - firstInBand + 1;
3920 for (int r = firstInBand; r <= lastInBand; ++r) {
3921 const QRect &currRect = rects.at(r);
3922 QSpan *span = m_spans + count;
3923 span->x = currRect.x();
3924 span->len = currRect.width();
3926 span->coverage = 255;
3932 firstInBand = lastInBand + 1;
3935 Q_ASSERT(count <= allocated);
3937 while (y < clipSpanHeight) {
3938 m_clipLines[y].spans = 0;
3939 m_clipLines[y].count = 0;
3945 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3950 free(m_clipLines); // same for clipLines
3956 void QClipData::fixup()
3961 ymin = ymax = xmin = xmax = 0;
3966 ymin = m_spans[0].y;
3967 ymax = m_spans[count-1].y + 1;
3971 const int firstLeft = m_spans[0].x;
3972 const int firstRight = m_spans[0].x + m_spans[0].len;
3975 for (int i = 0; i < count; ++i) {
3976 QT_FT_Span_& span = m_spans[i];
3979 if (span.y != y + 1 && y != -1)
3982 m_clipLines[y].spans = &span;
3983 m_clipLines[y].count = 1;
3985 ++m_clipLines[y].count;
3987 const int spanLeft = span.x;
3988 const int spanRight = spanLeft + span.len;
3990 if (spanLeft < xmin)
3993 if (spanRight > xmax)
3996 if (spanLeft != firstLeft || spanRight != firstRight)
4002 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4007 Convert \a rect to clip spans.
4009 void QClipData::setClipRect(const QRect &rect)
4011 if (hasRectClip && rect == clipRect)
4014 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4016 hasRegionClip = false;
4020 xmax = rect.x() + rect.width();
4021 ymin = qMin(rect.y(), clipSpanHeight);
4022 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4029 // qDebug() << xmin << xmax << ymin << ymax;
4033 Convert \a region to clip spans.
4035 void QClipData::setClipRegion(const QRegion ®ion)
4037 if (region.rectCount() == 1) {
4038 setClipRect(region.rects().at(0));
4042 hasRegionClip = true;
4043 hasRectClip = false;
4044 clipRegion = region;
4046 { // set bounding rect
4047 const QRect rect = region.boundingRect();
4049 xmax = rect.x() + rect.width();
4051 ymax = rect.y() + rect.height();
4063 spans must be sorted on y
4065 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4066 const QSpan *spans, const QSpan *end,
4067 QSpan **outSpans, int available)
4069 const_cast<QClipData *>(clip)->initialize();
4071 QSpan *out = *outSpans;
4073 const QSpan *clipSpans = clip->m_spans + *currentClip;
4074 const QSpan *clipEnd = clip->m_spans + clip->count;
4076 while (available && spans < end ) {
4077 if (clipSpans >= clipEnd) {
4081 if (clipSpans->y > spans->y) {
4085 if (spans->y != clipSpans->y) {
4086 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4087 clipSpans = clip->m_clipLines[spans->y].spans;
4092 Q_ASSERT(spans->y == clipSpans->y);
4095 int sx2 = sx1 + spans->len;
4096 int cx1 = clipSpans->x;
4097 int cx2 = cx1 + clipSpans->len;
4099 if (cx1 < sx1 && cx2 < sx1) {
4102 } else if (sx1 < cx1 && sx2 < cx1) {
4106 int x = qMax(sx1, cx1);
4107 int len = qMin(sx2, cx2) - x;
4109 out->x = qMax(sx1, cx1);
4110 out->len = qMin(sx2, cx2) - out->x;
4112 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4124 *currentClip = clipSpans - clip->m_spans;
4128 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4130 // qDebug() << "qt_span_fill_clipped" << spanCount;
4131 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4133 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4135 const int NSPANS = 256;
4136 QSpan cspans[NSPANS];
4137 int currentClip = 0;
4138 const QSpan *end = spans + spanCount;
4139 while (spans < end) {
4140 QSpan *clipped = cspans;
4141 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4142 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4143 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4145 if (clipped - cspans)
4146 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4152 Clip spans to \a{clip}-rectangle.
4153 Returns number of unclipped spans
4155 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4158 const short minx = clip.left();
4159 const short miny = clip.top();
4160 const short maxx = clip.right();
4161 const short maxy = clip.bottom();
4164 for (int i = 0; i < numSpans; ++i) {
4165 if (spans[i].y > maxy)
4167 if (spans[i].y < miny
4168 || spans[i].x > maxx
4169 || spans[i].x + spans[i].len <= minx) {
4172 if (spans[i].x < minx) {
4173 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4176 spans[n].x = spans[i].x;
4177 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4179 if (spans[n].len == 0)
4181 spans[n].y = spans[i].y;
4182 spans[n].coverage = spans[i].coverage;
4189 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4192 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4193 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4195 Q_ASSERT(fillData->clip);
4196 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4198 // hw: check if this const_cast<> is safe!!!
4199 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4200 fillData->clip->clipRect);
4202 fillData->unclipped_blend(count, spans, fillData);
4205 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4207 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4209 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4210 // for (int i = 0; i < qMin(count, 10); ++i) {
4211 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4214 switch (clipData->operation) {
4216 case Qt::IntersectClip:
4218 QClipData *newClip = clipData->newClip;
4219 newClip->initialize();
4221 int currentClip = 0;
4222 const QSpan *end = spans + count;
4223 while (spans < end) {
4224 QSpan *newspans = newClip->m_spans + newClip->count;
4225 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4226 &newspans, newClip->allocated - newClip->count);
4227 newClip->count = newspans - newClip->m_spans;
4229 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4230 newClip->allocated *= 2;
4236 case Qt::ReplaceClip:
4237 clipData->newClip->appendSpans(spans, count);
4245 QImage QRasterBuffer::bufferImage() const
4247 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4249 for (int y = 0; y < m_height; ++y) {
4250 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4252 for (int x=0; x<m_width; ++x) {
4253 uint argb = span[x];
4254 image.setPixel(x, y, argb);
4262 void QRasterBuffer::flushToARGBImage(QImage *target) const
4264 int w = qMin(m_width, target->width());
4265 int h = qMin(m_height, target->height());
4267 for (int y=0; y<h; ++y) {
4268 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4269 QRgb *dest = (QRgb *) target->scanLine(y);
4270 for (int x=0; x<w; ++x) {
4271 QRgb pixel = sourceLine[x];
4272 int alpha = qAlpha(pixel);
4276 dest[x] = (alpha << 24)
4277 | ((255*qRed(pixel)/alpha) << 16)
4278 | ((255*qGreen(pixel)/alpha) << 8)
4279 | ((255*qBlue(pixel)/alpha) << 0);
4286 class QGradientCache
4290 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4291 stops(s), opacity(op), interpolationMode(mode) {}
4292 uint buffer[GRADIENT_STOPTABLE_SIZE];
4293 QGradientStops stops;
4295 QGradient::InterpolationMode interpolationMode;
4298 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4301 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4302 quint64 hash_val = 0;
4304 QGradientStops stops = gradient.stops();
4305 for (int i = 0; i < stops.size() && i <= 2; i++)
4306 hash_val += stops[i].second.rgba();
4308 QMutexLocker lock(&mutex);
4309 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4311 if (it == cache.constEnd())
4312 return addCacheElement(hash_val, gradient, opacity);
4315 const CacheInfo &cache_info = it.value();
4316 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4317 return cache_info.buffer;
4319 } while (it != cache.constEnd() && it.key() == hash_val);
4320 // an exact match for these stops and opacity was not found, create new cache
4321 return addCacheElement(hash_val, gradient, opacity);
4325 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4327 inline int maxCacheSize() const { return 60; }
4328 inline void generateGradientColorTable(const QGradient& g,
4330 int size, int opacity) const;
4331 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4332 if (cache.size() == maxCacheSize()) {
4333 // may remove more than 1, but OK
4334 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4336 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4337 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4338 return cache.insert(hash_val, cache_entry).value().buffer;
4341 QGradientColorTableHash cache;
4345 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4347 QGradientStops stops = gradient.stops();
4348 int stopCount = stops.count();
4349 Q_ASSERT(stopCount > 0);
4351 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4353 if (stopCount == 2) {
4354 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4355 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4357 qreal first_stop = stops[0].first;
4358 qreal second_stop = stops[1].first;
4360 if (second_stop < first_stop) {
4361 qSwap(first_color, second_color);
4362 qSwap(first_stop, second_stop);
4365 if (colorInterpolation) {
4366 first_color = PREMUL(first_color);
4367 second_color = PREMUL(second_color);
4370 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4371 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4373 uint red_first = qRed(first_color) << 16;
4374 uint green_first = qGreen(first_color) << 16;
4375 uint blue_first = qBlue(first_color) << 16;
4376 uint alpha_first = qAlpha(first_color) << 16;
4378 uint red_second = qRed(second_color) << 16;
4379 uint green_second = qGreen(second_color) << 16;
4380 uint blue_second = qBlue(second_color) << 16;
4381 uint alpha_second = qAlpha(second_color) << 16;
4384 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4385 if (colorInterpolation)
4386 colorTable[i] = first_color;
4388 colorTable[i] = PREMUL(first_color);
4391 if (i < second_index) {
4392 qreal reciprocal = qreal(1) / (second_index - first_index);
4394 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4395 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4396 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4397 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4400 red_first += 1 << 15;
4401 green_first += 1 << 15;
4402 blue_first += 1 << 15;
4403 alpha_first += 1 << 15;
4405 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4406 red_first += red_delta;
4407 green_first += green_delta;
4408 blue_first += blue_delta;
4409 alpha_first += alpha_delta;
4411 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4412 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4414 if (colorInterpolation)
4415 colorTable[i] = color;
4417 colorTable[i] = PREMUL(color);
4421 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4422 if (colorInterpolation)
4423 colorTable[i] = second_color;
4425 colorTable[i] = PREMUL(second_color);
4431 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4432 if (stopCount == 1) {
4433 current_color = PREMUL(current_color);
4434 for (int i = 0; i < size; ++i)
4435 colorTable[i] = current_color;
4439 // The position where the gradient begins and ends
4440 qreal begin_pos = stops[0].first;
4441 qreal end_pos = stops[stopCount-1].first;
4443 int pos = 0; // The position in the color table.
4446 qreal incr = 1 / qreal(size); // the double increment.
4447 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4449 // Up to first point
4450 colorTable[pos++] = PREMUL(current_color);
4451 while (dpos <= begin_pos) {
4452 colorTable[pos] = colorTable[pos - 1];
4457 int current_stop = 0; // We always interpolate between current and current + 1.
4459 qreal t; // position between current left and right stops
4460 qreal t_delta; // the t increment per entry in the color table
4462 if (dpos < end_pos) {
4464 while (dpos > stops[current_stop+1].first)
4467 if (current_stop != 0)
4468 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4469 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4471 if (colorInterpolation) {
4472 current_color = PREMUL(current_color);
4473 next_color = PREMUL(next_color);
4476 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4477 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4478 t = (dpos - stops[current_stop].first) * c;
4482 Q_ASSERT(current_stop < stopCount);
4484 int dist = qRound(t);
4485 int idist = 256 - dist;
4487 if (colorInterpolation)
4488 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4490 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4495 if (dpos >= end_pos)
4501 while (dpos > stops[current_stop+skip+1].first)
4505 current_stop += skip;
4507 current_color = next_color;
4509 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4510 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4512 if (colorInterpolation) {
4514 current_color = PREMUL(current_color);
4515 next_color = PREMUL(next_color);
4518 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4519 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4520 t = (dpos - stops[current_stop].first) * c;
4527 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4528 while (pos < size - 1) {
4529 colorTable[pos] = current_color;
4533 // Make sure the last color stop is represented at the end of the table
4534 colorTable[size - 1] = current_color;
4537 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4540 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4546 m11 = m22 = m33 = 1.;
4547 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4548 clip = pe ? pe->d_func()->clip() : 0;
4551 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4553 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4555 Qt::BrushStyle brushStyle = qbrush_style(brush);
4556 switch (brushStyle) {
4557 case Qt::SolidPattern: {
4559 QColor c = qbrush_color(brush);
4560 QRgb rgba = c.rgba();
4561 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4562 if ((solid.color & 0xff000000) == 0
4563 && compositionMode == QPainter::CompositionMode_SourceOver) {
4569 case Qt::LinearGradientPattern:
4571 type = LinearGradient;
4572 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4573 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4574 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4575 gradient.spread = g->spread();
4577 QLinearGradientData &linearData = gradient.linear;
4579 linearData.origin.x = g->start().x();
4580 linearData.origin.y = g->start().y();
4581 linearData.end.x = g->finalStop().x();
4582 linearData.end.y = g->finalStop().y();
4586 case Qt::RadialGradientPattern:
4588 type = RadialGradient;
4589 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4590 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4591 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4592 gradient.spread = g->spread();
4594 QRadialGradientData &radialData = gradient.radial;
4596 QPointF center = g->center();
4597 radialData.center.x = center.x();
4598 radialData.center.y = center.y();
4599 radialData.center.radius = g->centerRadius();
4600 QPointF focal = g->focalPoint();
4601 radialData.focal.x = focal.x();
4602 radialData.focal.y = focal.y();
4603 radialData.focal.radius = g->focalRadius();
4607 case Qt::ConicalGradientPattern:
4609 type = ConicalGradient;
4610 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4611 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4612 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4613 gradient.spread = QGradient::RepeatSpread;
4615 QConicalGradientData &conicalData = gradient.conical;
4617 QPointF center = g->center();
4618 conicalData.center.x = center.x();
4619 conicalData.center.y = center.y();
4620 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4624 case Qt::Dense1Pattern:
4625 case Qt::Dense2Pattern:
4626 case Qt::Dense3Pattern:
4627 case Qt::Dense4Pattern:
4628 case Qt::Dense5Pattern:
4629 case Qt::Dense6Pattern:
4630 case Qt::Dense7Pattern:
4631 case Qt::HorPattern:
4632 case Qt::VerPattern:
4633 case Qt::CrossPattern:
4634 case Qt::BDiagPattern:
4635 case Qt::FDiagPattern:
4636 case Qt::DiagCrossPattern:
4639 tempImage = new QImage();
4640 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4641 initTexture(tempImage, alpha, QTextureData::Tiled);
4643 case Qt::TexturePattern:
4646 tempImage = new QImage();
4648 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4649 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4651 *tempImage = brush.textureImage();
4652 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4660 adjustSpanMethods();
4663 void QSpanData::adjustSpanMethods()
4673 unclipped_blend = 0;
4676 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4677 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4678 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4679 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4680 fillRect = rasterBuffer->drawHelper->fillRect;
4682 case LinearGradient:
4683 case RadialGradient:
4684 case ConicalGradient:
4685 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4688 unclipped_blend = qBlendTexture;
4689 if (!texture.imageData)
4690 unclipped_blend = 0;
4695 if (!unclipped_blend) {
4698 blend = unclipped_blend;
4699 } else if (clip->hasRectClip) {
4700 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4702 blend = qt_span_fill_clipped;
4706 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4709 // make sure we round off correctly in qdrawhelper.cpp
4710 delta.translate(1.0 / 65536, 1.0 / 65536);
4712 QTransform inv = (delta * matrix).inverted();
4725 const bool affine = !m13 && !m23;
4726 fast_matrix = affine
4727 && m11 * m11 + m21 * m21 < 1e4
4728 && m12 * m12 + m22 * m22 < 1e4
4732 adjustSpanMethods();
4735 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4737 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4739 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4740 if (!d || d->height == 0) {
4741 texture.imageData = 0;
4748 texture.bytesPerLine = 0;
4749 texture.format = QImage::Format_Invalid;
4750 texture.colorTable = 0;
4751 texture.hasAlpha = alpha != 256;
4753 texture.imageData = d->data;
4754 texture.width = d->width;
4755 texture.height = d->height;
4757 if (sourceRect.isNull()) {
4760 texture.x2 = texture.width;
4761 texture.y2 = texture.height;
4763 texture.x1 = sourceRect.x();
4764 texture.y1 = sourceRect.y();
4765 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4766 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4769 texture.bytesPerLine = d->bytes_per_line;
4771 texture.format = d->format;
4772 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4773 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4775 texture.const_alpha = alpha;
4776 texture.type = _type;
4778 adjustSpanMethods();
4783 \a x and \a y is relative to the midpoint of \a rect.
4785 static inline void drawEllipsePoints(int x, int y, int length,
4788 ProcessSpans pen_func, ProcessSpans brush_func,
4789 QSpanData *pen_data, QSpanData *brush_data)
4794 QT_FT_Span outline[4];
4795 const int midx = rect.x() + (rect.width() + 1) / 2;
4796 const int midy = rect.y() + (rect.height() + 1) / 2;
4802 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4803 outline[0].len = qMin(length, x - outline[0].x);
4805 outline[0].coverage = 255;
4809 outline[1].len = length;
4811 outline[1].coverage = 255;
4814 outline[2].x = outline[0].x;
4815 outline[2].len = outline[0].len;
4816 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4817 outline[2].coverage = 255;
4821 outline[3].len = length;
4822 outline[3].y = outline[2].y;
4823 outline[3].coverage = 255;
4825 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4829 fill[0].x = outline[0].x + outline[0].len - 1;
4830 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4831 fill[0].y = outline[1].y;
4832 fill[0].coverage = 255;
4835 fill[1].x = outline[2].x + outline[2].len - 1;
4836 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4837 fill[1].y = outline[3].y;
4838 fill[1].coverage = 255;
4840 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4841 n = qt_intersect_spans(fill, n, clip);
4843 brush_func(n, fill, brush_data);
4846 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4847 n = qt_intersect_spans(outline, n, clip);
4849 pen_func(n, outline, pen_data);
4855 Draws an ellipse using the integer point midpoint algorithm.
4857 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4858 ProcessSpans pen_func, ProcessSpans brush_func,
4859 QSpanData *pen_data, QSpanData *brush_data)
4861 const qreal a = qreal(rect.width()) / 2;
4862 const qreal b = qreal(rect.height()) / 2;
4863 qreal d = b*b - (a*a*b) + 0.25*a*a;
4866 int y = (rect.height() + 1) / 2;
4870 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4871 if (d < 0) { // select E
4874 } else { // select SE
4875 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4876 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4877 pen_func, brush_func, pen_data, brush_data);
4882 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4883 pen_func, brush_func, pen_data, brush_data);
4886 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4887 const int miny = rect.height() & 0x1;
4889 if (d < 0) { // select SE
4890 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4892 } else { // select S
4893 d += a*a*(-2*y + 3);
4896 drawEllipsePoints(x, y, 1, rect, clip,
4897 pen_func, brush_func, pen_data, brush_data);
4902 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4905 Draws the first \a pointCount points in the buffer \a points
4907 The default implementation converts the first \a pointCount QPoints in \a points
4908 to QPointFs and calls the floating point version of drawPoints.
4912 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4915 Reimplement this function to draw the largest ellipse that can be
4916 contained within rectangle \a rect.
4919 #ifdef QT_DEBUG_DRAW
4920 void dumpClip(int width, int height, const QClipData *clip)
4922 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4923 clipImg.fill(0xffff0000);
4930 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4932 for (int i = 0; i < clip->count; ++i) {
4933 const QSpan *span = ((QClipData *) clip)->spans() + i;
4934 for (int j = 0; j < span->len; ++j)
4935 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4936 x0 = qMin(x0, int(span->x));
4937 x1 = qMax(x1, int(span->x + span->len - 1));
4939 y0 = qMin(y0, int(span->y));
4940 y1 = qMax(y1, int(span->y));
4943 static int counter = 0;
4950 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4951 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));