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 if (stopCount == 2) {
4818 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4819 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4821 qreal first_stop = stops[0].first;
4822 qreal second_stop = stops[1].first;
4824 if (second_stop < first_stop) {
4825 qSwap(first_color, second_color);
4826 qSwap(first_stop, second_stop);
4829 if (colorInterpolation) {
4830 first_color = PREMUL(first_color);
4831 second_color = PREMUL(second_color);
4834 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4835 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4837 uint red_first = qRed(first_color) << 16;
4838 uint green_first = qGreen(first_color) << 16;
4839 uint blue_first = qBlue(first_color) << 16;
4840 uint alpha_first = qAlpha(first_color) << 16;
4842 uint red_second = qRed(second_color) << 16;
4843 uint green_second = qGreen(second_color) << 16;
4844 uint blue_second = qBlue(second_color) << 16;
4845 uint alpha_second = qAlpha(second_color) << 16;
4848 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4849 if (colorInterpolation)
4850 colorTable[i] = first_color;
4852 colorTable[i] = PREMUL(first_color);
4855 if (i < second_index) {
4856 qreal reciprocal = qreal(1) / (second_index - first_index);
4858 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4859 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4860 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4861 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4864 red_first += 1 << 15;
4865 green_first += 1 << 15;
4866 blue_first += 1 << 15;
4867 alpha_first += 1 << 15;
4869 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4870 red_first += red_delta;
4871 green_first += green_delta;
4872 blue_first += blue_delta;
4873 alpha_first += alpha_delta;
4875 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4876 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4878 if (colorInterpolation)
4879 colorTable[i] = color;
4881 colorTable[i] = PREMUL(color);
4885 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4886 if (colorInterpolation)
4887 colorTable[i] = second_color;
4889 colorTable[i] = PREMUL(second_color);
4895 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4896 if (stopCount == 1) {
4897 current_color = PREMUL(current_color);
4898 for (int i = 0; i < size; ++i)
4899 colorTable[i] = current_color;
4903 // The position where the gradient begins and ends
4904 qreal begin_pos = stops[0].first;
4905 qreal end_pos = stops[stopCount-1].first;
4907 int pos = 0; // The position in the color table.
4910 qreal incr = 1 / qreal(size); // the double increment.
4911 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4913 // Up to first point
4914 colorTable[pos++] = PREMUL(current_color);
4915 while (dpos <= begin_pos) {
4916 colorTable[pos] = colorTable[pos - 1];
4921 int current_stop = 0; // We always interpolate between current and current + 1.
4923 qreal t; // position between current left and right stops
4924 qreal t_delta; // the t increment per entry in the color table
4926 if (dpos < end_pos) {
4928 while (dpos > stops[current_stop+1].first)
4931 if (current_stop != 0)
4932 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4933 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4935 if (colorInterpolation) {
4936 current_color = PREMUL(current_color);
4937 next_color = PREMUL(next_color);
4940 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4941 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4942 t = (dpos - stops[current_stop].first) * c;
4946 Q_ASSERT(current_stop < stopCount);
4948 int dist = qRound(t);
4949 int idist = 256 - dist;
4951 if (colorInterpolation)
4952 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4954 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4959 if (dpos >= end_pos)
4965 while (dpos > stops[current_stop+skip+1].first)
4969 current_stop += skip;
4971 current_color = next_color;
4973 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4974 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4976 if (colorInterpolation) {
4978 current_color = PREMUL(current_color);
4979 next_color = PREMUL(next_color);
4982 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4983 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4984 t = (dpos - stops[current_stop].first) * c;
4991 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4992 while (pos < size - 1) {
4993 colorTable[pos] = current_color;
4997 // Make sure the last color stop is represented at the end of the table
4998 colorTable[size - 1] = current_color;
5001 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
5004 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
5010 m11 = m22 = m33 = 1.;
5011 m12 = m13 = m21 = m23 = dx = dy = 0.0;
5012 clip = pe ? pe->d_func()->clip() : 0;
5015 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
5017 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
5019 Qt::BrushStyle brushStyle = qbrush_style(brush);
5020 switch (brushStyle) {
5021 case Qt::SolidPattern: {
5023 QColor c = qbrush_color(brush);
5024 QRgb rgba = c.rgba();
5025 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
5026 if ((solid.color & 0xff000000) == 0
5027 && compositionMode == QPainter::CompositionMode_SourceOver) {
5033 case Qt::LinearGradientPattern:
5035 type = LinearGradient;
5036 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
5037 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5038 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5039 gradient.spread = g->spread();
5041 QLinearGradientData &linearData = gradient.linear;
5043 linearData.origin.x = g->start().x();
5044 linearData.origin.y = g->start().y();
5045 linearData.end.x = g->finalStop().x();
5046 linearData.end.y = g->finalStop().y();
5050 case Qt::RadialGradientPattern:
5052 type = RadialGradient;
5053 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
5054 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5055 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5056 gradient.spread = g->spread();
5058 QRadialGradientData &radialData = gradient.radial;
5060 QPointF center = g->center();
5061 radialData.center.x = center.x();
5062 radialData.center.y = center.y();
5063 radialData.center.radius = g->centerRadius();
5064 QPointF focal = g->focalPoint();
5065 radialData.focal.x = focal.x();
5066 radialData.focal.y = focal.y();
5067 radialData.focal.radius = g->focalRadius();
5071 case Qt::ConicalGradientPattern:
5073 type = ConicalGradient;
5074 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
5075 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5076 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5077 gradient.spread = QGradient::RepeatSpread;
5079 QConicalGradientData &conicalData = gradient.conical;
5081 QPointF center = g->center();
5082 conicalData.center.x = center.x();
5083 conicalData.center.y = center.y();
5084 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
5088 case Qt::Dense1Pattern:
5089 case Qt::Dense2Pattern:
5090 case Qt::Dense3Pattern:
5091 case Qt::Dense4Pattern:
5092 case Qt::Dense5Pattern:
5093 case Qt::Dense6Pattern:
5094 case Qt::Dense7Pattern:
5095 case Qt::HorPattern:
5096 case Qt::VerPattern:
5097 case Qt::CrossPattern:
5098 case Qt::BDiagPattern:
5099 case Qt::FDiagPattern:
5100 case Qt::DiagCrossPattern:
5103 tempImage = new QImage();
5104 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
5105 initTexture(tempImage, alpha, QTextureData::Tiled);
5107 case Qt::TexturePattern:
5110 tempImage = new QImage();
5112 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
5113 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
5115 *tempImage = brush.textureImage();
5116 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
5124 adjustSpanMethods();
5127 void QSpanData::adjustSpanMethods()
5137 unclipped_blend = 0;
5140 unclipped_blend = rasterBuffer->drawHelper->blendColor;
5141 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
5142 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
5143 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
5144 fillRect = rasterBuffer->drawHelper->fillRect;
5146 case LinearGradient:
5147 case RadialGradient:
5148 case ConicalGradient:
5149 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
5152 unclipped_blend = qBlendTexture;
5153 if (!texture.imageData)
5154 unclipped_blend = 0;
5159 if (!unclipped_blend) {
5162 blend = unclipped_blend;
5163 } else if (clip->hasRectClip) {
5164 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
5166 blend = qt_span_fill_clipped;
5170 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
5173 // make sure we round off correctly in qdrawhelper.cpp
5174 delta.translate(1.0 / 65536, 1.0 / 65536);
5176 QTransform inv = (delta * matrix).inverted();
5189 const bool affine = !m13 && !m23;
5190 fast_matrix = affine
5191 && m11 * m11 + m21 * m21 < 1e4
5192 && m12 * m12 + m22 * m22 < 1e4
5196 adjustSpanMethods();
5199 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
5201 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
5203 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
5204 if (!d || d->height == 0) {
5205 texture.imageData = 0;
5212 texture.bytesPerLine = 0;
5213 texture.format = QImage::Format_Invalid;
5214 texture.colorTable = 0;
5215 texture.hasAlpha = alpha != 256;
5217 texture.imageData = d->data;
5218 texture.width = d->width;
5219 texture.height = d->height;
5221 if (sourceRect.isNull()) {
5224 texture.x2 = texture.width;
5225 texture.y2 = texture.height;
5227 texture.x1 = sourceRect.x();
5228 texture.y1 = sourceRect.y();
5229 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
5230 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
5233 texture.bytesPerLine = d->bytes_per_line;
5235 texture.format = d->format;
5236 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5237 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5239 texture.const_alpha = alpha;
5240 texture.type = _type;
5242 adjustSpanMethods();
5254 Draws a line using the floating point midpoint algorithm. The line
5255 \a line is already in device coords at this point.
5258 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
5259 LineDrawMode style, const QIntRect &devRect)
5261 #ifdef QT_DEBUG_DRAW
5262 qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
5266 int dx, dy, d, incrE, incrNE;
5271 const int NSPANS = 256;
5272 QT_FT_Span spans[NSPANS];
5274 bool ordered = true;
5277 // specialcase horizontal lines
5278 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5279 int start = qMax(devRect.x1, qMin(x1, x2));
5280 int stop = qMax(x1, x2) + 1;
5281 int stop_clipped = qMin(devRect.x2, stop);
5282 int len = stop_clipped - start;
5283 if (style == LineDrawNormal && stop == stop_clipped)
5286 spans[0].x = ushort(start);
5287 spans[0].len = ushort(len);
5289 spans[0].coverage = 255;
5290 span_func(1, spans, data);
5294 } else if (dx == 0) {
5295 // specialcase vertical lines
5296 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5297 int start = qMax(devRect.y1, qMin(y1, y2));
5298 int stop = qMax(y1, y2) + 1;
5299 int stop_clipped = qMin(devRect.y2, stop);
5300 int len = stop_clipped - start;
5301 if (style == LineDrawNormal && stop == stop_clipped)
5303 // hw: create spans directly instead to possibly avoid clipping
5305 fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
5311 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5313 if (x2 < x1) { /* if coordinates are out of order */
5314 qt_swap_int(x1, x2);
5317 qt_swap_int(y1, y2);
5321 int x_lower_limit = - 128;
5322 if (x1 < x_lower_limit) {
5323 int cy = dy * (x_lower_limit - x1) / dx + y1;
5324 drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect);
5328 if (style == LineDrawNormal)
5331 // In the loops below we increment before call the span function so
5332 // we need to stop one pixel before
5333 x2 = qMin(x2, devRect.x2 - 1);
5335 // completely clipped, so abort
5347 const int index = (ordered ? current : NSPANS - 1 - current);
5348 spans[index].coverage = 255;
5352 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
5353 spans[index].len = 1;
5355 spans[index].len = 0;
5358 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5359 y2 = qMin(y2, devRect.y2 - 1);
5363 incrNE = (dy - dx) * 2;
5366 goto flush_and_return;
5371 if (spans[current].len > 0)
5373 if (current == NSPANS) {
5374 span_func(NSPANS, spans, data);
5381 goto flush_and_return;
5383 spans[current].len = 0;
5384 spans[current].coverage = 255;
5385 spans[current].x = x;
5386 spans[current].y = y;
5389 if (x == devRect.x1)
5390 spans[current].x = devRect.x1;
5393 if (x < devRect.x1 || y < devRect.y1)
5396 Q_ASSERT(x<devRect.x2);
5397 Q_ASSERT(y<devRect.y2);
5398 Q_ASSERT(spans[current].y == y);
5399 spans[current].len++;
5401 if (spans[current].len > 0) {
5404 } else { // 0-45 and 180->225 (unit circle degrees)
5406 y1 = qMin(y1, devRect.y2 - 1);
5410 incrNE = (dy + dx) * 2;
5413 goto flush_and_return;
5418 if (spans[NSPANS - 1 - current].len > 0)
5420 if (current == NSPANS) {
5421 span_func(NSPANS, spans, data);
5428 goto flush_and_return;
5430 const int index = NSPANS - 1 - current;
5431 spans[index].len = 0;
5432 spans[index].coverage = 255;
5437 if (x == devRect.x1)
5438 spans[NSPANS - 1 - current].x = devRect.x1;
5441 if (x < devRect.x1 || y > y1)
5444 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5445 Q_ASSERT(spans[NSPANS - 1 - current].y == y);
5446 spans[NSPANS - 1 - current].len++;
5448 if (spans[NSPANS - 1 - current].len > 0) {
5455 // if y is the major axis:
5457 if (y2 < y1) { /* if coordinates are out of order */
5458 qt_swap_int(y1, y2);
5461 qt_swap_int(x1, x2);
5465 int y_lower_limit = - 128;
5466 if (y1 < y_lower_limit) {
5467 int cx = dx * (y_lower_limit - y1) / dy + x1;
5468 drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect);
5472 if (style == LineDrawNormal)
5475 // In the loops below we increment before call the span function so
5476 // we need to stop one pixel before
5477 y2 = qMin(y2, devRect.y2 - 1);
5479 // completely clipped, so abort
5487 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5488 Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
5489 if (current == NSPANS) {
5490 span_func(NSPANS, spans, data);
5493 spans[current].len = 1;
5494 spans[current].coverage = 255;
5495 spans[current].x = x;
5496 spans[current].y = y;
5500 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5501 x2 = qMin(x2, devRect.x2 - 1);
5504 incrNE = (dx - dy) * 2;
5507 goto flush_and_return;
5514 goto flush_and_return;
5519 if (x < devRect.x1 || y < devRect.y1)
5521 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5522 if (current == NSPANS) {
5523 span_func(NSPANS, spans, data);
5526 spans[current].len = 1;
5527 spans[current].coverage = 255;
5528 spans[current].x = x;
5529 spans[current].y = y;
5532 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5533 x1 = qMin(x1, devRect.x2 - 1);
5536 incrNE = (dx + dy) * 2;
5539 goto flush_and_return;
5546 goto flush_and_return;
5551 if (y < devRect.y1 || x > x1)
5553 Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
5554 if (current == NSPANS) {
5555 span_func(NSPANS, spans, data);
5558 spans[current].len = 1;
5559 spans[current].coverage = 255;
5560 spans[current].x = x;
5561 spans[current].y = y;
5568 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5571 static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
5574 if (--*currentOffset == 0) {
5576 *dashIndex = ((*dashIndex + 1) % pattern.size());
5577 *currentOffset = int(pattern[*dashIndex]);
5582 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
5584 ProcessSpans span_func, QSpanData *data,
5585 LineDrawMode style, const QIntRect &devRect,
5588 #ifdef QT_DEBUG_DRAW
5589 qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
5593 int dx, dy, d, incrE, incrNE;
5598 Q_ASSERT(*patternOffset >= 0);
5600 const QVector<qreal> penPattern = pen->dashPattern();
5601 QVarLengthArray<qreal> pattern(penPattern.size());
5603 int patternLength = 0;
5604 for (int i = 0; i < penPattern.size(); ++i)
5605 patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
5607 // pattern must be reversed if coordinates are out of order
5608 int reverseLength = -1;
5609 if (dy == 0 && x1 > x2)
5610 reverseLength = x1 - x2;
5611 else if (dx == 0 && y1 > y2)
5612 reverseLength = y1 - y2;
5613 else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
5614 reverseLength = qAbs(dx);
5615 else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
5616 reverseLength = qAbs(dy);
5618 const bool reversed = (reverseLength > -1);
5619 if (reversed) { // reverse pattern
5620 for (int i = 0; i < penPattern.size(); ++i)
5621 pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
5623 *patternOffset = (patternLength - 1 - *patternOffset);
5624 *patternOffset += patternLength - (reverseLength % patternLength);
5625 *patternOffset = *patternOffset % patternLength;
5627 for (int i = 0; i < penPattern.size(); ++i)
5628 pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
5632 bool inDash = !reversed;
5633 int currPattern = int(pattern[dashIndex]);
5635 // adjust pattern for offset
5636 offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
5638 const int NSPANS = 256;
5639 QT_FT_Span spans[NSPANS];
5641 bool ordered = true;
5644 // specialcase horizontal lines
5645 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5646 int start_unclipped = qMin(x1, x2);
5647 int start = qMax(devRect.x1, start_unclipped);
5648 int stop = qMax(x1, x2) + 1;
5649 int stop_clipped = qMin(devRect.x2, stop);
5650 int len = stop_clipped - start;
5651 if (style == LineDrawNormal && stop == stop_clipped)
5654 // adjust pattern for starting offset
5655 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5659 while (x < stop_clipped) {
5660 if (current == NSPANS) {
5661 span_func(NSPANS, spans, data);
5664 const int dash = qMin(currPattern, stop_clipped - x);
5666 spans[current].x = ushort(x);
5667 spans[current].len = ushort(dash);
5668 spans[current].y = y1;
5669 spans[current].coverage = 255;
5672 if (dash < currPattern) {
5673 currPattern -= dash;
5675 dashIndex = (dashIndex + 1) % pattern.size();
5676 currPattern = int(pattern[dashIndex]);
5683 goto flush_and_return;
5684 } else if (dx == 0) {
5685 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5686 int start_unclipped = qMin(y1, y2);
5687 int start = qMax(devRect.y1, start_unclipped);
5688 int stop = qMax(y1, y2) + 1;
5689 int stop_clipped = qMin(devRect.y2, stop);
5690 if (style == LineDrawNormal && stop == stop_clipped)
5693 stop = stop_clipped;
5695 // adjust pattern for starting offset
5696 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5701 const int dash = qMin(currPattern, stop - y);
5703 for (int i = 0; i < dash; ++i) {
5704 if (current == NSPANS) {
5705 span_func(NSPANS, spans, data);
5708 spans[current].x = x1;
5709 spans[current].len = 1;
5710 spans[current].coverage = 255;
5711 spans[current].y = ushort(y + i);
5715 if (dash < currPattern) {
5716 currPattern -= dash;
5718 dashIndex = (dashIndex + 1) % pattern.size();
5719 currPattern = int(pattern[dashIndex]);
5725 goto flush_and_return;
5728 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5730 if (x2 < x1) { /* if coordinates are out of order */
5731 qt_swap_int(x1, x2);
5734 qt_swap_int(y1, y2);
5738 if (style == LineDrawNormal)
5741 // In the loops below we increment before call the span function so
5742 // we need to stop one pixel before
5743 x2 = qMin(x2, devRect.x2 - 1);
5745 // completely clipped, so abort
5747 goto flush_and_return;
5752 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
5753 Q_ASSERT(x < devRect.x2);
5755 if (current == NSPANS) {
5756 span_func(NSPANS, spans, data);
5759 spans[current].len = 1;
5760 spans[current].coverage = 255;
5761 spans[current].x = x;
5762 spans[current].y = y;
5765 if (--currPattern <= 0) {
5767 dashIndex = (dashIndex + 1) % pattern.size();
5768 currPattern = int(pattern[dashIndex]);
5772 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5773 y2 = qMin(y2, devRect.y2 - 1);
5777 incrNE = (dy - dx) * 2;
5780 goto flush_and_return;
5787 goto flush_and_return;
5793 const bool skip = x < devRect.x1 || y < devRect.y1;
5794 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5795 if (inDash && !skip) {
5796 if (current == NSPANS) {
5797 span_func(NSPANS, spans, data);
5800 spans[current].len = 1;
5801 spans[current].coverage = 255;
5802 spans[current].x = x;
5803 spans[current].y = y;
5806 if (--currPattern <= 0) {
5808 dashIndex = (dashIndex + 1) % pattern.size();
5809 currPattern = int(pattern[dashIndex]);
5812 } else { // 0-45 and 180->225 (unit circle degrees)
5813 y1 = qMin(y1, devRect.y2 - 1);
5817 incrNE = (dy + dx) * 2;
5820 goto flush_and_return;
5825 span_func(current, spans, data);
5832 goto flush_and_return;
5838 const bool skip = x < devRect.x1 || y > y1;
5839 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5840 if (inDash && !skip) {
5841 if (current == NSPANS) {
5842 span_func(NSPANS, spans, data);
5845 spans[current].len = 1;
5846 spans[current].coverage = 255;
5847 spans[current].x = x;
5848 spans[current].y = y;
5851 if (--currPattern <= 0) {
5853 dashIndex = (dashIndex + 1) % pattern.size();
5854 currPattern = int(pattern[dashIndex]);
5860 // if y is the major axis:
5862 if (y2 < y1) { /* if coordinates are out of order */
5863 qt_swap_int(y1, y2);
5866 qt_swap_int(x1, x2);
5870 if (style == LineDrawNormal)
5873 // In the loops below we increment before call the span function so
5874 // we need to stop one pixel before
5875 y2 = qMin(y2, devRect.y2 - 1);
5877 // completely clipped, so abort
5879 goto flush_and_return;
5884 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5885 Q_ASSERT(x < devRect.x2);
5887 if (current == NSPANS) {
5888 span_func(NSPANS, spans, data);
5891 spans[current].len = 1;
5892 spans[current].coverage = 255;
5893 spans[current].x = x;
5894 spans[current].y = y;
5897 if (--currPattern <= 0) {
5899 dashIndex = (dashIndex + 1) % pattern.size();
5900 currPattern = int(pattern[dashIndex]);
5904 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5905 x2 = qMin(x2, devRect.x2 - 1);
5908 incrNE = (dx - dy) * 2;
5911 goto flush_and_return;
5918 goto flush_and_return;
5923 const bool skip = x < devRect.x1 || y < devRect.y1;
5924 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
5925 if (inDash && !skip) {
5926 if (current == NSPANS) {
5927 span_func(NSPANS, spans, data);
5930 spans[current].len = 1;
5931 spans[current].coverage = 255;
5932 spans[current].x = x;
5933 spans[current].y = y;
5936 if (--currPattern <= 0) {
5938 dashIndex = (dashIndex + 1) % pattern.size();
5939 currPattern = int(pattern[dashIndex]);
5942 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5943 x1 = qMin(x1, devRect.x2 - 1);
5946 incrNE = (dx + dy) * 2;
5949 goto flush_and_return;
5956 goto flush_and_return;
5961 const bool skip = y < devRect.y1 || x > x1;
5962 Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
5963 if (inDash && !skip) {
5964 if (current == NSPANS) {
5965 span_func(NSPANS, spans, data);
5968 spans[current].len = 1;
5969 spans[current].coverage = 255;
5970 spans[current].x = x;
5971 spans[current].y = y;
5974 if (--currPattern <= 0) {
5976 dashIndex = (dashIndex + 1) % pattern.size();
5977 currPattern = int(pattern[dashIndex]);
5984 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5988 *patternOffset = (patternLength - 1 - *patternOffset);
5991 for (int i = 0; i <= dashIndex; ++i)
5992 *patternOffset += int(pattern[i]);
5993 *patternOffset += patternLength - currPattern - 1;
5994 *patternOffset = (*patternOffset % patternLength);
6000 \a x and \a y is relative to the midpoint of \a rect.
6002 static inline void drawEllipsePoints(int x, int y, int length,
6005 ProcessSpans pen_func, ProcessSpans brush_func,
6006 QSpanData *pen_data, QSpanData *brush_data)
6011 QT_FT_Span outline[4];
6012 const int midx = rect.x() + (rect.width() + 1) / 2;
6013 const int midy = rect.y() + (rect.height() + 1) / 2;
6019 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
6020 outline[0].len = qMin(length, x - outline[0].x);
6022 outline[0].coverage = 255;
6026 outline[1].len = length;
6028 outline[1].coverage = 255;
6031 outline[2].x = outline[0].x;
6032 outline[2].len = outline[0].len;
6033 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
6034 outline[2].coverage = 255;
6038 outline[3].len = length;
6039 outline[3].y = outline[2].y;
6040 outline[3].coverage = 255;
6042 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
6046 fill[0].x = outline[0].x + outline[0].len - 1;
6047 fill[0].len = qMax(0, outline[1].x - fill[0].x);
6048 fill[0].y = outline[1].y;
6049 fill[0].coverage = 255;
6052 fill[1].x = outline[2].x + outline[2].len - 1;
6053 fill[1].len = qMax(0, outline[3].x - fill[1].x);
6054 fill[1].y = outline[3].y;
6055 fill[1].coverage = 255;
6057 int n = (fill[0].y >= fill[1].y ? 1 : 2);
6058 n = qt_intersect_spans(fill, n, clip);
6060 brush_func(n, fill, brush_data);
6063 int n = (outline[1].y >= outline[2].y ? 2 : 4);
6064 n = qt_intersect_spans(outline, n, clip);
6066 pen_func(n, outline, pen_data);
6072 Draws an ellipse using the integer point midpoint algorithm.
6074 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
6075 ProcessSpans pen_func, ProcessSpans brush_func,
6076 QSpanData *pen_data, QSpanData *brush_data)
6078 const qreal a = qreal(rect.width()) / 2;
6079 const qreal b = qreal(rect.height()) / 2;
6080 qreal d = b*b - (a*a*b) + 0.25*a*a;
6083 int y = (rect.height() + 1) / 2;
6087 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
6088 if (d < 0) { // select E
6091 } else { // select SE
6092 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
6093 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6094 pen_func, brush_func, pen_data, brush_data);
6099 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6100 pen_func, brush_func, pen_data, brush_data);
6103 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
6104 const int miny = rect.height() & 0x1;
6106 if (d < 0) { // select SE
6107 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
6109 } else { // select S
6110 d += a*a*(-2*y + 3);
6113 drawEllipsePoints(x, y, 1, rect, clip,
6114 pen_func, brush_func, pen_data, brush_data);
6119 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
6122 Draws the first \a pointCount points in the buffer \a points
6124 The default implementation converts the first \a pointCount QPoints in \a points
6125 to QPointFs and calls the floating point version of drawPoints.
6129 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
6132 Reimplement this function to draw the largest ellipse that can be
6133 contained within rectangle \a rect.
6136 #ifdef QT_DEBUG_DRAW
6137 void dumpClip(int width, int height, const QClipData *clip)
6139 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
6140 clipImg.fill(0xffff0000);
6147 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
6149 for (int i = 0; i < clip->count; ++i) {
6150 const QSpan *span = ((QClipData *) clip)->spans() + i;
6151 for (int j = 0; j < span->len; ++j)
6152 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
6153 x0 = qMin(x0, int(span->x));
6154 x1 = qMax(x1, int(span->x + span->len - 1));
6156 y0 = qMin(y0, int(span->y));
6157 y1 = qMax(y1, int(span->y));
6160 static int counter = 0;
6167 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
6168 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));