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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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 "qmemrotate_p.h"
73 #include "qpaintengine_raster_p.h"
74 // #include "qbezier_p.h"
75 #include "qoutlinemapper_p.h"
78 # include <qt_windows.h>
79 # include <qvarlengtharray.h>
80 # include <private/qfontengine_p.h>
81 # if defined(Q_OS_WINCE)
82 # include "qguifunctions_wince.h"
84 #elif defined(Q_WS_MAC)
85 # include <private/qt_mac_p.h>
86 # include <private/qpixmap_mac_p.h>
87 # include <private/qpaintengine_mac_p.h>
88 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
89 # include <private/qfontengine_s60_p.h>
90 #elif defined(Q_WS_QPA)
91 # include <private/qfontengine_ft_p.h>
94 #if defined(Q_WS_WIN64)
101 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
103 #define qreal_to_fixed_26_6(f) (int(f * 64))
104 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
105 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
107 // #define QT_DEBUG_DRAW
109 void dumpClip(int width, int height, const QClipData *clip);
112 #define QT_FAST_SPANS
115 // A little helper macro to get a better approximation of dimensions.
116 // If we have a rect that starting at 0.5 of width 3.5 it should span
118 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
121 extern bool qt_cleartype_enabled;
125 extern bool qt_applefontsmoothing_enabled;
129 /********************************************************************************
132 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
133 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
134 static void qt_span_clip(int count, const QSpan *spans, void *userData);
135 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
141 Qt::ClipOperation operation;
147 LineDrawIncludeLastPixel
150 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
151 LineDrawMode style, const QIntRect &rect);
152 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
153 QPen *pen, ProcessSpans span_func, QSpanData *data,
154 LineDrawMode style, const QIntRect &devRect,
156 // static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2,
157 // ProcessSpans span_func, QSpanData *data,
158 // LineDrawMode style, const QRect &devRect);
160 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
161 ProcessSpans pen_func, ProcessSpans brush_func,
162 QSpanData *pen_data, QSpanData *brush_data);
164 struct QRasterFloatPoint {
170 static const QRectF boundingRect(const QPointF *points, int pointCount)
172 const QPointF *e = points;
173 const QPointF *last = points + pointCount;
174 qreal minx, maxx, miny, maxy;
175 minx = maxx = e->x();
176 miny = maxy = e->y();
180 else if (e->x() > maxx)
184 else if (e->y() > maxy)
187 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
191 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
192 return (elementCount == 5 // 5-point polygon, check for closed rect
193 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
194 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
195 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
196 && pts[0] < pts[4] && pts[1] < pts[5]
198 (elementCount == 4 // 4-point polygon, check for unclosed rect
199 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
200 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
201 && pts[0] < pts[4] && pts[1] < pts[5]
206 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
208 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
211 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
213 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
216 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
217 qfixed c2x, qfixed c2y,
218 qfixed ex, qfixed ey,
221 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
222 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
223 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
227 #if !defined(QT_NO_DEBUG) && 0
228 static void qt_debug_path(const QPainterPath &path)
230 const char *names[] = {
237 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
238 for (int i=0; i<path.elementCount(); ++i) {
239 const QPainterPath::Element &e = path.elementAt(i);
240 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
241 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
246 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
247 QPaintEngineExPrivate(),
254 \class QRasterPaintEngine
259 \brief The QRasterPaintEngine class enables hardware acceleration
260 of painting operations in Qt for Embedded Linux.
262 Note that this functionality is only available in
263 \l{Qt for Embedded Linux}.
265 In \l{Qt for Embedded Linux}, painting is a pure software
266 implementation. But starting with Qt 4.2, it is
267 possible to add an accelerated graphics driver to take advantage
268 of available hardware resources.
270 Hardware acceleration is accomplished by creating a custom screen
271 driver, accelerating the copying from memory to the screen, and
272 implementing a custom paint engine accelerating the various
273 painting operations. Then a custom paint device (derived from the
274 QCustomRasterPaintDevice class) and a custom window surface
275 (derived from QWSWindowSurface) must be implemented to make
276 \l{Qt for Embedded Linux} aware of the accelerated driver.
278 \note The QRasterPaintEngine class does not support 8-bit images.
279 Instead, they need to be converted to a supported format, such as
280 QImage::Format_ARGB32_Premultiplied.
282 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
283 documentation for details.
285 \sa QCustomRasterPaintDevice, QPaintEngine
289 \fn Type QRasterPaintEngine::type() const
295 \relates QRasterPaintEngine
297 A struct equivalent to QT_FT_Span, containing a position (x,
298 y), the span's length in pixels and its color/coverage (a value
299 ranging from 0 to 255).
305 Creates a raster based paint engine for operating on the given
306 \a device, with the complete set of \l
307 {QPaintEngine::PaintEngineFeature}{paint engine features and
310 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
311 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
313 d_func()->device = device;
320 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
323 d_func()->device = device;
327 void QRasterPaintEngine::init()
329 Q_D(QRasterPaintEngine);
336 // The antialiasing raster.
337 d->grayRaster.reset(new QT_FT_Raster);
338 Q_CHECK_PTR(d->grayRaster.data());
339 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
340 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
343 d->rasterizer.reset(new QRasterizer);
344 d->rasterBuffer.reset(new QRasterBuffer());
345 d->outlineMapper.reset(new QOutlineMapper);
346 d->outlinemapper_xform_dirty = true;
348 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
349 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
350 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
352 d->baseClip.reset(new QClipData(d->device->height()));
353 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
355 d->image_filler.init(d->rasterBuffer.data(), this);
356 d->image_filler.type = QSpanData::Texture;
358 d->image_filler_xform.init(d->rasterBuffer.data(), this);
359 d->image_filler_xform.type = QSpanData::Texture;
361 d->solid_color_filler.init(d->rasterBuffer.data(), this);
362 d->solid_color_filler.type = QSpanData::Solid;
364 d->deviceDepth = d->device->depth();
366 d->mono_surface = false;
367 gccaps &= ~PorterDuff;
369 QImage::Format format = QImage::Format_Invalid;
371 switch (d->device->devType()) {
372 case QInternal::Pixmap:
373 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
375 case QInternal::Image:
376 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
379 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
385 case QImage::Format_MonoLSB:
386 case QImage::Format_Mono:
387 d->mono_surface = true;
389 case QImage::Format_ARGB8565_Premultiplied:
390 case QImage::Format_ARGB8555_Premultiplied:
391 case QImage::Format_ARGB6666_Premultiplied:
392 case QImage::Format_ARGB4444_Premultiplied:
393 case QImage::Format_ARGB32_Premultiplied:
394 case QImage::Format_ARGB32:
395 gccaps |= PorterDuff;
397 case QImage::Format_RGB32:
398 case QImage::Format_RGB444:
399 case QImage::Format_RGB555:
400 case QImage::Format_RGB666:
401 case QImage::Format_RGB888:
402 case QImage::Format_RGB16:
413 Destroys this paint engine.
415 QRasterPaintEngine::~QRasterPaintEngine()
417 Q_D(QRasterPaintEngine);
419 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
425 bool QRasterPaintEngine::begin(QPaintDevice *device)
427 Q_D(QRasterPaintEngine);
429 if (device->devType() == QInternal::Pixmap) {
430 QPixmap *pixmap = static_cast<QPixmap *>(device);
431 QPixmapData *pd = pixmap->pixmapData();
432 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
433 d->device = pd->buffer();
438 // Make sure QPaintEngine::paintDevice() returns the proper device.
441 Q_ASSERT(d->device->devType() == QInternal::Image
442 || d->device->devType() == QInternal::CustomRaster);
444 d->systemStateChanged();
446 QRasterPaintEngineState *s = state();
447 ensureOutlineMapper();
448 d->outlineMapper->m_clip_rect = d->deviceRect;
450 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
451 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
452 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
453 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
455 d->rasterizer->setClipRect(d->deviceRect);
457 s->penData.init(d->rasterBuffer.data(), this);
458 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
459 s->stroker = &d->basicStroker;
460 d->basicStroker.setClipRect(d->deviceRect);
462 s->brushData.init(d->rasterBuffer.data(), this);
463 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
465 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
467 setDirty(DirtyBrushOrigin);
470 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
471 << ") devType:" << device->devType()
472 << "devRect:" << d->deviceRect;
474 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
478 #if defined(Q_WS_WIN)
479 d->isPlain45DegreeRotation = true;
483 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
484 #if defined(Q_WS_WIN)
485 else if (qt_cleartype_enabled)
486 #elif defined (Q_WS_MAC)
487 else if (qt_applefontsmoothing_enabled)
492 QImage::Format format = static_cast<QImage *>(d->device)->format();
493 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
494 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
496 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
507 bool QRasterPaintEngine::end()
510 Q_D(QRasterPaintEngine);
511 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
513 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
523 void QRasterPaintEngine::releaseBuffer()
525 Q_D(QRasterPaintEngine);
526 d->rasterBuffer.reset(new QRasterBuffer);
532 QSize QRasterPaintEngine::size() const
534 Q_D(const QRasterPaintEngine);
535 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
542 void QRasterPaintEngine::saveBuffer(const QString &s) const
544 Q_D(const QRasterPaintEngine);
545 d->rasterBuffer->bufferImage().save(s, "PNG");
552 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
554 QRasterPaintEngineState *s = state();
555 // FALCON: get rid of this line, see drawImage call below.
557 QTransform::TransformationType txop = s->matrix.type();
561 case QTransform::TxNone:
562 s->flags.int_xform = true;
565 case QTransform::TxTranslate:
566 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
567 && qreal(int(s->matrix.dy())) == s->matrix.dy();
570 case QTransform::TxScale:
571 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
572 && qreal(int(s->matrix.dy())) == s->matrix.dy()
573 && qreal(int(s->matrix.m11())) == s->matrix.m11()
574 && qreal(int(s->matrix.m22())) == s->matrix.m22();
577 default: // shear / perspective...
578 s->flags.int_xform = false;
582 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
584 ensureOutlineMapper();
587 Q_D(QRasterPaintEngine);
588 d->isPlain45DegreeRotation = false;
589 if (txop >= QTransform::TxRotate) {
590 d->isPlain45DegreeRotation =
591 (qFuzzyIsNull(matrix.m11())
592 && qFuzzyIsNull(matrix.m12() - qreal(1))
593 && qFuzzyIsNull(matrix.m21() + qreal(1))
594 && qFuzzyIsNull(matrix.m22())
597 (qFuzzyIsNull(matrix.m11() + qreal(1))
598 && qFuzzyIsNull(matrix.m12())
599 && qFuzzyIsNull(matrix.m21())
600 && qFuzzyIsNull(matrix.m22() + qreal(1))
603 (qFuzzyIsNull(matrix.m11())
604 && qFuzzyIsNull(matrix.m12() + qreal(1))
605 && qFuzzyIsNull(matrix.m21() - qreal(1))
606 && qFuzzyIsNull(matrix.m22())
616 QRasterPaintEngineState::~QRasterPaintEngineState()
618 if (flags.has_clip_ownership)
623 QRasterPaintEngineState::QRasterPaintEngineState()
635 flags.fast_pen = true;
636 flags.antialiased = false;
637 flags.bilinear = false;
638 flags.fast_text = true;
639 flags.int_xform = true;
640 flags.tx_noshear = true;
641 flags.fast_images = true;
644 flags.has_clip_ownership = false;
649 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
654 , strokeFlags(s.strokeFlags)
655 , lastBrush(s.lastBrush)
656 , brushData(s.brushData)
657 , fillFlags(s.fillFlags)
658 , pixmapFlags(s.pixmapFlags)
659 , intOpacity(s.intOpacity)
663 , flag_bits(s.flag_bits)
665 brushData.tempImage = 0;
666 penData.tempImage = 0;
667 flags.has_clip_ownership = false;
673 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
675 QRasterPaintEngineState *s;
677 s = new QRasterPaintEngineState();
679 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
687 void QRasterPaintEngine::setState(QPainterState *s)
689 Q_D(QRasterPaintEngine);
690 QPaintEngineEx::setState(s);
691 d->rasterBuffer->compositionMode = s->composition_mode;
695 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
700 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
707 void QRasterPaintEngine::penChanged()
710 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
712 QRasterPaintEngineState *s = state();
713 s->strokeFlags |= DirtyPen;
714 s->dirty |= DirtyPen;
720 void QRasterPaintEngine::updatePen(const QPen &pen)
722 Q_D(QRasterPaintEngine);
723 QRasterPaintEngineState *s = state();
725 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
728 Qt::PenStyle pen_style = qpen_style(pen);
733 s->penData.clip = d->clip();
734 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
736 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
737 || pen.brush().transform().type() >= QTransform::TxNone) {
738 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
741 // Slightly ugly handling of an uncommon case... We need to change
742 // the pen because it is reused in draw_midpoint to decide dashed
744 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
745 pen_style = Qt::SolidLine;
746 s->lastPen.setStyle(Qt::SolidLine);
749 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
750 d->basicStroker.setCapStyle(qpen_capStyle(pen));
751 d->basicStroker.setMiterLimit(pen.miterLimit());
753 qreal penWidth = qpen_widthf(pen);
755 d->basicStroker.setStrokeWidth(1);
757 d->basicStroker.setStrokeWidth(penWidth);
759 if(pen_style == Qt::SolidLine) {
760 s->stroker = &d->basicStroker;
761 } else if (pen_style != Qt::NoPen) {
763 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
764 if (pen.isCosmetic()) {
765 d->dashStroker->setClipRect(d->deviceRect);
767 // ### I've seen this inverted devrect multiple places now...
768 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
769 d->dashStroker->setClipRect(clipRect);
771 d->dashStroker->setDashPattern(pen.dashPattern());
772 d->dashStroker->setDashOffset(pen.dashOffset());
773 s->stroker = d->dashStroker.data();
778 s->flags.fast_pen = pen_style > Qt::NoPen
780 && !s->flags.antialiased
781 && (penWidth == 0 || (penWidth <= 1
782 && (s->matrix.type() <= QTransform::TxTranslate
783 || pen.isCosmetic())));
785 ensureState(); // needed because of tx_noshear...
786 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
796 void QRasterPaintEngine::brushOriginChanged()
798 QRasterPaintEngineState *s = state();
800 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
803 s->fillFlags |= DirtyBrushOrigin;
810 void QRasterPaintEngine::brushChanged()
812 QRasterPaintEngineState *s = state();
814 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
816 s->fillFlags |= DirtyBrush;
825 void QRasterPaintEngine::updateBrush(const QBrush &brush)
828 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
830 Q_D(QRasterPaintEngine);
831 QRasterPaintEngineState *s = state();
832 // must set clip prior to setup, as setup uses it...
833 s->brushData.clip = d->clip();
834 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
835 if (s->fillFlags & DirtyTransform
836 || brush.transform().type() >= QTransform::TxNone)
837 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
838 s->lastBrush = brush;
842 void QRasterPaintEngine::updateOutlineMapper()
844 Q_D(QRasterPaintEngine);
845 d->outlineMapper->setMatrix(state()->matrix);
848 void QRasterPaintEngine::updateState()
850 QRasterPaintEngineState *s = state();
852 if (s->dirty & DirtyTransform)
853 updateMatrix(s->matrix);
855 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
856 const QPainter::CompositionMode mode = s->composition_mode;
857 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
858 && s->intOpacity == 256
859 && (mode == QPainter::CompositionMode_Source
860 || (mode == QPainter::CompositionMode_SourceOver
861 && qAlpha(s->penData.solid.color) == 255));
871 void QRasterPaintEngine::opacityChanged()
873 QRasterPaintEngineState *s = state();
876 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
879 s->fillFlags |= DirtyOpacity;
880 s->strokeFlags |= DirtyOpacity;
881 s->pixmapFlags |= DirtyOpacity;
882 s->dirty |= DirtyOpacity;
883 s->intOpacity = (int) (s->opacity * 256);
889 void QRasterPaintEngine::compositionModeChanged()
891 Q_D(QRasterPaintEngine);
892 QRasterPaintEngineState *s = state();
895 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
898 s->fillFlags |= DirtyCompositionMode;
899 s->dirty |= DirtyCompositionMode;
901 s->strokeFlags |= DirtyCompositionMode;
902 d->rasterBuffer->compositionMode = s->composition_mode;
904 d->recalculateFastImages();
910 void QRasterPaintEngine::renderHintsChanged()
912 QRasterPaintEngineState *s = state();
915 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
918 bool was_aa = s->flags.antialiased;
919 bool was_bilinear = s->flags.bilinear;
921 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
922 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
924 if (was_aa != s->flags.antialiased)
925 s->strokeFlags |= DirtyHints;
927 if (was_bilinear != s->flags.bilinear) {
928 s->strokeFlags |= DirtyPen;
929 s->fillFlags |= DirtyBrush;
932 Q_D(QRasterPaintEngine);
933 d->recalculateFastImages();
939 void QRasterPaintEngine::transformChanged()
941 QRasterPaintEngineState *s = state();
944 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
947 s->fillFlags |= DirtyTransform;
948 s->strokeFlags |= DirtyTransform;
950 s->dirty |= DirtyTransform;
952 Q_D(QRasterPaintEngine);
953 d->recalculateFastImages();
959 void QRasterPaintEngine::clipEnabledChanged()
961 QRasterPaintEngineState *s = state();
964 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
968 s->clip->enabled = s->clipEnabled;
969 s->fillFlags |= DirtyClipEnabled;
970 s->strokeFlags |= DirtyClipEnabled;
971 s->pixmapFlags |= DirtyClipEnabled;
975 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
977 SrcOverBlendFunc func,
982 if (alpha == 0 || !clip.isValid())
985 Q_ASSERT(img.depth() >= 8);
987 int srcBPL = img.bytesPerLine();
988 const uchar *srcBits = img.bits();
989 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
990 int iw = img.width();
991 int ih = img.height();
996 // Adjust the image according to the source offset...
997 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1000 // adapt the x parameters
1001 int x = qRound(pt.x());
1003 int cx2 = clip.x() + clip.width();
1006 srcBits += srcSize * d;
1011 int d = x + iw - cx2;
1017 // adapt the y paremeters...
1019 int cy2 = clip.y() + clip.height();
1020 int y = qRound(pt.y());
1023 srcBits += srcBPL * d;
1028 int d = y + ih - cy2;
1034 // call the blend function...
1035 int dstSize = rasterBuffer->bytesPerPixel();
1036 int dstBPL = rasterBuffer->bytesPerLine();
1037 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1044 void QRasterPaintEnginePrivate::systemStateChanged()
1046 QRect clipRect(0, 0,
1047 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1048 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1050 if (!systemClip.isEmpty()) {
1051 QRegion clippedDeviceRgn = systemClip & clipRect;
1052 deviceRect = clippedDeviceRgn.boundingRect();
1053 baseClip->setClipRegion(clippedDeviceRgn);
1055 deviceRect = clipRect;
1056 baseClip->setClipRect(deviceRect);
1058 #ifdef QT_DEBUG_DRAW
1059 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1062 exDeviceRect = deviceRect;
1064 Q_Q(QRasterPaintEngine);
1065 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1066 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1067 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1070 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1072 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1075 Q_Q(QRasterPaintEngine);
1076 bool bilinear = q->state()->flags.bilinear;
1078 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1079 spanData->setupMatrix(b.transform() * m, bilinear);
1081 if (m.type() <= QTransform::TxTranslate) {
1082 // specialize setupMatrix for translation matrices
1083 // to avoid needless matrix inversion
1091 spanData->dx = -m.dx();
1092 spanData->dy = -m.dy();
1093 spanData->txop = m.type();
1094 spanData->bilinear = bilinear;
1095 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1096 spanData->adjustSpanMethods();
1098 spanData->setupMatrix(m, bilinear);
1103 // #define QT_CLIPPING_RATIOS
1105 #ifdef QT_CLIPPING_RATIOS
1110 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1112 if (d->clip()->hasRectClip)
1114 if (d->clip()->hasRegionClip)
1118 if ((totalClips % 5000) == 0) {
1119 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1120 rectClips * 100.0 / (qreal) totalClips,
1121 regionClips * 100.0 / (qreal) totalClips,
1122 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1131 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1133 if (s->flags.has_clip_ownership)
1136 s->flags.has_clip_ownership = false;
1139 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1141 s->fillFlags |= QPaintEngine::DirtyClipPath;
1142 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1143 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1145 d->solid_color_filler.clip = d->clip();
1146 d->solid_color_filler.adjustSpanMethods();
1148 #ifdef QT_DEBUG_DRAW
1149 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1158 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1160 #ifdef QT_DEBUG_DRAW
1161 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1163 if (path.elements()) {
1164 for (int i=0; i<path.elementCount(); ++i) {
1165 qDebug() << " - " << path.elements()[i]
1166 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1169 for (int i=0; i<path.elementCount(); ++i) {
1170 qDebug() << " ---- "
1171 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1176 Q_D(QRasterPaintEngine);
1177 QRasterPaintEngineState *s = state();
1179 const qreal *points = path.points();
1180 const QPainterPath::ElementType *types = path.elements();
1182 // There are some cases that are not supported by clip(QRect)
1183 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1184 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1185 if (s->matrix.type() <= QTransform::TxScale
1186 && ((path.shape() == QVectorPath::RectangleHint)
1187 || (isRect(points, path.elementCount())
1188 && (!types || (types[0] == QPainterPath::MoveToElement
1189 && types[1] == QPainterPath::LineToElement
1190 && types[2] == QPainterPath::LineToElement
1191 && types[3] == QPainterPath::LineToElement))))) {
1192 #ifdef QT_DEBUG_DRAW
1193 qDebug() << " --- optimizing vector clip to rect clip...";
1196 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1197 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1202 if (op == Qt::NoClip) {
1203 qrasterpaintengine_state_setNoClip(s);
1206 QClipData *base = d->baseClip.data();
1208 // Intersect with current clip when available...
1209 if (op == Qt::IntersectClip && s->clip)
1212 // We always intersect, except when there is nothing to
1213 // intersect with, in which case we simplify the operation to
1215 Qt::ClipOperation isectOp = Qt::IntersectClip;
1217 isectOp = Qt::ReplaceClip;
1219 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1220 newClip->initialize();
1221 ClipData clipData = { base, newClip, isectOp };
1222 ensureOutlineMapper();
1223 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1227 if (op == Qt::UniteClip) {
1229 QClipData *result = new QClipData(d->rasterBuffer->height());
1230 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1231 qt_merge_clip(current, newClip, result);
1239 if (s->flags.has_clip_ownership)
1243 s->flags.has_clip_ownership = true;
1245 qrasterpaintengine_dirty_clip(d, s);
1253 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1255 #ifdef QT_DEBUG_DRAW
1256 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1259 QRasterPaintEngineState *s = state();
1261 if (op == Qt::NoClip) {
1262 qrasterpaintengine_state_setNoClip(s);
1264 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1265 QPaintEngineEx::clip(rect, op);
1268 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1269 QPaintEngineEx::clip(rect, op);
1275 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1277 Q_D(QRasterPaintEngine);
1278 QRect clipRect = r & d->deviceRect;
1279 QRasterPaintEngineState *s = state();
1281 if (op == Qt::ReplaceClip || s->clip == 0) {
1283 // No current clip, hence we intersect with sysclip and be
1285 QRegion clipRegion = systemClip();
1286 QClipData *clip = new QClipData(d->rasterBuffer->height());
1288 if (clipRegion.isEmpty())
1289 clip->setClipRect(clipRect);
1291 clip->setClipRegion(clipRegion & clipRect);
1293 if (s->flags.has_clip_ownership)
1297 s->clip->enabled = true;
1298 s->flags.has_clip_ownership = true;
1300 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1301 QClipData *base = s->clip;
1304 if (base->hasRectClip || base->hasRegionClip) {
1305 if (!s->flags.has_clip_ownership) {
1306 s->clip = new QClipData(d->rasterBuffer->height());
1307 s->flags.has_clip_ownership = true;
1309 if (base->hasRectClip)
1310 s->clip->setClipRect(base->clipRect & clipRect);
1312 s->clip->setClipRegion(base->clipRegion & clipRect);
1313 s->clip->enabled = true;
1321 qrasterpaintengine_dirty_clip(d, s);
1329 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1331 #ifdef QT_DEBUG_DRAW
1332 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1335 Q_D(QRasterPaintEngine);
1337 if (region.rectCount() == 1) {
1338 clip(region.boundingRect(), op);
1342 QRasterPaintEngineState *s = state();
1343 const QClipData *clip = d->clip();
1344 const QClipData *baseClip = d->baseClip.data();
1346 if (op == Qt::NoClip) {
1347 qrasterpaintengine_state_setNoClip(s);
1348 } else if (s->matrix.type() > QTransform::TxScale
1349 || op == Qt::UniteClip
1350 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1351 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1352 QPaintEngineEx::clip(region, op);
1354 const QClipData *curClip;
1357 if (op == Qt::IntersectClip)
1362 if (s->flags.has_clip_ownership) {
1366 newClip = new QClipData(d->rasterBuffer->height());
1368 s->flags.has_clip_ownership = true;
1371 QRegion r = s->matrix.map(region);
1372 if (curClip->hasRectClip)
1373 newClip->setClipRegion(r & curClip->clipRect);
1374 else if (curClip->hasRegionClip)
1375 newClip->setClipRegion(r & curClip->clipRegion);
1377 qrasterpaintengine_dirty_clip(d, s);
1384 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1386 #ifdef QT_DEBUG_DRAW
1387 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1390 if (!fillData->blend)
1393 Q_D(QRasterPaintEngine);
1395 const QRectF controlPointRect = path.controlPointRect();
1397 QRasterPaintEngineState *s = state();
1398 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1399 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1400 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1401 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1402 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1403 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1405 if (!s->flags.antialiased && !do_clip) {
1406 d->initializeRasterizer(fillData);
1407 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1411 ensureOutlineMapper();
1412 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1415 static void fillRect_normalized(const QRect &r, QSpanData *data,
1416 QRasterPaintEnginePrivate *pe)
1420 bool rectClipped = true;
1423 x1 = qMax(r.x(), data->clip->xmin);
1424 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1425 y1 = qMax(r.y(), data->clip->ymin);
1426 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1427 rectClipped = data->clip->hasRectClip;
1430 x1 = qMax(r.x(), pe->deviceRect.x());
1431 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1432 y1 = qMax(r.y(), pe->deviceRect.y());
1433 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1435 x1 = qMax(r.x(), 0);
1436 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1437 y1 = qMax(r.y(), 0);
1438 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1441 if (x2 <= x1 || y2 <= y1)
1444 const int width = x2 - x1;
1445 const int height = y2 - y1;
1447 bool isUnclipped = rectClipped
1448 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1450 if (pe && isUnclipped) {
1451 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1453 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1454 || (mode == QPainter::CompositionMode_SourceOver
1455 && qAlpha(data->solid.color) == 255)))
1457 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1463 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1465 const int nspans = 256;
1466 QT_FT_Span spans[nspans];
1468 Q_ASSERT(data->blend);
1471 int n = qMin(nspans, y2 - y);
1475 spans[i].len = width;
1477 spans[i].coverage = 255;
1481 blend(n, spans, data);
1489 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1491 #ifdef QT_DEBUG_DRAW
1492 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1494 Q_D(QRasterPaintEngine);
1495 QRasterPaintEngineState *s = state();
1499 if (s->brushData.blend) {
1500 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1501 const QRect *r = rects;
1502 const QRect *lastRect = rects + rectCount;
1504 int offset_x = int(s->matrix.dx());
1505 int offset_y = int(s->matrix.dy());
1506 while (r < lastRect) {
1507 QRect rect = r->normalized();
1508 QRect rr = rect.translated(offset_x, offset_y);
1509 fillRect_normalized(rr, &s->brushData, d);
1513 QRectVectorPath path;
1514 for (int i=0; i<rectCount; ++i) {
1516 fill(path, s->brush);
1522 if (s->penData.blend) {
1523 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1524 const QRect *r = rects;
1525 const QRect *lastRect = rects + rectCount;
1526 while (r < lastRect) {
1528 int right = r->x() + r->width();
1530 int bottom = r->y() + r->height();
1533 int pts[] = { top, left,
1538 int pts[] = { left, top,
1544 strokePolygonCosmetic((QPoint *) pts, 4, WindingMode);
1548 QRectVectorPath path;
1549 for (int i = 0; i < rectCount; ++i) {
1551 stroke(path, s->pen);
1560 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1562 #ifdef QT_DEBUG_DRAW
1563 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1565 #ifdef QT_FAST_SPANS
1566 Q_D(QRasterPaintEngine);
1567 QRasterPaintEngineState *s = state();
1571 if (s->flags.tx_noshear) {
1573 if (s->brushData.blend) {
1574 d->initializeRasterizer(&s->brushData);
1575 for (int i = 0; i < rectCount; ++i) {
1576 const QRectF &rect = rects[i].normalized();
1579 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1580 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1581 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1586 if (s->penData.blend) {
1587 qreal width = s->pen.isCosmetic()
1588 ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF())
1589 : s->lastPen.widthF() * s->txscale;
1591 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1592 for (int i = 0; i < rectCount; ++i) {
1593 const QRectF &r = rects[i];
1595 qreal right = r.x() + r.width();
1597 qreal bottom = r.y() + r.height();
1598 qreal pts[] = { left, top,
1602 strokePolygonCosmetic((QPointF *) pts, 4, WindingMode);
1604 } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) {
1605 d->initializeRasterizer(&s->penData);
1607 for (int i = 0; i < rectCount; ++i) {
1608 const QRectF &rect = rects[i].normalized();
1609 if (rect.isEmpty()) {
1610 qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() };
1611 QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint);
1612 QPaintEngineEx::stroke(vp, s->lastPen);
1614 const QPointF tl = s->matrix.map(rect.topLeft());
1615 const QPointF tr = s->matrix.map(rect.topRight());
1616 const QPointF bl = s->matrix.map(rect.bottomLeft());
1617 const QPointF br = s->matrix.map(rect.bottomRight());
1618 const qreal w = width / (rect.width() * s->txscale);
1619 const qreal h = width / (rect.height() * s->txscale);
1620 d->rasterizer->rasterizeLine(tl, tr, w); // top
1621 d->rasterizer->rasterizeLine(bl, br, w); // bottom
1622 d->rasterizer->rasterizeLine(bl, tl, h); // left
1623 d->rasterizer->rasterizeLine(br, tr, h); // right
1627 for (int i = 0; i < rectCount; ++i) {
1628 const QRectF &r = rects[i];
1630 qreal right = r.x() + r.width();
1632 qreal bottom = r.y() + r.height();
1633 qreal pts[] = { left, top,
1638 QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
1639 QPaintEngineEx::stroke(vp, s->lastPen);
1646 #endif // QT_FAST_SPANS
1647 QPaintEngineEx::drawRects(rects, rectCount);
1654 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1656 QRasterPaintEngineState *s = state();
1658 if (!s->penData.blend)
1661 if (s->flags.fast_pen && !path.isCurved()
1662 && s->lastPen.brush().isOpaque()) {
1663 int count = path.elementCount();
1664 QPointF *points = (QPointF *) path.points();
1665 const QPainterPath::ElementType *types = path.elements();
1669 while (first < count) {
1670 while (first < count && types[first] != QPainterPath::MoveToElement) ++first;
1672 while (last < count && types[last] == QPainterPath::LineToElement) ++last;
1673 strokePolygonCosmetic(points + first, last - first,
1674 path.hasImplicitClose() && last == count // only close last one..
1680 strokePolygonCosmetic(points, count,
1681 path.hasImplicitClose()
1686 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1687 qreal width = s->lastPen.isCosmetic()
1688 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1689 : qpen_widthf(s->lastPen) * s->txscale;
1691 qreal dashOffset = s->lastPen.dashOffset();
1693 qreal patternLength = 0;
1694 const QVector<qreal> pattern = s->lastPen.dashPattern();
1695 for (int i = 0; i < pattern.size(); ++i)
1696 patternLength += pattern.at(i);
1698 if (patternLength > 0) {
1699 int n = qFloor(dashOffset / patternLength);
1700 dashOffset -= n * patternLength;
1701 while (dashOffset >= pattern.at(dashIndex)) {
1702 dashOffset -= pattern.at(dashIndex);
1703 if (++dashIndex >= pattern.size())
1709 Q_D(QRasterPaintEngine);
1710 d->initializeRasterizer(&s->penData);
1711 int lineCount = path.elementCount() / 2;
1712 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1714 for (int i = 0; i < lineCount; ++i) {
1715 if (lines[i].p1() == lines[i].p2()) {
1716 if (s->lastPen.capStyle() != Qt::FlatCap) {
1717 QPointF p = lines[i].p1();
1718 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1719 QPointF(p.x() + width*0.5, p.y())));
1720 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1725 const QLineF line = s->matrix.map(lines[i]);
1726 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1727 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1728 width / line.length(),
1729 s->lastPen.capStyle() == Qt::SquareCap);
1731 d->rasterizeLine_dashed(line, width,
1732 &dashIndex, &dashOffset, &inDash);
1737 QPaintEngineEx::stroke(path, pen);
1740 static inline QRect toNormalizedFillRect(const QRectF &rect)
1742 int x1 = qRound(rect.x());
1743 int y1 = qRound(rect.y());
1744 int x2 = qRound(rect.right());
1745 int y2 = qRound(rect.bottom());
1752 return QRect(x1, y1, x2 - x1, y2 - y1);
1758 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1762 #ifdef QT_DEBUG_DRAW
1763 QRectF rf = path.controlPointRect();
1764 qDebug() << "QRasterPaintEngine::fill(): "
1765 << "size=" << path.elementCount()
1766 << ", hints=" << hex << path.hints()
1770 Q_D(QRasterPaintEngine);
1771 QRasterPaintEngineState *s = state();
1774 if (!s->brushData.blend)
1777 if (path.shape() == QVectorPath::RectangleHint) {
1778 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1779 const qreal *p = path.points();
1780 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1781 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1782 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1786 if (s->flags.tx_noshear) {
1787 d->initializeRasterizer(&s->brushData);
1788 // ### Is normalizing really necessary here?
1789 const qreal *p = path.points();
1790 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1792 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1793 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1794 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1800 if (path.shape() == QVectorPath::EllipseHint) {
1801 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1802 const qreal *p = path.points();
1803 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1804 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1805 QRectF r = s->matrix.mapRect(QRectF(tl, br));
1807 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
1808 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
1809 const QRect brect = QRect(int(r.x()), int(r.y()),
1810 int_dim(r.x(), r.width()),
1811 int_dim(r.y(), r.height()));
1813 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
1814 &s->penData, &s->brushData);
1820 // ### Optimize for non transformed ellipses and rectangles...
1821 QRectF cpRect = path.controlPointRect();
1822 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1823 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1826 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1827 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1828 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1829 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1831 // ### Falonc: implement....
1832 // if (!s->flags.antialiased && !do_clip) {
1833 // d->initializeRasterizer(&s->brushData);
1834 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1838 ensureOutlineMapper();
1839 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1842 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1844 Q_D(QRasterPaintEngine);
1845 QRasterPaintEngineState *s = state();
1847 if (!s->flags.antialiased) {
1848 uint txop = s->matrix.type();
1849 if (txop == QTransform::TxNone) {
1850 fillRect_normalized(toNormalizedFillRect(r), data, d);
1852 } else if (txop == QTransform::TxTranslate) {
1853 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1854 fillRect_normalized(rr, data, d);
1856 } else if (txop == QTransform::TxScale) {
1857 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1858 fillRect_normalized(rr, data, d);
1863 if (s->flags.tx_noshear) {
1864 d->initializeRasterizer(data);
1865 QRectF nr = r.normalized();
1866 if (!nr.isEmpty()) {
1867 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1868 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1869 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1876 ensureOutlineMapper();
1877 fillPath(path, data);
1883 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1885 #ifdef QT_DEBUG_DRAW
1886 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1888 QRasterPaintEngineState *s = state();
1891 if (!s->brushData.blend)
1894 fillRect(r, &s->brushData);
1900 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1902 #ifdef QT_DEBUG_DRAW
1903 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1905 Q_D(QRasterPaintEngine);
1906 QRasterPaintEngineState *s = state();
1908 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1909 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1910 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1913 d->solid_color_filler.clip = d->clip();
1914 d->solid_color_filler.adjustSpanMethods();
1915 fillRect(r, &d->solid_color_filler);
1918 static inline bool isAbove(const QPointF *a, const QPointF *b)
1920 return a->y() < b->y();
1923 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1928 Q_ASSERT(pointCount >= 2);
1930 QVector<const QPointF *> sorted;
1931 sorted.reserve(pointCount);
1933 upper->reserve(pointCount * 3 / 4);
1934 lower->reserve(pointCount * 3 / 4);
1936 for (int i = 0; i < pointCount; ++i)
1937 sorted << points + i;
1939 qSort(sorted.begin(), sorted.end(), isAbove);
1941 qreal splitY = sorted.at(sorted.size() / 2)->y();
1943 const QPointF *end = points + pointCount;
1944 const QPointF *last = end - 1;
1946 QVector<QPointF> *bin[2] = { upper, lower };
1948 for (const QPointF *p = points; p < end; ++p) {
1949 int side = p->y() < splitY;
1950 int lastSide = last->y() < splitY;
1952 if (side != lastSide) {
1953 if (qFuzzyCompare(p->y(), splitY)) {
1954 bin[!side]->append(*p);
1955 } else if (qFuzzyCompare(last->y(), splitY)) {
1956 bin[side]->append(*last);
1958 QPointF delta = *p - *last;
1959 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1961 bin[0]->append(intersection);
1962 bin[1]->append(intersection);
1966 bin[side]->append(*p);
1971 // give up if we couldn't reduce the point count
1972 return upper->size() < pointCount && lower->size() < pointCount;
1978 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1980 Q_D(QRasterPaintEngine);
1981 QRasterPaintEngineState *s = state();
1983 const int maxPoints = 0xffff;
1985 // max amount of points that raster engine can reliably handle
1986 if (pointCount > maxPoints) {
1987 QVector<QPointF> upper, lower;
1989 if (splitPolygon(points, pointCount, &upper, &lower)) {
1990 fillPolygon(upper.constData(), upper.size(), mode);
1991 fillPolygon(lower.constData(), lower.size(), mode);
1993 qWarning("Polygon too complex for filling.");
1998 // Compose polygon fill..,
1999 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2000 ensureOutlineMapper();
2001 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
2004 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2006 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
2012 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2014 QRasterPaintEngineState *s = state();
2016 #ifdef QT_DEBUG_DRAW
2017 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
2018 for (int i=0; i<pointCount; ++i)
2019 qDebug() << " - " << points[i];
2021 Q_ASSERT(pointCount >= 2);
2023 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
2024 QRectF r(points[0], points[2]);
2031 if (mode != PolylineMode) {
2033 if (s->brushData.blend) {
2034 fillPolygon(points, pointCount, mode);
2038 // Do the outline...
2039 if (s->penData.blend) {
2040 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2041 strokePolygonCosmetic(points, pointCount, mode);
2043 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2044 QPaintEngineEx::stroke(vp, s->lastPen);
2052 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2054 Q_D(QRasterPaintEngine);
2055 QRasterPaintEngineState *s = state();
2057 #ifdef QT_DEBUG_DRAW
2058 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
2059 for (int i=0; i<pointCount; ++i)
2060 qDebug() << " - " << points[i];
2062 Q_ASSERT(pointCount >= 2);
2063 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
2064 QRect r(points[0].x(),
2066 points[2].x() - points[0].x(),
2067 points[2].y() - points[0].y());
2074 if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) {
2075 // this calls the float version
2076 QPaintEngineEx::drawPolygon(points, pointCount, mode);
2081 if (mode != PolylineMode) {
2083 if (s->brushData.blend) {
2084 // Compose polygon fill..,
2085 ensureOutlineMapper();
2086 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2087 d->outlineMapper->moveTo(*points);
2088 const QPoint *p = points;
2089 const QPoint *ep = points + pointCount - 1;
2091 d->outlineMapper->lineTo(*(++p));
2093 d->outlineMapper->endOutline();
2096 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2098 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2102 // Do the outline...
2103 if (s->penData.blend) {
2104 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2105 strokePolygonCosmetic(points, pointCount, mode);
2107 int count = pointCount * 2;
2108 QVarLengthArray<qreal> fpoints(count);
2110 for (int i=0; i<count; i+=2) {
2111 fpoints[i] = ((int *) points)[i+1];
2112 fpoints[i+1] = ((int *) points)[i];
2115 for (int i=0; i<count; ++i)
2116 fpoints[i] = ((int *) points)[i];
2118 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2119 QPaintEngineEx::stroke(vp, s->lastPen);
2127 void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode)
2129 Q_D(QRasterPaintEngine);
2130 QRasterPaintEngineState *s = state();
2132 Q_ASSERT(s->penData.blend);
2133 Q_ASSERT(s->flags.fast_pen);
2135 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2137 // Use fast path for 0 width / trivial pens.
2139 devRect.set(d->deviceRect);
2141 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2142 ? LineDrawIncludeLastPixel
2144 int dashOffset = int(s->lastPen.dashOffset());
2146 // Draw all the line segments.
2147 for (int i=1; i<pointCount; ++i) {
2149 QPointF lp1 = points[i-1] * s->matrix;
2150 QPointF lp2 = points[i] * s->matrix;
2152 const QRectF brect(lp1, lp2);
2153 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2154 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2155 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2156 qFloor(lp2.x()), qFloor(lp2.y()),
2157 penBlend, &s->penData,
2158 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2161 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2162 qFloor(lp2.x()), qFloor(lp2.y()),
2164 penBlend, &s->penData,
2165 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2166 devRect, &dashOffset);
2170 // Polygons are implicitly closed.
2171 if (needs_closing) {
2172 QPointF lp1 = points[pointCount-1] * s->matrix;
2173 QPointF lp2 = points[0] * s->matrix;
2175 const QRectF brect(lp1, lp2);
2176 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2177 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2178 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2179 qFloor(lp2.x()), qFloor(lp2.y()),
2180 penBlend, &s->penData,
2181 LineDrawIncludeLastPixel,
2184 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2185 qFloor(lp2.x()), qFloor(lp2.y()),
2187 penBlend, &s->penData,
2188 LineDrawIncludeLastPixel,
2189 devRect, &dashOffset);
2198 void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode)
2200 Q_D(QRasterPaintEngine);
2201 QRasterPaintEngineState *s = state();
2203 // We assert here because this function is called from drawRects
2204 // and drawPolygon and they already do ensurePen(), so we skip that
2205 // here to avoid duplicate checks..
2206 Q_ASSERT(s->penData.blend);
2208 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2211 devRect.set(d->deviceRect);
2213 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2214 ? LineDrawIncludeLastPixel
2217 int m11 = int(s->matrix.m11());
2218 int m22 = int(s->matrix.m22());
2219 int dx = int(s->matrix.dx());
2220 int dy = int(s->matrix.dy());
2221 int m13 = int(s->matrix.m13());
2222 int m23 = int(s->matrix.m23());
2223 bool affine = !m13 && !m23;
2225 int dashOffset = int(s->lastPen.dashOffset());
2228 // Draw all the line segments.
2229 for (int i=1; i<pointCount; ++i) {
2230 const QPoint lp1 = points[i-1] * s->matrix;
2231 const QPoint lp2 = points[i] * s->matrix;
2232 const QRect brect(lp1, lp2);
2233 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2235 if (qpen_style(s->lastPen) == Qt::SolidLine)
2236 drawLine_midpoint_i(lp1.x(), lp1.y(),
2238 penBlend, &s->penData,
2239 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2242 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2245 penBlend, &s->penData,
2246 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2247 devRect, &dashOffset);
2251 // Polygons are implicitly closed.
2252 if (needs_closing) {
2253 const QPoint lp1 = points[pointCount - 1] * s->matrix;
2254 const QPoint lp2 = points[0] * s->matrix;
2255 const QRect brect(lp1, lp2);
2256 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2258 if (qpen_style(s->lastPen) == Qt::SolidLine)
2259 drawLine_midpoint_i(lp1.x(), lp1.y(),
2261 penBlend, &s->penData, LineDrawIncludeLastPixel,
2264 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2267 penBlend, &s->penData, LineDrawIncludeLastPixel,
2268 devRect, &dashOffset);
2271 // Draw all the line segments.
2272 for (int i=1; i<pointCount; ++i) {
2273 int x1 = points[i-1].x() * m11 + dx;
2274 int y1 = points[i-1].y() * m22 + dy;
2275 qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.;
2279 int x2 = points[i].x() * m11 + dx;
2280 int y2 = points[i].y() * m22 + dy;
2281 w = m13*points[i].x() + m23*points[i].y() + 1.;
2286 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2287 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2288 if (qpen_style(s->lastPen) == Qt::SolidLine)
2289 drawLine_midpoint_i(x1, y1, x2, y2,
2290 penBlend, &s->penData,
2291 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2294 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2296 penBlend, &s->penData,
2297 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2298 devRect, &dashOffset);
2302 int x1 = points[pointCount-1].x() * m11 + dx;
2303 int y1 = points[pointCount-1].y() * m22 + dy;
2304 qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.;
2308 int x2 = points[0].x() * m11 + dx;
2309 int y2 = points[0].y() * m22 + dy;
2310 w = m13*points[0].x() + m23*points[0].y() + 1.;
2314 // Polygons are implicitly closed.
2316 if (needs_closing) {
2317 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2318 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2319 if (qpen_style(s->lastPen) == Qt::SolidLine)
2320 drawLine_midpoint_i(x1, y1, x2, y2,
2321 penBlend, &s->penData, LineDrawIncludeLastPixel,
2324 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2326 penBlend, &s->penData, LineDrawIncludeLastPixel,
2327 devRect, &dashOffset);
2335 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2337 #ifdef QT_DEBUG_DRAW
2338 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2341 QPixmapData *pd = pixmap.pixmapData();
2342 if (pd->classId() == QPixmapData::RasterClass) {
2343 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2344 if (image.depth() == 1) {
2345 Q_D(QRasterPaintEngine);
2346 QRasterPaintEngineState *s = state();
2347 if (s->matrix.type() <= QTransform::TxTranslate) {
2349 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2351 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2354 QRasterPaintEngine::drawImage(pos, image);
2357 const QImage image = pixmap.toImage();
2358 if (pixmap.depth() == 1) {
2359 Q_D(QRasterPaintEngine);
2360 QRasterPaintEngineState *s = state();
2361 if (s->matrix.type() <= QTransform::TxTranslate) {
2363 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2365 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2368 QRasterPaintEngine::drawImage(pos, image);
2376 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2378 #ifdef QT_DEBUG_DRAW
2379 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2382 QPixmapData* pd = pixmap.pixmapData();
2383 if (pd->classId() == QPixmapData::RasterClass) {
2384 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2385 if (image.depth() == 1) {
2386 Q_D(QRasterPaintEngine);
2387 QRasterPaintEngineState *s = state();
2388 if (s->matrix.type() <= QTransform::TxTranslate
2389 && r.size() == sr.size()
2390 && r.size() == pixmap.size()) {
2392 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2395 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2398 drawImage(r, image, sr);
2401 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2402 const QImage image = pd->toImage(clippedSource);
2403 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2404 if (image.depth() == 1) {
2405 Q_D(QRasterPaintEngine);
2406 QRasterPaintEngineState *s = state();
2407 if (s->matrix.type() <= QTransform::TxTranslate
2408 && r.size() == sr.size()
2409 && r.size() == pixmap.size()) {
2411 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2414 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2417 drawImage(r, image, translatedSource);
2422 // assumes that rect has positive width and height
2423 static inline const QRect toRect_normalized(const QRectF &rect)
2425 const int x = qRound(rect.x());
2426 const int y = qRound(rect.y());
2427 const int w = int(rect.width() + qreal(0.5));
2428 const int h = int(rect.height() + qreal(0.5));
2430 return QRect(x, y, w, h);
2433 static inline int fast_ceil_positive(const qreal &v)
2435 const int iv = int(v);
2442 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2444 const int xmin = int(rect.x());
2445 const int xmax = int(fast_ceil_positive(rect.right()));
2446 const int ymin = int(rect.y());
2447 const int ymax = int(fast_ceil_positive(rect.bottom()));
2448 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2454 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2456 #ifdef QT_DEBUG_DRAW
2457 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2460 Q_D(QRasterPaintEngine);
2461 QRasterPaintEngineState *s = state();
2463 if (s->matrix.type() > QTransform::TxTranslate) {
2464 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2466 QRectF(0, 0, img.width(), img.height()));
2469 const QClipData *clip = d->clip();
2470 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2472 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2473 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2476 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2478 } else if (clip->hasRectClip) {
2479 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2487 d->image_filler.clip = clip;
2488 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2489 if (!d->image_filler.blend)
2491 d->image_filler.dx = -pt.x();
2492 d->image_filler.dy = -pt.y();
2493 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2495 fillRect_normalized(rr, &d->image_filler, d);
2500 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2502 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2513 inline RotationType qRotationType(const QTransform &transform)
2515 QTransform::TransformationType type = transform.type();
2517 if (type > QTransform::TxRotate)
2520 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2521 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2524 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2525 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2528 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2529 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2535 inline bool isPixelAligned(const QRectF &rect) {
2536 return QRectF(rect.toRect()) == rect;
2543 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2544 Qt::ImageConversionFlags)
2546 #ifdef QT_DEBUG_DRAW
2547 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2553 Q_D(QRasterPaintEngine);
2554 QRasterPaintEngineState *s = state();
2555 int sr_l = qFloor(sr.left());
2556 int sr_r = qCeil(sr.right()) - 1;
2557 int sr_t = qFloor(sr.top());
2558 int sr_b = qCeil(sr.bottom()) - 1;
2560 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2561 QTransform old = s->matrix;
2563 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2564 QRgb color = img.pixel(sr_l, sr_t);
2565 switch (img.format()) {
2566 case QImage::Format_ARGB32_Premultiplied:
2567 case QImage::Format_ARGB8565_Premultiplied:
2568 case QImage::Format_ARGB6666_Premultiplied:
2569 case QImage::Format_ARGB8555_Premultiplied:
2570 case QImage::Format_ARGB4444_Premultiplied:
2571 // Combine premultiplied color with the opacity set on the painter.
2572 d->solid_color_filler.solid.color =
2573 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2574 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2577 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2581 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2582 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2586 d->solid_color_filler.clip = d->clip();
2587 d->solid_color_filler.adjustSpanMethods();
2588 fillRect(r, &d->solid_color_filler);
2594 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2596 const QClipData *clip = d->clip();
2598 if (s->matrix.type() > QTransform::TxTranslate
2600 && (!clip || clip->hasRectClip)
2601 && s->intOpacity == 256
2602 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2603 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2604 && d->rasterBuffer->format == img.format()
2605 && (d->rasterBuffer->format == QImage::Format_RGB16
2606 || d->rasterBuffer->format == QImage::Format_RGB32
2607 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2608 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2610 RotationType rotationType = qRotationType(s->matrix);
2612 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2613 QRectF transformedTargetRect = s->matrix.mapRect(r);
2615 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2616 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2618 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2619 if (clippedTransformedTargetRect.isNull())
2622 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2624 QRect clippedSourceRect
2625 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2626 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2628 uint dbpl = d->rasterBuffer->bytesPerLine();
2629 uint sbpl = img.bytesPerLine();
2631 uchar *dst = d->rasterBuffer->buffer();
2632 uint bpp = img.depth() >> 3;
2634 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2635 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2637 uint cw = clippedSourceRect.width();
2638 uint ch = clippedSourceRect.height();
2640 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2647 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2649 QRectF targetBounds = s->matrix.mapRect(r);
2650 bool exceedsPrecision = targetBounds.width() > 0xffff
2651 || targetBounds.height() > 0xffff;
2653 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2654 if (s->matrix.type() > QTransform::TxScale) {
2655 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2656 if (func && (!clip || clip->hasRectClip)) {
2657 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2658 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2659 s->matrix, s->intOpacity);
2663 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2664 if (func && (!clip || clip->hasRectClip)) {
2665 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2666 img.bits(), img.bytesPerLine(),
2667 qt_mapRect_non_normalizing(r, s->matrix), sr,
2668 !clip ? d->deviceRect : clip->clipRect,
2675 QTransform copy = s->matrix;
2676 copy.translate(r.x(), r.y());
2678 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2679 copy.translate(-sr.x(), -sr.y());
2681 d->image_filler_xform.clip = clip;
2682 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2683 if (!d->image_filler_xform.blend)
2685 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2687 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2688 QRectF rr = s->matrix.mapRect(r);
2690 const int x1 = qRound(rr.x());
2691 const int y1 = qRound(rr.y());
2692 const int x2 = qRound(rr.right());
2693 const int y2 = qRound(rr.bottom());
2695 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2699 #ifdef QT_FAST_SPANS
2701 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2702 d->initializeRasterizer(&d->image_filler_xform);
2703 d->rasterizer->setAntialiased(s->flags.antialiased);
2705 const QRectF &rect = r.normalized();
2706 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2707 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2709 if (s->flags.tx_noshear)
2710 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2712 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2718 QTransform m = s->matrix;
2719 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2720 m.m21(), m.m22(), m.m23(),
2721 m.m31(), m.m32(), m.m33());
2722 fillPath(path, &d->image_filler_xform);
2725 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2726 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2728 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2730 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2732 } else if (clip->hasRectClip) {
2733 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2739 d->image_filler.clip = clip;
2740 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2741 if (!d->image_filler.blend)
2743 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2744 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2747 rr.translate(s->matrix.dx(), s->matrix.dy());
2749 const int x1 = qRound(rr.x());
2750 const int y1 = qRound(rr.y());
2751 const int x2 = qRound(rr.right());
2752 const int y2 = qRound(rr.bottom());
2754 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2761 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2763 #ifdef QT_DEBUG_DRAW
2764 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2766 Q_D(QRasterPaintEngine);
2767 QRasterPaintEngineState *s = state();
2771 QPixmapData *pd = pixmap.pixmapData();
2772 if (pd->classId() == QPixmapData::RasterClass) {
2773 image = static_cast<QRasterPixmapData *>(pd)->image;
2775 image = pixmap.toImage();
2778 if (image.depth() == 1)
2779 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2781 if (s->matrix.type() > QTransform::TxTranslate) {
2782 QTransform copy = s->matrix;
2783 copy.translate(r.x(), r.y());
2784 copy.translate(-sr.x(), -sr.y());
2785 d->image_filler_xform.clip = d->clip();
2786 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2787 if (!d->image_filler_xform.blend)
2789 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2791 #ifdef QT_FAST_SPANS
2793 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2794 d->initializeRasterizer(&d->image_filler_xform);
2795 d->rasterizer->setAntialiased(s->flags.antialiased);
2797 const QRectF &rect = r.normalized();
2798 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2799 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2800 if (s->flags.tx_noshear)
2801 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2803 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2809 fillPath(path, &d->image_filler_xform);
2811 d->image_filler.clip = d->clip();
2813 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2814 if (!d->image_filler.blend)
2816 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2817 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2820 rr.translate(s->matrix.dx(), s->matrix.dy());
2821 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2827 static inline bool monoVal(const uchar* s, int x)
2829 return (s[x>>3] << (x&7)) & 0x80;
2835 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2837 Q_D(QRasterPaintEngine);
2838 QRasterPaintEngineState *s = state();
2840 if (!s->penData.blend)
2843 QRasterBuffer *rb = d->rasterBuffer.data();
2845 const QRect rect(rx, ry, w, h);
2846 const QClipData *clip = d->clip();
2847 bool unclipped = false;
2849 // inlined QRect::intersects
2850 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2851 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2853 if (clip->hasRectClip) {
2854 unclipped = rx > clip->xmin
2855 && rx + w < clip->xmax
2857 && ry + h < clip->ymax;
2863 // inlined QRect::intersects
2864 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2865 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2869 // inlined QRect::contains
2870 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2871 && rect.top() >= 0 && rect.bottom() < rb->height();
2873 unclipped = contains && d->isUnclipped_normalized(rect);
2876 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2877 const uchar * scanline = static_cast<const uchar *>(src);
2879 if (s->flags.fast_text) {
2882 if (s->penData.bitmapBlit) {
2883 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2884 scanline, w, h, bpl);
2887 } else if (depth == 8) {
2888 if (s->penData.alphamapBlit) {
2889 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2890 scanline, w, h, bpl, 0);
2893 } else if (depth == 32) {
2894 // (A)RGB Alpha mask where the alpha component is not used.
2895 if (s->penData.alphaRGBBlit) {
2896 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2897 (const uint *) scanline, w, h, bpl / 4, 0);
2901 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2902 // (A)RGB Alpha mask where the alpha component is not used.
2904 int nx = qMax(0, rx);
2905 int ny = qMax(0, ry);
2907 // Move scanline pointer to compensate for moved x and y
2908 int xdiff = nx - rx;
2909 int ydiff = ny - ry;
2910 scanline += ydiff * bpl;
2911 scanline += xdiff * (depth == 32 ? 4 : 1);
2916 if (nx + w > d->rasterBuffer->width())
2917 w = d->rasterBuffer->width() - nx;
2918 if (ny + h > d->rasterBuffer->height())
2919 h = d->rasterBuffer->height() - ny;
2924 if (depth == 8 && s->penData.alphamapBlit) {
2925 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2926 scanline, w, h, bpl, clip);
2927 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2928 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2929 (const uint *) scanline, w, h, bpl / 4, clip);
2944 scanline += bpl * y0;
2948 w = qMin(w, rb->width() - qMax(0, rx));
2949 h = qMin(h, rb->height() - qMax(0, ry));
2951 if (w <= 0 || h <= 0)
2954 const int NSPANS = 256;
2955 QSpan spans[NSPANS];
2958 const int x1 = x0 + w;
2959 const int y1 = y0 + h;
2962 for (int y = y0; y < y1; ++y) {
2963 for (int x = x0; x < x1; ) {
2964 if (!monoVal(scanline, x)) {
2969 if (current == NSPANS) {
2970 blend(current, spans, &s->penData);
2973 spans[current].x = x + rx;
2974 spans[current].y = y + ry;
2975 spans[current].coverage = 255;
2978 // extend span until we find a different one.
2979 while (x < x1 && monoVal(scanline, x)) {
2983 spans[current].len = len;
2988 } else if (depth == 8) {
2989 for (int y = y0; y < y1; ++y) {
2990 for (int x = x0; x < x1; ) {
2991 // Skip those with 0 coverage
2992 if (scanline[x] == 0) {
2997 if (current == NSPANS) {
2998 blend(current, spans, &s->penData);
3001 int coverage = scanline[x];
3002 spans[current].x = x + rx;
3003 spans[current].y = y + ry;
3004 spans[current].coverage = coverage;
3008 // extend span until we find a different one.
3009 while (x < x1 && scanline[x] == coverage) {
3013 spans[current].len = len;
3018 } else { // 32-bit alpha...
3019 uint *sl = (uint *) src;
3020 for (int y = y0; y < y1; ++y) {
3021 for (int x = x0; x < x1; ) {
3022 // Skip those with 0 coverage
3023 if ((sl[x] & 0x00ffffff) == 0) {
3028 if (current == NSPANS) {
3029 blend(current, spans, &s->penData);
3032 uint rgbCoverage = sl[x];
3033 int coverage = qGreen(rgbCoverage);
3034 spans[current].x = x + rx;
3035 spans[current].y = y + ry;
3036 spans[current].coverage = coverage;
3040 // extend span until we find a different one.
3041 while (x < x1 && sl[x] == rgbCoverage) {
3045 spans[current].len = len;
3048 sl += bpl / sizeof(uint);
3051 // qDebug() << "alphaPenBlt: num spans=" << current
3052 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
3053 // Call span func for current set of spans.
3055 blend(current, spans, &s->penData);
3058 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
3059 const QFixedPoint *positions, QFontEngine *fontEngine)
3061 Q_D(QRasterPaintEngine);
3062 QRasterPaintEngineState *s = state();
3064 #if !defined(QT_NO_FREETYPE)
3065 if (fontEngine->type() == QFontEngine::Freetype) {
3066 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3067 QFontEngineFT::GlyphFormat neededFormat =
3068 painter()->device()->devType() == QInternal::Widget
3069 ? fe->defaultGlyphFormat()
3070 : QFontEngineFT::Format_A8;
3072 if (d_func()->mono_surface
3073 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
3075 neededFormat = QFontEngineFT::Format_Mono;
3077 if (neededFormat == QFontEngineFT::Format_None)
3078 neededFormat = QFontEngineFT::Format_A8;
3080 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
3081 if (s->matrix.type() >= QTransform::TxScale) {
3082 if (s->matrix.isAffine())
3083 gset = fe->loadTransformedGlyphSet(s->matrix);
3088 if (!gset || gset->outline_drawing
3089 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
3092 FT_Face lockedFace = 0;
3095 switch (neededFormat) {
3096 case QFontEngineFT::Format_Mono:
3099 case QFontEngineFT::Format_A8:
3102 case QFontEngineFT::Format_A32:
3110 for (int i = 0; i < numGlyphs; i++) {
3111 QFixed spp = fe->subPixelPositionForX(positions[i].x);
3112 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
3114 if (!glyph || glyph->format != neededFormat) {
3116 lockedFace = fe->lockFace();
3117 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
3120 if (!glyph || !glyph->data)
3124 switch (neededFormat) {
3125 case QFontEngineFT::Format_Mono:
3126 pitch = ((glyph->width + 31) & ~31) >> 3;
3128 case QFontEngineFT::Format_A8:
3129 pitch = (glyph->width + 3) & ~3;
3131 case QFontEngineFT::Format_A32:
3132 pitch = glyph->width * 4;
3139 alphaPenBlt(glyph->data, pitch, depth,
3140 qFloor(positions[i].x) + glyph->x,
3141 qFloor(positions[i].y) - glyph->y,
3142 glyph->width, glyph->height);
3149 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
3151 QImageTextureGlyphCache *cache =
3152 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
3154 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
3155 fontEngine->setGlyphCache(0, cache);
3158 cache->populate(fontEngine, numGlyphs, glyphs, positions);
3159 cache->fillInPendingGlyphs();
3161 const QImage &image = cache->image();
3162 int bpl = image.bytesPerLine();
3164 int depth = image.depth();
3168 leftShift = 2; // multiply by 4
3169 else if (depth == 1)
3170 rightShift = 3; // divide by 8
3172 int margin = cache->glyphMargin();
3173 const uchar *bits = image.bits();
3174 for (int i=0; i<numGlyphs; ++i) {
3176 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
3177 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
3178 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
3182 int x = qFloor(positions[i].x) + c.baseLineX - margin;
3183 int y = qFloor(positions[i].y) - c.baseLineY - margin;
3185 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
3188 // c.baseLineX, c.baseLineY,
3191 // positions[i].x.toInt(), positions[i].y.toInt());
3193 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
3199 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
3200 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
3202 Q_D(QRasterPaintEngine);
3203 QRasterPaintEngineState *s = state();
3205 QFontEngine *fontEngine = ti.fontEngine;
3206 if (fontEngine->type() != QFontEngine::S60FontEngine) {
3207 QPaintEngineEx::drawTextItem(p, ti);
3211 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
3213 QVarLengthArray<QFixedPoint> positions;
3214 QVarLengthArray<glyph_t> glyphs;
3215 QTransform matrix = s->matrix;
3216 matrix.translate(p.x(), p.y());
3217 if (matrix.type() == QTransform::TxScale)
3218 fe->setFontScale(matrix.m11());
3219 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3221 for (int i=0; i<glyphs.size(); ++i) {
3222 TOpenFontCharMetrics tmetrics;
3223 const TUint8 *glyphBitmapBytes;
3224 TSize glyphBitmapSize;
3225 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
3226 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
3227 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
3228 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
3231 if (matrix.type() == QTransform::TxScale)
3232 fe->setFontScale(1.0);
3236 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
3239 * Returns true if the rectangle is completely within the current clip
3240 * state of the paint engine.
3242 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
3244 const QClipData *cl = clip();
3246 // inline contains() for performance (we know the rects are normalized)
3247 const QRect &r1 = deviceRect;
3248 return (r.left() >= r1.left() && r.right() <= r1.right()
3249 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3253 if (cl->hasRectClip) {
3254 // currently all painting functions clips to deviceRect internally
3255 if (cl->clipRect == deviceRect)
3258 // inline contains() for performance (we know the rects are normalized)
3259 const QRect &r1 = cl->clipRect;
3260 return (r.left() >= r1.left() && r.right() <= r1.right()
3261 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3263 return qt_region_strictContains(cl->clipRegion, r);
3267 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
3270 Q_Q(const QRasterPaintEngine);
3271 const QRasterPaintEngineState *s = q->state();
3272 const QClipData *cl = clip();
3274 QRect r = rect.normalized();
3275 // inline contains() for performance (we know the rects are normalized)
3276 const QRect &r1 = deviceRect;
3277 return (r.left() >= r1.left() && r.right() <= r1.right()
3278 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3282 // currently all painting functions that call this function clip to deviceRect internally
3283 if (cl->hasRectClip && cl->clipRect == deviceRect)
3286 if (s->flags.antialiased)
3289 QRect r = rect.normalized();
3291 r.setX(r.x() - penWidth);
3292 r.setY(r.y() - penWidth);
3293 r.setWidth(r.width() + 2 * penWidth);
3294 r.setHeight(r.height() + 2 * penWidth);
3297 if (cl->hasRectClip) {
3298 // inline contains() for performance (we know the rects are normalized)
3299 const QRect &r1 = cl->clipRect;
3300 return (r.left() >= r1.left() && r.right() <= r1.right()
3301 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3303 return qt_region_strictContains(cl->clipRegion, r);
3307 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3310 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3314 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3315 const QSpanData *data) const
3317 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3321 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3322 const QSpanData *data) const
3324 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3328 QRasterPaintEnginePrivate::getPenFunc(const QRect &rect,
3329 const QSpanData *data) const
3331 Q_Q(const QRasterPaintEngine);
3332 const QRasterPaintEngineState *s = q->state();
3334 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3336 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF());
3337 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3341 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3342 const QSpanData *data) const
3344 Q_Q(const QRasterPaintEngine);
3345 const QRasterPaintEngineState *s = q->state();
3347 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3349 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3350 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3356 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3361 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3362 textItem->fontEngine());
3368 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3370 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3371 QRasterPaintEngineState *s = state();
3373 #ifdef QT_DEBUG_DRAW
3374 Q_D(QRasterPaintEngine);
3375 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3376 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3383 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3385 bool drawCached = true;
3387 if (s->matrix.type() >= QTransform::TxProject)
3390 // don't try to cache huge fonts
3391 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3392 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3395 // ### Remove the TestFontEngine and Box engine crap, in these
3396 // ### cases we should delegate painting to the font engine
3399 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3400 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3401 // qDebug() << "type" << fontEngineType << s->matrix.type();
3402 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3403 || (s->matrix.type() <= QTransform::TxTranslate
3404 && (fontEngineType == QFontEngine::TestFontEngine
3405 || fontEngineType == QFontEngine::Box))) {
3409 if (s->matrix.type() > QTransform::TxTranslate)
3413 QRasterPaintEngineState *s = state();
3415 QVarLengthArray<QFixedPoint> positions;
3416 QVarLengthArray<glyph_t> glyphs;
3418 QTransform matrix = s->matrix;
3419 matrix.translate(p.x(), p.y());
3421 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3423 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3427 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3428 if (s->matrix.type() <= QTransform::TxTranslate
3429 || (s->matrix.type() == QTransform::TxScale
3430 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3431 drawGlyphsS60(p, ti);
3434 #else // Q_WS_WIN || Q_WS_MAC
3436 QFontEngine *fontEngine = ti.fontEngine;
3439 if (s->matrix.type() < QTransform::TxScale) {
3441 QVarLengthArray<QFixedPoint> positions;
3442 QVarLengthArray<glyph_t> glyphs;
3443 QTransform matrix = state()->transform();
3445 qreal _x = qFloor(p.x());
3446 qreal _y = qFloor(p.y());
3447 matrix.translate(_x, _y);
3449 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3450 if (glyphs.size() == 0)
3453 for(int i = 0; i < glyphs.size(); i++) {
3454 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3455 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3456 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3457 qRound(positions[i].x + metrics.x),
3458 qRound(positions[i].y + metrics.y),
3459 img.width(), img.height());
3465 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3467 if (fontEngine->type() != QFontEngine::Freetype) {
3468 QPaintEngineEx::drawTextItem(p, ti);
3472 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3474 QTransform matrix = s->matrix;
3475 matrix.translate(p.x(), p.y());
3477 QVarLengthArray<QFixedPoint> positions;
3478 QVarLengthArray<glyph_t> glyphs;
3479 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3480 if (glyphs.size() == 0)
3483 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3484 QPaintEngine::drawTextItem(p, ti);
3490 QPaintEngineEx::drawTextItem(p, ti);
3496 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3498 Q_D(QRasterPaintEngine);
3499 QRasterPaintEngineState *s = state();
3502 qreal pw = s->lastPen.widthF();
3503 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3504 QPaintEngineEx::drawPoints(points, pointCount);
3507 if (!s->penData.blend)
3510 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3511 QT_FT_Span span = { 0, 1, 0, 255 };
3512 const QPointF *end = points + pointCount;
3513 qreal trans_x, trans_y;
3515 int left = d->deviceRect.x();
3516 int right = left + d->deviceRect.width();
3517 int top = d->deviceRect.y();
3518 int bottom = top + d->deviceRect.height();
3520 while (points < end) {
3521 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3522 x = qFloor(trans_x);
3523 y = qFloor(trans_y);
3524 if (x >= left && x < right && y >= top && y < bottom) {
3526 const QT_FT_Span &last = array[count - 1];
3527 // spans must be sorted on y (primary) and x (secondary)
3528 if (y < last.y || (y == last.y && x < last.x)) {
3529 s->penData.blend(count, array.constData(), &s->penData);
3536 array[count++] = span;
3542 s->penData.blend(count, array.constData(), &s->penData);
3547 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3549 Q_D(QRasterPaintEngine);
3550 QRasterPaintEngineState *s = state();
3553 double pw = s->lastPen.widthF();
3554 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3555 QPaintEngineEx::drawPoints(points, pointCount);
3558 if (!s->penData.blend)
3561 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3562 QT_FT_Span span = { 0, 1, 0, 255 };
3563 const QPoint *end = points + pointCount;
3564 qreal trans_x, trans_y;
3566 int left = d->deviceRect.x();
3567 int right = left + d->deviceRect.width();
3568 int top = d->deviceRect.y();
3569 int bottom = top + d->deviceRect.height();
3571 while (points < end) {
3572 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3573 x = qFloor(trans_x);
3574 y = qFloor(trans_y);
3575 if (x >= left && x < right && y >= top && y < bottom) {
3577 const QT_FT_Span &last = array[count - 1];
3578 // spans must be sorted on y (primary) and x (secondary)
3579 if (y < last.y || (y == last.y && x < last.x)) {
3580 s->penData.blend(count, array.constData(), &s->penData);
3587 array[count++] = span;
3593 s->penData.blend(count, array.constData(), &s->penData);
3600 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3602 #ifdef QT_DEBUG_DRAW
3603 qDebug() << " - QRasterPaintEngine::drawLine()";
3605 Q_D(QRasterPaintEngine);
3606 QRasterPaintEngineState *s = state();
3609 if (s->flags.fast_pen) {
3610 QIntRect bounds; bounds.set(d->deviceRect);
3611 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3613 : LineDrawIncludeLastPixel;
3615 int m11 = int(s->matrix.m11());
3616 int m22 = int(s->matrix.m22());
3617 int dx = qFloor(s->matrix.dx());
3618 int dy = qFloor(s->matrix.dy());
3619 for (int i=0; i<lineCount; ++i) {
3620 int dashOffset = int(s->lastPen.dashOffset());
3621 if (s->flags.int_xform) {
3622 const QLine &l = lines[i];
3623 int x1 = l.x1() * m11 + dx;
3624 int y1 = l.y1() * m22 + dy;
3625 int x2 = l.x2() * m11 + dx;
3626 int y2 = l.y2() * m22 + dy;
3628 const QRect brect(QPoint(x1, y1), QPoint(x2, y2));
3629 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3630 if (qpen_style(s->lastPen) == Qt::SolidLine)
3631 drawLine_midpoint_i(x1, y1, x2, y2,
3632 penBlend, &s->penData, mode, bounds);
3634 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
3635 &s->lastPen, penBlend,
3636 &s->penData, mode, bounds,
3639 QLineF line = lines[i] * s->matrix;
3640 const QRectF brect(QPointF(line.x1(), line.y1()),
3641 QPointF(line.x2(), line.y2()));
3642 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3643 if (qpen_style(s->lastPen) == Qt::SolidLine)
3644 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3645 int(line.x2()), int(line.y2()),
3646 penBlend, &s->penData, mode, bounds);
3648 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3649 int(line.x2()), int(line.y2()),
3650 &s->lastPen, penBlend,
3651 &s->penData, mode, bounds,
3655 } else if (s->penData.blend) {
3656 QPaintEngineEx::drawLines(lines, lineCount);
3660 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3666 Q_Q(QRasterPaintEngine);
3667 QRasterPaintEngineState *s = q->state();
3669 const QPen &pen = s->lastPen;
3670 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3671 const QVector<qreal> pattern = pen.dashPattern();
3673 qreal patternLength = 0;
3674 for (int i = 0; i < pattern.size(); ++i)
3675 patternLength += pattern.at(i);
3677 if (patternLength <= 0)
3680 qreal length = line.length();
3681 Q_ASSERT(length > 0);
3682 while (length > 0) {
3683 const bool rasterize = *inDash;
3684 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3687 if (dash >= length) {
3689 *dashOffset += dash / width;
3693 *inDash = !(*inDash);
3694 if (++*dashIndex >= pattern.size())
3701 if (rasterize && dash > 0)
3702 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3709 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3711 #ifdef QT_DEBUG_DRAW
3712 qDebug() << " - QRasterPaintEngine::drawLine()";
3714 Q_D(QRasterPaintEngine);
3715 QRasterPaintEngineState *s = state();
3718 if (!s->penData.blend)
3720 if (s->flags.fast_pen) {
3722 bounds.set(d->deviceRect);
3723 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3725 : LineDrawIncludeLastPixel;
3727 for (int i=0; i<lineCount; ++i) {
3728 int dashOffset = int(s->lastPen.dashOffset());
3729 QLineF line = lines[i] * s->matrix;
3730 const QRectF brect(QPointF(line.x1(), line.y1()),
3731 QPointF(line.x2(), line.y2()));
3732 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3733 if (qpen_style(s->lastPen) == Qt::SolidLine)
3734 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3735 int(line.x2()), int(line.y2()),
3736 penBlend, &s->penData, mode, bounds);
3738 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3739 int(line.x2()), int(line.y2()),
3741 penBlend, &s->penData, mode,
3742 bounds, &dashOffset);
3745 QPaintEngineEx::drawLines(lines, lineCount);
3753 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3755 Q_D(QRasterPaintEngine);
3756 QRasterPaintEngineState *s = state();
3759 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3760 || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased))
3761 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3763 && s->matrix.type() <= QTransform::TxScale) // no shear
3766 const QRectF r = s->matrix.mapRect(rect);
3767 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3768 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3769 const QRect brect = QRect(int(r.x()), int(r.y()),
3770 int_dim(r.x(), r.width()),
3771 int_dim(r.y(), r.height()));
3773 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3774 &s->penData, &s->brushData);
3778 QPaintEngineEx::drawEllipse(rect);
3785 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3787 Q_D(QRasterPaintEngine);
3794 CGContextRef QRasterPaintEngine::getCGContext() const
3796 Q_D(const QRasterPaintEngine);
3797 return d->cgContext;
3805 void QRasterPaintEngine::setDC(HDC hdc) {
3806 Q_D(QRasterPaintEngine);
3813 HDC QRasterPaintEngine::getDC() const
3815 Q_D(const QRasterPaintEngine);
3822 void QRasterPaintEngine::releaseDC(HDC) const
3831 QPoint QRasterPaintEngine::coordinateOffset() const
3833 return QPoint(0, 0);
3836 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3841 Q_D(QRasterPaintEngine);
3843 Q_ASSERT(image.depth() == 1);
3845 const int spanCount = 256;
3846 QT_FT_Span spans[spanCount];
3850 int w = image.width();
3851 int h = image.height();
3852 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3853 int ymin = qMax(qRound(pos.y()), 0);
3854 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3855 int xmin = qMax(qRound(pos.x()), 0);
3857 int x_offset = xmin - qRound(pos.x());
3859 QImage::Format format = image.format();
3860 for (int y = ymin; y < ymax; ++y) {
3861 const uchar *src = image.scanLine(y - qRound(pos.y()));
3862 if (format == QImage::Format_MonoLSB) {
3863 for (int x = 0; x < xmax - xmin; ++x) {
3864 int src_x = x + x_offset;
3865 uchar pixel = src[src_x >> 3];
3870 if (pixel & (0x1 << (src_x & 7))) {
3871 spans[n].x = xmin + x;
3873 spans[n].coverage = 255;
3875 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3879 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3882 if (n == spanCount) {
3883 fg->blend(n, spans, fg);
3889 for (int x = 0; x < xmax - xmin; ++x) {
3890 int src_x = x + x_offset;
3891 uchar pixel = src[src_x >> 3];
3896 if (pixel & (0x80 >> (x & 7))) {
3897 spans[n].x = xmin + x;
3899 spans[n].coverage = 255;
3901 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3905 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3908 if (n == spanCount) {
3909 fg->blend(n, spans, fg);
3917 fg->blend(n, spans, fg);
3923 \enum QRasterPaintEngine::ClipType
3926 \value RectClip Indicates that the currently set clip is a single rectangle.
3927 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3932 Returns the type of the clip currently set.
3934 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3936 Q_D(const QRasterPaintEngine);
3938 const QClipData *clip = d->clip();
3939 if (!clip || clip->hasRectClip)
3947 Returns the bounding rect of the currently set clip.
3949 QRect QRasterPaintEngine::clipBoundingRect() const
3951 Q_D(const QRasterPaintEngine);
3953 const QClipData *clip = d->clip();
3956 return d->deviceRect;
3958 if (clip->hasRectClip)
3959 return clip->clipRect;
3961 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3964 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3966 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3968 QVarLengthArray<short, 4096> buffer;
3970 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3971 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3972 result->initialize();
3974 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3975 const QSpan *c1_spans = c1ClipLines[y].spans;
3976 int c1_count = c1ClipLines[y].count;
3977 const QSpan *c2_spans = c2ClipLines[y].spans;
3978 int c2_count = c2ClipLines[y].count;
3980 if (c1_count == 0 && c2_count == 0)
3982 if (c1_count == 0) {
3983 result->appendSpans(c2_spans, c2_count);
3985 } else if (c2_count == 0) {
3986 result->appendSpans(c1_spans, c1_count);
3990 // we need to merge the two
3992 // find required length
3993 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3994 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3996 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3998 // Fill with old spans.
3999 for (int i = 0; i < c1_count; ++i) {
4000 const QSpan *cs = c1_spans + i;
4001 for (int j=cs->x; j<cs->x + cs->len; ++j)
4002 buffer[j] = cs->coverage;
4005 // Fill with new spans
4006 for (int i = 0; i < c2_count; ++i) {
4007 const QSpan *cs = c2_spans + i;
4008 for (int j = cs->x; j < cs->x + cs->len; ++j) {
4009 buffer[j] += cs->coverage;
4010 if (buffer[j] > 255)
4018 // Skip to next span
4019 while (x < max && buffer[x] == 0) ++x;
4020 if (x >= max) break;
4023 int coverage = buffer[x];
4025 // Find length of span
4026 while (x < max && buffer[x] == coverage)
4029 result->appendSpan(sx, x - sx, y, coverage);
4034 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
4036 Q_Q(QRasterPaintEngine);
4037 QRasterPaintEngineState *s = q->state();
4039 rasterizer->setAntialiased(s->flags.antialiased);
4041 QRect clipRect(deviceRect);
4043 // ### get from optimized rectbased QClipData
4045 const QClipData *c = clip();
4047 const QRect r(QPoint(c->xmin, c->ymin),
4048 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
4049 clipRect = clipRect.intersected(r);
4050 blend = data->blend;
4052 blend = data->unclipped_blend;
4055 rasterizer->setClipRect(clipRect);
4056 rasterizer->initialize(blend, data);
4059 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4060 ProcessSpans callback,
4061 QSpanData *spanData, QRasterBuffer *rasterBuffer)
4063 if (!callback || !outline)
4066 Q_Q(QRasterPaintEngine);
4067 QRasterPaintEngineState *s = q->state();
4069 if (!s->flags.antialiased) {
4070 initializeRasterizer(spanData);
4072 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4076 rasterizer->rasterize(outline, fillRule);
4080 rasterize(outline, callback, (void *)spanData, rasterBuffer);
4084 int q_gray_rendered_spans(QT_FT_Raster raster);
4087 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4088 ProcessSpans callback,
4089 void *userData, QRasterBuffer *)
4091 if (!callback || !outline)
4094 Q_Q(QRasterPaintEngine);
4095 QRasterPaintEngineState *s = q->state();
4097 if (!s->flags.antialiased) {
4098 rasterizer->setAntialiased(s->flags.antialiased);
4099 rasterizer->setClipRect(deviceRect);
4100 rasterizer->initialize(callback, userData);
4102 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4106 rasterizer->rasterize(outline, fillRule);
4110 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
4111 // minimize memory reallocations. However if initial size for
4112 // raster pool is changed for lower value, reallocations will
4114 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
4115 int rasterPoolSize = rasterPoolInitialSize;
4116 unsigned char *rasterPoolBase;
4117 #if defined(Q_WS_WIN64)
4119 // We make use of setjmp and longjmp in qgrayraster.c which requires
4120 // 16-byte alignment, hence we hardcode this requirement here..
4121 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4123 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
4124 rasterPoolBase = rasterPoolOnStack;
4126 Q_CHECK_PTR(rasterPoolBase);
4128 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4130 void *data = userData;
4132 QT_FT_BBox clip_box = { deviceRect.x(),
4134 deviceRect.x() + deviceRect.width(),
4135 deviceRect.y() + deviceRect.height() };
4137 QT_FT_Raster_Params rasterParams;
4138 rasterParams.target = 0;
4139 rasterParams.source = outline;
4140 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
4141 rasterParams.gray_spans = 0;
4142 rasterParams.black_spans = 0;
4143 rasterParams.bit_test = 0;
4144 rasterParams.bit_set = 0;
4145 rasterParams.user = data;
4146 rasterParams.clip_box = clip_box;
4151 int rendered_spans = 0;
4155 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
4156 rasterParams.gray_spans = callback;
4157 rasterParams.skip_spans = rendered_spans;
4158 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
4160 // Out of memory, reallocate some more and try again...
4161 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
4162 int new_size = rasterPoolSize * 2;
4163 if (new_size > 1024 * 1024) {
4164 qWarning("QPainter: Rasterization of primitive failed");
4168 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
4170 #if defined(Q_WS_WIN64)
4171 _aligned_free(rasterPoolBase);
4173 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4174 free(rasterPoolBase);
4177 rasterPoolSize = new_size;
4179 #if defined(Q_WS_WIN64)
4180 // We make use of setjmp and longjmp in qgrayraster.c which requires
4181 // 16-byte alignment, hence we hardcode this requirement here..
4182 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4184 (unsigned char *) malloc(rasterPoolSize);
4186 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
4188 qt_ft_grays_raster.raster_done(*grayRaster.data());
4189 qt_ft_grays_raster.raster_new(grayRaster.data());
4190 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4196 #if defined(Q_WS_WIN64)
4197 _aligned_free(rasterPoolBase);
4199 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4200 free(rasterPoolBase);
4204 void QRasterPaintEnginePrivate::recalculateFastImages()
4206 Q_Q(QRasterPaintEngine);
4207 QRasterPaintEngineState *s = q->state();
4209 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
4210 && s->matrix.type() <= QTransform::TxShear;
4213 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
4215 Q_Q(const QRasterPaintEngine);
4216 const QRasterPaintEngineState *s = q->state();
4218 return s->flags.fast_images
4219 && (mode == QPainter::CompositionMode_SourceOver
4220 || (mode == QPainter::CompositionMode_Source
4221 && !image.hasAlphaChannel()));
4224 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
4226 Q_ASSERT(image.depth() == 1);
4228 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
4229 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
4231 QRgb fg = PREMUL(color.rgba());
4234 int height = sourceImage.height();
4235 int width = sourceImage.width();
4236 for (int y=0; y<height; ++y) {
4237 uchar *source = sourceImage.scanLine(y);
4238 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
4239 if (!source || !target)
4240 QT_THROW(std::bad_alloc()); // we must have run out of memory
4241 for (int x=0; x < width; ++x)
4242 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
4247 QRasterBuffer::~QRasterBuffer()
4251 void QRasterBuffer::init()
4253 compositionMode = QPainter::CompositionMode_SourceOver;
4254 monoDestinationWithClut = false;
4259 QImage::Format QRasterBuffer::prepare(QImage *image)
4261 m_buffer = (uchar *)image->bits();
4262 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
4263 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
4264 bytes_per_pixel = image->depth()/8;
4265 bytes_per_line = image->bytesPerLine();
4267 format = image->format();
4268 drawHelper = qDrawHelper + format;
4269 if (image->depth() == 1 && image->colorTable().size() == 2) {
4270 monoDestinationWithClut = true;
4271 destColor0 = PREMUL(image->colorTable()[0]);
4272 destColor1 = PREMUL(image->colorTable()[1]);
4278 void QRasterBuffer::resetBuffer(int val)
4280 memset(m_buffer, val, m_height*bytes_per_line);
4283 QClipData::QClipData(int height)
4285 clipSpanHeight = height;
4290 xmin = xmax = ymin = ymax = 0;
4294 hasRectClip = hasRegionClip = false;
4297 QClipData::~QClipData()
4305 void QClipData::initialize()
4311 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4313 Q_CHECK_PTR(m_clipLines);
4315 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4316 allocated = clipSpanHeight;
4317 Q_CHECK_PTR(m_spans);
4323 m_clipLines[y].spans = 0;
4324 m_clipLines[y].count = 0;
4328 const int len = clipRect.width();
4331 QSpan *span = m_spans + count;
4335 span->coverage = 255;
4338 m_clipLines[y].spans = span;
4339 m_clipLines[y].count = 1;
4343 while (y < clipSpanHeight) {
4344 m_clipLines[y].spans = 0;
4345 m_clipLines[y].count = 0;
4348 } else if (hasRegionClip) {
4350 const QVector<QRect> rects = clipRegion.rects();
4351 const int numRects = rects.size();
4354 const int maxSpans = (ymax - ymin) * numRects;
4355 if (maxSpans > allocated) {
4356 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4357 allocated = maxSpans;
4362 int firstInBand = 0;
4364 while (firstInBand < numRects) {
4365 const int currMinY = rects.at(firstInBand).y();
4366 const int currMaxY = currMinY + rects.at(firstInBand).height();
4368 while (y < currMinY) {
4369 m_clipLines[y].spans = 0;
4370 m_clipLines[y].count = 0;
4374 int lastInBand = firstInBand;
4375 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4378 while (y < currMaxY) {
4380 m_clipLines[y].spans = m_spans + count;
4381 m_clipLines[y].count = lastInBand - firstInBand + 1;
4383 for (int r = firstInBand; r <= lastInBand; ++r) {
4384 const QRect &currRect = rects.at(r);
4385 QSpan *span = m_spans + count;
4386 span->x = currRect.x();
4387 span->len = currRect.width();
4389 span->coverage = 255;
4395 firstInBand = lastInBand + 1;
4398 Q_ASSERT(count <= allocated);
4400 while (y < clipSpanHeight) {
4401 m_clipLines[y].spans = 0;
4402 m_clipLines[y].count = 0;
4408 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4413 free(m_clipLines); // same for clipLines
4419 void QClipData::fixup()
4424 ymin = ymax = xmin = xmax = 0;
4429 ymin = m_spans[0].y;
4430 ymax = m_spans[count-1].y + 1;
4434 const int firstLeft = m_spans[0].x;
4435 const int firstRight = m_spans[0].x + m_spans[0].len;
4438 for (int i = 0; i < count; ++i) {
4439 QT_FT_Span_& span = m_spans[i];
4442 if (span.y != y + 1 && y != -1)
4445 m_clipLines[y].spans = &span;
4446 m_clipLines[y].count = 1;
4448 ++m_clipLines[y].count;
4450 const int spanLeft = span.x;
4451 const int spanRight = spanLeft + span.len;
4453 if (spanLeft < xmin)
4456 if (spanRight > xmax)
4459 if (spanLeft != firstLeft || spanRight != firstRight)
4465 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4470 Convert \a rect to clip spans.
4472 void QClipData::setClipRect(const QRect &rect)
4474 if (hasRectClip && rect == clipRect)
4477 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4479 hasRegionClip = false;
4483 xmax = rect.x() + rect.width();
4484 ymin = qMin(rect.y(), clipSpanHeight);
4485 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4492 // qDebug() << xmin << xmax << ymin << ymax;
4496 Convert \a region to clip spans.
4498 void QClipData::setClipRegion(const QRegion ®ion)
4500 if (region.rectCount() == 1) {
4501 setClipRect(region.rects().at(0));
4505 hasRegionClip = true;
4506 hasRectClip = false;
4507 clipRegion = region;
4509 { // set bounding rect
4510 const QRect rect = region.boundingRect();
4512 xmax = rect.x() + rect.width();
4514 ymax = rect.y() + rect.height();
4526 spans must be sorted on y
4528 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4529 const QSpan *spans, const QSpan *end,
4530 QSpan **outSpans, int available)
4532 const_cast<QClipData *>(clip)->initialize();
4534 QSpan *out = *outSpans;
4536 const QSpan *clipSpans = clip->m_spans + *currentClip;
4537 const QSpan *clipEnd = clip->m_spans + clip->count;
4539 while (available && spans < end ) {
4540 if (clipSpans >= clipEnd) {
4544 if (clipSpans->y > spans->y) {
4548 if (spans->y != clipSpans->y) {
4549 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4550 clipSpans = clip->m_clipLines[spans->y].spans;
4555 Q_ASSERT(spans->y == clipSpans->y);
4558 int sx2 = sx1 + spans->len;
4559 int cx1 = clipSpans->x;
4560 int cx2 = cx1 + clipSpans->len;
4562 if (cx1 < sx1 && cx2 < sx1) {
4565 } else if (sx1 < cx1 && sx2 < cx1) {
4569 int x = qMax(sx1, cx1);
4570 int len = qMin(sx2, cx2) - x;
4572 out->x = qMax(sx1, cx1);
4573 out->len = qMin(sx2, cx2) - out->x;
4575 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4587 *currentClip = clipSpans - clip->m_spans;
4591 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4593 // qDebug() << "qt_span_fill_clipped" << spanCount;
4594 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4596 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4598 const int NSPANS = 256;
4599 QSpan cspans[NSPANS];
4600 int currentClip = 0;
4601 const QSpan *end = spans + spanCount;
4602 while (spans < end) {
4603 QSpan *clipped = cspans;
4604 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4605 // qDebug() << "processed " << processed << "clipped" << clipped-cspans
4606 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4608 if (clipped - cspans)
4609 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4615 Clip spans to \a{clip}-rectangle.
4616 Returns number of unclipped spans
4618 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4621 const short minx = clip.left();
4622 const short miny = clip.top();
4623 const short maxx = clip.right();
4624 const short maxy = clip.bottom();
4627 for (int i = 0; i < numSpans; ++i) {
4628 if (spans[i].y > maxy)
4630 if (spans[i].y < miny
4631 || spans[i].x > maxx
4632 || spans[i].x + spans[i].len <= minx) {
4635 if (spans[i].x < minx) {
4636 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4639 spans[n].x = spans[i].x;
4640 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4642 if (spans[n].len == 0)
4644 spans[n].y = spans[i].y;
4645 spans[n].coverage = spans[i].coverage;
4652 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4655 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4656 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4658 Q_ASSERT(fillData->clip);
4659 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4661 // hw: check if this const_cast<> is safe!!!
4662 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4663 fillData->clip->clipRect);
4665 fillData->unclipped_blend(count, spans, fillData);
4668 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4670 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4672 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4673 // for (int i = 0; i < qMin(count, 10); ++i) {
4674 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4677 switch (clipData->operation) {
4679 case Qt::IntersectClip:
4681 QClipData *newClip = clipData->newClip;
4682 newClip->initialize();
4684 int currentClip = 0;
4685 const QSpan *end = spans + count;
4686 while (spans < end) {
4687 QSpan *newspans = newClip->m_spans + newClip->count;
4688 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4689 &newspans, newClip->allocated - newClip->count);
4690 newClip->count = newspans - newClip->m_spans;
4692 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4693 newClip->allocated *= 2;
4700 case Qt::ReplaceClip:
4701 clipData->newClip->appendSpans(spans, count);
4709 QImage QRasterBuffer::bufferImage() const
4711 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4713 for (int y = 0; y < m_height; ++y) {
4714 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4716 for (int x=0; x<m_width; ++x) {
4717 uint argb = span[x];
4718 image.setPixel(x, y, argb);
4726 void QRasterBuffer::flushToARGBImage(QImage *target) const
4728 int w = qMin(m_width, target->width());
4729 int h = qMin(m_height, target->height());
4731 for (int y=0; y<h; ++y) {
4732 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4733 QRgb *dest = (QRgb *) target->scanLine(y);
4734 for (int x=0; x<w; ++x) {
4735 QRgb pixel = sourceLine[x];
4736 int alpha = qAlpha(pixel);
4740 dest[x] = (alpha << 24)
4741 | ((255*qRed(pixel)/alpha) << 16)
4742 | ((255*qGreen(pixel)/alpha) << 8)
4743 | ((255*qBlue(pixel)/alpha) << 0);
4750 class QGradientCache
4754 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4755 stops(s), opacity(op), interpolationMode(mode) {}
4756 uint buffer[GRADIENT_STOPTABLE_SIZE];
4757 QGradientStops stops;
4759 QGradient::InterpolationMode interpolationMode;
4762 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4765 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4766 quint64 hash_val = 0;
4768 QGradientStops stops = gradient.stops();
4769 for (int i = 0; i < stops.size() && i <= 2; i++)
4770 hash_val += stops[i].second.rgba();
4772 QMutexLocker lock(&mutex);
4773 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4775 if (it == cache.constEnd())
4776 return addCacheElement(hash_val, gradient, opacity);
4779 const CacheInfo &cache_info = it.value();
4780 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4781 return cache_info.buffer;
4783 } while (it != cache.constEnd() && it.key() == hash_val);
4784 // an exact match for these stops and opacity was not found, create new cache
4785 return addCacheElement(hash_val, gradient, opacity);
4789 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4791 inline int maxCacheSize() const { return 60; }
4792 inline void generateGradientColorTable(const QGradient& g,
4794 int size, int opacity) const;
4795 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4796 if (cache.size() == maxCacheSize()) {
4797 // may remove more than 1, but OK
4798 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4800 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4801 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4802 return cache.insert(hash_val, cache_entry).value().buffer;
4805 QGradientColorTableHash cache;
4809 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4811 QGradientStops stops = gradient.stops();
4812 int stopCount = stops.count();
4813 Q_ASSERT(stopCount > 0);
4815 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4817 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4818 if (stopCount == 1) {
4819 current_color = PREMUL(current_color);
4820 for (int i = 0; i < size; ++i)
4821 colorTable[i] = current_color;
4825 // The position where the gradient begins and ends
4826 qreal begin_pos = stops[0].first;
4827 qreal end_pos = stops[stopCount-1].first;
4829 int pos = 0; // The position in the color table.
4832 qreal incr = 1 / qreal(size); // the double increment.
4833 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4835 // Up to first point
4836 colorTable[pos++] = PREMUL(current_color);
4837 while (dpos <= begin_pos) {
4838 colorTable[pos] = colorTable[pos - 1];
4843 int current_stop = 0; // We always interpolate between current and current + 1.
4845 qreal t; // position between current left and right stops
4846 qreal t_delta; // the t increment per entry in the color table
4848 if (dpos < end_pos) {
4850 while (dpos > stops[current_stop+1].first)
4853 if (current_stop != 0)
4854 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4855 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4857 if (colorInterpolation) {
4858 current_color = PREMUL(current_color);
4859 next_color = PREMUL(next_color);
4862 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4863 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4864 t = (dpos - stops[current_stop].first) * c;
4868 Q_ASSERT(current_stop < stopCount);
4870 int dist = qRound(t);
4871 int idist = 256 - dist;
4873 if (colorInterpolation)
4874 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4876 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4881 if (dpos >= end_pos)
4887 while (dpos > stops[current_stop+skip+1].first)
4891 current_stop += skip;
4893 current_color = next_color;
4895 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4896 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4898 if (colorInterpolation) {
4900 current_color = PREMUL(current_color);
4901 next_color = PREMUL(next_color);
4904 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4905 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4906 t = (dpos - stops[current_stop].first) * c;
4913 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4914 while (pos < size - 1) {
4915 colorTable[pos] = current_color;
4919 // Make sure the last color stop is represented at the end of the table
4920 colorTable[size - 1] = current_color;
4923 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4926 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4932 m11 = m22 = m33 = 1.;
4933 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4934 clip = pe ? pe->d_func()->clip() : 0;
4937 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4939 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4941 Qt::BrushStyle brushStyle = qbrush_style(brush);
4942 switch (brushStyle) {
4943 case Qt::SolidPattern: {
4945 QColor c = qbrush_color(brush);
4946 QRgb rgba = c.rgba();
4947 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4948 if ((solid.color & 0xff000000) == 0
4949 && compositionMode == QPainter::CompositionMode_SourceOver) {
4955 case Qt::LinearGradientPattern:
4957 type = LinearGradient;
4958 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4959 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4960 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4961 gradient.spread = g->spread();
4963 QLinearGradientData &linearData = gradient.linear;
4965 linearData.origin.x = g->start().x();
4966 linearData.origin.y = g->start().y();
4967 linearData.end.x = g->finalStop().x();
4968 linearData.end.y = g->finalStop().y();
4972 case Qt::RadialGradientPattern:
4974 type = RadialGradient;
4975 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4976 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4977 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4978 gradient.spread = g->spread();
4980 QRadialGradientData &radialData = gradient.radial;
4982 QPointF center = g->center();
4983 radialData.center.x = center.x();
4984 radialData.center.y = center.y();
4985 QPointF focal = g->focalPoint();
4986 radialData.focal.x = focal.x();
4987 radialData.focal.y = focal.y();
4988 radialData.radius = g->radius();
4992 case Qt::ConicalGradientPattern:
4994 type = ConicalGradient;
4995 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4996 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4997 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4998 gradient.spread = QGradient::RepeatSpread;
5000 QConicalGradientData &conicalData = gradient.conical;
5002 QPointF center = g->center();
5003 conicalData.center.x = center.x();
5004 conicalData.center.y = center.y();
5005 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
5009 case Qt::Dense1Pattern:
5010 case Qt::Dense2Pattern:
5011 case Qt::Dense3Pattern:
5012 case Qt::Dense4Pattern:
5013 case Qt::Dense5Pattern:
5014 case Qt::Dense6Pattern:
5015 case Qt::Dense7Pattern:
5016 case Qt::HorPattern:
5017 case Qt::VerPattern:
5018 case Qt::CrossPattern:
5019 case Qt::BDiagPattern:
5020 case Qt::FDiagPattern:
5021 case Qt::DiagCrossPattern:
5024 tempImage = new QImage();
5025 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
5026 initTexture(tempImage, alpha, QTextureData::Tiled);
5028 case Qt::TexturePattern:
5031 tempImage = new QImage();
5033 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
5034 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
5036 *tempImage = brush.textureImage();
5037 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
5045 adjustSpanMethods();
5048 void QSpanData::adjustSpanMethods()
5058 unclipped_blend = 0;
5061 unclipped_blend = rasterBuffer->drawHelper->blendColor;
5062 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
5063 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
5064 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
5065 fillRect = rasterBuffer->drawHelper->fillRect;
5067 case LinearGradient:
5068 case RadialGradient:
5069 case ConicalGradient:
5070 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
5073 unclipped_blend = qBlendTexture;
5074 if (!texture.imageData)
5075 unclipped_blend = 0;
5080 if (!unclipped_blend) {
5083 blend = unclipped_blend;
5084 } else if (clip->hasRectClip) {
5085 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
5087 blend = qt_span_fill_clipped;
5091 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
5094 // make sure we round off correctly in qdrawhelper.cpp
5095 delta.translate(1.0 / 65536, 1.0 / 65536);
5097 QTransform inv = (delta * matrix).inverted();
5110 const bool affine = !m13 && !m23;
5111 fast_matrix = affine
5112 && m11 * m11 + m21 * m21 < 1e4
5113 && m12 * m12 + m22 * m22 < 1e4
5117 adjustSpanMethods();
5120 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
5122 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
5124 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
5125 if (!d || d->height == 0) {
5126 texture.imageData = 0;
5133 texture.bytesPerLine = 0;
5134 texture.format = QImage::Format_Invalid;
5135 texture.colorTable = 0;
5136 texture.hasAlpha = alpha != 256;
5138 texture.imageData = d->data;
5139 texture.width = d->width;
5140 texture.height = d->height;
5142 if (sourceRect.isNull()) {
5145 texture.x2 = texture.width;
5146 texture.y2 = texture.height;
5148 texture.x1 = sourceRect.x();
5149 texture.y1 = sourceRect.y();
5150 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
5151 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
5154 texture.bytesPerLine = d->bytes_per_line;
5156 texture.format = d->format;
5157 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5158 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5160 texture.const_alpha = alpha;
5161 texture.type = _type;
5163 adjustSpanMethods();
5175 Draws a line using the floating point midpoint algorithm. The line
5176 \a line is already in device coords at this point.
5179 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
5180 LineDrawMode style, const QIntRect &devRect)
5182 #ifdef QT_DEBUG_DRAW
5183 qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
5187 int dx, dy, d, incrE, incrNE;
5192 const int NSPANS = 256;
5193 QT_FT_Span spans[NSPANS];
5195 bool ordered = true;
5198 // specialcase horizontal lines
5199 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5200 int start = qMax(devRect.x1, qMin(x1, x2));
5201 int stop = qMax(x1, x2) + 1;
5202 int stop_clipped = qMin(devRect.x2, stop);
5203 int len = stop_clipped - start;
5204 if (style == LineDrawNormal && stop == stop_clipped)
5207 spans[0].x = ushort(start);
5208 spans[0].len = ushort(len);
5210 spans[0].coverage = 255;
5211 span_func(1, spans, data);
5215 } else if (dx == 0) {
5216 // specialcase vertical lines
5217 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5218 int start = qMax(devRect.y1, qMin(y1, y2));
5219 int stop = qMax(y1, y2) + 1;
5220 int stop_clipped = qMin(devRect.y2, stop);
5221 int len = stop_clipped - start;
5222 if (style == LineDrawNormal && stop == stop_clipped)
5224 // hw: create spans directly instead to possibly avoid clipping
5226 fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
5232 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5234 if (x2 < x1) { /* if coordinates are out of order */
5235 qt_swap_int(x1, x2);
5238 qt_swap_int(y1, y2);
5242 int x_lower_limit = - 128;
5243 if (x1 < x_lower_limit) {
5244 int cy = dy * (x_lower_limit - x1) / dx + y1;
5245 drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect);
5249 if (style == LineDrawNormal)
5252 // In the loops below we increment before call the span function so
5253 // we need to stop one pixel before
5254 x2 = qMin(x2, devRect.x2 - 1);
5256 // completely clipped, so abort
5268 const int index = (ordered ? current : NSPANS - 1 - current);
5269 spans[index].coverage = 255;
5273 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
5274 spans[index].len = 1;
5276 spans[index].len = 0;
5279 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5280 y2 = qMin(y2, devRect.y2 - 1);
5284 incrNE = (dy - dx) * 2;
5287 goto flush_and_return;
5292 if (spans[current].len > 0)
5294 if (current == NSPANS) {
5295 span_func(NSPANS, spans, data);
5302 goto flush_and_return;
5304 spans[current].len = 0;
5305 spans[current].coverage = 255;
5306 spans[current].x = x;
5307 spans[current].y = y;
5310 if (x == devRect.x1)
5311 spans[current].x = devRect.x1;
5314 if (x < devRect.x1 || y < devRect.y1)
5317 Q_ASSERT(x<devRect.x2);
5318 Q_ASSERT(y<devRect.y2);
5319 Q_ASSERT(spans[current].y == y);
5320 spans[current].len++;
5322 if (spans[current].len > 0) {
5325 } else { // 0-45 and 180->225 (unit circle degrees)
5327 y1 = qMin(y1, devRect.y2 - 1);
5331 incrNE = (dy + dx) * 2;
5334 goto flush_and_return;
5339 if (spans[NSPANS - 1 - current].len > 0)
5341 if (current == NSPANS) {
5342 span_func(NSPANS, spans, data);
5349 goto flush_and_return;
5351 const int index = NSPANS - 1 - current;
5352 spans[index].len = 0;
5353 spans[index].coverage = 255;
5358 if (x == devRect.x1)
5359 spans[NSPANS - 1 - current].x = devRect.x1;
5362 if (x < devRect.x1 || y > y1)
5365 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5366 Q_ASSERT(spans[NSPANS - 1 - current].y == y);
5367 spans[NSPANS - 1 - current].len++;
5369 if (spans[NSPANS - 1 - current].len > 0) {
5376 // if y is the major axis:
5378 if (y2 < y1) { /* if coordinates are out of order */
5379 qt_swap_int(y1, y2);
5382 qt_swap_int(x1, x2);
5386 int y_lower_limit = - 128;
5387 if (y1 < y_lower_limit) {
5388 int cx = dx * (y_lower_limit - y1) / dy + x1;
5389 drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect);
5393 if (style == LineDrawNormal)
5396 // In the loops below we increment before call the span function so
5397 // we need to stop one pixel before
5398 y2 = qMin(y2, devRect.y2 - 1);
5400 // completely clipped, so abort
5408 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5409 Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
5410 if (current == NSPANS) {
5411 span_func(NSPANS, spans, data);
5414 spans[current].len = 1;
5415 spans[current].coverage = 255;
5416 spans[current].x = x;
5417 spans[current].y = y;
5421 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5422 x2 = qMin(x2, devRect.x2 - 1);
5425 incrNE = (dx - dy) * 2;
5428 goto flush_and_return;
5435 goto flush_and_return;
5440 if (x < devRect.x1 || y < devRect.y1)
5442 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5443 if (current == NSPANS) {
5444 span_func(NSPANS, spans, data);
5447 spans[current].len = 1;
5448 spans[current].coverage = 255;
5449 spans[current].x = x;
5450 spans[current].y = y;
5453 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5454 x1 = qMin(x1, devRect.x2 - 1);
5457 incrNE = (dx + dy) * 2;
5460 goto flush_and_return;
5467 goto flush_and_return;
5472 if (y < devRect.y1 || x > x1)
5474 Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
5475 if (current == NSPANS) {
5476 span_func(NSPANS, spans, data);
5479 spans[current].len = 1;
5480 spans[current].coverage = 255;
5481 spans[current].x = x;
5482 spans[current].y = y;
5489 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5492 static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
5495 if (--*currentOffset == 0) {
5497 *dashIndex = ((*dashIndex + 1) % pattern.size());
5498 *currentOffset = int(pattern[*dashIndex]);
5503 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
5505 ProcessSpans span_func, QSpanData *data,
5506 LineDrawMode style, const QIntRect &devRect,
5509 #ifdef QT_DEBUG_DRAW
5510 qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
5514 int dx, dy, d, incrE, incrNE;
5519 Q_ASSERT(*patternOffset >= 0);
5521 const QVector<qreal> penPattern = pen->dashPattern();
5522 QVarLengthArray<qreal> pattern(penPattern.size());
5524 int patternLength = 0;
5525 for (int i = 0; i < penPattern.size(); ++i)
5526 patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
5528 // pattern must be reversed if coordinates are out of order
5529 int reverseLength = -1;
5530 if (dy == 0 && x1 > x2)
5531 reverseLength = x1 - x2;
5532 else if (dx == 0 && y1 > y2)
5533 reverseLength = y1 - y2;
5534 else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
5535 reverseLength = qAbs(dx);
5536 else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
5537 reverseLength = qAbs(dy);
5539 const bool reversed = (reverseLength > -1);
5540 if (reversed) { // reverse pattern
5541 for (int i = 0; i < penPattern.size(); ++i)
5542 pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
5544 *patternOffset = (patternLength - 1 - *patternOffset);
5545 *patternOffset += patternLength - (reverseLength % patternLength);
5546 *patternOffset = *patternOffset % patternLength;
5548 for (int i = 0; i < penPattern.size(); ++i)
5549 pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
5553 bool inDash = !reversed;
5554 int currPattern = int(pattern[dashIndex]);
5556 // adjust pattern for offset
5557 offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
5559 const int NSPANS = 256;
5560 QT_FT_Span spans[NSPANS];
5562 bool ordered = true;
5565 // specialcase horizontal lines
5566 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5567 int start_unclipped = qMin(x1, x2);
5568 int start = qMax(devRect.x1, start_unclipped);
5569 int stop = qMax(x1, x2) + 1;
5570 int stop_clipped = qMin(devRect.x2, stop);
5571 int len = stop_clipped - start;
5572 if (style == LineDrawNormal && stop == stop_clipped)
5575 // adjust pattern for starting offset
5576 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5580 while (x < stop_clipped) {
5581 if (current == NSPANS) {
5582 span_func(NSPANS, spans, data);
5585 const int dash = qMin(currPattern, stop_clipped - x);
5587 spans[current].x = ushort(x);
5588 spans[current].len = ushort(dash);
5589 spans[current].y = y1;
5590 spans[current].coverage = 255;
5593 if (dash < currPattern) {
5594 currPattern -= dash;
5596 dashIndex = (dashIndex + 1) % pattern.size();
5597 currPattern = int(pattern[dashIndex]);
5604 goto flush_and_return;
5605 } else if (dx == 0) {
5606 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5607 int start_unclipped = qMin(y1, y2);
5608 int start = qMax(devRect.y1, start_unclipped);
5609 int stop = qMax(y1, y2) + 1;
5610 int stop_clipped = qMin(devRect.y2, stop);
5611 if (style == LineDrawNormal && stop == stop_clipped)
5614 stop = stop_clipped;
5616 // adjust pattern for starting offset
5617 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5622 const int dash = qMin(currPattern, stop - y);
5624 for (int i = 0; i < dash; ++i) {
5625 if (current == NSPANS) {
5626 span_func(NSPANS, spans, data);
5629 spans[current].x = x1;
5630 spans[current].len = 1;
5631 spans[current].coverage = 255;
5632 spans[current].y = ushort(y + i);
5636 if (dash < currPattern) {
5637 currPattern -= dash;
5639 dashIndex = (dashIndex + 1) % pattern.size();
5640 currPattern = int(pattern[dashIndex]);
5646 goto flush_and_return;
5649 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5651 if (x2 < x1) { /* if coordinates are out of order */
5652 qt_swap_int(x1, x2);
5655 qt_swap_int(y1, y2);
5659 if (style == LineDrawNormal)
5662 // In the loops below we increment before call the span function so
5663 // we need to stop one pixel before
5664 x2 = qMin(x2, devRect.x2 - 1);
5666 // completely clipped, so abort
5668 goto flush_and_return;
5673 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
5674 Q_ASSERT(x < devRect.x2);
5676 if (current == NSPANS) {
5677 span_func(NSPANS, spans, data);
5680 spans[current].len = 1;
5681 spans[current].coverage = 255;
5682 spans[current].x = x;
5683 spans[current].y = y;
5686 if (--currPattern <= 0) {
5688 dashIndex = (dashIndex + 1) % pattern.size();
5689 currPattern = int(pattern[dashIndex]);
5693 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5694 y2 = qMin(y2, devRect.y2 - 1);
5698 incrNE = (dy - dx) * 2;
5701 goto flush_and_return;
5708 goto flush_and_return;
5714 const bool skip = x < devRect.x1 || y < devRect.y1;
5715 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5716 if (inDash && !skip) {
5717 if (current == NSPANS) {
5718 span_func(NSPANS, spans, data);
5721 spans[current].len = 1;
5722 spans[current].coverage = 255;
5723 spans[current].x = x;
5724 spans[current].y = y;
5727 if (--currPattern <= 0) {
5729 dashIndex = (dashIndex + 1) % pattern.size();
5730 currPattern = int(pattern[dashIndex]);
5733 } else { // 0-45 and 180->225 (unit circle degrees)
5734 y1 = qMin(y1, devRect.y2 - 1);
5738 incrNE = (dy + dx) * 2;
5741 goto flush_and_return;
5746 span_func(current, spans, data);
5753 goto flush_and_return;
5759 const bool skip = x < devRect.x1 || y > y1;
5760 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5761 if (inDash && !skip) {
5762 if (current == NSPANS) {
5763 span_func(NSPANS, spans, data);
5766 spans[current].len = 1;
5767 spans[current].coverage = 255;
5768 spans[current].x = x;
5769 spans[current].y = y;
5772 if (--currPattern <= 0) {
5774 dashIndex = (dashIndex + 1) % pattern.size();
5775 currPattern = int(pattern[dashIndex]);
5781 // if y is the major axis:
5783 if (y2 < y1) { /* if coordinates are out of order */
5784 qt_swap_int(y1, y2);
5787 qt_swap_int(x1, x2);
5791 if (style == LineDrawNormal)
5794 // In the loops below we increment before call the span function so
5795 // we need to stop one pixel before
5796 y2 = qMin(y2, devRect.y2 - 1);
5798 // completely clipped, so abort
5800 goto flush_and_return;
5805 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5806 Q_ASSERT(x < devRect.x2);
5808 if (current == NSPANS) {
5809 span_func(NSPANS, spans, data);
5812 spans[current].len = 1;
5813 spans[current].coverage = 255;
5814 spans[current].x = x;
5815 spans[current].y = y;
5818 if (--currPattern <= 0) {
5820 dashIndex = (dashIndex + 1) % pattern.size();
5821 currPattern = int(pattern[dashIndex]);
5825 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5826 x2 = qMin(x2, devRect.x2 - 1);
5829 incrNE = (dx - dy) * 2;
5832 goto flush_and_return;
5839 goto flush_and_return;
5844 const bool skip = x < devRect.x1 || y < devRect.y1;
5845 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5846 if (inDash && !skip) {
5847 if (current == NSPANS) {
5848 span_func(NSPANS, spans, data);
5851 spans[current].len = 1;
5852 spans[current].coverage = 255;
5853 spans[current].x = x;
5854 spans[current].y = y;
5857 if (--currPattern <= 0) {
5859 dashIndex = (dashIndex + 1) % pattern.size();
5860 currPattern = int(pattern[dashIndex]);
5863 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5864 x1 = qMin(x1, devRect.x2 - 1);
5867 incrNE = (dx + dy) * 2;
5870 goto flush_and_return;
5877 goto flush_and_return;
5882 const bool skip = y < devRect.y1 || x > x1;
5883 Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
5884 if (inDash && !skip) {
5885 if (current == NSPANS) {
5886 span_func(NSPANS, spans, data);
5889 spans[current].len = 1;
5890 spans[current].coverage = 255;
5891 spans[current].x = x;
5892 spans[current].y = y;
5895 if (--currPattern <= 0) {
5897 dashIndex = (dashIndex + 1) % pattern.size();
5898 currPattern = int(pattern[dashIndex]);
5905 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5909 *patternOffset = (patternLength - 1 - *patternOffset);
5912 for (int i = 0; i <= dashIndex; ++i)
5913 *patternOffset += int(pattern[i]);
5914 *patternOffset += patternLength - currPattern - 1;
5915 *patternOffset = (*patternOffset % patternLength);
5921 \a x and \a y is relative to the midpoint of \a rect.
5923 static inline void drawEllipsePoints(int x, int y, int length,
5926 ProcessSpans pen_func, ProcessSpans brush_func,
5927 QSpanData *pen_data, QSpanData *brush_data)
5932 QT_FT_Span outline[4];
5933 const int midx = rect.x() + (rect.width() + 1) / 2;
5934 const int midy = rect.y() + (rect.height() + 1) / 2;
5940 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
5941 outline[0].len = qMin(length, x - outline[0].x);
5943 outline[0].coverage = 255;
5947 outline[1].len = length;
5949 outline[1].coverage = 255;
5952 outline[2].x = outline[0].x;
5953 outline[2].len = outline[0].len;
5954 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
5955 outline[2].coverage = 255;
5959 outline[3].len = length;
5960 outline[3].y = outline[2].y;
5961 outline[3].coverage = 255;
5963 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
5967 fill[0].x = outline[0].x + outline[0].len - 1;
5968 fill[0].len = qMax(0, outline[1].x - fill[0].x);
5969 fill[0].y = outline[1].y;
5970 fill[0].coverage = 255;
5973 fill[1].x = outline[2].x + outline[2].len - 1;
5974 fill[1].len = qMax(0, outline[3].x - fill[1].x);
5975 fill[1].y = outline[3].y;
5976 fill[1].coverage = 255;
5978 int n = (fill[0].y >= fill[1].y ? 1 : 2);
5979 n = qt_intersect_spans(fill, n, clip);
5981 brush_func(n, fill, brush_data);
5984 int n = (outline[1].y >= outline[2].y ? 2 : 4);
5985 n = qt_intersect_spans(outline, n, clip);
5987 pen_func(n, outline, pen_data);
5993 Draws an ellipse using the integer point midpoint algorithm.
5995 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
5996 ProcessSpans pen_func, ProcessSpans brush_func,
5997 QSpanData *pen_data, QSpanData *brush_data)
5999 const qreal a = qreal(rect.width()) / 2;
6000 const qreal b = qreal(rect.height()) / 2;
6001 qreal d = b*b - (a*a*b) + 0.25*a*a;
6004 int y = (rect.height() + 1) / 2;
6008 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
6009 if (d < 0) { // select E
6012 } else { // select SE
6013 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
6014 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6015 pen_func, brush_func, pen_data, brush_data);
6020 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6021 pen_func, brush_func, pen_data, brush_data);
6024 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
6025 const int miny = rect.height() & 0x1;
6027 if (d < 0) { // select SE
6028 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
6030 } else { // select S
6031 d += a*a*(-2*y + 3);
6034 drawEllipsePoints(x, y, 1, rect, clip,
6035 pen_func, brush_func, pen_data, brush_data);
6040 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
6043 Draws the first \a pointCount points in the buffer \a points
6045 The default implementation converts the first \a pointCount QPoints in \a points
6046 to QPointFs and calls the floating point version of drawPoints.
6050 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
6053 Reimplement this function to draw the largest ellipse that can be
6054 contained within rectangle \a rect.
6057 #ifdef QT_DEBUG_DRAW
6058 void dumpClip(int width, int height, const QClipData *clip)
6060 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
6061 clipImg.fill(0xffff0000);
6068 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
6070 for (int i = 0; i < clip->count; ++i) {
6071 const QSpan *span = ((QClipData *) clip)->spans() + i;
6072 for (int j = 0; j < span->len; ++j)
6073 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
6074 x0 = qMin(x0, int(span->x));
6075 x1 = qMax(x1, int(span->x + span->len - 1));
6077 y0 = qMin(y0, int(span->y));
6078 y1 = qMax(y1, int(span->y));
6081 static int counter = 0;
6088 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
6089 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));