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>
58 #if defined (Q_WS_X11)
59 # include <private/qfontengine_ft_p.h>
62 // #include <private/qdatabuffer_p.h>
63 // #include <private/qpainter_p.h>
64 #include <private/qmath_p.h>
65 #include <private/qtextengine_p.h>
66 #include <private/qfontengine_p.h>
67 #include <private/qpixmap_raster_p.h>
68 // #include <private/qpolygonclipper_p.h>
69 // #include <private/qrasterizer_p.h>
70 #include <private/qimage_p.h>
71 #include <private/qstatictext_p.h>
72 #include "qmemrotate_p.h"
74 #include "qpaintengine_raster_p.h"
75 // #include "qbezier_p.h"
76 #include "qoutlinemapper_p.h"
79 # include <qt_windows.h>
80 # include <qvarlengtharray.h>
81 # include <private/qfontengine_p.h>
82 # if defined(Q_OS_WINCE)
83 # include "qguifunctions_wince.h"
85 #elif defined(Q_WS_MAC)
86 # include <private/qt_mac_p.h>
87 # include <private/qpixmap_mac_p.h>
88 # include <private/qpaintengine_mac_p.h>
89 #elif defined(Q_WS_QWS)
90 # if !defined(QT_NO_FREETYPE)
91 # include <private/qfontengine_ft_p.h>
93 # if !defined(QT_NO_QWS_QPF2)
94 # include <private/qfontengine_qpf_p.h>
96 # include <private/qabstractfontengine_p.h>
97 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
98 # include <private/qfontengine_s60_p.h>
99 #elif defined(Q_WS_QPA)
100 # include <private/qfontengine_ft_p.h>
103 #if defined(Q_WS_WIN64)
110 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
112 #define qreal_to_fixed_26_6(f) (int(f * 64))
113 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
114 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
116 // #define QT_DEBUG_DRAW
118 void dumpClip(int width, int height, const QClipData *clip);
121 #define QT_FAST_SPANS
124 // A little helper macro to get a better approximation of dimensions.
125 // If we have a rect that starting at 0.5 of width 3.5 it should span
127 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
130 extern bool qt_cleartype_enabled;
134 extern bool qt_applefontsmoothing_enabled;
138 /********************************************************************************
141 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
142 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
143 static void qt_span_clip(int count, const QSpan *spans, void *userData);
144 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
150 Qt::ClipOperation operation;
156 LineDrawIncludeLastPixel
159 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
160 LineDrawMode style, const QIntRect &rect);
161 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
162 QPen *pen, ProcessSpans span_func, QSpanData *data,
163 LineDrawMode style, const QIntRect &devRect,
165 // static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2,
166 // ProcessSpans span_func, QSpanData *data,
167 // LineDrawMode style, const QRect &devRect);
169 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
170 ProcessSpans pen_func, ProcessSpans brush_func,
171 QSpanData *pen_data, QSpanData *brush_data);
173 struct QRasterFloatPoint {
179 static const QRectF boundingRect(const QPointF *points, int pointCount)
181 const QPointF *e = points;
182 const QPointF *last = points + pointCount;
183 qreal minx, maxx, miny, maxy;
184 minx = maxx = e->x();
185 miny = maxy = e->y();
189 else if (e->x() > maxx)
193 else if (e->y() > maxy)
196 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
200 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
201 return (elementCount == 5 // 5-point polygon, check for closed rect
202 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
203 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
204 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
205 && pts[0] < pts[4] && pts[1] < pts[5]
207 (elementCount == 4 // 4-point polygon, check for unclosed rect
208 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
209 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
210 && pts[0] < pts[4] && pts[1] < pts[5]
215 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
217 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
220 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
222 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
225 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
226 qfixed c2x, qfixed c2y,
227 qfixed ex, qfixed ey,
230 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
231 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
232 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
236 #if !defined(QT_NO_DEBUG) && 0
237 static void qt_debug_path(const QPainterPath &path)
239 const char *names[] = {
246 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
247 for (int i=0; i<path.elementCount(); ++i) {
248 const QPainterPath::Element &e = path.elementAt(i);
249 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
250 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
255 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
256 QPaintEngineExPrivate(),
263 \class QRasterPaintEngine
268 \brief The QRasterPaintEngine class enables hardware acceleration
269 of painting operations in Qt for Embedded Linux.
271 Note that this functionality is only available in
272 \l{Qt for Embedded Linux}.
274 In \l{Qt for Embedded Linux}, painting is a pure software
275 implementation. But starting with Qt 4.2, it is
276 possible to add an accelerated graphics driver to take advantage
277 of available hardware resources.
279 Hardware acceleration is accomplished by creating a custom screen
280 driver, accelerating the copying from memory to the screen, and
281 implementing a custom paint engine accelerating the various
282 painting operations. Then a custom paint device (derived from the
283 QCustomRasterPaintDevice class) and a custom window surface
284 (derived from QWSWindowSurface) must be implemented to make
285 \l{Qt for Embedded Linux} aware of the accelerated driver.
287 \note The QRasterPaintEngine class does not support 8-bit images.
288 Instead, they need to be converted to a supported format, such as
289 QImage::Format_ARGB32_Premultiplied.
291 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
292 documentation for details.
294 \sa QCustomRasterPaintDevice, QPaintEngine
298 \fn Type QRasterPaintEngine::type() const
304 \relates QRasterPaintEngine
306 A struct equivalent to QT_FT_Span, containing a position (x,
307 y), the span's length in pixels and its color/coverage (a value
308 ranging from 0 to 255).
314 Creates a raster based paint engine for operating on the given
315 \a device, with the complete set of \l
316 {QPaintEngine::PaintEngineFeature}{paint engine features and
319 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
320 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
322 d_func()->device = device;
329 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
332 d_func()->device = device;
336 void QRasterPaintEngine::init()
338 Q_D(QRasterPaintEngine);
345 // The antialiasing raster.
346 d->grayRaster.reset(new QT_FT_Raster);
347 Q_CHECK_PTR(d->grayRaster.data());
348 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
349 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
352 d->rasterizer.reset(new QRasterizer);
353 d->rasterBuffer.reset(new QRasterBuffer());
354 d->outlineMapper.reset(new QOutlineMapper);
355 d->outlinemapper_xform_dirty = true;
357 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
358 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
359 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
361 d->baseClip.reset(new QClipData(d->device->height()));
362 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
364 d->image_filler.init(d->rasterBuffer.data(), this);
365 d->image_filler.type = QSpanData::Texture;
367 d->image_filler_xform.init(d->rasterBuffer.data(), this);
368 d->image_filler_xform.type = QSpanData::Texture;
370 d->solid_color_filler.init(d->rasterBuffer.data(), this);
371 d->solid_color_filler.type = QSpanData::Solid;
373 d->deviceDepth = d->device->depth();
375 d->mono_surface = false;
376 gccaps &= ~PorterDuff;
378 QImage::Format format = QImage::Format_Invalid;
380 switch (d->device->devType()) {
381 case QInternal::Pixmap:
382 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
384 case QInternal::Image:
385 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
388 case QInternal::CustomRaster:
389 d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
393 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
399 case QImage::Format_MonoLSB:
400 case QImage::Format_Mono:
401 d->mono_surface = true;
403 case QImage::Format_ARGB8565_Premultiplied:
404 case QImage::Format_ARGB8555_Premultiplied:
405 case QImage::Format_ARGB6666_Premultiplied:
406 case QImage::Format_ARGB4444_Premultiplied:
407 case QImage::Format_ARGB32_Premultiplied:
408 case QImage::Format_ARGB32:
409 gccaps |= PorterDuff;
411 case QImage::Format_RGB32:
412 case QImage::Format_RGB444:
413 case QImage::Format_RGB555:
414 case QImage::Format_RGB666:
415 case QImage::Format_RGB888:
416 case QImage::Format_RGB16:
427 Destroys this paint engine.
429 QRasterPaintEngine::~QRasterPaintEngine()
431 Q_D(QRasterPaintEngine);
433 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
439 bool QRasterPaintEngine::begin(QPaintDevice *device)
441 Q_D(QRasterPaintEngine);
443 if (device->devType() == QInternal::Pixmap) {
444 QPixmap *pixmap = static_cast<QPixmap *>(device);
445 QPixmapData *pd = pixmap->pixmapData();
446 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
447 d->device = pd->buffer();
452 // Make sure QPaintEngine::paintDevice() returns the proper device.
455 Q_ASSERT(d->device->devType() == QInternal::Image
456 || d->device->devType() == QInternal::CustomRaster);
458 d->systemStateChanged();
460 QRasterPaintEngineState *s = state();
461 ensureOutlineMapper();
462 d->outlineMapper->m_clip_rect = d->deviceRect;
464 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
465 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
466 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
467 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
469 d->rasterizer->setClipRect(d->deviceRect);
471 s->penData.init(d->rasterBuffer.data(), this);
472 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
473 s->stroker = &d->basicStroker;
474 d->basicStroker.setClipRect(d->deviceRect);
476 s->brushData.init(d->rasterBuffer.data(), this);
477 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
479 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
481 setDirty(DirtyBrushOrigin);
484 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
485 << ") devType:" << device->devType()
486 << "devRect:" << d->deviceRect;
488 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
492 #if defined(Q_WS_WIN)
493 d->isPlain45DegreeRotation = true;
497 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
498 #if defined(Q_WS_WIN)
499 else if (qt_cleartype_enabled)
500 #elif defined (Q_WS_MAC)
501 else if (qt_applefontsmoothing_enabled)
506 QImage::Format format = static_cast<QImage *>(d->device)->format();
507 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
508 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
510 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
512 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
521 bool QRasterPaintEngine::end()
524 Q_D(QRasterPaintEngine);
525 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
527 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
537 void QRasterPaintEngine::releaseBuffer()
539 Q_D(QRasterPaintEngine);
540 d->rasterBuffer.reset(new QRasterBuffer);
546 QSize QRasterPaintEngine::size() const
548 Q_D(const QRasterPaintEngine);
549 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
556 void QRasterPaintEngine::saveBuffer(const QString &s) const
558 Q_D(const QRasterPaintEngine);
559 d->rasterBuffer->bufferImage().save(s, "PNG");
566 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
568 QRasterPaintEngineState *s = state();
569 // FALCON: get rid of this line, see drawImage call below.
571 QTransform::TransformationType txop = s->matrix.type();
575 case QTransform::TxNone:
576 s->flags.int_xform = true;
579 case QTransform::TxTranslate:
580 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
581 && qreal(int(s->matrix.dy())) == s->matrix.dy();
584 case QTransform::TxScale:
585 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
586 && qreal(int(s->matrix.dy())) == s->matrix.dy()
587 && qreal(int(s->matrix.m11())) == s->matrix.m11()
588 && qreal(int(s->matrix.m22())) == s->matrix.m22();
591 default: // shear / perspective...
592 s->flags.int_xform = false;
596 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
598 ensureOutlineMapper();
601 Q_D(QRasterPaintEngine);
602 d->isPlain45DegreeRotation = false;
603 if (txop >= QTransform::TxRotate) {
604 d->isPlain45DegreeRotation =
605 (qFuzzyIsNull(matrix.m11())
606 && qFuzzyIsNull(matrix.m12() - qreal(1))
607 && qFuzzyIsNull(matrix.m21() + qreal(1))
608 && qFuzzyIsNull(matrix.m22())
611 (qFuzzyIsNull(matrix.m11() + qreal(1))
612 && qFuzzyIsNull(matrix.m12())
613 && qFuzzyIsNull(matrix.m21())
614 && qFuzzyIsNull(matrix.m22() + qreal(1))
617 (qFuzzyIsNull(matrix.m11())
618 && qFuzzyIsNull(matrix.m12() + qreal(1))
619 && qFuzzyIsNull(matrix.m21() - qreal(1))
620 && qFuzzyIsNull(matrix.m22())
630 QRasterPaintEngineState::~QRasterPaintEngineState()
632 if (flags.has_clip_ownership)
637 QRasterPaintEngineState::QRasterPaintEngineState()
649 flags.fast_pen = true;
650 flags.antialiased = false;
651 flags.bilinear = false;
652 flags.fast_text = true;
653 flags.int_xform = true;
654 flags.tx_noshear = true;
655 flags.fast_images = true;
658 flags.has_clip_ownership = false;
663 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
668 , strokeFlags(s.strokeFlags)
669 , lastBrush(s.lastBrush)
670 , brushData(s.brushData)
671 , fillFlags(s.fillFlags)
672 , pixmapFlags(s.pixmapFlags)
673 , intOpacity(s.intOpacity)
677 , flag_bits(s.flag_bits)
679 brushData.tempImage = 0;
680 penData.tempImage = 0;
681 flags.has_clip_ownership = false;
687 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
689 QRasterPaintEngineState *s;
691 s = new QRasterPaintEngineState();
693 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
701 void QRasterPaintEngine::setState(QPainterState *s)
703 Q_D(QRasterPaintEngine);
704 QPaintEngineEx::setState(s);
705 d->rasterBuffer->compositionMode = s->composition_mode;
709 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
714 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
721 void QRasterPaintEngine::penChanged()
724 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
726 QRasterPaintEngineState *s = state();
727 s->strokeFlags |= DirtyPen;
728 s->dirty |= DirtyPen;
734 void QRasterPaintEngine::updatePen(const QPen &pen)
736 Q_D(QRasterPaintEngine);
737 QRasterPaintEngineState *s = state();
739 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
742 Qt::PenStyle pen_style = qpen_style(pen);
747 s->penData.clip = d->clip();
748 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
750 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
751 || pen.brush().transform().type() >= QTransform::TxNone) {
752 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
755 // Slightly ugly handling of an uncommon case... We need to change
756 // the pen because it is reused in draw_midpoint to decide dashed
758 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
759 pen_style = Qt::SolidLine;
760 s->lastPen.setStyle(Qt::SolidLine);
763 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
764 d->basicStroker.setCapStyle(qpen_capStyle(pen));
765 d->basicStroker.setMiterLimit(pen.miterLimit());
767 qreal penWidth = qpen_widthf(pen);
769 d->basicStroker.setStrokeWidth(1);
771 d->basicStroker.setStrokeWidth(penWidth);
773 if(pen_style == Qt::SolidLine) {
774 s->stroker = &d->basicStroker;
775 } else if (pen_style != Qt::NoPen) {
777 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
778 if (pen.isCosmetic()) {
779 d->dashStroker->setClipRect(d->deviceRect);
781 // ### I've seen this inverted devrect multiple places now...
782 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
783 d->dashStroker->setClipRect(clipRect);
785 d->dashStroker->setDashPattern(pen.dashPattern());
786 d->dashStroker->setDashOffset(pen.dashOffset());
787 s->stroker = d->dashStroker.data();
792 s->flags.fast_pen = pen_style > Qt::NoPen
794 && !s->flags.antialiased
795 && (penWidth == 0 || (penWidth <= 1
796 && (s->matrix.type() <= QTransform::TxTranslate
797 || pen.isCosmetic())));
799 ensureState(); // needed because of tx_noshear...
800 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
810 void QRasterPaintEngine::brushOriginChanged()
812 QRasterPaintEngineState *s = state();
814 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
817 s->fillFlags |= DirtyBrushOrigin;
824 void QRasterPaintEngine::brushChanged()
826 QRasterPaintEngineState *s = state();
828 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
830 s->fillFlags |= DirtyBrush;
839 void QRasterPaintEngine::updateBrush(const QBrush &brush)
842 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
844 Q_D(QRasterPaintEngine);
845 QRasterPaintEngineState *s = state();
846 // must set clip prior to setup, as setup uses it...
847 s->brushData.clip = d->clip();
848 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
849 if (s->fillFlags & DirtyTransform
850 || brush.transform().type() >= QTransform::TxNone)
851 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
852 s->lastBrush = brush;
856 void QRasterPaintEngine::updateOutlineMapper()
858 Q_D(QRasterPaintEngine);
859 d->outlineMapper->setMatrix(state()->matrix);
862 void QRasterPaintEngine::updateState()
864 QRasterPaintEngineState *s = state();
866 if (s->dirty & DirtyTransform)
867 updateMatrix(s->matrix);
869 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
870 const QPainter::CompositionMode mode = s->composition_mode;
871 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
872 && s->intOpacity == 256
873 && (mode == QPainter::CompositionMode_Source
874 || (mode == QPainter::CompositionMode_SourceOver
875 && qAlpha(s->penData.solid.color) == 255));
885 void QRasterPaintEngine::opacityChanged()
887 QRasterPaintEngineState *s = state();
890 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
893 s->fillFlags |= DirtyOpacity;
894 s->strokeFlags |= DirtyOpacity;
895 s->pixmapFlags |= DirtyOpacity;
896 s->dirty |= DirtyOpacity;
897 s->intOpacity = (int) (s->opacity * 256);
903 void QRasterPaintEngine::compositionModeChanged()
905 Q_D(QRasterPaintEngine);
906 QRasterPaintEngineState *s = state();
909 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
912 s->fillFlags |= DirtyCompositionMode;
913 s->dirty |= DirtyCompositionMode;
915 s->strokeFlags |= DirtyCompositionMode;
916 d->rasterBuffer->compositionMode = s->composition_mode;
918 d->recalculateFastImages();
924 void QRasterPaintEngine::renderHintsChanged()
926 QRasterPaintEngineState *s = state();
929 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
932 bool was_aa = s->flags.antialiased;
933 bool was_bilinear = s->flags.bilinear;
935 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
936 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
938 if (was_aa != s->flags.antialiased)
939 s->strokeFlags |= DirtyHints;
941 if (was_bilinear != s->flags.bilinear) {
942 s->strokeFlags |= DirtyPen;
943 s->fillFlags |= DirtyBrush;
946 Q_D(QRasterPaintEngine);
947 d->recalculateFastImages();
953 void QRasterPaintEngine::transformChanged()
955 QRasterPaintEngineState *s = state();
958 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
961 s->fillFlags |= DirtyTransform;
962 s->strokeFlags |= DirtyTransform;
964 s->dirty |= DirtyTransform;
966 Q_D(QRasterPaintEngine);
967 d->recalculateFastImages();
973 void QRasterPaintEngine::clipEnabledChanged()
975 QRasterPaintEngineState *s = state();
978 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
982 s->clip->enabled = s->clipEnabled;
983 s->fillFlags |= DirtyClipEnabled;
984 s->strokeFlags |= DirtyClipEnabled;
985 s->pixmapFlags |= DirtyClipEnabled;
990 void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
992 rasterBuffer->prepare(device);
996 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
998 SrcOverBlendFunc func,
1003 if (alpha == 0 || !clip.isValid())
1006 Q_ASSERT(img.depth() >= 8);
1008 int srcBPL = img.bytesPerLine();
1009 const uchar *srcBits = img.bits();
1010 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1011 int iw = img.width();
1012 int ih = img.height();
1014 if (!sr.isEmpty()) {
1017 // Adjust the image according to the source offset...
1018 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1021 // adapt the x parameters
1022 int x = qRound(pt.x());
1024 int cx2 = clip.x() + clip.width();
1027 srcBits += srcSize * d;
1032 int d = x + iw - cx2;
1038 // adapt the y paremeters...
1040 int cy2 = clip.y() + clip.height();
1041 int y = qRound(pt.y());
1044 srcBits += srcBPL * d;
1049 int d = y + ih - cy2;
1055 // call the blend function...
1056 int dstSize = rasterBuffer->bytesPerPixel();
1057 int dstBPL = rasterBuffer->bytesPerLine();
1058 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1065 void QRasterPaintEnginePrivate::systemStateChanged()
1067 QRect clipRect(0, 0,
1068 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1069 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1071 if (!systemClip.isEmpty()) {
1072 QRegion clippedDeviceRgn = systemClip & clipRect;
1073 deviceRect = clippedDeviceRgn.boundingRect();
1074 baseClip->setClipRegion(clippedDeviceRgn);
1076 deviceRect = clipRect;
1077 baseClip->setClipRect(deviceRect);
1079 #ifdef QT_DEBUG_DRAW
1080 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1083 exDeviceRect = deviceRect;
1085 Q_Q(QRasterPaintEngine);
1086 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1087 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1088 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1091 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1093 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1096 Q_Q(QRasterPaintEngine);
1097 bool bilinear = q->state()->flags.bilinear;
1099 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1100 spanData->setupMatrix(b.transform() * m, bilinear);
1102 if (m.type() <= QTransform::TxTranslate) {
1103 // specialize setupMatrix for translation matrices
1104 // to avoid needless matrix inversion
1112 spanData->dx = -m.dx();
1113 spanData->dy = -m.dy();
1114 spanData->txop = m.type();
1115 spanData->bilinear = bilinear;
1116 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1117 spanData->adjustSpanMethods();
1119 spanData->setupMatrix(m, bilinear);
1124 // #define QT_CLIPPING_RATIOS
1126 #ifdef QT_CLIPPING_RATIOS
1131 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1133 if (d->clip()->hasRectClip)
1135 if (d->clip()->hasRegionClip)
1139 if ((totalClips % 5000) == 0) {
1140 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1141 rectClips * 100.0 / (qreal) totalClips,
1142 regionClips * 100.0 / (qreal) totalClips,
1143 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1152 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1154 if (s->flags.has_clip_ownership)
1157 s->flags.has_clip_ownership = false;
1160 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1162 s->fillFlags |= QPaintEngine::DirtyClipPath;
1163 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1164 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1166 d->solid_color_filler.clip = d->clip();
1167 d->solid_color_filler.adjustSpanMethods();
1169 #ifdef QT_DEBUG_DRAW
1170 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1179 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1181 #ifdef QT_DEBUG_DRAW
1182 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1184 if (path.elements()) {
1185 for (int i=0; i<path.elementCount(); ++i) {
1186 qDebug() << " - " << path.elements()[i]
1187 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1190 for (int i=0; i<path.elementCount(); ++i) {
1191 qDebug() << " ---- "
1192 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1197 Q_D(QRasterPaintEngine);
1198 QRasterPaintEngineState *s = state();
1200 const qreal *points = path.points();
1201 const QPainterPath::ElementType *types = path.elements();
1203 // There are some cases that are not supported by clip(QRect)
1204 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1205 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1206 if (s->matrix.type() <= QTransform::TxScale
1207 && ((path.shape() == QVectorPath::RectangleHint)
1208 || (isRect(points, path.elementCount())
1209 && (!types || (types[0] == QPainterPath::MoveToElement
1210 && types[1] == QPainterPath::LineToElement
1211 && types[2] == QPainterPath::LineToElement
1212 && types[3] == QPainterPath::LineToElement))))) {
1213 #ifdef QT_DEBUG_DRAW
1214 qDebug() << " --- optimizing vector clip to rect clip...";
1217 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1218 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1223 if (op == Qt::NoClip) {
1224 qrasterpaintengine_state_setNoClip(s);
1227 QClipData *base = d->baseClip.data();
1229 // Intersect with current clip when available...
1230 if (op == Qt::IntersectClip && s->clip)
1233 // We always intersect, except when there is nothing to
1234 // intersect with, in which case we simplify the operation to
1236 Qt::ClipOperation isectOp = Qt::IntersectClip;
1238 isectOp = Qt::ReplaceClip;
1240 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1241 newClip->initialize();
1242 ClipData clipData = { base, newClip, isectOp };
1243 ensureOutlineMapper();
1244 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1248 if (op == Qt::UniteClip) {
1250 QClipData *result = new QClipData(d->rasterBuffer->height());
1251 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1252 qt_merge_clip(current, newClip, result);
1260 if (s->flags.has_clip_ownership)
1264 s->flags.has_clip_ownership = true;
1266 qrasterpaintengine_dirty_clip(d, s);
1274 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1276 #ifdef QT_DEBUG_DRAW
1277 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1280 QRasterPaintEngineState *s = state();
1282 if (op == Qt::NoClip) {
1283 qrasterpaintengine_state_setNoClip(s);
1285 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1286 QPaintEngineEx::clip(rect, op);
1289 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1290 QPaintEngineEx::clip(rect, op);
1296 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1298 Q_D(QRasterPaintEngine);
1299 QRect clipRect = r & d->deviceRect;
1300 QRasterPaintEngineState *s = state();
1302 if (op == Qt::ReplaceClip || s->clip == 0) {
1304 // No current clip, hence we intersect with sysclip and be
1306 QRegion clipRegion = systemClip();
1307 QClipData *clip = new QClipData(d->rasterBuffer->height());
1309 if (clipRegion.isEmpty())
1310 clip->setClipRect(clipRect);
1312 clip->setClipRegion(clipRegion & clipRect);
1314 if (s->flags.has_clip_ownership)
1318 s->clip->enabled = true;
1319 s->flags.has_clip_ownership = true;
1321 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1322 QClipData *base = s->clip;
1325 if (base->hasRectClip || base->hasRegionClip) {
1326 if (!s->flags.has_clip_ownership) {
1327 s->clip = new QClipData(d->rasterBuffer->height());
1328 s->flags.has_clip_ownership = true;
1330 if (base->hasRectClip)
1331 s->clip->setClipRect(base->clipRect & clipRect);
1333 s->clip->setClipRegion(base->clipRegion & clipRect);
1334 s->clip->enabled = true;
1342 qrasterpaintengine_dirty_clip(d, s);
1350 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1352 #ifdef QT_DEBUG_DRAW
1353 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1356 Q_D(QRasterPaintEngine);
1358 if (region.rectCount() == 1) {
1359 clip(region.boundingRect(), op);
1363 QRasterPaintEngineState *s = state();
1364 const QClipData *clip = d->clip();
1365 const QClipData *baseClip = d->baseClip.data();
1367 if (op == Qt::NoClip) {
1368 qrasterpaintengine_state_setNoClip(s);
1369 } else if (s->matrix.type() > QTransform::TxScale
1370 || op == Qt::UniteClip
1371 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1372 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1373 QPaintEngineEx::clip(region, op);
1375 const QClipData *curClip;
1378 if (op == Qt::IntersectClip)
1383 if (s->flags.has_clip_ownership) {
1387 newClip = new QClipData(d->rasterBuffer->height());
1389 s->flags.has_clip_ownership = true;
1392 QRegion r = s->matrix.map(region);
1393 if (curClip->hasRectClip)
1394 newClip->setClipRegion(r & curClip->clipRect);
1395 else if (curClip->hasRegionClip)
1396 newClip->setClipRegion(r & curClip->clipRegion);
1398 qrasterpaintengine_dirty_clip(d, s);
1405 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1407 #ifdef QT_DEBUG_DRAW
1408 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1411 if (!fillData->blend)
1414 Q_D(QRasterPaintEngine);
1416 const QRectF controlPointRect = path.controlPointRect();
1418 QRasterPaintEngineState *s = state();
1419 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1420 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1421 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1422 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1423 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1424 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1426 if (!s->flags.antialiased && !do_clip) {
1427 d->initializeRasterizer(fillData);
1428 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1432 ensureOutlineMapper();
1433 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1436 static void fillRect_normalized(const QRect &r, QSpanData *data,
1437 QRasterPaintEnginePrivate *pe)
1441 bool rectClipped = true;
1444 x1 = qMax(r.x(), data->clip->xmin);
1445 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1446 y1 = qMax(r.y(), data->clip->ymin);
1447 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1448 rectClipped = data->clip->hasRectClip;
1451 x1 = qMax(r.x(), pe->deviceRect.x());
1452 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1453 y1 = qMax(r.y(), pe->deviceRect.y());
1454 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1456 x1 = qMax(r.x(), 0);
1457 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1458 y1 = qMax(r.y(), 0);
1459 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1462 if (x2 <= x1 || y2 <= y1)
1465 const int width = x2 - x1;
1466 const int height = y2 - y1;
1468 bool isUnclipped = rectClipped
1469 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1471 if (pe && isUnclipped) {
1472 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1474 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1475 || (mode == QPainter::CompositionMode_SourceOver
1476 && qAlpha(data->solid.color) == 255)))
1478 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1484 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1486 const int nspans = 256;
1487 QT_FT_Span spans[nspans];
1489 Q_ASSERT(data->blend);
1492 int n = qMin(nspans, y2 - y);
1496 spans[i].len = width;
1498 spans[i].coverage = 255;
1502 blend(n, spans, data);
1510 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1512 #ifdef QT_DEBUG_DRAW
1513 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1515 Q_D(QRasterPaintEngine);
1516 QRasterPaintEngineState *s = state();
1520 if (s->brushData.blend) {
1521 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1522 const QRect *r = rects;
1523 const QRect *lastRect = rects + rectCount;
1525 int offset_x = int(s->matrix.dx());
1526 int offset_y = int(s->matrix.dy());
1527 while (r < lastRect) {
1528 QRect rect = r->normalized();
1529 QRect rr = rect.translated(offset_x, offset_y);
1530 fillRect_normalized(rr, &s->brushData, d);
1534 QRectVectorPath path;
1535 for (int i=0; i<rectCount; ++i) {
1537 fill(path, s->brush);
1543 if (s->penData.blend) {
1544 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1545 const QRect *r = rects;
1546 const QRect *lastRect = rects + rectCount;
1547 while (r < lastRect) {
1549 int right = r->x() + r->width();
1551 int bottom = r->y() + r->height();
1554 int pts[] = { top, left,
1559 int pts[] = { left, top,
1565 strokePolygonCosmetic((QPoint *) pts, 4, WindingMode);
1569 QRectVectorPath path;
1570 for (int i = 0; i < rectCount; ++i) {
1572 stroke(path, s->pen);
1581 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1583 #ifdef QT_DEBUG_DRAW
1584 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1586 #ifdef QT_FAST_SPANS
1587 Q_D(QRasterPaintEngine);
1588 QRasterPaintEngineState *s = state();
1592 if (s->flags.tx_noshear) {
1594 if (s->brushData.blend) {
1595 d->initializeRasterizer(&s->brushData);
1596 for (int i = 0; i < rectCount; ++i) {
1597 const QRectF &rect = rects[i].normalized();
1600 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1601 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1602 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1607 if (s->penData.blend) {
1608 qreal width = s->pen.isCosmetic()
1609 ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF())
1610 : s->lastPen.widthF() * s->txscale;
1612 if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
1613 for (int i = 0; i < rectCount; ++i) {
1614 const QRectF &r = rects[i];
1616 qreal right = r.x() + r.width();
1618 qreal bottom = r.y() + r.height();
1619 qreal pts[] = { left, top,
1623 strokePolygonCosmetic((QPointF *) pts, 4, WindingMode);
1625 } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) {
1626 d->initializeRasterizer(&s->penData);
1628 for (int i = 0; i < rectCount; ++i) {
1629 const QRectF &rect = rects[i].normalized();
1630 if (rect.isEmpty()) {
1631 qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() };
1632 QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint);
1633 QPaintEngineEx::stroke(vp, s->lastPen);
1635 const QPointF tl = s->matrix.map(rect.topLeft());
1636 const QPointF tr = s->matrix.map(rect.topRight());
1637 const QPointF bl = s->matrix.map(rect.bottomLeft());
1638 const QPointF br = s->matrix.map(rect.bottomRight());
1639 const qreal w = width / (rect.width() * s->txscale);
1640 const qreal h = width / (rect.height() * s->txscale);
1641 d->rasterizer->rasterizeLine(tl, tr, w); // top
1642 d->rasterizer->rasterizeLine(bl, br, w); // bottom
1643 d->rasterizer->rasterizeLine(bl, tl, h); // left
1644 d->rasterizer->rasterizeLine(br, tr, h); // right
1648 for (int i = 0; i < rectCount; ++i) {
1649 const QRectF &r = rects[i];
1651 qreal right = r.x() + r.width();
1653 qreal bottom = r.y() + r.height();
1654 qreal pts[] = { left, top,
1659 QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
1660 QPaintEngineEx::stroke(vp, s->lastPen);
1667 #endif // QT_FAST_SPANS
1668 QPaintEngineEx::drawRects(rects, rectCount);
1675 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1677 QRasterPaintEngineState *s = state();
1679 if (!s->penData.blend)
1682 if (s->flags.fast_pen && !path.isCurved()
1683 && s->lastPen.brush().isOpaque()) {
1684 int count = path.elementCount();
1685 QPointF *points = (QPointF *) path.points();
1686 const QPainterPath::ElementType *types = path.elements();
1690 while (first < count) {
1691 while (first < count && types[first] != QPainterPath::MoveToElement) ++first;
1693 while (last < count && types[last] == QPainterPath::LineToElement) ++last;
1694 strokePolygonCosmetic(points + first, last - first,
1695 path.hasImplicitClose() && last == count // only close last one..
1701 strokePolygonCosmetic(points, count,
1702 path.hasImplicitClose()
1707 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1708 qreal width = s->lastPen.isCosmetic()
1709 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1710 : qpen_widthf(s->lastPen) * s->txscale;
1712 qreal dashOffset = s->lastPen.dashOffset();
1714 qreal patternLength = 0;
1715 const QVector<qreal> pattern = s->lastPen.dashPattern();
1716 for (int i = 0; i < pattern.size(); ++i)
1717 patternLength += pattern.at(i);
1719 if (patternLength > 0) {
1720 int n = qFloor(dashOffset / patternLength);
1721 dashOffset -= n * patternLength;
1722 while (dashOffset >= pattern.at(dashIndex)) {
1723 dashOffset -= pattern.at(dashIndex);
1724 if (++dashIndex >= pattern.size())
1730 Q_D(QRasterPaintEngine);
1731 d->initializeRasterizer(&s->penData);
1732 int lineCount = path.elementCount() / 2;
1733 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1735 for (int i = 0; i < lineCount; ++i) {
1736 if (lines[i].p1() == lines[i].p2()) {
1737 if (s->lastPen.capStyle() != Qt::FlatCap) {
1738 QPointF p = lines[i].p1();
1739 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1740 QPointF(p.x() + width*0.5, p.y())));
1741 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1746 const QLineF line = s->matrix.map(lines[i]);
1747 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1748 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1749 width / line.length(),
1750 s->lastPen.capStyle() == Qt::SquareCap);
1752 d->rasterizeLine_dashed(line, width,
1753 &dashIndex, &dashOffset, &inDash);
1758 QPaintEngineEx::stroke(path, pen);
1761 static inline QRect toNormalizedFillRect(const QRectF &rect)
1763 int x1 = qRound(rect.x());
1764 int y1 = qRound(rect.y());
1765 int x2 = qRound(rect.right());
1766 int y2 = qRound(rect.bottom());
1773 return QRect(x1, y1, x2 - x1, y2 - y1);
1779 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1783 #ifdef QT_DEBUG_DRAW
1784 QRectF rf = path.controlPointRect();
1785 qDebug() << "QRasterPaintEngine::fill(): "
1786 << "size=" << path.elementCount()
1787 << ", hints=" << hex << path.hints()
1791 Q_D(QRasterPaintEngine);
1792 QRasterPaintEngineState *s = state();
1795 if (!s->brushData.blend)
1798 if (path.shape() == QVectorPath::RectangleHint) {
1799 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1800 const qreal *p = path.points();
1801 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1802 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1803 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1807 if (s->flags.tx_noshear) {
1808 d->initializeRasterizer(&s->brushData);
1809 // ### Is normalizing really necessary here?
1810 const qreal *p = path.points();
1811 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1813 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1814 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1815 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1821 if (path.shape() == QVectorPath::EllipseHint) {
1822 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1823 const qreal *p = path.points();
1824 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1825 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1826 QRectF r = s->matrix.mapRect(QRectF(tl, br));
1828 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
1829 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
1830 const QRect brect = QRect(int(r.x()), int(r.y()),
1831 int_dim(r.x(), r.width()),
1832 int_dim(r.y(), r.height()));
1834 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
1835 &s->penData, &s->brushData);
1841 // ### Optimize for non transformed ellipses and rectangles...
1842 QRectF cpRect = path.controlPointRect();
1843 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1844 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1847 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1848 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1849 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1850 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1852 // ### Falonc: implement....
1853 // if (!s->flags.antialiased && !do_clip) {
1854 // d->initializeRasterizer(&s->brushData);
1855 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1859 ensureOutlineMapper();
1860 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1863 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1865 Q_D(QRasterPaintEngine);
1866 QRasterPaintEngineState *s = state();
1868 if (!s->flags.antialiased) {
1869 uint txop = s->matrix.type();
1870 if (txop == QTransform::TxNone) {
1871 fillRect_normalized(toNormalizedFillRect(r), data, d);
1873 } else if (txop == QTransform::TxTranslate) {
1874 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1875 fillRect_normalized(rr, data, d);
1877 } else if (txop == QTransform::TxScale) {
1878 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1879 fillRect_normalized(rr, data, d);
1884 if (s->flags.tx_noshear) {
1885 d->initializeRasterizer(data);
1886 QRectF nr = r.normalized();
1887 if (!nr.isEmpty()) {
1888 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1889 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1890 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1897 ensureOutlineMapper();
1898 fillPath(path, data);
1904 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1906 #ifdef QT_DEBUG_DRAW
1907 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1909 QRasterPaintEngineState *s = state();
1912 if (!s->brushData.blend)
1915 fillRect(r, &s->brushData);
1921 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1923 #ifdef QT_DEBUG_DRAW
1924 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1926 Q_D(QRasterPaintEngine);
1927 QRasterPaintEngineState *s = state();
1929 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1930 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1931 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1934 d->solid_color_filler.clip = d->clip();
1935 d->solid_color_filler.adjustSpanMethods();
1936 fillRect(r, &d->solid_color_filler);
1939 static inline bool isAbove(const QPointF *a, const QPointF *b)
1941 return a->y() < b->y();
1944 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1949 Q_ASSERT(pointCount >= 2);
1951 QVector<const QPointF *> sorted;
1952 sorted.reserve(pointCount);
1954 upper->reserve(pointCount * 3 / 4);
1955 lower->reserve(pointCount * 3 / 4);
1957 for (int i = 0; i < pointCount; ++i)
1958 sorted << points + i;
1960 qSort(sorted.begin(), sorted.end(), isAbove);
1962 qreal splitY = sorted.at(sorted.size() / 2)->y();
1964 const QPointF *end = points + pointCount;
1965 const QPointF *last = end - 1;
1967 QVector<QPointF> *bin[2] = { upper, lower };
1969 for (const QPointF *p = points; p < end; ++p) {
1970 int side = p->y() < splitY;
1971 int lastSide = last->y() < splitY;
1973 if (side != lastSide) {
1974 if (qFuzzyCompare(p->y(), splitY)) {
1975 bin[!side]->append(*p);
1976 } else if (qFuzzyCompare(last->y(), splitY)) {
1977 bin[side]->append(*last);
1979 QPointF delta = *p - *last;
1980 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1982 bin[0]->append(intersection);
1983 bin[1]->append(intersection);
1987 bin[side]->append(*p);
1992 // give up if we couldn't reduce the point count
1993 return upper->size() < pointCount && lower->size() < pointCount;
1999 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2001 Q_D(QRasterPaintEngine);
2002 QRasterPaintEngineState *s = state();
2004 const int maxPoints = 0xffff;
2006 // max amount of points that raster engine can reliably handle
2007 if (pointCount > maxPoints) {
2008 QVector<QPointF> upper, lower;
2010 if (splitPolygon(points, pointCount, &upper, &lower)) {
2011 fillPolygon(upper.constData(), upper.size(), mode);
2012 fillPolygon(lower.constData(), lower.size(), mode);
2014 qWarning("Polygon too complex for filling.");
2019 // Compose polygon fill..,
2020 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2021 ensureOutlineMapper();
2022 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
2025 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2027 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
2033 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2035 QRasterPaintEngineState *s = state();
2037 #ifdef QT_DEBUG_DRAW
2038 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
2039 for (int i=0; i<pointCount; ++i)
2040 qDebug() << " - " << points[i];
2042 Q_ASSERT(pointCount >= 2);
2044 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
2045 QRectF r(points[0], points[2]);
2052 if (mode != PolylineMode) {
2054 if (s->brushData.blend) {
2055 fillPolygon(points, pointCount, mode);
2059 // Do the outline...
2060 if (s->penData.blend) {
2061 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2062 strokePolygonCosmetic(points, pointCount, mode);
2064 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2065 QPaintEngineEx::stroke(vp, s->lastPen);
2073 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2075 Q_D(QRasterPaintEngine);
2076 QRasterPaintEngineState *s = state();
2078 #ifdef QT_DEBUG_DRAW
2079 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
2080 for (int i=0; i<pointCount; ++i)
2081 qDebug() << " - " << points[i];
2083 Q_ASSERT(pointCount >= 2);
2084 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
2085 QRect r(points[0].x(),
2087 points[2].x() - points[0].x(),
2088 points[2].y() - points[0].y());
2095 if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) {
2096 // this calls the float version
2097 QPaintEngineEx::drawPolygon(points, pointCount, mode);
2102 if (mode != PolylineMode) {
2104 if (s->brushData.blend) {
2105 // Compose polygon fill..,
2106 ensureOutlineMapper();
2107 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2108 d->outlineMapper->moveTo(*points);
2109 const QPoint *p = points;
2110 const QPoint *ep = points + pointCount - 1;
2112 d->outlineMapper->lineTo(*(++p));
2114 d->outlineMapper->endOutline();
2117 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2119 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2123 // Do the outline...
2124 if (s->penData.blend) {
2125 if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
2126 strokePolygonCosmetic(points, pointCount, mode);
2128 int count = pointCount * 2;
2129 QVarLengthArray<qreal> fpoints(count);
2131 for (int i=0; i<count; i+=2) {
2132 fpoints[i] = ((int *) points)[i+1];
2133 fpoints[i+1] = ((int *) points)[i];
2136 for (int i=0; i<count; ++i)
2137 fpoints[i] = ((int *) points)[i];
2139 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2140 QPaintEngineEx::stroke(vp, s->lastPen);
2148 void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode)
2150 Q_D(QRasterPaintEngine);
2151 QRasterPaintEngineState *s = state();
2153 Q_ASSERT(s->penData.blend);
2154 Q_ASSERT(s->flags.fast_pen);
2156 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2158 // Use fast path for 0 width / trivial pens.
2160 devRect.set(d->deviceRect);
2162 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2163 ? LineDrawIncludeLastPixel
2165 int dashOffset = int(s->lastPen.dashOffset());
2167 // Draw all the line segments.
2168 for (int i=1; i<pointCount; ++i) {
2170 QPointF lp1 = points[i-1] * s->matrix;
2171 QPointF lp2 = points[i] * s->matrix;
2173 const QRectF brect(lp1, lp2);
2174 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2175 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2176 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2177 qFloor(lp2.x()), qFloor(lp2.y()),
2178 penBlend, &s->penData,
2179 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2182 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2183 qFloor(lp2.x()), qFloor(lp2.y()),
2185 penBlend, &s->penData,
2186 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2187 devRect, &dashOffset);
2191 // Polygons are implicitly closed.
2192 if (needs_closing) {
2193 QPointF lp1 = points[pointCount-1] * s->matrix;
2194 QPointF lp2 = points[0] * s->matrix;
2196 const QRectF brect(lp1, lp2);
2197 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2198 if (qpen_style(s->lastPen) == Qt::SolidLine) {
2199 drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
2200 qFloor(lp2.x()), qFloor(lp2.y()),
2201 penBlend, &s->penData,
2202 LineDrawIncludeLastPixel,
2205 drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
2206 qFloor(lp2.x()), qFloor(lp2.y()),
2208 penBlend, &s->penData,
2209 LineDrawIncludeLastPixel,
2210 devRect, &dashOffset);
2219 void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode)
2221 Q_D(QRasterPaintEngine);
2222 QRasterPaintEngineState *s = state();
2224 // We assert here because this function is called from drawRects
2225 // and drawPolygon and they already do ensurePen(), so we skip that
2226 // here to avoid duplicate checks..
2227 Q_ASSERT(s->penData.blend);
2229 bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
2232 devRect.set(d->deviceRect);
2234 LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
2235 ? LineDrawIncludeLastPixel
2238 int m11 = int(s->matrix.m11());
2239 int m22 = int(s->matrix.m22());
2240 int dx = int(s->matrix.dx());
2241 int dy = int(s->matrix.dy());
2242 int m13 = int(s->matrix.m13());
2243 int m23 = int(s->matrix.m23());
2244 bool affine = !m13 && !m23;
2246 int dashOffset = int(s->lastPen.dashOffset());
2249 // Draw all the line segments.
2250 for (int i=1; i<pointCount; ++i) {
2251 const QPoint lp1 = points[i-1] * s->matrix;
2252 const QPoint lp2 = points[i] * s->matrix;
2253 const QRect brect(lp1, lp2);
2254 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2256 if (qpen_style(s->lastPen) == Qt::SolidLine)
2257 drawLine_midpoint_i(lp1.x(), lp1.y(),
2259 penBlend, &s->penData,
2260 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2263 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2266 penBlend, &s->penData,
2267 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2268 devRect, &dashOffset);
2272 // Polygons are implicitly closed.
2273 if (needs_closing) {
2274 const QPoint lp1 = points[pointCount - 1] * s->matrix;
2275 const QPoint lp2 = points[0] * s->matrix;
2276 const QRect brect(lp1, lp2);
2277 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2279 if (qpen_style(s->lastPen) == Qt::SolidLine)
2280 drawLine_midpoint_i(lp1.x(), lp1.y(),
2282 penBlend, &s->penData, LineDrawIncludeLastPixel,
2285 drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
2288 penBlend, &s->penData, LineDrawIncludeLastPixel,
2289 devRect, &dashOffset);
2292 // Draw all the line segments.
2293 for (int i=1; i<pointCount; ++i) {
2294 int x1 = points[i-1].x() * m11 + dx;
2295 int y1 = points[i-1].y() * m22 + dy;
2296 qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.;
2300 int x2 = points[i].x() * m11 + dx;
2301 int y2 = points[i].y() * m22 + dy;
2302 w = m13*points[i].x() + m23*points[i].y() + 1.;
2307 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2308 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2309 if (qpen_style(s->lastPen) == Qt::SolidLine)
2310 drawLine_midpoint_i(x1, y1, x2, y2,
2311 penBlend, &s->penData,
2312 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2315 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2317 penBlend, &s->penData,
2318 i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
2319 devRect, &dashOffset);
2323 int x1 = points[pointCount-1].x() * m11 + dx;
2324 int y1 = points[pointCount-1].y() * m22 + dy;
2325 qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.;
2329 int x2 = points[0].x() * m11 + dx;
2330 int y2 = points[0].y() * m22 + dy;
2331 w = m13*points[0].x() + m23*points[0].y() + 1.;
2335 // Polygons are implicitly closed.
2337 if (needs_closing) {
2338 const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2339 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
2340 if (qpen_style(s->lastPen) == Qt::SolidLine)
2341 drawLine_midpoint_i(x1, y1, x2, y2,
2342 penBlend, &s->penData, LineDrawIncludeLastPixel,
2345 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
2347 penBlend, &s->penData, LineDrawIncludeLastPixel,
2348 devRect, &dashOffset);
2356 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2358 #ifdef QT_DEBUG_DRAW
2359 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2362 QPixmapData *pd = pixmap.pixmapData();
2363 if (pd->classId() == QPixmapData::RasterClass) {
2364 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2365 if (image.depth() == 1) {
2366 Q_D(QRasterPaintEngine);
2367 QRasterPaintEngineState *s = state();
2368 if (s->matrix.type() <= QTransform::TxTranslate) {
2370 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2372 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2375 QRasterPaintEngine::drawImage(pos, image);
2378 const QImage image = pixmap.toImage();
2379 if (pixmap.depth() == 1) {
2380 Q_D(QRasterPaintEngine);
2381 QRasterPaintEngineState *s = state();
2382 if (s->matrix.type() <= QTransform::TxTranslate) {
2384 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2386 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2389 QRasterPaintEngine::drawImage(pos, image);
2397 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2399 #ifdef QT_DEBUG_DRAW
2400 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2403 QPixmapData* pd = pixmap.pixmapData();
2404 if (pd->classId() == QPixmapData::RasterClass) {
2405 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2406 if (image.depth() == 1) {
2407 Q_D(QRasterPaintEngine);
2408 QRasterPaintEngineState *s = state();
2409 if (s->matrix.type() <= QTransform::TxTranslate
2410 && r.size() == sr.size()
2411 && r.size() == pixmap.size()) {
2413 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2416 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2419 drawImage(r, image, sr);
2422 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2423 const QImage image = pd->toImage(clippedSource);
2424 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2425 if (image.depth() == 1) {
2426 Q_D(QRasterPaintEngine);
2427 QRasterPaintEngineState *s = state();
2428 if (s->matrix.type() <= QTransform::TxTranslate
2429 && r.size() == sr.size()
2430 && r.size() == pixmap.size()) {
2432 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2435 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2438 drawImage(r, image, translatedSource);
2443 // assumes that rect has positive width and height
2444 static inline const QRect toRect_normalized(const QRectF &rect)
2446 const int x = qRound(rect.x());
2447 const int y = qRound(rect.y());
2448 const int w = int(rect.width() + qreal(0.5));
2449 const int h = int(rect.height() + qreal(0.5));
2451 return QRect(x, y, w, h);
2454 static inline int fast_ceil_positive(const qreal &v)
2456 const int iv = int(v);
2463 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2465 const int xmin = int(rect.x());
2466 const int xmax = int(fast_ceil_positive(rect.right()));
2467 const int ymin = int(rect.y());
2468 const int ymax = int(fast_ceil_positive(rect.bottom()));
2469 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2475 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2477 #ifdef QT_DEBUG_DRAW
2478 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2481 Q_D(QRasterPaintEngine);
2482 QRasterPaintEngineState *s = state();
2484 if (s->matrix.type() > QTransform::TxTranslate) {
2485 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2487 QRectF(0, 0, img.width(), img.height()));
2490 const QClipData *clip = d->clip();
2491 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2493 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2494 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2497 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2499 } else if (clip->hasRectClip) {
2500 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2508 d->image_filler.clip = clip;
2509 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2510 if (!d->image_filler.blend)
2512 d->image_filler.dx = -pt.x();
2513 d->image_filler.dy = -pt.y();
2514 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2516 fillRect_normalized(rr, &d->image_filler, d);
2521 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2523 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2534 inline RotationType qRotationType(const QTransform &transform)
2536 QTransform::TransformationType type = transform.type();
2538 if (type > QTransform::TxRotate)
2541 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2542 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2545 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2546 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2549 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2550 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2556 inline bool isPixelAligned(const QRectF &rect) {
2557 return QRectF(rect.toRect()) == rect;
2564 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2565 Qt::ImageConversionFlags)
2567 #ifdef QT_DEBUG_DRAW
2568 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2574 Q_D(QRasterPaintEngine);
2575 QRasterPaintEngineState *s = state();
2576 int sr_l = qFloor(sr.left());
2577 int sr_r = qCeil(sr.right()) - 1;
2578 int sr_t = qFloor(sr.top());
2579 int sr_b = qCeil(sr.bottom()) - 1;
2581 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2582 QTransform old = s->matrix;
2584 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2585 QRgb color = img.pixel(sr_l, sr_t);
2586 switch (img.format()) {
2587 case QImage::Format_ARGB32_Premultiplied:
2588 case QImage::Format_ARGB8565_Premultiplied:
2589 case QImage::Format_ARGB6666_Premultiplied:
2590 case QImage::Format_ARGB8555_Premultiplied:
2591 case QImage::Format_ARGB4444_Premultiplied:
2592 // Combine premultiplied color with the opacity set on the painter.
2593 d->solid_color_filler.solid.color =
2594 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2595 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2598 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2602 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2603 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2607 d->solid_color_filler.clip = d->clip();
2608 d->solid_color_filler.adjustSpanMethods();
2609 fillRect(r, &d->solid_color_filler);
2615 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2617 const QClipData *clip = d->clip();
2619 if (s->matrix.type() > QTransform::TxTranslate
2621 && (!clip || clip->hasRectClip)
2622 && s->intOpacity == 256
2623 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2624 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2625 && d->rasterBuffer->format == img.format()
2626 && (d->rasterBuffer->format == QImage::Format_RGB16
2627 || d->rasterBuffer->format == QImage::Format_RGB32
2628 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2629 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2631 RotationType rotationType = qRotationType(s->matrix);
2633 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2634 QRectF transformedTargetRect = s->matrix.mapRect(r);
2636 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2637 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2639 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2640 if (clippedTransformedTargetRect.isNull())
2643 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2645 QRect clippedSourceRect
2646 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2647 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2649 uint dbpl = d->rasterBuffer->bytesPerLine();
2650 uint sbpl = img.bytesPerLine();
2652 uchar *dst = d->rasterBuffer->buffer();
2653 uint bpp = img.depth() >> 3;
2655 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2656 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2658 uint cw = clippedSourceRect.width();
2659 uint ch = clippedSourceRect.height();
2661 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2668 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2670 QRectF targetBounds = s->matrix.mapRect(r);
2671 bool exceedsPrecision = targetBounds.width() > 0xffff
2672 || targetBounds.height() > 0xffff;
2674 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2675 if (s->matrix.type() > QTransform::TxScale) {
2676 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2677 if (func && (!clip || clip->hasRectClip)) {
2678 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2679 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2680 s->matrix, s->intOpacity);
2684 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2685 if (func && (!clip || clip->hasRectClip)) {
2686 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2687 img.bits(), img.bytesPerLine(),
2688 qt_mapRect_non_normalizing(r, s->matrix), sr,
2689 !clip ? d->deviceRect : clip->clipRect,
2696 QTransform copy = s->matrix;
2697 copy.translate(r.x(), r.y());
2699 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2700 copy.translate(-sr.x(), -sr.y());
2702 d->image_filler_xform.clip = clip;
2703 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2704 if (!d->image_filler_xform.blend)
2706 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2708 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2709 QRectF rr = s->matrix.mapRect(r);
2711 const int x1 = qRound(rr.x());
2712 const int y1 = qRound(rr.y());
2713 const int x2 = qRound(rr.right());
2714 const int y2 = qRound(rr.bottom());
2716 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2720 #ifdef QT_FAST_SPANS
2722 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2723 d->initializeRasterizer(&d->image_filler_xform);
2724 d->rasterizer->setAntialiased(s->flags.antialiased);
2726 const QRectF &rect = r.normalized();
2727 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2728 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2730 if (s->flags.tx_noshear)
2731 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2733 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2739 QTransform m = s->matrix;
2740 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2741 m.m21(), m.m22(), m.m23(),
2742 m.m31(), m.m32(), m.m33());
2743 fillPath(path, &d->image_filler_xform);
2746 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2747 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2749 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2751 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2753 } else if (clip->hasRectClip) {
2754 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2760 d->image_filler.clip = clip;
2761 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2762 if (!d->image_filler.blend)
2764 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2765 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2768 rr.translate(s->matrix.dx(), s->matrix.dy());
2770 const int x1 = qRound(rr.x());
2771 const int y1 = qRound(rr.y());
2772 const int x2 = qRound(rr.right());
2773 const int y2 = qRound(rr.bottom());
2775 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2782 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2784 #ifdef QT_DEBUG_DRAW
2785 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2787 Q_D(QRasterPaintEngine);
2788 QRasterPaintEngineState *s = state();
2792 QPixmapData *pd = pixmap.pixmapData();
2793 if (pd->classId() == QPixmapData::RasterClass) {
2794 image = static_cast<QRasterPixmapData *>(pd)->image;
2796 image = pixmap.toImage();
2799 if (image.depth() == 1)
2800 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2802 if (s->matrix.type() > QTransform::TxTranslate) {
2803 QTransform copy = s->matrix;
2804 copy.translate(r.x(), r.y());
2805 copy.translate(-sr.x(), -sr.y());
2806 d->image_filler_xform.clip = d->clip();
2807 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2808 if (!d->image_filler_xform.blend)
2810 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2812 #ifdef QT_FAST_SPANS
2814 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2815 d->initializeRasterizer(&d->image_filler_xform);
2816 d->rasterizer->setAntialiased(s->flags.antialiased);
2818 const QRectF &rect = r.normalized();
2819 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2820 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2821 if (s->flags.tx_noshear)
2822 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2824 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2830 fillPath(path, &d->image_filler_xform);
2832 d->image_filler.clip = d->clip();
2834 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2835 if (!d->image_filler.blend)
2837 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2838 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2841 rr.translate(s->matrix.dx(), s->matrix.dy());
2842 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2848 static inline bool monoVal(const uchar* s, int x)
2850 return (s[x>>3] << (x&7)) & 0x80;
2856 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2858 Q_D(QRasterPaintEngine);
2859 QRasterPaintEngineState *s = state();
2861 if (!s->penData.blend)
2864 QRasterBuffer *rb = d->rasterBuffer.data();
2866 const QRect rect(rx, ry, w, h);
2867 const QClipData *clip = d->clip();
2868 bool unclipped = false;
2870 // inlined QRect::intersects
2871 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2872 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2874 if (clip->hasRectClip) {
2875 unclipped = rx > clip->xmin
2876 && rx + w < clip->xmax
2878 && ry + h < clip->ymax;
2884 // inlined QRect::intersects
2885 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2886 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2890 // inlined QRect::contains
2891 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2892 && rect.top() >= 0 && rect.bottom() < rb->height();
2894 unclipped = contains && d->isUnclipped_normalized(rect);
2897 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2898 const uchar * scanline = static_cast<const uchar *>(src);
2900 if (s->flags.fast_text) {
2903 if (s->penData.bitmapBlit) {
2904 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2905 scanline, w, h, bpl);
2908 } else if (depth == 8) {
2909 if (s->penData.alphamapBlit) {
2910 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2911 scanline, w, h, bpl, 0);
2914 } else if (depth == 32) {
2915 // (A)RGB Alpha mask where the alpha component is not used.
2916 if (s->penData.alphaRGBBlit) {
2917 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2918 (const uint *) scanline, w, h, bpl / 4, 0);
2922 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2923 // (A)RGB Alpha mask where the alpha component is not used.
2925 int nx = qMax(0, rx);
2926 int ny = qMax(0, ry);
2928 // Move scanline pointer to compensate for moved x and y
2929 int xdiff = nx - rx;
2930 int ydiff = ny - ry;
2931 scanline += ydiff * bpl;
2932 scanline += xdiff * (depth == 32 ? 4 : 1);
2937 if (nx + w > d->rasterBuffer->width())
2938 w = d->rasterBuffer->width() - nx;
2939 if (ny + h > d->rasterBuffer->height())
2940 h = d->rasterBuffer->height() - ny;
2945 if (depth == 8 && s->penData.alphamapBlit) {
2946 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2947 scanline, w, h, bpl, clip);
2948 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2949 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2950 (const uint *) scanline, w, h, bpl / 4, clip);
2965 scanline += bpl * y0;
2969 w = qMin(w, rb->width() - qMax(0, rx));
2970 h = qMin(h, rb->height() - qMax(0, ry));
2972 if (w <= 0 || h <= 0)
2975 const int NSPANS = 256;
2976 QSpan spans[NSPANS];
2979 const int x1 = x0 + w;
2980 const int y1 = y0 + h;
2983 for (int y = y0; y < y1; ++y) {
2984 for (int x = x0; x < x1; ) {
2985 if (!monoVal(scanline, x)) {
2990 if (current == NSPANS) {
2991 blend(current, spans, &s->penData);
2994 spans[current].x = x + rx;
2995 spans[current].y = y + ry;
2996 spans[current].coverage = 255;
2999 // extend span until we find a different one.
3000 while (x < x1 && monoVal(scanline, x)) {
3004 spans[current].len = len;
3009 } else if (depth == 8) {
3010 for (int y = y0; y < y1; ++y) {
3011 for (int x = x0; x < x1; ) {
3012 // Skip those with 0 coverage
3013 if (scanline[x] == 0) {
3018 if (current == NSPANS) {
3019 blend(current, spans, &s->penData);
3022 int coverage = scanline[x];
3023 spans[current].x = x + rx;
3024 spans[current].y = y + ry;
3025 spans[current].coverage = coverage;
3029 // extend span until we find a different one.
3030 while (x < x1 && scanline[x] == coverage) {
3034 spans[current].len = len;
3039 } else { // 32-bit alpha...
3040 uint *sl = (uint *) src;
3041 for (int y = y0; y < y1; ++y) {
3042 for (int x = x0; x < x1; ) {
3043 // Skip those with 0 coverage
3044 if ((sl[x] & 0x00ffffff) == 0) {
3049 if (current == NSPANS) {
3050 blend(current, spans, &s->penData);
3053 uint rgbCoverage = sl[x];
3054 int coverage = qGreen(rgbCoverage);
3055 spans[current].x = x + rx;
3056 spans[current].y = y + ry;
3057 spans[current].coverage = coverage;
3061 // extend span until we find a different one.
3062 while (x < x1 && sl[x] == rgbCoverage) {
3066 spans[current].len = len;
3069 sl += bpl / sizeof(uint);
3072 // qDebug() << "alphaPenBlt: num spans=" << current
3073 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
3074 // Call span func for current set of spans.
3076 blend(current, spans, &s->penData);
3079 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
3080 const QFixedPoint *positions, QFontEngine *fontEngine)
3082 Q_D(QRasterPaintEngine);
3083 QRasterPaintEngineState *s = state();
3085 #if !defined(QT_NO_FREETYPE)
3086 if (fontEngine->type() == QFontEngine::Freetype) {
3087 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3088 QFontEngineFT::GlyphFormat neededFormat =
3089 painter()->device()->devType() == QInternal::Widget
3090 ? fe->defaultGlyphFormat()
3091 : QFontEngineFT::Format_A8;
3093 if (d_func()->mono_surface
3094 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
3096 neededFormat = QFontEngineFT::Format_Mono;
3098 if (neededFormat == QFontEngineFT::Format_None)
3099 neededFormat = QFontEngineFT::Format_A8;
3101 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
3102 if (s->matrix.type() >= QTransform::TxScale) {
3103 if (s->matrix.isAffine())
3104 gset = fe->loadTransformedGlyphSet(s->matrix);
3109 if (!gset || gset->outline_drawing
3110 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
3113 FT_Face lockedFace = 0;
3116 switch (neededFormat) {
3117 case QFontEngineFT::Format_Mono:
3120 case QFontEngineFT::Format_A8:
3123 case QFontEngineFT::Format_A32:
3131 for (int i = 0; i < numGlyphs; i++) {
3132 QFixed spp = fe->subPixelPositionForX(positions[i].x);
3133 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
3135 if (!glyph || glyph->format != neededFormat) {
3137 lockedFace = fe->lockFace();
3138 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
3141 if (!glyph || !glyph->data)
3145 switch (neededFormat) {
3146 case QFontEngineFT::Format_Mono:
3147 pitch = ((glyph->width + 31) & ~31) >> 3;
3149 case QFontEngineFT::Format_A8:
3150 pitch = (glyph->width + 3) & ~3;
3152 case QFontEngineFT::Format_A32:
3153 pitch = glyph->width * 4;
3160 alphaPenBlt(glyph->data, pitch, depth,
3161 qFloor(positions[i].x) + glyph->x,
3162 qFloor(positions[i].y) - glyph->y,
3163 glyph->width, glyph->height);
3170 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
3172 QImageTextureGlyphCache *cache =
3173 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
3175 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
3176 fontEngine->setGlyphCache(0, cache);
3179 cache->populate(fontEngine, numGlyphs, glyphs, positions);
3180 cache->fillInPendingGlyphs();
3182 const QImage &image = cache->image();
3183 int bpl = image.bytesPerLine();
3185 int depth = image.depth();
3189 leftShift = 2; // multiply by 4
3190 else if (depth == 1)
3191 rightShift = 3; // divide by 8
3193 int margin = cache->glyphMargin();
3194 const uchar *bits = image.bits();
3195 for (int i=0; i<numGlyphs; ++i) {
3197 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
3198 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
3199 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
3203 int x = qFloor(positions[i].x) + c.baseLineX - margin;
3204 int y = qFloor(positions[i].y) - c.baseLineY - margin;
3206 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
3209 // c.baseLineX, c.baseLineY,
3212 // positions[i].x.toInt(), positions[i].y.toInt());
3214 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
3220 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
3221 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
3223 Q_D(QRasterPaintEngine);
3224 QRasterPaintEngineState *s = state();
3226 QFontEngine *fontEngine = ti.fontEngine;
3227 if (fontEngine->type() != QFontEngine::S60FontEngine) {
3228 QPaintEngineEx::drawTextItem(p, ti);
3232 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
3234 QVarLengthArray<QFixedPoint> positions;
3235 QVarLengthArray<glyph_t> glyphs;
3236 QTransform matrix = s->matrix;
3237 matrix.translate(p.x(), p.y());
3238 if (matrix.type() == QTransform::TxScale)
3239 fe->setFontScale(matrix.m11());
3240 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3242 for (int i=0; i<glyphs.size(); ++i) {
3243 TOpenFontCharMetrics tmetrics;
3244 const TUint8 *glyphBitmapBytes;
3245 TSize glyphBitmapSize;
3246 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
3247 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
3248 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
3249 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
3252 if (matrix.type() == QTransform::TxScale)
3253 fe->setFontScale(1.0);
3257 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
3260 * Returns true if the rectangle is completely within the current clip
3261 * state of the paint engine.
3263 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
3265 const QClipData *cl = clip();
3267 // inline contains() for performance (we know the rects are normalized)
3268 const QRect &r1 = deviceRect;
3269 return (r.left() >= r1.left() && r.right() <= r1.right()
3270 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3274 if (cl->hasRectClip) {
3275 // currently all painting functions clips to deviceRect internally
3276 if (cl->clipRect == deviceRect)
3279 // inline contains() for performance (we know the rects are normalized)
3280 const QRect &r1 = cl->clipRect;
3281 return (r.left() >= r1.left() && r.right() <= r1.right()
3282 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3284 return qt_region_strictContains(cl->clipRegion, r);
3288 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
3291 Q_Q(const QRasterPaintEngine);
3292 const QRasterPaintEngineState *s = q->state();
3293 const QClipData *cl = clip();
3295 QRect r = rect.normalized();
3296 // inline contains() for performance (we know the rects are normalized)
3297 const QRect &r1 = deviceRect;
3298 return (r.left() >= r1.left() && r.right() <= r1.right()
3299 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3303 // currently all painting functions that call this function clip to deviceRect internally
3304 if (cl->hasRectClip && cl->clipRect == deviceRect)
3307 if (s->flags.antialiased)
3310 QRect r = rect.normalized();
3312 r.setX(r.x() - penWidth);
3313 r.setY(r.y() - penWidth);
3314 r.setWidth(r.width() + 2 * penWidth);
3315 r.setHeight(r.height() + 2 * penWidth);
3318 if (cl->hasRectClip) {
3319 // inline contains() for performance (we know the rects are normalized)
3320 const QRect &r1 = cl->clipRect;
3321 return (r.left() >= r1.left() && r.right() <= r1.right()
3322 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3324 return qt_region_strictContains(cl->clipRegion, r);
3328 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3331 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3335 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3336 const QSpanData *data) const
3338 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3342 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3343 const QSpanData *data) const
3345 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3349 QRasterPaintEnginePrivate::getPenFunc(const QRect &rect,
3350 const QSpanData *data) const
3352 Q_Q(const QRasterPaintEngine);
3353 const QRasterPaintEngineState *s = q->state();
3355 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3357 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF());
3358 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3362 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3363 const QSpanData *data) const
3365 Q_Q(const QRasterPaintEngine);
3366 const QRasterPaintEngineState *s = q->state();
3368 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3370 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3371 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3377 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3382 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3383 textItem->fontEngine());
3389 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3391 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3392 QRasterPaintEngineState *s = state();
3394 #ifdef QT_DEBUG_DRAW
3395 Q_D(QRasterPaintEngine);
3396 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3397 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3404 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3406 bool drawCached = true;
3408 if (s->matrix.type() >= QTransform::TxProject)
3411 // don't try to cache huge fonts
3412 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3413 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3416 // ### Remove the TestFontEngine and Box engine crap, in these
3417 // ### cases we should delegate painting to the font engine
3420 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3421 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3422 // qDebug() << "type" << fontEngineType << s->matrix.type();
3423 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3424 || (s->matrix.type() <= QTransform::TxTranslate
3425 && (fontEngineType == QFontEngine::TestFontEngine
3426 || fontEngineType == QFontEngine::Box))) {
3430 if (s->matrix.type() > QTransform::TxTranslate)
3434 QRasterPaintEngineState *s = state();
3436 QVarLengthArray<QFixedPoint> positions;
3437 QVarLengthArray<glyph_t> glyphs;
3439 QTransform matrix = s->matrix;
3440 matrix.translate(p.x(), p.y());
3442 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3444 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3448 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3449 if (s->matrix.type() <= QTransform::TxTranslate
3450 || (s->matrix.type() == QTransform::TxScale
3451 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3452 drawGlyphsS60(p, ti);
3455 #else // Q_WS_WIN || Q_WS_MAC
3457 QFontEngine *fontEngine = ti.fontEngine;
3459 #if defined(Q_WS_QWS)
3460 if (fontEngine->type() == QFontEngine::Box) {
3461 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3465 if (s->matrix.type() < QTransform::TxScale
3466 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3467 || (fontEngine->type() == QFontEngine::Proxy
3468 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3470 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3476 if (s->matrix.type() < QTransform::TxScale) {
3478 QVarLengthArray<QFixedPoint> positions;
3479 QVarLengthArray<glyph_t> glyphs;
3480 QTransform matrix = state()->transform();
3482 qreal _x = qFloor(p.x());
3483 qreal _y = qFloor(p.y());
3484 matrix.translate(_x, _y);
3486 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3487 if (glyphs.size() == 0)
3490 for(int i = 0; i < glyphs.size(); i++) {
3491 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3492 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3493 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3494 qRound(positions[i].x + metrics.x),
3495 qRound(positions[i].y + metrics.y),
3496 img.width(), img.height());
3502 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3504 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3505 if (fontEngine->type() == QFontEngine::QPF2) {
3506 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3507 if (renderingEngine)
3508 fontEngine = renderingEngine;
3512 if (fontEngine->type() != QFontEngine::Freetype) {
3513 QPaintEngineEx::drawTextItem(p, ti);
3517 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3519 QTransform matrix = s->matrix;
3520 matrix.translate(p.x(), p.y());
3522 QVarLengthArray<QFixedPoint> positions;
3523 QVarLengthArray<glyph_t> glyphs;
3524 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3525 if (glyphs.size() == 0)
3528 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3529 QPaintEngine::drawTextItem(p, ti);
3535 QPaintEngineEx::drawTextItem(p, ti);
3541 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3543 Q_D(QRasterPaintEngine);
3544 QRasterPaintEngineState *s = state();
3547 qreal pw = s->lastPen.widthF();
3548 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3549 QPaintEngineEx::drawPoints(points, pointCount);
3552 if (!s->penData.blend)
3555 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3556 QT_FT_Span span = { 0, 1, 0, 255 };
3557 const QPointF *end = points + pointCount;
3558 qreal trans_x, trans_y;
3560 int left = d->deviceRect.x();
3561 int right = left + d->deviceRect.width();
3562 int top = d->deviceRect.y();
3563 int bottom = top + d->deviceRect.height();
3565 while (points < end) {
3566 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3567 x = qFloor(trans_x);
3568 y = qFloor(trans_y);
3569 if (x >= left && x < right && y >= top && y < bottom) {
3571 const QT_FT_Span &last = array[count - 1];
3572 // spans must be sorted on y (primary) and x (secondary)
3573 if (y < last.y || (y == last.y && x < last.x)) {
3574 s->penData.blend(count, array.constData(), &s->penData);
3581 array[count++] = span;
3587 s->penData.blend(count, array.constData(), &s->penData);
3592 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3594 Q_D(QRasterPaintEngine);
3595 QRasterPaintEngineState *s = state();
3598 double pw = s->lastPen.widthF();
3599 if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
3600 QPaintEngineEx::drawPoints(points, pointCount);
3603 if (!s->penData.blend)
3606 QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
3607 QT_FT_Span span = { 0, 1, 0, 255 };
3608 const QPoint *end = points + pointCount;
3609 qreal trans_x, trans_y;
3611 int left = d->deviceRect.x();
3612 int right = left + d->deviceRect.width();
3613 int top = d->deviceRect.y();
3614 int bottom = top + d->deviceRect.height();
3616 while (points < end) {
3617 s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
3618 x = qFloor(trans_x);
3619 y = qFloor(trans_y);
3620 if (x >= left && x < right && y >= top && y < bottom) {
3622 const QT_FT_Span &last = array[count - 1];
3623 // spans must be sorted on y (primary) and x (secondary)
3624 if (y < last.y || (y == last.y && x < last.x)) {
3625 s->penData.blend(count, array.constData(), &s->penData);
3632 array[count++] = span;
3638 s->penData.blend(count, array.constData(), &s->penData);
3645 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3647 #ifdef QT_DEBUG_DRAW
3648 qDebug() << " - QRasterPaintEngine::drawLine()";
3650 Q_D(QRasterPaintEngine);
3651 QRasterPaintEngineState *s = state();
3654 if (s->flags.fast_pen) {
3655 QIntRect bounds; bounds.set(d->deviceRect);
3656 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3658 : LineDrawIncludeLastPixel;
3660 int m11 = int(s->matrix.m11());
3661 int m22 = int(s->matrix.m22());
3662 int dx = qFloor(s->matrix.dx());
3663 int dy = qFloor(s->matrix.dy());
3664 for (int i=0; i<lineCount; ++i) {
3665 int dashOffset = int(s->lastPen.dashOffset());
3666 if (s->flags.int_xform) {
3667 const QLine &l = lines[i];
3668 int x1 = l.x1() * m11 + dx;
3669 int y1 = l.y1() * m22 + dy;
3670 int x2 = l.x2() * m11 + dx;
3671 int y2 = l.y2() * m22 + dy;
3673 const QRect brect(QPoint(x1, y1), QPoint(x2, y2));
3674 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3675 if (qpen_style(s->lastPen) == Qt::SolidLine)
3676 drawLine_midpoint_i(x1, y1, x2, y2,
3677 penBlend, &s->penData, mode, bounds);
3679 drawLine_midpoint_dashed_i(x1, y1, x2, y2,
3680 &s->lastPen, penBlend,
3681 &s->penData, mode, bounds,
3684 QLineF line = lines[i] * s->matrix;
3685 const QRectF brect(QPointF(line.x1(), line.y1()),
3686 QPointF(line.x2(), line.y2()));
3687 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3688 if (qpen_style(s->lastPen) == Qt::SolidLine)
3689 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3690 int(line.x2()), int(line.y2()),
3691 penBlend, &s->penData, mode, bounds);
3693 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3694 int(line.x2()), int(line.y2()),
3695 &s->lastPen, penBlend,
3696 &s->penData, mode, bounds,
3700 } else if (s->penData.blend) {
3701 QPaintEngineEx::drawLines(lines, lineCount);
3705 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3711 Q_Q(QRasterPaintEngine);
3712 QRasterPaintEngineState *s = q->state();
3714 const QPen &pen = s->lastPen;
3715 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3716 const QVector<qreal> pattern = pen.dashPattern();
3718 qreal patternLength = 0;
3719 for (int i = 0; i < pattern.size(); ++i)
3720 patternLength += pattern.at(i);
3722 if (patternLength <= 0)
3725 qreal length = line.length();
3726 Q_ASSERT(length > 0);
3727 while (length > 0) {
3728 const bool rasterize = *inDash;
3729 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3732 if (dash >= length) {
3734 *dashOffset += dash / width;
3738 *inDash = !(*inDash);
3739 if (++*dashIndex >= pattern.size())
3746 if (rasterize && dash > 0)
3747 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3754 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3756 #ifdef QT_DEBUG_DRAW
3757 qDebug() << " - QRasterPaintEngine::drawLine()";
3759 Q_D(QRasterPaintEngine);
3760 QRasterPaintEngineState *s = state();
3763 if (!s->penData.blend)
3765 if (s->flags.fast_pen) {
3767 bounds.set(d->deviceRect);
3768 LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
3770 : LineDrawIncludeLastPixel;
3772 for (int i=0; i<lineCount; ++i) {
3773 int dashOffset = int(s->lastPen.dashOffset());
3774 QLineF line = lines[i] * s->matrix;
3775 const QRectF brect(QPointF(line.x1(), line.y1()),
3776 QPointF(line.x2(), line.y2()));
3777 ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
3778 if (qpen_style(s->lastPen) == Qt::SolidLine)
3779 drawLine_midpoint_i(int(line.x1()), int(line.y1()),
3780 int(line.x2()), int(line.y2()),
3781 penBlend, &s->penData, mode, bounds);
3783 drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
3784 int(line.x2()), int(line.y2()),
3786 penBlend, &s->penData, mode,
3787 bounds, &dashOffset);
3790 QPaintEngineEx::drawLines(lines, lineCount);
3798 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3800 Q_D(QRasterPaintEngine);
3801 QRasterPaintEngineState *s = state();
3804 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3805 || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased))
3806 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3808 && s->matrix.type() <= QTransform::TxScale) // no shear
3811 const QRectF r = s->matrix.mapRect(rect);
3812 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3813 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3814 const QRect brect = QRect(int(r.x()), int(r.y()),
3815 int_dim(r.x(), r.width()),
3816 int_dim(r.y(), r.height()));
3818 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3819 &s->penData, &s->brushData);
3823 QPaintEngineEx::drawEllipse(rect);
3830 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3832 Q_D(QRasterPaintEngine);
3839 CGContextRef QRasterPaintEngine::getCGContext() const
3841 Q_D(const QRasterPaintEngine);
3842 return d->cgContext;
3850 void QRasterPaintEngine::setDC(HDC hdc) {
3851 Q_D(QRasterPaintEngine);
3858 HDC QRasterPaintEngine::getDC() const
3860 Q_D(const QRasterPaintEngine);
3867 void QRasterPaintEngine::releaseDC(HDC) const
3876 QPoint QRasterPaintEngine::coordinateOffset() const
3878 return QPoint(0, 0);
3882 Draws the given color \a spans with the specified \a color. The \a
3883 count parameter specifies the number of spans.
3885 The default implementation does nothing; reimplement this function
3886 to draw the given color \a spans with the specified \a color. Note
3887 that this function \e must be reimplemented if the framebuffer is
3890 \sa drawBufferSpan()
3892 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3893 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3898 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3899 "a non memory-mapped device");
3903 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3905 Draws the given \a buffer.
3907 The default implementation does nothing; reimplement this function
3908 to draw a buffer that contains more than one color. Note that this
3909 function \e must be reimplemented if the framebuffer is not
3912 The \a size parameter specifies the total size of the given \a
3913 buffer, while the \a length parameter specifies the number of
3914 pixels to draw. The buffer's position is given by (\a x, \a
3915 y). The provided \a alpha value is added to each pixel in the
3916 buffer when drawing.
3918 \sa drawColorSpans()
3920 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3921 int x, int y, int length, uint const_alpha)
3928 Q_UNUSED(const_alpha);
3929 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3930 "a non memory-mapped device");
3934 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3939 Q_D(QRasterPaintEngine);
3941 Q_ASSERT(image.depth() == 1);
3943 const int spanCount = 256;
3944 QT_FT_Span spans[spanCount];
3948 int w = image.width();
3949 int h = image.height();
3950 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3951 int ymin = qMax(qRound(pos.y()), 0);
3952 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3953 int xmin = qMax(qRound(pos.x()), 0);
3955 int x_offset = xmin - qRound(pos.x());
3957 QImage::Format format = image.format();
3958 for (int y = ymin; y < ymax; ++y) {
3959 const uchar *src = image.scanLine(y - qRound(pos.y()));
3960 if (format == QImage::Format_MonoLSB) {
3961 for (int x = 0; x < xmax - xmin; ++x) {
3962 int src_x = x + x_offset;
3963 uchar pixel = src[src_x >> 3];
3968 if (pixel & (0x1 << (src_x & 7))) {
3969 spans[n].x = xmin + x;
3971 spans[n].coverage = 255;
3973 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3977 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3980 if (n == spanCount) {
3981 fg->blend(n, spans, fg);
3987 for (int x = 0; x < xmax - xmin; ++x) {
3988 int src_x = x + x_offset;
3989 uchar pixel = src[src_x >> 3];
3994 if (pixel & (0x80 >> (x & 7))) {
3995 spans[n].x = xmin + x;
3997 spans[n].coverage = 255;
3999 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
4003 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
4006 if (n == spanCount) {
4007 fg->blend(n, spans, fg);
4015 fg->blend(n, spans, fg);
4021 \enum QRasterPaintEngine::ClipType
4024 \value RectClip Indicates that the currently set clip is a single rectangle.
4025 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
4030 Returns the type of the clip currently set.
4032 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
4034 Q_D(const QRasterPaintEngine);
4036 const QClipData *clip = d->clip();
4037 if (!clip || clip->hasRectClip)
4045 Returns the bounding rect of the currently set clip.
4047 QRect QRasterPaintEngine::clipBoundingRect() const
4049 Q_D(const QRasterPaintEngine);
4051 const QClipData *clip = d->clip();
4054 return d->deviceRect;
4056 if (clip->hasRectClip)
4057 return clip->clipRect;
4059 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
4062 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
4064 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
4066 QVarLengthArray<short, 4096> buffer;
4068 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
4069 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
4070 result->initialize();
4072 for (int y = 0; y < c1->clipSpanHeight; ++y) {
4073 const QSpan *c1_spans = c1ClipLines[y].spans;
4074 int c1_count = c1ClipLines[y].count;
4075 const QSpan *c2_spans = c2ClipLines[y].spans;
4076 int c2_count = c2ClipLines[y].count;
4078 if (c1_count == 0 && c2_count == 0)
4080 if (c1_count == 0) {
4081 result->appendSpans(c2_spans, c2_count);
4083 } else if (c2_count == 0) {
4084 result->appendSpans(c1_spans, c1_count);
4088 // we need to merge the two
4090 // find required length
4091 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
4092 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
4094 memset(buffer.data(), 0, buffer.size() * sizeof(short));
4096 // Fill with old spans.
4097 for (int i = 0; i < c1_count; ++i) {
4098 const QSpan *cs = c1_spans + i;
4099 for (int j=cs->x; j<cs->x + cs->len; ++j)
4100 buffer[j] = cs->coverage;
4103 // Fill with new spans
4104 for (int i = 0; i < c2_count; ++i) {
4105 const QSpan *cs = c2_spans + i;
4106 for (int j = cs->x; j < cs->x + cs->len; ++j) {
4107 buffer[j] += cs->coverage;
4108 if (buffer[j] > 255)
4116 // Skip to next span
4117 while (x < max && buffer[x] == 0) ++x;
4118 if (x >= max) break;
4121 int coverage = buffer[x];
4123 // Find length of span
4124 while (x < max && buffer[x] == coverage)
4127 result->appendSpan(sx, x - sx, y, coverage);
4132 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
4134 Q_Q(QRasterPaintEngine);
4135 QRasterPaintEngineState *s = q->state();
4137 rasterizer->setAntialiased(s->flags.antialiased);
4139 QRect clipRect(deviceRect);
4141 // ### get from optimized rectbased QClipData
4143 const QClipData *c = clip();
4145 const QRect r(QPoint(c->xmin, c->ymin),
4146 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
4147 clipRect = clipRect.intersected(r);
4148 blend = data->blend;
4150 blend = data->unclipped_blend;
4153 rasterizer->setClipRect(clipRect);
4154 rasterizer->initialize(blend, data);
4157 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4158 ProcessSpans callback,
4159 QSpanData *spanData, QRasterBuffer *rasterBuffer)
4161 if (!callback || !outline)
4164 Q_Q(QRasterPaintEngine);
4165 QRasterPaintEngineState *s = q->state();
4167 if (!s->flags.antialiased) {
4168 initializeRasterizer(spanData);
4170 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4174 rasterizer->rasterize(outline, fillRule);
4178 rasterize(outline, callback, (void *)spanData, rasterBuffer);
4182 int q_gray_rendered_spans(QT_FT_Raster raster);
4185 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
4186 ProcessSpans callback,
4187 void *userData, QRasterBuffer *)
4189 if (!callback || !outline)
4192 Q_Q(QRasterPaintEngine);
4193 QRasterPaintEngineState *s = q->state();
4195 if (!s->flags.antialiased) {
4196 rasterizer->setAntialiased(s->flags.antialiased);
4197 rasterizer->setClipRect(deviceRect);
4198 rasterizer->initialize(callback, userData);
4200 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
4204 rasterizer->rasterize(outline, fillRule);
4208 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
4209 // minimize memory reallocations. However if initial size for
4210 // raster pool is changed for lower value, reallocations will
4212 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
4213 int rasterPoolSize = rasterPoolInitialSize;
4214 unsigned char *rasterPoolBase;
4215 #if defined(Q_WS_WIN64)
4217 // We make use of setjmp and longjmp in qgrayraster.c which requires
4218 // 16-byte alignment, hence we hardcode this requirement here..
4219 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4221 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
4222 rasterPoolBase = rasterPoolOnStack;
4224 Q_CHECK_PTR(rasterPoolBase);
4226 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4228 void *data = userData;
4230 QT_FT_BBox clip_box = { deviceRect.x(),
4232 deviceRect.x() + deviceRect.width(),
4233 deviceRect.y() + deviceRect.height() };
4235 QT_FT_Raster_Params rasterParams;
4236 rasterParams.target = 0;
4237 rasterParams.source = outline;
4238 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
4239 rasterParams.gray_spans = 0;
4240 rasterParams.black_spans = 0;
4241 rasterParams.bit_test = 0;
4242 rasterParams.bit_set = 0;
4243 rasterParams.user = data;
4244 rasterParams.clip_box = clip_box;
4249 int rendered_spans = 0;
4253 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
4254 rasterParams.gray_spans = callback;
4255 rasterParams.skip_spans = rendered_spans;
4256 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
4258 // Out of memory, reallocate some more and try again...
4259 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
4260 int new_size = rasterPoolSize * 2;
4261 if (new_size > 1024 * 1024) {
4262 qWarning("QPainter: Rasterization of primitive failed");
4266 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
4268 #if defined(Q_WS_WIN64)
4269 _aligned_free(rasterPoolBase);
4271 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4272 free(rasterPoolBase);
4275 rasterPoolSize = new_size;
4277 #if defined(Q_WS_WIN64)
4278 // We make use of setjmp and longjmp in qgrayraster.c which requires
4279 // 16-byte alignment, hence we hardcode this requirement here..
4280 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
4282 (unsigned char *) malloc(rasterPoolSize);
4284 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
4286 qt_ft_grays_raster.raster_done(*grayRaster.data());
4287 qt_ft_grays_raster.raster_new(grayRaster.data());
4288 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
4294 #if defined(Q_WS_WIN64)
4295 _aligned_free(rasterPoolBase);
4297 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
4298 free(rasterPoolBase);
4302 void QRasterPaintEnginePrivate::recalculateFastImages()
4304 Q_Q(QRasterPaintEngine);
4305 QRasterPaintEngineState *s = q->state();
4307 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
4308 && s->matrix.type() <= QTransform::TxShear;
4311 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
4313 Q_Q(const QRasterPaintEngine);
4314 const QRasterPaintEngineState *s = q->state();
4316 return s->flags.fast_images
4317 && (mode == QPainter::CompositionMode_SourceOver
4318 || (mode == QPainter::CompositionMode_Source
4319 && !image.hasAlphaChannel()));
4322 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
4324 Q_ASSERT(image.depth() == 1);
4326 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
4327 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
4329 QRgb fg = PREMUL(color.rgba());
4332 int height = sourceImage.height();
4333 int width = sourceImage.width();
4334 for (int y=0; y<height; ++y) {
4335 uchar *source = sourceImage.scanLine(y);
4336 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
4337 if (!source || !target)
4338 QT_THROW(std::bad_alloc()); // we must have run out of memory
4339 for (int x=0; x < width; ++x)
4340 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
4345 QRasterBuffer::~QRasterBuffer()
4349 void QRasterBuffer::init()
4351 compositionMode = QPainter::CompositionMode_SourceOver;
4352 monoDestinationWithClut = false;
4357 QImage::Format QRasterBuffer::prepare(QImage *image)
4359 m_buffer = (uchar *)image->bits();
4360 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
4361 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
4362 bytes_per_pixel = image->depth()/8;
4363 bytes_per_line = image->bytesPerLine();
4365 format = image->format();
4366 drawHelper = qDrawHelper + format;
4367 if (image->depth() == 1 && image->colorTable().size() == 2) {
4368 monoDestinationWithClut = true;
4369 destColor0 = PREMUL(image->colorTable()[0]);
4370 destColor1 = PREMUL(image->colorTable()[1]);
4376 void QRasterBuffer::resetBuffer(int val)
4378 memset(m_buffer, val, m_height*bytes_per_line);
4382 #if defined(Q_WS_QWS)
4383 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
4385 m_buffer = reinterpret_cast<uchar*>(device->memory());
4386 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
4387 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
4388 bytes_per_pixel = device->depth() / 8;
4389 bytes_per_line = device->bytesPerLine();
4390 format = device->format();
4391 #ifndef QT_NO_RASTERCALLBACKS
4393 drawHelper = qDrawHelperCallback + format;
4396 drawHelper = qDrawHelper + format;
4399 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
4403 return widget->frameGeometry().width();
4405 return widget->frameGeometry().height();
4410 return qt_paint_device_metric(widget, m);
4413 int QCustomRasterPaintDevice::bytesPerLine() const
4415 return (width() * depth() + 7) / 8;
4418 #elif defined(Q_OS_SYMBIAN)
4420 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
4424 #endif // Q_OS_SYMBIAN
4427 \class QCustomRasterPaintDevice
4432 \brief The QCustomRasterPaintDevice class is provided to activate
4433 hardware accelerated paint engines in Qt for Embedded Linux.
4435 Note that this class is only available in \l{Qt for Embedded Linux}.
4437 In \l{Qt for Embedded Linux}, painting is a pure software
4438 implementation. But starting with Qt 4.2, it is
4439 possible to add an accelerated graphics driver to take advantage
4440 of available hardware resources.
4442 Hardware acceleration is accomplished by creating a custom screen
4443 driver, accelerating the copying from memory to the screen, and
4444 implementing a custom paint engine accelerating the various
4445 painting operations. Then a custom paint device (derived from the
4446 QCustomRasterPaintDevice class) and a custom window surface
4447 (derived from QWSWindowSurface) must be implemented to make
4448 \l{Qt for Embedded Linux} aware of the accelerated driver.
4450 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
4451 documentation for details.
4453 \sa QRasterPaintEngine, QPaintDevice
4457 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
4459 Constructs a custom raster based paint device for the given
4460 top-level \a widget.
4464 \fn int QCustomRasterPaintDevice::bytesPerLine() const
4466 Returns the number of bytes per line in the framebuffer. Note that
4467 this number might be larger than the framebuffer width.
4471 \fn int QCustomRasterPaintDevice::devType() const
4476 \fn QImage::Format QCustomRasterPaintDevice::format() const
4478 Returns the format of the device's memory buffet.
4480 The default format is QImage::Format_ARGB32_Premultiplied. The
4481 only other valid format is QImage::Format_RGB16.
4485 \fn void * QCustomRasterPaintDevice::memory () const
4487 Returns a pointer to the paint device's memory buffer, or 0 if no
4492 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4497 \fn QSize QCustomRasterPaintDevice::size () const
4502 QClipData::QClipData(int height)
4504 clipSpanHeight = height;
4509 xmin = xmax = ymin = ymax = 0;
4513 hasRectClip = hasRegionClip = false;
4516 QClipData::~QClipData()
4524 void QClipData::initialize()
4530 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4532 Q_CHECK_PTR(m_clipLines);
4534 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4535 allocated = clipSpanHeight;
4536 Q_CHECK_PTR(m_spans);
4542 m_clipLines[y].spans = 0;
4543 m_clipLines[y].count = 0;
4547 const int len = clipRect.width();
4550 QSpan *span = m_spans + count;
4554 span->coverage = 255;
4557 m_clipLines[y].spans = span;
4558 m_clipLines[y].count = 1;
4562 while (y < clipSpanHeight) {
4563 m_clipLines[y].spans = 0;
4564 m_clipLines[y].count = 0;
4567 } else if (hasRegionClip) {
4569 const QVector<QRect> rects = clipRegion.rects();
4570 const int numRects = rects.size();
4573 const int maxSpans = (ymax - ymin) * numRects;
4574 if (maxSpans > allocated) {
4575 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4576 allocated = maxSpans;
4581 int firstInBand = 0;
4583 while (firstInBand < numRects) {
4584 const int currMinY = rects.at(firstInBand).y();
4585 const int currMaxY = currMinY + rects.at(firstInBand).height();
4587 while (y < currMinY) {
4588 m_clipLines[y].spans = 0;
4589 m_clipLines[y].count = 0;
4593 int lastInBand = firstInBand;
4594 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4597 while (y < currMaxY) {
4599 m_clipLines[y].spans = m_spans + count;
4600 m_clipLines[y].count = lastInBand - firstInBand + 1;
4602 for (int r = firstInBand; r <= lastInBand; ++r) {
4603 const QRect &currRect = rects.at(r);
4604 QSpan *span = m_spans + count;
4605 span->x = currRect.x();
4606 span->len = currRect.width();
4608 span->coverage = 255;
4614 firstInBand = lastInBand + 1;
4617 Q_ASSERT(count <= allocated);
4619 while (y < clipSpanHeight) {
4620 m_clipLines[y].spans = 0;
4621 m_clipLines[y].count = 0;
4627 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4632 free(m_clipLines); // same for clipLines
4638 void QClipData::fixup()
4643 ymin = ymax = xmin = xmax = 0;
4648 ymin = m_spans[0].y;
4649 ymax = m_spans[count-1].y + 1;
4653 const int firstLeft = m_spans[0].x;
4654 const int firstRight = m_spans[0].x + m_spans[0].len;
4657 for (int i = 0; i < count; ++i) {
4658 QT_FT_Span_& span = m_spans[i];
4661 if (span.y != y + 1 && y != -1)
4664 m_clipLines[y].spans = &span;
4665 m_clipLines[y].count = 1;
4667 ++m_clipLines[y].count;
4669 const int spanLeft = span.x;
4670 const int spanRight = spanLeft + span.len;
4672 if (spanLeft < xmin)
4675 if (spanRight > xmax)
4678 if (spanLeft != firstLeft || spanRight != firstRight)
4684 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4689 Convert \a rect to clip spans.
4691 void QClipData::setClipRect(const QRect &rect)
4693 if (hasRectClip && rect == clipRect)
4696 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4698 hasRegionClip = false;
4702 xmax = rect.x() + rect.width();
4703 ymin = qMin(rect.y(), clipSpanHeight);
4704 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4711 // qDebug() << xmin << xmax << ymin << ymax;
4715 Convert \a region to clip spans.
4717 void QClipData::setClipRegion(const QRegion ®ion)
4719 if (region.rectCount() == 1) {
4720 setClipRect(region.rects().at(0));
4724 hasRegionClip = true;
4725 hasRectClip = false;
4726 clipRegion = region;
4728 { // set bounding rect
4729 const QRect rect = region.boundingRect();
4731 xmax = rect.x() + rect.width();
4733 ymax = rect.y() + rect.height();
4745 spans must be sorted on y
4747 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4748 const QSpan *spans, const QSpan *end,
4749 QSpan **outSpans, int available)
4751 const_cast<QClipData *>(clip)->initialize();
4753 QSpan *out = *outSpans;
4755 const QSpan *clipSpans = clip->m_spans + *currentClip;
4756 const QSpan *clipEnd = clip->m_spans + clip->count;
4758 while (available && spans < end ) {
4759 if (clipSpans >= clipEnd) {
4763 if (clipSpans->y > spans->y) {
4767 if (spans->y != clipSpans->y) {
4768 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4769 clipSpans = clip->m_clipLines[spans->y].spans;
4774 Q_ASSERT(spans->y == clipSpans->y);
4777 int sx2 = sx1 + spans->len;
4778 int cx1 = clipSpans->x;
4779 int cx2 = cx1 + clipSpans->len;
4781 if (cx1 < sx1 && cx2 < sx1) {
4784 } else if (sx1 < cx1 && sx2 < cx1) {
4788 int x = qMax(sx1, cx1);
4789 int len = qMin(sx2, cx2) - x;
4791 out->x = qMax(sx1, cx1);
4792 out->len = qMin(sx2, cx2) - out->x;
4794 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4806 *currentClip = clipSpans - clip->m_spans;
4810 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4812 // qDebug() << "qt_span_fill_clipped" << spanCount;
4813 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4815 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4817 const int NSPANS = 256;
4818 QSpan cspans[NSPANS];
4819 int currentClip = 0;
4820 const QSpan *end = spans + spanCount;
4821 while (spans < end) {
4822 QSpan *clipped = cspans;
4823 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4824 // qDebug() << "processed " << processed << "clipped" << clipped-cspans
4825 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4827 if (clipped - cspans)
4828 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4834 Clip spans to \a{clip}-rectangle.
4835 Returns number of unclipped spans
4837 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4840 const short minx = clip.left();
4841 const short miny = clip.top();
4842 const short maxx = clip.right();
4843 const short maxy = clip.bottom();
4846 for (int i = 0; i < numSpans; ++i) {
4847 if (spans[i].y > maxy)
4849 if (spans[i].y < miny
4850 || spans[i].x > maxx
4851 || spans[i].x + spans[i].len <= minx) {
4854 if (spans[i].x < minx) {
4855 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4858 spans[n].x = spans[i].x;
4859 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4861 if (spans[n].len == 0)
4863 spans[n].y = spans[i].y;
4864 spans[n].coverage = spans[i].coverage;
4871 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4874 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4875 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4877 Q_ASSERT(fillData->clip);
4878 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4880 // hw: check if this const_cast<> is safe!!!
4881 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4882 fillData->clip->clipRect);
4884 fillData->unclipped_blend(count, spans, fillData);
4887 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4889 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4891 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4892 // for (int i = 0; i < qMin(count, 10); ++i) {
4893 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4896 switch (clipData->operation) {
4898 case Qt::IntersectClip:
4900 QClipData *newClip = clipData->newClip;
4901 newClip->initialize();
4903 int currentClip = 0;
4904 const QSpan *end = spans + count;
4905 while (spans < end) {
4906 QSpan *newspans = newClip->m_spans + newClip->count;
4907 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4908 &newspans, newClip->allocated - newClip->count);
4909 newClip->count = newspans - newClip->m_spans;
4911 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4912 newClip->allocated *= 2;
4919 case Qt::ReplaceClip:
4920 clipData->newClip->appendSpans(spans, count);
4928 QImage QRasterBuffer::bufferImage() const
4930 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4932 for (int y = 0; y < m_height; ++y) {
4933 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4935 for (int x=0; x<m_width; ++x) {
4936 uint argb = span[x];
4937 image.setPixel(x, y, argb);
4945 void QRasterBuffer::flushToARGBImage(QImage *target) const
4947 int w = qMin(m_width, target->width());
4948 int h = qMin(m_height, target->height());
4950 for (int y=0; y<h; ++y) {
4951 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4952 QRgb *dest = (QRgb *) target->scanLine(y);
4953 for (int x=0; x<w; ++x) {
4954 QRgb pixel = sourceLine[x];
4955 int alpha = qAlpha(pixel);
4959 dest[x] = (alpha << 24)
4960 | ((255*qRed(pixel)/alpha) << 16)
4961 | ((255*qGreen(pixel)/alpha) << 8)
4962 | ((255*qBlue(pixel)/alpha) << 0);
4969 class QGradientCache
4973 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4974 stops(s), opacity(op), interpolationMode(mode) {}
4975 uint buffer[GRADIENT_STOPTABLE_SIZE];
4976 QGradientStops stops;
4978 QGradient::InterpolationMode interpolationMode;
4981 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4984 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4985 quint64 hash_val = 0;
4987 QGradientStops stops = gradient.stops();
4988 for (int i = 0; i < stops.size() && i <= 2; i++)
4989 hash_val += stops[i].second.rgba();
4991 QMutexLocker lock(&mutex);
4992 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4994 if (it == cache.constEnd())
4995 return addCacheElement(hash_val, gradient, opacity);
4998 const CacheInfo &cache_info = it.value();
4999 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
5000 return cache_info.buffer;
5002 } while (it != cache.constEnd() && it.key() == hash_val);
5003 // an exact match for these stops and opacity was not found, create new cache
5004 return addCacheElement(hash_val, gradient, opacity);
5008 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
5010 inline int maxCacheSize() const { return 60; }
5011 inline void generateGradientColorTable(const QGradient& g,
5013 int size, int opacity) const;
5014 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
5015 if (cache.size() == maxCacheSize()) {
5016 // may remove more than 1, but OK
5017 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
5019 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
5020 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
5021 return cache.insert(hash_val, cache_entry).value().buffer;
5024 QGradientColorTableHash cache;
5028 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
5030 QGradientStops stops = gradient.stops();
5031 int stopCount = stops.count();
5032 Q_ASSERT(stopCount > 0);
5034 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
5036 if (stopCount == 2) {
5037 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
5038 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
5040 qreal first_stop = stops[0].first;
5041 qreal second_stop = stops[1].first;
5043 if (second_stop < first_stop) {
5044 qSwap(first_color, second_color);
5045 qSwap(first_stop, second_stop);
5048 if (colorInterpolation) {
5049 first_color = PREMUL(first_color);
5050 second_color = PREMUL(second_color);
5053 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
5054 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
5056 uint red_first = qRed(first_color) << 16;
5057 uint green_first = qGreen(first_color) << 16;
5058 uint blue_first = qBlue(first_color) << 16;
5059 uint alpha_first = qAlpha(first_color) << 16;
5061 uint red_second = qRed(second_color) << 16;
5062 uint green_second = qGreen(second_color) << 16;
5063 uint blue_second = qBlue(second_color) << 16;
5064 uint alpha_second = qAlpha(second_color) << 16;
5067 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
5068 if (colorInterpolation)
5069 colorTable[i] = first_color;
5071 colorTable[i] = PREMUL(first_color);
5074 if (i < second_index) {
5075 qreal reciprocal = qreal(1) / (second_index - first_index);
5077 int red_delta = qRound(int(red_second - red_first) * reciprocal);
5078 int green_delta = qRound(int(green_second - green_first) * reciprocal);
5079 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
5080 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
5083 red_first += 1 << 15;
5084 green_first += 1 << 15;
5085 blue_first += 1 << 15;
5086 alpha_first += 1 << 15;
5088 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
5089 red_first += red_delta;
5090 green_first += green_delta;
5091 blue_first += blue_delta;
5092 alpha_first += alpha_delta;
5094 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
5095 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
5097 if (colorInterpolation)
5098 colorTable[i] = color;
5100 colorTable[i] = PREMUL(color);
5104 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
5105 if (colorInterpolation)
5106 colorTable[i] = second_color;
5108 colorTable[i] = PREMUL(second_color);
5114 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
5115 if (stopCount == 1) {
5116 current_color = PREMUL(current_color);
5117 for (int i = 0; i < size; ++i)
5118 colorTable[i] = current_color;
5122 // The position where the gradient begins and ends
5123 qreal begin_pos = stops[0].first;
5124 qreal end_pos = stops[stopCount-1].first;
5126 int pos = 0; // The position in the color table.
5129 qreal incr = 1 / qreal(size); // the double increment.
5130 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
5132 // Up to first point
5133 colorTable[pos++] = PREMUL(current_color);
5134 while (dpos <= begin_pos) {
5135 colorTable[pos] = colorTable[pos - 1];
5140 int current_stop = 0; // We always interpolate between current and current + 1.
5142 qreal t; // position between current left and right stops
5143 qreal t_delta; // the t increment per entry in the color table
5145 if (dpos < end_pos) {
5147 while (dpos > stops[current_stop+1].first)
5150 if (current_stop != 0)
5151 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
5152 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
5154 if (colorInterpolation) {
5155 current_color = PREMUL(current_color);
5156 next_color = PREMUL(next_color);
5159 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
5160 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
5161 t = (dpos - stops[current_stop].first) * c;
5165 Q_ASSERT(current_stop < stopCount);
5167 int dist = qRound(t);
5168 int idist = 256 - dist;
5170 if (colorInterpolation)
5171 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
5173 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
5178 if (dpos >= end_pos)
5184 while (dpos > stops[current_stop+skip+1].first)
5188 current_stop += skip;
5190 current_color = next_color;
5192 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
5193 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
5195 if (colorInterpolation) {
5197 current_color = PREMUL(current_color);
5198 next_color = PREMUL(next_color);
5201 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
5202 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
5203 t = (dpos - stops[current_stop].first) * c;
5210 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
5211 while (pos < size - 1) {
5212 colorTable[pos] = current_color;
5216 // Make sure the last color stop is represented at the end of the table
5217 colorTable[size - 1] = current_color;
5220 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
5223 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
5227 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
5232 m11 = m22 = m33 = 1.;
5233 m12 = m13 = m21 = m23 = dx = dy = 0.0;
5234 clip = pe ? pe->d_func()->clip() : 0;
5237 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
5239 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
5241 Qt::BrushStyle brushStyle = qbrush_style(brush);
5242 switch (brushStyle) {
5243 case Qt::SolidPattern: {
5245 QColor c = qbrush_color(brush);
5246 QRgb rgba = c.rgba();
5247 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
5248 if ((solid.color & 0xff000000) == 0
5249 && compositionMode == QPainter::CompositionMode_SourceOver) {
5255 case Qt::LinearGradientPattern:
5257 type = LinearGradient;
5258 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
5259 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5260 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5261 gradient.spread = g->spread();
5263 QLinearGradientData &linearData = gradient.linear;
5265 linearData.origin.x = g->start().x();
5266 linearData.origin.y = g->start().y();
5267 linearData.end.x = g->finalStop().x();
5268 linearData.end.y = g->finalStop().y();
5272 case Qt::RadialGradientPattern:
5274 type = RadialGradient;
5275 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
5276 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5277 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5278 gradient.spread = g->spread();
5280 QRadialGradientData &radialData = gradient.radial;
5282 QPointF center = g->center();
5283 radialData.center.x = center.x();
5284 radialData.center.y = center.y();
5285 radialData.center.radius = g->centerRadius();
5286 QPointF focal = g->focalPoint();
5287 radialData.focal.x = focal.x();
5288 radialData.focal.y = focal.y();
5289 radialData.focal.radius = g->focalRadius();
5293 case Qt::ConicalGradientPattern:
5295 type = ConicalGradient;
5296 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
5297 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
5298 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
5299 gradient.spread = QGradient::RepeatSpread;
5301 QConicalGradientData &conicalData = gradient.conical;
5303 QPointF center = g->center();
5304 conicalData.center.x = center.x();
5305 conicalData.center.y = center.y();
5306 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
5310 case Qt::Dense1Pattern:
5311 case Qt::Dense2Pattern:
5312 case Qt::Dense3Pattern:
5313 case Qt::Dense4Pattern:
5314 case Qt::Dense5Pattern:
5315 case Qt::Dense6Pattern:
5316 case Qt::Dense7Pattern:
5317 case Qt::HorPattern:
5318 case Qt::VerPattern:
5319 case Qt::CrossPattern:
5320 case Qt::BDiagPattern:
5321 case Qt::FDiagPattern:
5322 case Qt::DiagCrossPattern:
5325 tempImage = new QImage();
5326 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
5327 initTexture(tempImage, alpha, QTextureData::Tiled);
5329 case Qt::TexturePattern:
5332 tempImage = new QImage();
5334 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
5335 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
5337 *tempImage = brush.textureImage();
5338 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
5346 adjustSpanMethods();
5349 void QSpanData::adjustSpanMethods()
5359 unclipped_blend = 0;
5362 unclipped_blend = rasterBuffer->drawHelper->blendColor;
5363 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
5364 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
5365 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
5366 fillRect = rasterBuffer->drawHelper->fillRect;
5368 case LinearGradient:
5369 case RadialGradient:
5370 case ConicalGradient:
5371 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
5375 #ifndef QT_NO_RASTERCALLBACKS
5376 if (!rasterBuffer->buffer())
5377 unclipped_blend = qBlendTextureCallback;
5380 unclipped_blend = qBlendTexture;
5382 unclipped_blend = qBlendTexture;
5384 if (!texture.imageData)
5385 unclipped_blend = 0;
5390 if (!unclipped_blend) {
5393 blend = unclipped_blend;
5394 } else if (clip->hasRectClip) {
5395 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
5397 blend = qt_span_fill_clipped;
5401 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
5404 // make sure we round off correctly in qdrawhelper.cpp
5405 delta.translate(1.0 / 65536, 1.0 / 65536);
5407 QTransform inv = (delta * matrix).inverted();
5420 const bool affine = !m13 && !m23;
5421 fast_matrix = affine
5422 && m11 * m11 + m21 * m21 < 1e4
5423 && m12 * m12 + m22 * m22 < 1e4
5427 adjustSpanMethods();
5430 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
5432 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
5434 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
5435 if (!d || d->height == 0) {
5436 texture.imageData = 0;
5443 texture.bytesPerLine = 0;
5444 texture.format = QImage::Format_Invalid;
5445 texture.colorTable = 0;
5446 texture.hasAlpha = alpha != 256;
5448 texture.imageData = d->data;
5449 texture.width = d->width;
5450 texture.height = d->height;
5452 if (sourceRect.isNull()) {
5455 texture.x2 = texture.width;
5456 texture.y2 = texture.height;
5458 texture.x1 = sourceRect.x();
5459 texture.y1 = sourceRect.y();
5460 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
5461 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
5464 texture.bytesPerLine = d->bytes_per_line;
5466 texture.format = d->format;
5467 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5468 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5470 texture.const_alpha = alpha;
5471 texture.type = _type;
5473 adjustSpanMethods();
5485 Draws a line using the floating point midpoint algorithm. The line
5486 \a line is already in device coords at this point.
5489 static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
5490 LineDrawMode style, const QIntRect &devRect)
5492 #ifdef QT_DEBUG_DRAW
5493 qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
5497 int dx, dy, d, incrE, incrNE;
5502 const int NSPANS = 256;
5503 QT_FT_Span spans[NSPANS];
5505 bool ordered = true;
5508 // specialcase horizontal lines
5509 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5510 int start = qMax(devRect.x1, qMin(x1, x2));
5511 int stop = qMax(x1, x2) + 1;
5512 int stop_clipped = qMin(devRect.x2, stop);
5513 int len = stop_clipped - start;
5514 if (style == LineDrawNormal && stop == stop_clipped)
5517 spans[0].x = ushort(start);
5518 spans[0].len = ushort(len);
5520 spans[0].coverage = 255;
5521 span_func(1, spans, data);
5525 } else if (dx == 0) {
5526 // specialcase vertical lines
5527 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5528 int start = qMax(devRect.y1, qMin(y1, y2));
5529 int stop = qMax(y1, y2) + 1;
5530 int stop_clipped = qMin(devRect.y2, stop);
5531 int len = stop_clipped - start;
5532 if (style == LineDrawNormal && stop == stop_clipped)
5534 // hw: create spans directly instead to possibly avoid clipping
5536 fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
5542 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5544 if (x2 < x1) { /* if coordinates are out of order */
5545 qt_swap_int(x1, x2);
5548 qt_swap_int(y1, y2);
5552 int x_lower_limit = - 128;
5553 if (x1 < x_lower_limit) {
5554 int cy = dy * (x_lower_limit - x1) / dx + y1;
5555 drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect);
5559 if (style == LineDrawNormal)
5562 // In the loops below we increment before call the span function so
5563 // we need to stop one pixel before
5564 x2 = qMin(x2, devRect.x2 - 1);
5566 // completely clipped, so abort
5578 const int index = (ordered ? current : NSPANS - 1 - current);
5579 spans[index].coverage = 255;
5583 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
5584 spans[index].len = 1;
5586 spans[index].len = 0;
5589 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
5590 y2 = qMin(y2, devRect.y2 - 1);
5594 incrNE = (dy - dx) * 2;
5597 goto flush_and_return;
5602 if (spans[current].len > 0)
5604 if (current == NSPANS) {
5605 span_func(NSPANS, spans, data);
5612 goto flush_and_return;
5614 spans[current].len = 0;
5615 spans[current].coverage = 255;
5616 spans[current].x = x;
5617 spans[current].y = y;
5620 if (x == devRect.x1)
5621 spans[current].x = devRect.x1;
5624 if (x < devRect.x1 || y < devRect.y1)
5627 Q_ASSERT(x<devRect.x2);
5628 Q_ASSERT(y<devRect.y2);
5629 Q_ASSERT(spans[current].y == y);
5630 spans[current].len++;
5632 if (spans[current].len > 0) {
5635 } else { // 0-45 and 180->225 (unit circle degrees)
5637 y1 = qMin(y1, devRect.y2 - 1);
5641 incrNE = (dy + dx) * 2;
5644 goto flush_and_return;
5649 if (spans[NSPANS - 1 - current].len > 0)
5651 if (current == NSPANS) {
5652 span_func(NSPANS, spans, data);
5659 goto flush_and_return;
5661 const int index = NSPANS - 1 - current;
5662 spans[index].len = 0;
5663 spans[index].coverage = 255;
5668 if (x == devRect.x1)
5669 spans[NSPANS - 1 - current].x = devRect.x1;
5672 if (x < devRect.x1 || y > y1)
5675 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5676 Q_ASSERT(spans[NSPANS - 1 - current].y == y);
5677 spans[NSPANS - 1 - current].len++;
5679 if (spans[NSPANS - 1 - current].len > 0) {
5686 // if y is the major axis:
5688 if (y2 < y1) { /* if coordinates are out of order */
5689 qt_swap_int(y1, y2);
5692 qt_swap_int(x1, x2);
5696 int y_lower_limit = - 128;
5697 if (y1 < y_lower_limit) {
5698 int cx = dx * (y_lower_limit - y1) / dy + x1;
5699 drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect);
5703 if (style == LineDrawNormal)
5706 // In the loops below we increment before call the span function so
5707 // we need to stop one pixel before
5708 y2 = qMin(y2, devRect.y2 - 1);
5710 // completely clipped, so abort
5718 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
5719 Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
5720 if (current == NSPANS) {
5721 span_func(NSPANS, spans, data);
5724 spans[current].len = 1;
5725 spans[current].coverage = 255;
5726 spans[current].x = x;
5727 spans[current].y = y;
5731 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
5732 x2 = qMin(x2, devRect.x2 - 1);
5735 incrNE = (dx - dy) * 2;
5738 goto flush_and_return;
5745 goto flush_and_return;
5750 if (x < devRect.x1 || y < devRect.y1)
5752 Q_ASSERT(x<devRect.x2 && y<devRect.y2);
5753 if (current == NSPANS) {
5754 span_func(NSPANS, spans, data);
5757 spans[current].len = 1;
5758 spans[current].coverage = 255;
5759 spans[current].x = x;
5760 spans[current].y = y;
5763 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
5764 x1 = qMin(x1, devRect.x2 - 1);
5767 incrNE = (dx + dy) * 2;
5770 goto flush_and_return;
5777 goto flush_and_return;
5782 if (y < devRect.y1 || x > x1)
5784 Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
5785 if (current == NSPANS) {
5786 span_func(NSPANS, spans, data);
5789 spans[current].len = 1;
5790 spans[current].coverage = 255;
5791 spans[current].x = x;
5792 spans[current].y = y;
5799 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
5802 static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
5805 if (--*currentOffset == 0) {
5807 *dashIndex = ((*dashIndex + 1) % pattern.size());
5808 *currentOffset = int(pattern[*dashIndex]);
5813 static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
5815 ProcessSpans span_func, QSpanData *data,
5816 LineDrawMode style, const QIntRect &devRect,
5819 #ifdef QT_DEBUG_DRAW
5820 qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
5824 int dx, dy, d, incrE, incrNE;
5829 Q_ASSERT(*patternOffset >= 0);
5831 const QVector<qreal> penPattern = pen->dashPattern();
5832 QVarLengthArray<qreal> pattern(penPattern.size());
5834 int patternLength = 0;
5835 for (int i = 0; i < penPattern.size(); ++i)
5836 patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
5838 // pattern must be reversed if coordinates are out of order
5839 int reverseLength = -1;
5840 if (dy == 0 && x1 > x2)
5841 reverseLength = x1 - x2;
5842 else if (dx == 0 && y1 > y2)
5843 reverseLength = y1 - y2;
5844 else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
5845 reverseLength = qAbs(dx);
5846 else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
5847 reverseLength = qAbs(dy);
5849 const bool reversed = (reverseLength > -1);
5850 if (reversed) { // reverse pattern
5851 for (int i = 0; i < penPattern.size(); ++i)
5852 pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
5854 *patternOffset = (patternLength - 1 - *patternOffset);
5855 *patternOffset += patternLength - (reverseLength % patternLength);
5856 *patternOffset = *patternOffset % patternLength;
5858 for (int i = 0; i < penPattern.size(); ++i)
5859 pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
5863 bool inDash = !reversed;
5864 int currPattern = int(pattern[dashIndex]);
5866 // adjust pattern for offset
5867 offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
5869 const int NSPANS = 256;
5870 QT_FT_Span spans[NSPANS];
5872 bool ordered = true;
5875 // specialcase horizontal lines
5876 if (y1 >= devRect.y1 && y1 < devRect.y2) {
5877 int start_unclipped = qMin(x1, x2);
5878 int start = qMax(devRect.x1, start_unclipped);
5879 int stop = qMax(x1, x2) + 1;
5880 int stop_clipped = qMin(devRect.x2, stop);
5881 int len = stop_clipped - start;
5882 if (style == LineDrawNormal && stop == stop_clipped)
5885 // adjust pattern for starting offset
5886 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5890 while (x < stop_clipped) {
5891 if (current == NSPANS) {
5892 span_func(NSPANS, spans, data);
5895 const int dash = qMin(currPattern, stop_clipped - x);
5897 spans[current].x = ushort(x);
5898 spans[current].len = ushort(dash);
5899 spans[current].y = y1;
5900 spans[current].coverage = 255;
5903 if (dash < currPattern) {
5904 currPattern -= dash;
5906 dashIndex = (dashIndex + 1) % pattern.size();
5907 currPattern = int(pattern[dashIndex]);
5914 goto flush_and_return;
5915 } else if (dx == 0) {
5916 if (x1 >= devRect.x1 && x1 < devRect.x2) {
5917 int start_unclipped = qMin(y1, y2);
5918 int start = qMax(devRect.y1, start_unclipped);
5919 int stop = qMax(y1, y2) + 1;
5920 int stop_clipped = qMin(devRect.y2, stop);
5921 if (style == LineDrawNormal && stop == stop_clipped)
5924 stop = stop_clipped;
5926 // adjust pattern for starting offset
5927 offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
5932 const int dash = qMin(currPattern, stop - y);
5934 for (int i = 0; i < dash; ++i) {
5935 if (current == NSPANS) {
5936 span_func(NSPANS, spans, data);
5939 spans[current].x = x1;
5940 spans[current].len = 1;
5941 spans[current].coverage = 255;
5942 spans[current].y = ushort(y + i);
5946 if (dash < currPattern) {
5947 currPattern -= dash;
5949 dashIndex = (dashIndex + 1) % pattern.size();
5950 currPattern = int(pattern[dashIndex]);
5956 goto flush_and_return;
5959 if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
5961 if (x2 < x1) { /* if coordinates are out of order */
5962 qt_swap_int(x1, x2);
5965 qt_swap_int(y1, y2);
5969 if (style == LineDrawNormal)
5972 // In the loops below we increment before call the span function so
5973 // we need to stop one pixel before
5974 x2 = qMin(x2, devRect.x2 - 1);
5976 // completely clipped, so abort
5978 goto flush_and_return;
5983 if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
5984 Q_ASSERT(x < devRect.x2);
5986 if (current == NSPANS) {
5987 span_func(NSPANS, spans, data);
5990 spans[current].len = 1;
5991 spans[current].coverage = 255;
5992 spans[current].x = x;
5993 spans[current].y = y;
5996 if (--currPattern <= 0) {
5998 dashIndex = (dashIndex + 1) % pattern.size();
5999 currPattern = int(pattern[dashIndex]);
6003 if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
6004 y2 = qMin(y2, devRect.y2 - 1);
6008 incrNE = (dy - dx) * 2;
6011 goto flush_and_return;
6018 goto flush_and_return;
6024 const bool skip = x < devRect.x1 || y < devRect.y1;
6025 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
6026 if (inDash && !skip) {
6027 if (current == NSPANS) {
6028 span_func(NSPANS, spans, data);
6031 spans[current].len = 1;
6032 spans[current].coverage = 255;
6033 spans[current].x = x;
6034 spans[current].y = y;
6037 if (--currPattern <= 0) {
6039 dashIndex = (dashIndex + 1) % pattern.size();
6040 currPattern = int(pattern[dashIndex]);
6043 } else { // 0-45 and 180->225 (unit circle degrees)
6044 y1 = qMin(y1, devRect.y2 - 1);
6048 incrNE = (dy + dx) * 2;
6051 goto flush_and_return;
6056 span_func(current, spans, data);
6063 goto flush_and_return;
6069 const bool skip = x < devRect.x1 || y > y1;
6070 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
6071 if (inDash && !skip) {
6072 if (current == NSPANS) {
6073 span_func(NSPANS, spans, data);
6076 spans[current].len = 1;
6077 spans[current].coverage = 255;
6078 spans[current].x = x;
6079 spans[current].y = y;
6082 if (--currPattern <= 0) {
6084 dashIndex = (dashIndex + 1) % pattern.size();
6085 currPattern = int(pattern[dashIndex]);
6091 // if y is the major axis:
6093 if (y2 < y1) { /* if coordinates are out of order */
6094 qt_swap_int(y1, y2);
6097 qt_swap_int(x1, x2);
6101 if (style == LineDrawNormal)
6104 // In the loops below we increment before call the span function so
6105 // we need to stop one pixel before
6106 y2 = qMin(y2, devRect.y2 - 1);
6108 // completely clipped, so abort
6110 goto flush_and_return;
6115 if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
6116 Q_ASSERT(x < devRect.x2);
6118 if (current == NSPANS) {
6119 span_func(NSPANS, spans, data);
6122 spans[current].len = 1;
6123 spans[current].coverage = 255;
6124 spans[current].x = x;
6125 spans[current].y = y;
6128 if (--currPattern <= 0) {
6130 dashIndex = (dashIndex + 1) % pattern.size();
6131 currPattern = int(pattern[dashIndex]);
6135 if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
6136 x2 = qMin(x2, devRect.x2 - 1);
6139 incrNE = (dx - dy) * 2;
6142 goto flush_and_return;
6149 goto flush_and_return;
6154 const bool skip = x < devRect.x1 || y < devRect.y1;
6155 Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
6156 if (inDash && !skip) {
6157 if (current == NSPANS) {
6158 span_func(NSPANS, spans, data);
6161 spans[current].len = 1;
6162 spans[current].coverage = 255;
6163 spans[current].x = x;
6164 spans[current].y = y;
6167 if (--currPattern <= 0) {
6169 dashIndex = (dashIndex + 1) % pattern.size();
6170 currPattern = int(pattern[dashIndex]);
6173 } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
6174 x1 = qMin(x1, devRect.x2 - 1);
6177 incrNE = (dx + dy) * 2;
6180 goto flush_and_return;
6187 goto flush_and_return;
6192 const bool skip = y < devRect.y1 || x > x1;
6193 Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
6194 if (inDash && !skip) {
6195 if (current == NSPANS) {
6196 span_func(NSPANS, spans, data);
6199 spans[current].len = 1;
6200 spans[current].coverage = 255;
6201 spans[current].x = x;
6202 spans[current].y = y;
6205 if (--currPattern <= 0) {
6207 dashIndex = (dashIndex + 1) % pattern.size();
6208 currPattern = int(pattern[dashIndex]);
6215 span_func(current, ordered ? spans : spans + (NSPANS - current), data);
6219 *patternOffset = (patternLength - 1 - *patternOffset);
6222 for (int i = 0; i <= dashIndex; ++i)
6223 *patternOffset += int(pattern[i]);
6224 *patternOffset += patternLength - currPattern - 1;
6225 *patternOffset = (*patternOffset % patternLength);
6231 \a x and \a y is relative to the midpoint of \a rect.
6233 static inline void drawEllipsePoints(int x, int y, int length,
6236 ProcessSpans pen_func, ProcessSpans brush_func,
6237 QSpanData *pen_data, QSpanData *brush_data)
6242 QT_FT_Span outline[4];
6243 const int midx = rect.x() + (rect.width() + 1) / 2;
6244 const int midy = rect.y() + (rect.height() + 1) / 2;
6250 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
6251 outline[0].len = qMin(length, x - outline[0].x);
6253 outline[0].coverage = 255;
6257 outline[1].len = length;
6259 outline[1].coverage = 255;
6262 outline[2].x = outline[0].x;
6263 outline[2].len = outline[0].len;
6264 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
6265 outline[2].coverage = 255;
6269 outline[3].len = length;
6270 outline[3].y = outline[2].y;
6271 outline[3].coverage = 255;
6273 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
6277 fill[0].x = outline[0].x + outline[0].len - 1;
6278 fill[0].len = qMax(0, outline[1].x - fill[0].x);
6279 fill[0].y = outline[1].y;
6280 fill[0].coverage = 255;
6283 fill[1].x = outline[2].x + outline[2].len - 1;
6284 fill[1].len = qMax(0, outline[3].x - fill[1].x);
6285 fill[1].y = outline[3].y;
6286 fill[1].coverage = 255;
6288 int n = (fill[0].y >= fill[1].y ? 1 : 2);
6289 n = qt_intersect_spans(fill, n, clip);
6291 brush_func(n, fill, brush_data);
6294 int n = (outline[1].y >= outline[2].y ? 2 : 4);
6295 n = qt_intersect_spans(outline, n, clip);
6297 pen_func(n, outline, pen_data);
6303 Draws an ellipse using the integer point midpoint algorithm.
6305 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
6306 ProcessSpans pen_func, ProcessSpans brush_func,
6307 QSpanData *pen_data, QSpanData *brush_data)
6309 const qreal a = qreal(rect.width()) / 2;
6310 const qreal b = qreal(rect.height()) / 2;
6311 qreal d = b*b - (a*a*b) + 0.25*a*a;
6314 int y = (rect.height() + 1) / 2;
6318 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
6319 if (d < 0) { // select E
6322 } else { // select SE
6323 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
6324 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6325 pen_func, brush_func, pen_data, brush_data);
6330 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
6331 pen_func, brush_func, pen_data, brush_data);
6334 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
6335 const int miny = rect.height() & 0x1;
6337 if (d < 0) { // select SE
6338 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
6340 } else { // select S
6341 d += a*a*(-2*y + 3);
6344 drawEllipsePoints(x, y, 1, rect, clip,
6345 pen_func, brush_func, pen_data, brush_data);
6350 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
6353 Draws the first \a pointCount points in the buffer \a points
6355 The default implementation converts the first \a pointCount QPoints in \a points
6356 to QPointFs and calls the floating point version of drawPoints.
6360 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
6363 Reimplement this function to draw the largest ellipse that can be
6364 contained within rectangle \a rect.
6367 #ifdef QT_DEBUG_DRAW
6368 void dumpClip(int width, int height, const QClipData *clip)
6370 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
6371 clipImg.fill(0xffff0000);
6378 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
6380 for (int i = 0; i < clip->count; ++i) {
6381 const QSpan *span = ((QClipData *) clip)->spans() + i;
6382 for (int j = 0; j < span->len; ++j)
6383 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
6384 x0 = qMin(x0, int(span->x));
6385 x1 = qMax(x1, int(span->x + span->len - 1));
6387 y0 = qMin(y0, int(span->y));
6388 y1 = qMax(y1, int(span->y));
6391 static int counter = 0;
6398 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
6399 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));