1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
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 <private/qcosmeticstroker_p.h>
73 #include "qmemrotate_p.h"
75 #include "qpaintengine_raster_p.h"
76 // #include "qbezier_p.h"
77 #include "qoutlinemapper_p.h"
80 # include <qt_windows.h>
81 # include <qvarlengtharray.h>
82 # include <private/qfontengine_p.h>
83 # if defined(Q_OS_WINCE)
84 # include "qguifunctions_wince.h"
86 #elif defined(Q_WS_MAC)
87 # include <private/qt_mac_p.h>
88 # include <private/qpixmap_mac_p.h>
89 # include <private/qpaintengine_mac_p.h>
90 #elif defined(Q_WS_QWS)
91 # if !defined(QT_NO_FREETYPE)
92 # include <private/qfontengine_ft_p.h>
94 # if !defined(QT_NO_QWS_QPF2)
95 # include <private/qfontengine_qpf_p.h>
97 # include <private/qabstractfontengine_p.h>
98 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
99 # include <private/qfontengine_s60_p.h>
100 #elif defined(Q_WS_QPA)
101 # include <private/qfontengine_ft_p.h>
104 #if defined(Q_WS_WIN64)
111 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
113 #define qreal_to_fixed_26_6(f) (int(f * 64))
114 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
115 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
117 // #define QT_DEBUG_DRAW
119 void dumpClip(int width, int height, const QClipData *clip);
122 #define QT_FAST_SPANS
125 // A little helper macro to get a better approximation of dimensions.
126 // If we have a rect that starting at 0.5 of width 3.5 it should span
128 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
130 // use the same rounding as in qrasterizer.cpp (6 bit fixed point)
131 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
134 extern bool qt_cleartype_enabled;
138 extern bool qt_applefontsmoothing_enabled;
142 /********************************************************************************
145 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
146 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
147 static void qt_span_clip(int count, const QSpan *spans, void *userData);
148 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
154 Qt::ClipOperation operation;
160 LineDrawIncludeLastPixel
163 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
164 ProcessSpans pen_func, ProcessSpans brush_func,
165 QSpanData *pen_data, QSpanData *brush_data);
167 struct QRasterFloatPoint {
173 static const QRectF boundingRect(const QPointF *points, int pointCount)
175 const QPointF *e = points;
176 const QPointF *last = points + pointCount;
177 qreal minx, maxx, miny, maxy;
178 minx = maxx = e->x();
179 miny = maxy = e->y();
183 else if (e->x() > maxx)
187 else if (e->y() > maxy)
190 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
194 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
195 return (elementCount == 5 // 5-point polygon, check for closed rect
196 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
197 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
198 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
199 && pts[0] < pts[4] && pts[1] < pts[5]
201 (elementCount == 4 // 4-point polygon, check for unclosed rect
202 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
203 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
204 && pts[0] < pts[4] && pts[1] < pts[5]
209 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
211 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
214 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
216 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
219 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
220 qfixed c2x, qfixed c2y,
221 qfixed ex, qfixed ey,
224 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
225 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
226 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
230 #if !defined(QT_NO_DEBUG) && 0
231 static void qt_debug_path(const QPainterPath &path)
233 const char *names[] = {
240 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
241 for (int i=0; i<path.elementCount(); ++i) {
242 const QPainterPath::Element &e = path.elementAt(i);
243 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
244 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
249 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
250 QPaintEngineExPrivate(),
257 \class QRasterPaintEngine
262 \brief The QRasterPaintEngine class enables hardware acceleration
263 of painting operations in Qt for Embedded Linux.
265 Note that this functionality is only available in
266 \l{Qt for Embedded Linux}.
268 In \l{Qt for Embedded Linux}, painting is a pure software
269 implementation. But starting with Qt 4.2, it is
270 possible to add an accelerated graphics driver to take advantage
271 of available hardware resources.
273 Hardware acceleration is accomplished by creating a custom screen
274 driver, accelerating the copying from memory to the screen, and
275 implementing a custom paint engine accelerating the various
276 painting operations. Then a custom paint device (derived from the
277 QCustomRasterPaintDevice class) and a custom window surface
278 (derived from QWSWindowSurface) must be implemented to make
279 \l{Qt for Embedded Linux} aware of the accelerated driver.
281 \note The QRasterPaintEngine class does not support 8-bit images.
282 Instead, they need to be converted to a supported format, such as
283 QImage::Format_ARGB32_Premultiplied.
285 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
286 documentation for details.
288 \sa QCustomRasterPaintDevice, QPaintEngine
292 \fn Type QRasterPaintEngine::type() const
298 \relates QRasterPaintEngine
300 A struct equivalent to QT_FT_Span, containing a position (x,
301 y), the span's length in pixels and its color/coverage (a value
302 ranging from 0 to 255).
308 Creates a raster based paint engine for operating on the given
309 \a device, with the complete set of \l
310 {QPaintEngine::PaintEngineFeature}{paint engine features and
313 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
314 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
316 d_func()->device = device;
323 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
326 d_func()->device = device;
330 void QRasterPaintEngine::init()
332 Q_D(QRasterPaintEngine);
339 // The antialiasing raster.
340 d->grayRaster.reset(new QT_FT_Raster);
341 Q_CHECK_PTR(d->grayRaster.data());
342 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
343 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
346 d->rasterizer.reset(new QRasterizer);
347 d->rasterBuffer.reset(new QRasterBuffer());
348 d->outlineMapper.reset(new QOutlineMapper);
349 d->outlinemapper_xform_dirty = true;
351 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
352 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
353 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
355 d->baseClip.reset(new QClipData(d->device->height()));
356 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
358 d->image_filler.init(d->rasterBuffer.data(), this);
359 d->image_filler.type = QSpanData::Texture;
361 d->image_filler_xform.init(d->rasterBuffer.data(), this);
362 d->image_filler_xform.type = QSpanData::Texture;
364 d->solid_color_filler.init(d->rasterBuffer.data(), this);
365 d->solid_color_filler.type = QSpanData::Solid;
367 d->deviceDepth = d->device->depth();
369 d->mono_surface = false;
370 gccaps &= ~PorterDuff;
372 QImage::Format format = QImage::Format_Invalid;
374 switch (d->device->devType()) {
375 case QInternal::Pixmap:
376 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
378 case QInternal::Image:
379 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
382 case QInternal::CustomRaster:
383 d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
387 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
393 case QImage::Format_MonoLSB:
394 case QImage::Format_Mono:
395 d->mono_surface = true;
397 case QImage::Format_ARGB8565_Premultiplied:
398 case QImage::Format_ARGB8555_Premultiplied:
399 case QImage::Format_ARGB6666_Premultiplied:
400 case QImage::Format_ARGB4444_Premultiplied:
401 case QImage::Format_ARGB32_Premultiplied:
402 case QImage::Format_ARGB32:
403 gccaps |= PorterDuff;
405 case QImage::Format_RGB32:
406 case QImage::Format_RGB444:
407 case QImage::Format_RGB555:
408 case QImage::Format_RGB666:
409 case QImage::Format_RGB888:
410 case QImage::Format_RGB16:
421 Destroys this paint engine.
423 QRasterPaintEngine::~QRasterPaintEngine()
425 Q_D(QRasterPaintEngine);
427 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
433 bool QRasterPaintEngine::begin(QPaintDevice *device)
435 Q_D(QRasterPaintEngine);
437 if (device->devType() == QInternal::Pixmap) {
438 QPixmap *pixmap = static_cast<QPixmap *>(device);
439 QPixmapData *pd = pixmap->pixmapData();
440 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
441 d->device = pd->buffer();
446 // Make sure QPaintEngine::paintDevice() returns the proper device.
449 Q_ASSERT(d->device->devType() == QInternal::Image
450 || d->device->devType() == QInternal::CustomRaster);
452 d->systemStateChanged();
454 QRasterPaintEngineState *s = state();
455 ensureOutlineMapper();
456 d->outlineMapper->m_clip_rect = d->deviceRect;
458 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
459 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
460 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
461 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
463 d->rasterizer->setClipRect(d->deviceRect);
465 s->penData.init(d->rasterBuffer.data(), this);
466 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
467 s->stroker = &d->basicStroker;
468 d->basicStroker.setClipRect(d->deviceRect);
470 s->brushData.init(d->rasterBuffer.data(), this);
471 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
473 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
475 setDirty(DirtyBrushOrigin);
478 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
479 << ") devType:" << device->devType()
480 << "devRect:" << d->deviceRect;
482 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
486 #if defined(Q_WS_WIN)
487 d->isPlain45DegreeRotation = true;
491 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
492 #if defined(Q_WS_WIN)
493 else if (qt_cleartype_enabled)
494 #elif defined (Q_WS_MAC)
495 else if (qt_applefontsmoothing_enabled)
500 QImage::Format format = static_cast<QImage *>(d->device)->format();
501 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
502 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
504 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
506 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
515 bool QRasterPaintEngine::end()
518 Q_D(QRasterPaintEngine);
519 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
521 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
531 void QRasterPaintEngine::releaseBuffer()
533 Q_D(QRasterPaintEngine);
534 d->rasterBuffer.reset(new QRasterBuffer);
540 QSize QRasterPaintEngine::size() const
542 Q_D(const QRasterPaintEngine);
543 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
550 void QRasterPaintEngine::saveBuffer(const QString &s) const
552 Q_D(const QRasterPaintEngine);
553 d->rasterBuffer->bufferImage().save(s, "PNG");
560 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
562 QRasterPaintEngineState *s = state();
563 // FALCON: get rid of this line, see drawImage call below.
565 QTransform::TransformationType txop = s->matrix.type();
569 case QTransform::TxNone:
570 s->flags.int_xform = true;
573 case QTransform::TxTranslate:
574 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
575 && qreal(int(s->matrix.dy())) == s->matrix.dy();
578 case QTransform::TxScale:
579 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
580 && qreal(int(s->matrix.dy())) == s->matrix.dy()
581 && qreal(int(s->matrix.m11())) == s->matrix.m11()
582 && qreal(int(s->matrix.m22())) == s->matrix.m22();
585 default: // shear / perspective...
586 s->flags.int_xform = false;
590 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
592 ensureOutlineMapper();
595 Q_D(QRasterPaintEngine);
596 d->isPlain45DegreeRotation = false;
597 if (txop >= QTransform::TxRotate) {
598 d->isPlain45DegreeRotation =
599 (qFuzzyIsNull(matrix.m11())
600 && qFuzzyIsNull(matrix.m12() - qreal(1))
601 && qFuzzyIsNull(matrix.m21() + qreal(1))
602 && qFuzzyIsNull(matrix.m22())
605 (qFuzzyIsNull(matrix.m11() + qreal(1))
606 && qFuzzyIsNull(matrix.m12())
607 && qFuzzyIsNull(matrix.m21())
608 && qFuzzyIsNull(matrix.m22() + qreal(1))
611 (qFuzzyIsNull(matrix.m11())
612 && qFuzzyIsNull(matrix.m12() + qreal(1))
613 && qFuzzyIsNull(matrix.m21() - qreal(1))
614 && qFuzzyIsNull(matrix.m22())
624 QRasterPaintEngineState::~QRasterPaintEngineState()
626 if (flags.has_clip_ownership)
631 QRasterPaintEngineState::QRasterPaintEngineState()
643 flags.fast_pen = true;
644 flags.antialiased = false;
645 flags.bilinear = false;
646 flags.fast_text = true;
647 flags.int_xform = true;
648 flags.tx_noshear = true;
649 flags.fast_images = true;
652 flags.has_clip_ownership = false;
657 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
662 , strokeFlags(s.strokeFlags)
663 , lastBrush(s.lastBrush)
664 , brushData(s.brushData)
665 , fillFlags(s.fillFlags)
666 , pixmapFlags(s.pixmapFlags)
667 , intOpacity(s.intOpacity)
671 , flag_bits(s.flag_bits)
673 brushData.tempImage = 0;
674 penData.tempImage = 0;
675 flags.has_clip_ownership = false;
681 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
683 QRasterPaintEngineState *s;
685 s = new QRasterPaintEngineState();
687 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
695 void QRasterPaintEngine::setState(QPainterState *s)
697 Q_D(QRasterPaintEngine);
698 QPaintEngineEx::setState(s);
699 d->rasterBuffer->compositionMode = s->composition_mode;
703 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
708 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
715 void QRasterPaintEngine::penChanged()
718 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
720 QRasterPaintEngineState *s = state();
721 s->strokeFlags |= DirtyPen;
722 s->dirty |= DirtyPen;
728 void QRasterPaintEngine::updatePen(const QPen &pen)
730 Q_D(QRasterPaintEngine);
731 QRasterPaintEngineState *s = state();
733 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
736 Qt::PenStyle pen_style = qpen_style(pen);
741 s->penData.clip = d->clip();
742 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
744 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
745 || pen.brush().transform().type() >= QTransform::TxNone) {
746 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
749 // Slightly ugly handling of an uncommon case... We need to change
750 // the pen because it is reused in draw_midpoint to decide dashed
752 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
753 pen_style = Qt::SolidLine;
754 s->lastPen.setStyle(Qt::SolidLine);
757 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
758 d->basicStroker.setCapStyle(qpen_capStyle(pen));
759 d->basicStroker.setMiterLimit(pen.miterLimit());
761 qreal penWidth = qpen_widthf(pen);
763 d->basicStroker.setStrokeWidth(1);
765 d->basicStroker.setStrokeWidth(penWidth);
767 if(pen_style == Qt::SolidLine) {
768 s->stroker = &d->basicStroker;
769 } else if (pen_style != Qt::NoPen) {
771 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
772 if (pen.isCosmetic()) {
773 d->dashStroker->setClipRect(d->deviceRect);
775 // ### I've seen this inverted devrect multiple places now...
776 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
777 d->dashStroker->setClipRect(clipRect);
779 d->dashStroker->setDashPattern(pen.dashPattern());
780 d->dashStroker->setDashOffset(pen.dashOffset());
781 s->stroker = d->dashStroker.data();
786 ensureState(); // needed because of tx_noshear...
787 s->flags.fast_pen = pen_style > Qt::NoPen
789 && ((pen.isCosmetic() && penWidth <= 1)
790 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
792 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
802 void QRasterPaintEngine::brushOriginChanged()
804 QRasterPaintEngineState *s = state();
806 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
809 s->fillFlags |= DirtyBrushOrigin;
816 void QRasterPaintEngine::brushChanged()
818 QRasterPaintEngineState *s = state();
820 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
822 s->fillFlags |= DirtyBrush;
831 void QRasterPaintEngine::updateBrush(const QBrush &brush)
834 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
836 Q_D(QRasterPaintEngine);
837 QRasterPaintEngineState *s = state();
838 // must set clip prior to setup, as setup uses it...
839 s->brushData.clip = d->clip();
840 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
841 if (s->fillFlags & DirtyTransform
842 || brush.transform().type() >= QTransform::TxNone)
843 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
844 s->lastBrush = brush;
848 void QRasterPaintEngine::updateOutlineMapper()
850 Q_D(QRasterPaintEngine);
851 d->outlineMapper->setMatrix(state()->matrix);
854 void QRasterPaintEngine::updateState()
856 QRasterPaintEngineState *s = state();
858 if (s->dirty & DirtyTransform)
859 updateMatrix(s->matrix);
861 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
862 const QPainter::CompositionMode mode = s->composition_mode;
863 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
864 && s->intOpacity == 256
865 && (mode == QPainter::CompositionMode_Source
866 || (mode == QPainter::CompositionMode_SourceOver
867 && qAlpha(s->penData.solid.color) == 255));
877 void QRasterPaintEngine::opacityChanged()
879 QRasterPaintEngineState *s = state();
882 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
885 s->fillFlags |= DirtyOpacity;
886 s->strokeFlags |= DirtyOpacity;
887 s->pixmapFlags |= DirtyOpacity;
888 s->dirty |= DirtyOpacity;
889 s->intOpacity = (int) (s->opacity * 256);
895 void QRasterPaintEngine::compositionModeChanged()
897 Q_D(QRasterPaintEngine);
898 QRasterPaintEngineState *s = state();
901 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
904 s->fillFlags |= DirtyCompositionMode;
905 s->dirty |= DirtyCompositionMode;
907 s->strokeFlags |= DirtyCompositionMode;
908 d->rasterBuffer->compositionMode = s->composition_mode;
910 d->recalculateFastImages();
916 void QRasterPaintEngine::renderHintsChanged()
918 QRasterPaintEngineState *s = state();
921 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
924 bool was_aa = s->flags.antialiased;
925 bool was_bilinear = s->flags.bilinear;
927 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
928 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
930 if (was_aa != s->flags.antialiased)
931 s->strokeFlags |= DirtyHints;
933 if (was_bilinear != s->flags.bilinear) {
934 s->strokeFlags |= DirtyPen;
935 s->fillFlags |= DirtyBrush;
938 Q_D(QRasterPaintEngine);
939 d->recalculateFastImages();
945 void QRasterPaintEngine::transformChanged()
947 QRasterPaintEngineState *s = state();
950 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
953 s->fillFlags |= DirtyTransform;
954 s->strokeFlags |= DirtyTransform;
956 s->dirty |= DirtyTransform;
958 Q_D(QRasterPaintEngine);
959 d->recalculateFastImages();
965 void QRasterPaintEngine::clipEnabledChanged()
967 QRasterPaintEngineState *s = state();
970 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
974 s->clip->enabled = s->clipEnabled;
975 s->fillFlags |= DirtyClipEnabled;
976 s->strokeFlags |= DirtyClipEnabled;
977 s->pixmapFlags |= DirtyClipEnabled;
982 void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
984 rasterBuffer->prepare(device);
988 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
990 SrcOverBlendFunc func,
995 if (alpha == 0 || !clip.isValid())
998 Q_ASSERT(img.depth() >= 8);
1000 int srcBPL = img.bytesPerLine();
1001 const uchar *srcBits = img.bits();
1002 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1003 int iw = img.width();
1004 int ih = img.height();
1006 if (!sr.isEmpty()) {
1009 // Adjust the image according to the source offset...
1010 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1013 // adapt the x parameters
1014 int x = qRound(pt.x());
1016 int cx2 = clip.x() + clip.width();
1019 srcBits += srcSize * d;
1024 int d = x + iw - cx2;
1030 // adapt the y paremeters...
1032 int cy2 = clip.y() + clip.height();
1033 int y = qRound(pt.y());
1036 srcBits += srcBPL * d;
1041 int d = y + ih - cy2;
1047 // call the blend function...
1048 int dstSize = rasterBuffer->bytesPerPixel();
1049 int dstBPL = rasterBuffer->bytesPerLine();
1050 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1057 void QRasterPaintEnginePrivate::systemStateChanged()
1059 QRect clipRect(0, 0,
1060 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1061 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1063 if (!systemClip.isEmpty()) {
1064 QRegion clippedDeviceRgn = systemClip & clipRect;
1065 deviceRect = clippedDeviceRgn.boundingRect();
1066 baseClip->setClipRegion(clippedDeviceRgn);
1068 deviceRect = clipRect;
1069 baseClip->setClipRect(deviceRect);
1071 #ifdef QT_DEBUG_DRAW
1072 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1075 exDeviceRect = deviceRect;
1077 Q_Q(QRasterPaintEngine);
1078 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1079 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1080 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1083 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1085 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1088 Q_Q(QRasterPaintEngine);
1089 bool bilinear = q->state()->flags.bilinear;
1091 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1092 spanData->setupMatrix(b.transform() * m, bilinear);
1094 if (m.type() <= QTransform::TxTranslate) {
1095 // specialize setupMatrix for translation matrices
1096 // to avoid needless matrix inversion
1104 spanData->dx = -m.dx();
1105 spanData->dy = -m.dy();
1106 spanData->txop = m.type();
1107 spanData->bilinear = bilinear;
1108 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1109 spanData->adjustSpanMethods();
1111 spanData->setupMatrix(m, bilinear);
1116 // #define QT_CLIPPING_RATIOS
1118 #ifdef QT_CLIPPING_RATIOS
1123 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1125 if (d->clip()->hasRectClip)
1127 if (d->clip()->hasRegionClip)
1131 if ((totalClips % 5000) == 0) {
1132 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1133 rectClips * 100.0 / (qreal) totalClips,
1134 regionClips * 100.0 / (qreal) totalClips,
1135 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1144 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1146 if (s->flags.has_clip_ownership)
1149 s->flags.has_clip_ownership = false;
1152 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1154 s->fillFlags |= QPaintEngine::DirtyClipPath;
1155 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1156 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1158 d->solid_color_filler.clip = d->clip();
1159 d->solid_color_filler.adjustSpanMethods();
1161 #ifdef QT_DEBUG_DRAW
1162 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1171 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1173 #ifdef QT_DEBUG_DRAW
1174 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1176 if (path.elements()) {
1177 for (int i=0; i<path.elementCount(); ++i) {
1178 qDebug() << " - " << path.elements()[i]
1179 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1182 for (int i=0; i<path.elementCount(); ++i) {
1183 qDebug() << " ---- "
1184 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1189 Q_D(QRasterPaintEngine);
1190 QRasterPaintEngineState *s = state();
1192 const qreal *points = path.points();
1193 const QPainterPath::ElementType *types = path.elements();
1195 // There are some cases that are not supported by clip(QRect)
1196 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1197 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1198 if (s->matrix.type() <= QTransform::TxScale
1199 && ((path.shape() == QVectorPath::RectangleHint)
1200 || (isRect(points, path.elementCount())
1201 && (!types || (types[0] == QPainterPath::MoveToElement
1202 && types[1] == QPainterPath::LineToElement
1203 && types[2] == QPainterPath::LineToElement
1204 && types[3] == QPainterPath::LineToElement))))) {
1205 #ifdef QT_DEBUG_DRAW
1206 qDebug() << " --- optimizing vector clip to rect clip...";
1209 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1210 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1215 if (op == Qt::NoClip) {
1216 qrasterpaintengine_state_setNoClip(s);
1219 QClipData *base = d->baseClip.data();
1221 // Intersect with current clip when available...
1222 if (op == Qt::IntersectClip && s->clip)
1225 // We always intersect, except when there is nothing to
1226 // intersect with, in which case we simplify the operation to
1228 Qt::ClipOperation isectOp = Qt::IntersectClip;
1230 isectOp = Qt::ReplaceClip;
1232 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1233 newClip->initialize();
1234 ClipData clipData = { base, newClip, isectOp };
1235 ensureOutlineMapper();
1236 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1240 if (op == Qt::UniteClip) {
1242 QClipData *result = new QClipData(d->rasterBuffer->height());
1243 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1244 qt_merge_clip(current, newClip, result);
1252 if (s->flags.has_clip_ownership)
1256 s->flags.has_clip_ownership = true;
1258 qrasterpaintengine_dirty_clip(d, s);
1266 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1268 #ifdef QT_DEBUG_DRAW
1269 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1272 QRasterPaintEngineState *s = state();
1274 if (op == Qt::NoClip) {
1275 qrasterpaintengine_state_setNoClip(s);
1277 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1278 QPaintEngineEx::clip(rect, op);
1281 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1282 QPaintEngineEx::clip(rect, op);
1288 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1290 Q_D(QRasterPaintEngine);
1291 QRect clipRect = r & d->deviceRect;
1292 QRasterPaintEngineState *s = state();
1294 if (op == Qt::ReplaceClip || s->clip == 0) {
1296 // No current clip, hence we intersect with sysclip and be
1298 QRegion clipRegion = systemClip();
1299 QClipData *clip = new QClipData(d->rasterBuffer->height());
1301 if (clipRegion.isEmpty())
1302 clip->setClipRect(clipRect);
1304 clip->setClipRegion(clipRegion & clipRect);
1306 if (s->flags.has_clip_ownership)
1310 s->clip->enabled = true;
1311 s->flags.has_clip_ownership = true;
1313 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1314 QClipData *base = s->clip;
1317 if (base->hasRectClip || base->hasRegionClip) {
1318 if (!s->flags.has_clip_ownership) {
1319 s->clip = new QClipData(d->rasterBuffer->height());
1320 s->flags.has_clip_ownership = true;
1322 if (base->hasRectClip)
1323 s->clip->setClipRect(base->clipRect & clipRect);
1325 s->clip->setClipRegion(base->clipRegion & clipRect);
1326 s->clip->enabled = true;
1334 qrasterpaintengine_dirty_clip(d, s);
1342 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1344 #ifdef QT_DEBUG_DRAW
1345 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1348 Q_D(QRasterPaintEngine);
1350 if (region.rectCount() == 1) {
1351 clip(region.boundingRect(), op);
1355 QRasterPaintEngineState *s = state();
1356 const QClipData *clip = d->clip();
1357 const QClipData *baseClip = d->baseClip.data();
1359 if (op == Qt::NoClip) {
1360 qrasterpaintengine_state_setNoClip(s);
1361 } else if (s->matrix.type() > QTransform::TxScale
1362 || op == Qt::UniteClip
1363 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1364 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1365 QPaintEngineEx::clip(region, op);
1367 const QClipData *curClip;
1370 if (op == Qt::IntersectClip)
1375 if (s->flags.has_clip_ownership) {
1379 newClip = new QClipData(d->rasterBuffer->height());
1381 s->flags.has_clip_ownership = true;
1384 QRegion r = s->matrix.map(region);
1385 if (curClip->hasRectClip)
1386 newClip->setClipRegion(r & curClip->clipRect);
1387 else if (curClip->hasRegionClip)
1388 newClip->setClipRegion(r & curClip->clipRegion);
1390 qrasterpaintengine_dirty_clip(d, s);
1397 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1399 #ifdef QT_DEBUG_DRAW
1400 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1403 if (!fillData->blend)
1406 Q_D(QRasterPaintEngine);
1408 const QRectF controlPointRect = path.controlPointRect();
1410 QRasterPaintEngineState *s = state();
1411 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1412 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1413 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1414 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1415 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1416 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1418 if (!s->flags.antialiased && !do_clip) {
1419 d->initializeRasterizer(fillData);
1420 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1424 ensureOutlineMapper();
1425 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1428 static void fillRect_normalized(const QRect &r, QSpanData *data,
1429 QRasterPaintEnginePrivate *pe)
1433 bool rectClipped = true;
1436 x1 = qMax(r.x(), data->clip->xmin);
1437 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1438 y1 = qMax(r.y(), data->clip->ymin);
1439 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1440 rectClipped = data->clip->hasRectClip;
1443 x1 = qMax(r.x(), pe->deviceRect.x());
1444 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1445 y1 = qMax(r.y(), pe->deviceRect.y());
1446 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1448 x1 = qMax(r.x(), 0);
1449 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1450 y1 = qMax(r.y(), 0);
1451 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1454 if (x2 <= x1 || y2 <= y1)
1457 const int width = x2 - x1;
1458 const int height = y2 - y1;
1460 bool isUnclipped = rectClipped
1461 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1463 if (pe && isUnclipped) {
1464 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1466 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1467 || (mode == QPainter::CompositionMode_SourceOver
1468 && qAlpha(data->solid.color) == 255)))
1470 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1476 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1478 const int nspans = 256;
1479 QT_FT_Span spans[nspans];
1481 Q_ASSERT(data->blend);
1484 int n = qMin(nspans, y2 - y);
1488 spans[i].len = width;
1490 spans[i].coverage = 255;
1494 blend(n, spans, data);
1502 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1504 #ifdef QT_DEBUG_DRAW
1505 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1507 Q_D(QRasterPaintEngine);
1509 QRasterPaintEngineState *s = state();
1513 if (s->brushData.blend) {
1514 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1515 const QRect *r = rects;
1516 const QRect *lastRect = rects + rectCount;
1518 int offset_x = int(s->matrix.dx());
1519 int offset_y = int(s->matrix.dy());
1520 while (r < lastRect) {
1521 QRect rect = r->normalized();
1522 QRect rr = rect.translated(offset_x, offset_y);
1523 fillRect_normalized(rr, &s->brushData, d);
1527 QRectVectorPath path;
1528 for (int i=0; i<rectCount; ++i) {
1530 fill(path, s->brush);
1536 if (s->penData.blend) {
1537 QRectVectorPath path;
1538 if (s->flags.fast_pen) {
1539 QCosmeticStroker stroker(s, d->deviceRect);
1540 for (int i = 0; i < rectCount; ++i) {
1542 stroker.drawPath(path);
1545 for (int i = 0; i < rectCount; ++i) {
1547 stroke(path, s->pen);
1556 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1558 #ifdef QT_DEBUG_DRAW
1559 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1561 #ifdef QT_FAST_SPANS
1562 Q_D(QRasterPaintEngine);
1564 QRasterPaintEngineState *s = state();
1567 if (s->flags.tx_noshear) {
1569 if (s->brushData.blend) {
1570 d->initializeRasterizer(&s->brushData);
1571 for (int i = 0; i < rectCount; ++i) {
1572 const QRectF &rect = rects[i].normalized();
1575 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1576 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1577 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1582 if (s->penData.blend) {
1583 QRectVectorPath path;
1584 if (s->flags.fast_pen) {
1585 QCosmeticStroker stroker(s, d->deviceRect);
1586 for (int i = 0; i < rectCount; ++i) {
1588 stroker.drawPath(path);
1591 for (int i = 0; i < rectCount; ++i) {
1593 QPaintEngineEx::stroke(path, s->lastPen);
1600 #endif // QT_FAST_SPANS
1601 QPaintEngineEx::drawRects(rects, rectCount);
1608 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1610 Q_D(QRasterPaintEngine);
1611 QRasterPaintEngineState *s = state();
1614 if (!s->penData.blend)
1617 if (s->flags.fast_pen) {
1618 QCosmeticStroker stroker(s, d->deviceRect);
1619 stroker.drawPath(path);
1620 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1621 qreal width = s->lastPen.isCosmetic()
1622 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1623 : qpen_widthf(s->lastPen) * s->txscale;
1625 qreal dashOffset = s->lastPen.dashOffset();
1627 qreal patternLength = 0;
1628 const QVector<qreal> pattern = s->lastPen.dashPattern();
1629 for (int i = 0; i < pattern.size(); ++i)
1630 patternLength += pattern.at(i);
1632 if (patternLength > 0) {
1633 int n = qFloor(dashOffset / patternLength);
1634 dashOffset -= n * patternLength;
1635 while (dashOffset >= pattern.at(dashIndex)) {
1636 dashOffset -= pattern.at(dashIndex);
1637 if (++dashIndex >= pattern.size())
1643 Q_D(QRasterPaintEngine);
1644 d->initializeRasterizer(&s->penData);
1645 int lineCount = path.elementCount() / 2;
1646 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1648 for (int i = 0; i < lineCount; ++i) {
1649 if (lines[i].p1() == lines[i].p2()) {
1650 if (s->lastPen.capStyle() != Qt::FlatCap) {
1651 QPointF p = lines[i].p1();
1652 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1653 QPointF(p.x() + width*0.5, p.y())));
1654 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1659 const QLineF line = s->matrix.map(lines[i]);
1660 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1661 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1662 width / line.length(),
1663 s->lastPen.capStyle() == Qt::SquareCap);
1665 d->rasterizeLine_dashed(line, width,
1666 &dashIndex, &dashOffset, &inDash);
1671 QPaintEngineEx::stroke(path, pen);
1674 static inline QRect toNormalizedFillRect(const QRectF &rect)
1676 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1677 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1678 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1679 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1686 return QRect(x1, y1, x2 - x1, y2 - y1);
1692 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1696 #ifdef QT_DEBUG_DRAW
1697 QRectF rf = path.controlPointRect();
1698 qDebug() << "QRasterPaintEngine::fill(): "
1699 << "size=" << path.elementCount()
1700 << ", hints=" << hex << path.hints()
1704 Q_D(QRasterPaintEngine);
1705 QRasterPaintEngineState *s = state();
1708 if (!s->brushData.blend)
1711 if (path.shape() == QVectorPath::RectangleHint) {
1712 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1713 const qreal *p = path.points();
1714 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1715 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1716 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1720 if (s->flags.tx_noshear) {
1721 d->initializeRasterizer(&s->brushData);
1722 // ### Is normalizing really necessary here?
1723 const qreal *p = path.points();
1724 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1726 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1727 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1728 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1734 // ### Optimize for non transformed ellipses and rectangles...
1735 QRectF cpRect = path.controlPointRect();
1736 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1737 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1740 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1741 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1742 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1743 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1745 // ### Falonc: implement....
1746 // if (!s->flags.antialiased && !do_clip) {
1747 // d->initializeRasterizer(&s->brushData);
1748 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1752 ensureOutlineMapper();
1753 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1756 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1758 Q_D(QRasterPaintEngine);
1759 QRasterPaintEngineState *s = state();
1761 if (!s->flags.antialiased) {
1762 uint txop = s->matrix.type();
1763 if (txop == QTransform::TxNone) {
1764 fillRect_normalized(toNormalizedFillRect(r), data, d);
1766 } else if (txop == QTransform::TxTranslate) {
1767 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1768 fillRect_normalized(rr, data, d);
1770 } else if (txop == QTransform::TxScale) {
1771 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1772 fillRect_normalized(rr, data, d);
1777 if (s->flags.tx_noshear) {
1778 d->initializeRasterizer(data);
1779 QRectF nr = r.normalized();
1780 if (!nr.isEmpty()) {
1781 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1782 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1783 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1790 ensureOutlineMapper();
1791 fillPath(path, data);
1797 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1799 #ifdef QT_DEBUG_DRAW
1800 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1802 QRasterPaintEngineState *s = state();
1805 if (!s->brushData.blend)
1808 fillRect(r, &s->brushData);
1814 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1816 #ifdef QT_DEBUG_DRAW
1817 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1819 Q_D(QRasterPaintEngine);
1820 QRasterPaintEngineState *s = state();
1822 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1823 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1824 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1827 d->solid_color_filler.clip = d->clip();
1828 d->solid_color_filler.adjustSpanMethods();
1829 fillRect(r, &d->solid_color_filler);
1832 static inline bool isAbove(const QPointF *a, const QPointF *b)
1834 return a->y() < b->y();
1837 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1842 Q_ASSERT(pointCount >= 2);
1844 QVector<const QPointF *> sorted;
1845 sorted.reserve(pointCount);
1847 upper->reserve(pointCount * 3 / 4);
1848 lower->reserve(pointCount * 3 / 4);
1850 for (int i = 0; i < pointCount; ++i)
1851 sorted << points + i;
1853 qSort(sorted.begin(), sorted.end(), isAbove);
1855 qreal splitY = sorted.at(sorted.size() / 2)->y();
1857 const QPointF *end = points + pointCount;
1858 const QPointF *last = end - 1;
1860 QVector<QPointF> *bin[2] = { upper, lower };
1862 for (const QPointF *p = points; p < end; ++p) {
1863 int side = p->y() < splitY;
1864 int lastSide = last->y() < splitY;
1866 if (side != lastSide) {
1867 if (qFuzzyCompare(p->y(), splitY)) {
1868 bin[!side]->append(*p);
1869 } else if (qFuzzyCompare(last->y(), splitY)) {
1870 bin[side]->append(*last);
1872 QPointF delta = *p - *last;
1873 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1875 bin[0]->append(intersection);
1876 bin[1]->append(intersection);
1880 bin[side]->append(*p);
1885 // give up if we couldn't reduce the point count
1886 return upper->size() < pointCount && lower->size() < pointCount;
1892 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1894 Q_D(QRasterPaintEngine);
1895 QRasterPaintEngineState *s = state();
1897 const int maxPoints = 0xffff;
1899 // max amount of points that raster engine can reliably handle
1900 if (pointCount > maxPoints) {
1901 QVector<QPointF> upper, lower;
1903 if (splitPolygon(points, pointCount, &upper, &lower)) {
1904 fillPolygon(upper.constData(), upper.size(), mode);
1905 fillPolygon(lower.constData(), lower.size(), mode);
1907 qWarning("Polygon too complex for filling.");
1912 // Compose polygon fill..,
1913 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1914 ensureOutlineMapper();
1915 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1918 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1920 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1926 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1928 Q_D(QRasterPaintEngine);
1929 QRasterPaintEngineState *s = state();
1931 #ifdef QT_DEBUG_DRAW
1932 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1933 for (int i=0; i<pointCount; ++i)
1934 qDebug() << " - " << points[i];
1936 Q_ASSERT(pointCount >= 2);
1938 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1939 QRectF r(points[0], points[2]);
1945 if (mode != PolylineMode) {
1948 if (s->brushData.blend) {
1949 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1950 fillPolygon(points, pointCount, mode);
1951 d->outlineMapper->setCoordinateRounding(false);
1955 // Do the outline...
1956 if (s->penData.blend) {
1957 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1958 if (s->flags.fast_pen) {
1959 QCosmeticStroker stroker(s, d->deviceRect);
1960 stroker.drawPath(vp);
1962 QPaintEngineEx::stroke(vp, s->lastPen);
1970 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1972 Q_D(QRasterPaintEngine);
1973 QRasterPaintEngineState *s = state();
1975 #ifdef QT_DEBUG_DRAW
1976 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1977 for (int i=0; i<pointCount; ++i)
1978 qDebug() << " - " << points[i];
1980 Q_ASSERT(pointCount >= 2);
1981 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1982 QRect r(points[0].x(),
1984 points[2].x() - points[0].x(),
1985 points[2].y() - points[0].y());
1993 if (mode != PolylineMode) {
1995 if (s->brushData.blend) {
1996 // Compose polygon fill..,
1997 ensureOutlineMapper();
1998 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1999 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2000 d->outlineMapper->moveTo(*points);
2001 const QPoint *p = points;
2002 const QPoint *ep = points + pointCount - 1;
2004 d->outlineMapper->lineTo(*(++p));
2006 d->outlineMapper->endOutline();
2009 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2011 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2012 d->outlineMapper->setCoordinateRounding(false);
2016 // Do the outline...
2017 if (s->penData.blend) {
2018 int count = pointCount * 2;
2019 QVarLengthArray<qreal> fpoints(count);
2020 for (int i=0; i<count; ++i)
2021 fpoints[i] = ((int *) points)[i];
2022 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2024 if (s->flags.fast_pen) {
2025 QCosmeticStroker stroker(s, d->deviceRect);
2026 stroker.drawPath(vp);
2028 QPaintEngineEx::stroke(vp, s->lastPen);
2036 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2038 #ifdef QT_DEBUG_DRAW
2039 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2042 QPixmapData *pd = pixmap.pixmapData();
2043 if (pd->classId() == QPixmapData::RasterClass) {
2044 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2045 if (image.depth() == 1) {
2046 Q_D(QRasterPaintEngine);
2047 QRasterPaintEngineState *s = state();
2048 if (s->matrix.type() <= QTransform::TxTranslate) {
2050 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2052 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2055 QRasterPaintEngine::drawImage(pos, image);
2058 const QImage image = pixmap.toImage();
2059 if (pixmap.depth() == 1) {
2060 Q_D(QRasterPaintEngine);
2061 QRasterPaintEngineState *s = state();
2062 if (s->matrix.type() <= QTransform::TxTranslate) {
2064 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2066 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2069 QRasterPaintEngine::drawImage(pos, image);
2077 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2079 #ifdef QT_DEBUG_DRAW
2080 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2083 QPixmapData* pd = pixmap.pixmapData();
2084 if (pd->classId() == QPixmapData::RasterClass) {
2085 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2086 if (image.depth() == 1) {
2087 Q_D(QRasterPaintEngine);
2088 QRasterPaintEngineState *s = state();
2089 if (s->matrix.type() <= QTransform::TxTranslate
2090 && r.size() == sr.size()
2091 && r.size() == pixmap.size()) {
2093 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2096 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2099 drawImage(r, image, sr);
2102 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2103 const QImage image = pd->toImage(clippedSource);
2104 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2105 if (image.depth() == 1) {
2106 Q_D(QRasterPaintEngine);
2107 QRasterPaintEngineState *s = state();
2108 if (s->matrix.type() <= QTransform::TxTranslate
2109 && r.size() == sr.size()
2110 && r.size() == pixmap.size()) {
2112 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2115 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2118 drawImage(r, image, translatedSource);
2123 // assumes that rect has positive width and height
2124 static inline const QRect toRect_normalized(const QRectF &rect)
2126 const int x = qRound(rect.x());
2127 const int y = qRound(rect.y());
2128 const int w = int(rect.width() + qreal(0.5));
2129 const int h = int(rect.height() + qreal(0.5));
2131 return QRect(x, y, w, h);
2134 static inline int fast_ceil_positive(const qreal &v)
2136 const int iv = int(v);
2143 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2145 const int xmin = int(rect.x());
2146 const int xmax = int(fast_ceil_positive(rect.right()));
2147 const int ymin = int(rect.y());
2148 const int ymax = int(fast_ceil_positive(rect.bottom()));
2149 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2155 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2157 #ifdef QT_DEBUG_DRAW
2158 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2161 Q_D(QRasterPaintEngine);
2162 QRasterPaintEngineState *s = state();
2164 if (s->matrix.type() > QTransform::TxTranslate) {
2165 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2167 QRectF(0, 0, img.width(), img.height()));
2170 const QClipData *clip = d->clip();
2171 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2173 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2174 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2177 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2179 } else if (clip->hasRectClip) {
2180 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2188 d->image_filler.clip = clip;
2189 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2190 if (!d->image_filler.blend)
2192 d->image_filler.dx = -pt.x();
2193 d->image_filler.dy = -pt.y();
2194 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2196 fillRect_normalized(rr, &d->image_filler, d);
2201 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2203 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2214 inline RotationType qRotationType(const QTransform &transform)
2216 QTransform::TransformationType type = transform.type();
2218 if (type > QTransform::TxRotate)
2221 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2222 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2225 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2226 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2229 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2230 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2236 inline bool isPixelAligned(const QRectF &rect) {
2237 return QRectF(rect.toRect()) == rect;
2244 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2245 Qt::ImageConversionFlags)
2247 #ifdef QT_DEBUG_DRAW
2248 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2254 Q_D(QRasterPaintEngine);
2255 QRasterPaintEngineState *s = state();
2256 int sr_l = qFloor(sr.left());
2257 int sr_r = qCeil(sr.right()) - 1;
2258 int sr_t = qFloor(sr.top());
2259 int sr_b = qCeil(sr.bottom()) - 1;
2261 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2262 // as fillRect will apply the aliased coordinate delta we need to
2263 // subtract it here as we don't use it for image drawing
2264 QTransform old = s->matrix;
2265 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2267 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2268 QRgb color = img.pixel(sr_l, sr_t);
2269 switch (img.format()) {
2270 case QImage::Format_ARGB32_Premultiplied:
2271 case QImage::Format_ARGB8565_Premultiplied:
2272 case QImage::Format_ARGB6666_Premultiplied:
2273 case QImage::Format_ARGB8555_Premultiplied:
2274 case QImage::Format_ARGB4444_Premultiplied:
2275 // Combine premultiplied color with the opacity set on the painter.
2276 d->solid_color_filler.solid.color =
2277 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2278 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2281 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2285 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2286 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2290 d->solid_color_filler.clip = d->clip();
2291 d->solid_color_filler.adjustSpanMethods();
2292 fillRect(r, &d->solid_color_filler);
2298 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2300 const QClipData *clip = d->clip();
2302 if (s->matrix.type() > QTransform::TxTranslate
2304 && (!clip || clip->hasRectClip)
2305 && s->intOpacity == 256
2306 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2307 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2308 && d->rasterBuffer->format == img.format()
2309 && (d->rasterBuffer->format == QImage::Format_RGB16
2310 || d->rasterBuffer->format == QImage::Format_RGB32
2311 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2312 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2314 RotationType rotationType = qRotationType(s->matrix);
2316 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2317 QRectF transformedTargetRect = s->matrix.mapRect(r);
2319 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2320 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2322 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2323 if (clippedTransformedTargetRect.isNull())
2326 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2328 QRect clippedSourceRect
2329 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2330 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2332 uint dbpl = d->rasterBuffer->bytesPerLine();
2333 uint sbpl = img.bytesPerLine();
2335 uchar *dst = d->rasterBuffer->buffer();
2336 uint bpp = img.depth() >> 3;
2338 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2339 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2341 uint cw = clippedSourceRect.width();
2342 uint ch = clippedSourceRect.height();
2344 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2351 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2353 QRectF targetBounds = s->matrix.mapRect(r);
2354 bool exceedsPrecision = targetBounds.width() > 0xffff
2355 || targetBounds.height() > 0xffff;
2357 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2358 if (s->matrix.type() > QTransform::TxScale) {
2359 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2360 if (func && (!clip || clip->hasRectClip)) {
2361 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2362 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2363 s->matrix, s->intOpacity);
2367 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2368 if (func && (!clip || clip->hasRectClip)) {
2369 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2370 img.bits(), img.bytesPerLine(),
2371 qt_mapRect_non_normalizing(r, s->matrix), sr,
2372 !clip ? d->deviceRect : clip->clipRect,
2379 QTransform copy = s->matrix;
2380 copy.translate(r.x(), r.y());
2382 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2383 copy.translate(-sr.x(), -sr.y());
2385 d->image_filler_xform.clip = clip;
2386 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2387 if (!d->image_filler_xform.blend)
2389 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2391 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2392 QRectF rr = s->matrix.mapRect(r);
2394 const int x1 = qRound(rr.x());
2395 const int y1 = qRound(rr.y());
2396 const int x2 = qRound(rr.right());
2397 const int y2 = qRound(rr.bottom());
2399 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2403 #ifdef QT_FAST_SPANS
2405 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2406 d->initializeRasterizer(&d->image_filler_xform);
2407 d->rasterizer->setAntialiased(s->flags.antialiased);
2409 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2411 const QRectF &rect = r.normalized();
2412 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2413 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2415 if (s->flags.tx_noshear)
2416 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2418 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2422 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2425 QTransform m = s->matrix;
2426 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2427 m.m21(), m.m22(), m.m23(),
2428 m.m31() - offs, m.m32() - offs, m.m33());
2429 fillPath(path, &d->image_filler_xform);
2432 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2433 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2435 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2437 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2439 } else if (clip->hasRectClip) {
2440 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2446 d->image_filler.clip = clip;
2447 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2448 if (!d->image_filler.blend)
2450 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2451 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2454 rr.translate(s->matrix.dx(), s->matrix.dy());
2456 const int x1 = qRound(rr.x());
2457 const int y1 = qRound(rr.y());
2458 const int x2 = qRound(rr.right());
2459 const int y2 = qRound(rr.bottom());
2461 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2468 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2470 #ifdef QT_DEBUG_DRAW
2471 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2473 Q_D(QRasterPaintEngine);
2474 QRasterPaintEngineState *s = state();
2478 QPixmapData *pd = pixmap.pixmapData();
2479 if (pd->classId() == QPixmapData::RasterClass) {
2480 image = static_cast<QRasterPixmapData *>(pd)->image;
2482 image = pixmap.toImage();
2485 if (image.depth() == 1)
2486 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2488 if (s->matrix.type() > QTransform::TxTranslate) {
2489 QTransform copy = s->matrix;
2490 copy.translate(r.x(), r.y());
2491 copy.translate(-sr.x(), -sr.y());
2492 d->image_filler_xform.clip = d->clip();
2493 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2494 if (!d->image_filler_xform.blend)
2496 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2498 #ifdef QT_FAST_SPANS
2500 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2501 d->initializeRasterizer(&d->image_filler_xform);
2502 d->rasterizer->setAntialiased(s->flags.antialiased);
2504 const QRectF &rect = r.normalized();
2505 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2506 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2507 if (s->flags.tx_noshear)
2508 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2510 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2516 fillPath(path, &d->image_filler_xform);
2518 d->image_filler.clip = d->clip();
2520 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2521 if (!d->image_filler.blend)
2523 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2524 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2527 rr.translate(s->matrix.dx(), s->matrix.dy());
2528 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2534 static inline bool monoVal(const uchar* s, int x)
2536 return (s[x>>3] << (x&7)) & 0x80;
2542 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2544 Q_D(QRasterPaintEngine);
2545 QRasterPaintEngineState *s = state();
2547 if (!s->penData.blend)
2550 QRasterBuffer *rb = d->rasterBuffer.data();
2552 const QRect rect(rx, ry, w, h);
2553 const QClipData *clip = d->clip();
2554 bool unclipped = false;
2556 // inlined QRect::intersects
2557 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2558 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2560 if (clip->hasRectClip) {
2561 unclipped = rx > clip->xmin
2562 && rx + w < clip->xmax
2564 && ry + h < clip->ymax;
2570 // inlined QRect::intersects
2571 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2572 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2576 // inlined QRect::contains
2577 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2578 && rect.top() >= 0 && rect.bottom() < rb->height();
2580 unclipped = contains && d->isUnclipped_normalized(rect);
2583 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2584 const uchar * scanline = static_cast<const uchar *>(src);
2586 if (s->flags.fast_text) {
2589 if (s->penData.bitmapBlit) {
2590 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2591 scanline, w, h, bpl);
2594 } else if (depth == 8) {
2595 if (s->penData.alphamapBlit) {
2596 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2597 scanline, w, h, bpl, 0);
2600 } else if (depth == 32) {
2601 // (A)RGB Alpha mask where the alpha component is not used.
2602 if (s->penData.alphaRGBBlit) {
2603 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2604 (const uint *) scanline, w, h, bpl / 4, 0);
2608 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2609 // (A)RGB Alpha mask where the alpha component is not used.
2611 int nx = qMax(0, rx);
2612 int ny = qMax(0, ry);
2614 // Move scanline pointer to compensate for moved x and y
2615 int xdiff = nx - rx;
2616 int ydiff = ny - ry;
2617 scanline += ydiff * bpl;
2618 scanline += xdiff * (depth == 32 ? 4 : 1);
2623 if (nx + w > d->rasterBuffer->width())
2624 w = d->rasterBuffer->width() - nx;
2625 if (ny + h > d->rasterBuffer->height())
2626 h = d->rasterBuffer->height() - ny;
2631 if (depth == 8 && s->penData.alphamapBlit) {
2632 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2633 scanline, w, h, bpl, clip);
2634 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2635 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2636 (const uint *) scanline, w, h, bpl / 4, clip);
2651 scanline += bpl * y0;
2655 w = qMin(w, rb->width() - qMax(0, rx));
2656 h = qMin(h, rb->height() - qMax(0, ry));
2658 if (w <= 0 || h <= 0)
2661 const int NSPANS = 256;
2662 QSpan spans[NSPANS];
2665 const int x1 = x0 + w;
2666 const int y1 = y0 + h;
2669 for (int y = y0; y < y1; ++y) {
2670 for (int x = x0; x < x1; ) {
2671 if (!monoVal(scanline, x)) {
2676 if (current == NSPANS) {
2677 blend(current, spans, &s->penData);
2680 spans[current].x = x + rx;
2681 spans[current].y = y + ry;
2682 spans[current].coverage = 255;
2685 // extend span until we find a different one.
2686 while (x < x1 && monoVal(scanline, x)) {
2690 spans[current].len = len;
2695 } else if (depth == 8) {
2696 for (int y = y0; y < y1; ++y) {
2697 for (int x = x0; x < x1; ) {
2698 // Skip those with 0 coverage
2699 if (scanline[x] == 0) {
2704 if (current == NSPANS) {
2705 blend(current, spans, &s->penData);
2708 int coverage = scanline[x];
2709 spans[current].x = x + rx;
2710 spans[current].y = y + ry;
2711 spans[current].coverage = coverage;
2715 // extend span until we find a different one.
2716 while (x < x1 && scanline[x] == coverage) {
2720 spans[current].len = len;
2725 } else { // 32-bit alpha...
2726 uint *sl = (uint *) src;
2727 for (int y = y0; y < y1; ++y) {
2728 for (int x = x0; x < x1; ) {
2729 // Skip those with 0 coverage
2730 if ((sl[x] & 0x00ffffff) == 0) {
2735 if (current == NSPANS) {
2736 blend(current, spans, &s->penData);
2739 uint rgbCoverage = sl[x];
2740 int coverage = qGreen(rgbCoverage);
2741 spans[current].x = x + rx;
2742 spans[current].y = y + ry;
2743 spans[current].coverage = coverage;
2747 // extend span until we find a different one.
2748 while (x < x1 && sl[x] == rgbCoverage) {
2752 spans[current].len = len;
2755 sl += bpl / sizeof(uint);
2758 // qDebug() << "alphaPenBlt: num spans=" << current
2759 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2760 // Call span func for current set of spans.
2762 blend(current, spans, &s->penData);
2765 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2766 const QFixedPoint *positions, QFontEngine *fontEngine)
2768 Q_D(QRasterPaintEngine);
2769 QRasterPaintEngineState *s = state();
2771 #if !defined(QT_NO_FREETYPE)
2772 if (fontEngine->type() == QFontEngine::Freetype) {
2773 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2774 QFontEngineFT::GlyphFormat neededFormat =
2775 painter()->device()->devType() == QInternal::Widget
2776 ? fe->defaultGlyphFormat()
2777 : QFontEngineFT::Format_A8;
2779 if (d_func()->mono_surface
2780 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2782 neededFormat = QFontEngineFT::Format_Mono;
2784 if (neededFormat == QFontEngineFT::Format_None)
2785 neededFormat = QFontEngineFT::Format_A8;
2787 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2788 if (s->matrix.type() >= QTransform::TxScale) {
2789 if (s->matrix.isAffine())
2790 gset = fe->loadTransformedGlyphSet(s->matrix);
2795 if (!gset || gset->outline_drawing
2796 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2799 FT_Face lockedFace = 0;
2802 switch (neededFormat) {
2803 case QFontEngineFT::Format_Mono:
2806 case QFontEngineFT::Format_A8:
2809 case QFontEngineFT::Format_A32:
2817 for (int i = 0; i < numGlyphs; i++) {
2818 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2819 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2821 if (!glyph || glyph->format != neededFormat) {
2823 lockedFace = fe->lockFace();
2824 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2827 if (!glyph || !glyph->data)
2831 switch (neededFormat) {
2832 case QFontEngineFT::Format_Mono:
2833 pitch = ((glyph->width + 31) & ~31) >> 3;
2835 case QFontEngineFT::Format_A8:
2836 pitch = (glyph->width + 3) & ~3;
2838 case QFontEngineFT::Format_A32:
2839 pitch = glyph->width * 4;
2846 alphaPenBlt(glyph->data, pitch, depth,
2847 qFloor(positions[i].x) + glyph->x,
2848 qFloor(positions[i].y) - glyph->y,
2849 glyph->width, glyph->height);
2856 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2858 QImageTextureGlyphCache *cache =
2859 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2861 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2862 fontEngine->setGlyphCache(0, cache);
2865 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2866 cache->fillInPendingGlyphs();
2868 const QImage &image = cache->image();
2869 int bpl = image.bytesPerLine();
2871 int depth = image.depth();
2875 leftShift = 2; // multiply by 4
2876 else if (depth == 1)
2877 rightShift = 3; // divide by 8
2879 int margin = cache->glyphMargin();
2880 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2881 const uchar *bits = image.bits();
2882 for (int i=0; i<numGlyphs; ++i) {
2884 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2885 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2886 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2890 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2891 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2893 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2896 // c.baseLineX, c.baseLineY,
2899 // positions[i].x.toInt(), positions[i].y.toInt());
2901 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2907 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2908 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2910 Q_D(QRasterPaintEngine);
2911 QRasterPaintEngineState *s = state();
2913 QFontEngine *fontEngine = ti.fontEngine;
2914 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2915 QPaintEngineEx::drawTextItem(p, ti);
2919 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2921 QVarLengthArray<QFixedPoint> positions;
2922 QVarLengthArray<glyph_t> glyphs;
2923 QTransform matrix = s->matrix;
2924 matrix.translate(p.x(), p.y());
2925 if (matrix.type() == QTransform::TxScale)
2926 fe->setFontScale(matrix.m11());
2927 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2929 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2931 for (int i=0; i<glyphs.size(); ++i) {
2932 TOpenFontCharMetrics tmetrics;
2933 const TUint8 *glyphBitmapBytes;
2934 TSize glyphBitmapSize;
2935 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2936 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2937 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2938 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2941 if (matrix.type() == QTransform::TxScale)
2942 fe->setFontScale(1.0);
2946 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2949 * Returns true if the rectangle is completely within the current clip
2950 * state of the paint engine.
2952 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2954 const QClipData *cl = clip();
2956 // inline contains() for performance (we know the rects are normalized)
2957 const QRect &r1 = deviceRect;
2958 return (r.left() >= r1.left() && r.right() <= r1.right()
2959 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2963 if (cl->hasRectClip) {
2964 // currently all painting functions clips to deviceRect internally
2965 if (cl->clipRect == deviceRect)
2968 // inline contains() for performance (we know the rects are normalized)
2969 const QRect &r1 = cl->clipRect;
2970 return (r.left() >= r1.left() && r.right() <= r1.right()
2971 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2973 return qt_region_strictContains(cl->clipRegion, r);
2977 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2980 Q_Q(const QRasterPaintEngine);
2981 const QRasterPaintEngineState *s = q->state();
2982 const QClipData *cl = clip();
2984 QRect r = rect.normalized();
2985 // inline contains() for performance (we know the rects are normalized)
2986 const QRect &r1 = deviceRect;
2987 return (r.left() >= r1.left() && r.right() <= r1.right()
2988 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2992 // currently all painting functions that call this function clip to deviceRect internally
2993 if (cl->hasRectClip && cl->clipRect == deviceRect)
2996 if (s->flags.antialiased)
2999 QRect r = rect.normalized();
3001 r.setX(r.x() - penWidth);
3002 r.setY(r.y() - penWidth);
3003 r.setWidth(r.width() + 2 * penWidth);
3004 r.setHeight(r.height() + 2 * penWidth);
3007 if (cl->hasRectClip) {
3008 // inline contains() for performance (we know the rects are normalized)
3009 const QRect &r1 = cl->clipRect;
3010 return (r.left() >= r1.left() && r.right() <= r1.right()
3011 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3013 return qt_region_strictContains(cl->clipRegion, r);
3017 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3020 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3024 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3025 const QSpanData *data) const
3027 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3031 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3032 const QSpanData *data) const
3034 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3038 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3039 const QSpanData *data) const
3041 Q_Q(const QRasterPaintEngine);
3042 const QRasterPaintEngineState *s = q->state();
3044 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3046 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3047 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3053 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3058 QFontEngine *fontEngine = textItem->fontEngine();
3059 if (!supportsTransformations(fontEngine)) {
3060 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3063 QPaintEngineEx::drawStaticTextItem(textItem);
3070 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3072 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3073 QRasterPaintEngineState *s = state();
3075 #ifdef QT_DEBUG_DRAW
3076 Q_D(QRasterPaintEngine);
3077 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3078 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3085 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3087 if (!supportsTransformations(ti.fontEngine)) {
3088 QVarLengthArray<QFixedPoint> positions;
3089 QVarLengthArray<glyph_t> glyphs;
3091 QTransform matrix = s->matrix;
3092 matrix.translate(p.x(), p.y());
3094 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3096 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3100 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3101 if (s->matrix.type() <= QTransform::TxTranslate
3102 || (s->matrix.type() == QTransform::TxScale
3103 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3104 drawGlyphsS60(p, ti);
3107 #else // Q_WS_WIN || Q_WS_MAC
3109 QFontEngine *fontEngine = ti.fontEngine;
3111 #if defined(Q_WS_QWS)
3112 if (fontEngine->type() == QFontEngine::Box) {
3113 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3117 if (s->matrix.type() < QTransform::TxScale
3118 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3119 || (fontEngine->type() == QFontEngine::Proxy
3120 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3122 fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
3128 if (s->matrix.type() < QTransform::TxScale) {
3130 QVarLengthArray<QFixedPoint> positions;
3131 QVarLengthArray<glyph_t> glyphs;
3132 QTransform matrix = state()->transform();
3134 qreal _x = qFloor(p.x());
3135 qreal _y = qFloor(p.y());
3136 matrix.translate(_x, _y);
3138 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3139 if (glyphs.size() == 0)
3142 for(int i = 0; i < glyphs.size(); i++) {
3143 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3144 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3145 // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here?
3146 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3147 qRound(positions[i].x + metrics.x),
3148 qRound(positions[i].y + metrics.y),
3149 img.width(), img.height());
3155 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3157 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3158 if (fontEngine->type() == QFontEngine::QPF2) {
3159 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3160 if (renderingEngine)
3161 fontEngine = renderingEngine;
3165 if (fontEngine->type() != QFontEngine::Freetype) {
3166 QPaintEngineEx::drawTextItem(p, ti);
3170 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3172 QTransform matrix = s->matrix;
3173 matrix.translate(p.x(), p.y());
3175 QVarLengthArray<QFixedPoint> positions;
3176 QVarLengthArray<glyph_t> glyphs;
3177 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3178 if (glyphs.size() == 0)
3181 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3182 QPaintEngine::drawTextItem(p, ti);
3188 QPaintEngineEx::drawTextItem(p, ti);
3194 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3196 Q_D(QRasterPaintEngine);
3197 QRasterPaintEngineState *s = state();
3200 if (!s->penData.blend)
3203 if (!s->flags.fast_pen) {
3204 QPaintEngineEx::drawPoints(points, pointCount);
3208 QCosmeticStroker stroker(s, d->deviceRect);
3209 stroker.drawPoints(points, pointCount);
3213 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3215 Q_D(QRasterPaintEngine);
3216 QRasterPaintEngineState *s = state();
3219 if (!s->penData.blend)
3222 if (!s->flags.fast_pen) {
3223 QPaintEngineEx::drawPoints(points, pointCount);
3227 QCosmeticStroker stroker(s, d->deviceRect);
3228 stroker.drawPoints(points, pointCount);
3234 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3236 #ifdef QT_DEBUG_DRAW
3237 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3239 Q_D(QRasterPaintEngine);
3240 QRasterPaintEngineState *s = state();
3243 if (!s->penData.blend)
3246 if (s->flags.fast_pen) {
3247 QCosmeticStroker stroker(s, d->deviceRect);
3248 for (int i=0; i<lineCount; ++i) {
3249 const QLine &l = lines[i];
3250 stroker.drawLine(l.p1(), l.p2());
3253 QPaintEngineEx::drawLines(lines, lineCount);
3257 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3263 Q_Q(QRasterPaintEngine);
3264 QRasterPaintEngineState *s = q->state();
3266 const QPen &pen = s->lastPen;
3267 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3268 const QVector<qreal> pattern = pen.dashPattern();
3270 qreal patternLength = 0;
3271 for (int i = 0; i < pattern.size(); ++i)
3272 patternLength += pattern.at(i);
3274 if (patternLength <= 0)
3277 qreal length = line.length();
3278 Q_ASSERT(length > 0);
3279 while (length > 0) {
3280 const bool rasterize = *inDash;
3281 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3284 if (dash >= length) {
3286 *dashOffset += dash / width;
3290 *inDash = !(*inDash);
3291 if (++*dashIndex >= pattern.size())
3298 if (rasterize && dash > 0)
3299 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3306 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3308 #ifdef QT_DEBUG_DRAW
3309 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3311 Q_D(QRasterPaintEngine);
3312 QRasterPaintEngineState *s = state();
3315 if (!s->penData.blend)
3317 if (s->flags.fast_pen) {
3318 QCosmeticStroker stroker(s, d->deviceRect);
3319 for (int i=0; i<lineCount; ++i) {
3320 QLineF line = lines[i];
3321 stroker.drawLine(line.p1(), line.p2());
3324 QPaintEngineEx::drawLines(lines, lineCount);
3332 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3334 Q_D(QRasterPaintEngine);
3335 QRasterPaintEngineState *s = state();
3338 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3339 || (qpen_style(s->lastPen) == Qt::NoPen))
3340 && !s->flags.antialiased
3341 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3343 && s->matrix.type() <= QTransform::TxScale) // no shear
3346 const QRectF r = s->matrix.mapRect(rect);
3347 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3348 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3349 const QRect brect = QRect(int(r.x()), int(r.y()),
3350 int_dim(r.x(), r.width()),
3351 int_dim(r.y(), r.height()));
3353 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3354 &s->penData, &s->brushData);
3358 QPaintEngineEx::drawEllipse(rect);
3365 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3367 Q_D(QRasterPaintEngine);
3374 CGContextRef QRasterPaintEngine::getCGContext() const
3376 Q_D(const QRasterPaintEngine);
3377 return d->cgContext;
3385 void QRasterPaintEngine::setDC(HDC hdc) {
3386 Q_D(QRasterPaintEngine);
3393 HDC QRasterPaintEngine::getDC() const
3395 Q_D(const QRasterPaintEngine);
3402 void QRasterPaintEngine::releaseDC(HDC) const
3408 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3410 const QTransform &m = state()->matrix;
3411 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3412 QFontEngine::Type fontEngineType = fontEngine->type();
3413 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3414 || (m.type() <= QTransform::TxTranslate
3415 && (fontEngineType == QFontEngine::TestFontEngine
3416 || fontEngineType == QFontEngine::Box))) {
3420 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3423 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3425 #if defined(Q_WS_MAC)
3426 // Mac font engines don't support scaling and rotation
3427 if (m.type() > QTransform::TxTranslate)
3429 if (m.type() >= QTransform::TxProject)
3433 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3442 QPoint QRasterPaintEngine::coordinateOffset() const
3444 return QPoint(0, 0);
3448 Draws the given color \a spans with the specified \a color. The \a
3449 count parameter specifies the number of spans.
3451 The default implementation does nothing; reimplement this function
3452 to draw the given color \a spans with the specified \a color. Note
3453 that this function \e must be reimplemented if the framebuffer is
3456 \sa drawBufferSpan()
3458 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3459 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3464 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3465 "a non memory-mapped device");
3469 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3471 Draws the given \a buffer.
3473 The default implementation does nothing; reimplement this function
3474 to draw a buffer that contains more than one color. Note that this
3475 function \e must be reimplemented if the framebuffer is not
3478 The \a size parameter specifies the total size of the given \a
3479 buffer, while the \a length parameter specifies the number of
3480 pixels to draw. The buffer's position is given by (\a x, \a
3481 y). The provided \a alpha value is added to each pixel in the
3482 buffer when drawing.
3484 \sa drawColorSpans()
3486 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3487 int x, int y, int length, uint const_alpha)
3494 Q_UNUSED(const_alpha);
3495 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3496 "a non memory-mapped device");
3500 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3505 Q_D(QRasterPaintEngine);
3507 Q_ASSERT(image.depth() == 1);
3509 const int spanCount = 256;
3510 QT_FT_Span spans[spanCount];
3514 int w = image.width();
3515 int h = image.height();
3516 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3517 int ymin = qMax(qRound(pos.y()), 0);
3518 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3519 int xmin = qMax(qRound(pos.x()), 0);
3521 int x_offset = xmin - qRound(pos.x());
3523 QImage::Format format = image.format();
3524 for (int y = ymin; y < ymax; ++y) {
3525 const uchar *src = image.scanLine(y - qRound(pos.y()));
3526 if (format == QImage::Format_MonoLSB) {
3527 for (int x = 0; x < xmax - xmin; ++x) {
3528 int src_x = x + x_offset;
3529 uchar pixel = src[src_x >> 3];
3534 if (pixel & (0x1 << (src_x & 7))) {
3535 spans[n].x = xmin + x;
3537 spans[n].coverage = 255;
3539 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3543 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3546 if (n == spanCount) {
3547 fg->blend(n, spans, fg);
3553 for (int x = 0; x < xmax - xmin; ++x) {
3554 int src_x = x + x_offset;
3555 uchar pixel = src[src_x >> 3];
3560 if (pixel & (0x80 >> (x & 7))) {
3561 spans[n].x = xmin + x;
3563 spans[n].coverage = 255;
3565 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3569 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3572 if (n == spanCount) {
3573 fg->blend(n, spans, fg);
3581 fg->blend(n, spans, fg);
3587 \enum QRasterPaintEngine::ClipType
3590 \value RectClip Indicates that the currently set clip is a single rectangle.
3591 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3596 Returns the type of the clip currently set.
3598 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3600 Q_D(const QRasterPaintEngine);
3602 const QClipData *clip = d->clip();
3603 if (!clip || clip->hasRectClip)
3611 Returns the bounding rect of the currently set clip.
3613 QRect QRasterPaintEngine::clipBoundingRect() const
3615 Q_D(const QRasterPaintEngine);
3617 const QClipData *clip = d->clip();
3620 return d->deviceRect;
3622 if (clip->hasRectClip)
3623 return clip->clipRect;
3625 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3628 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3630 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3632 QVarLengthArray<short, 4096> buffer;
3634 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3635 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3636 result->initialize();
3638 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3639 const QSpan *c1_spans = c1ClipLines[y].spans;
3640 int c1_count = c1ClipLines[y].count;
3641 const QSpan *c2_spans = c2ClipLines[y].spans;
3642 int c2_count = c2ClipLines[y].count;
3644 if (c1_count == 0 && c2_count == 0)
3646 if (c1_count == 0) {
3647 result->appendSpans(c2_spans, c2_count);
3649 } else if (c2_count == 0) {
3650 result->appendSpans(c1_spans, c1_count);
3654 // we need to merge the two
3656 // find required length
3657 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3658 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3660 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3662 // Fill with old spans.
3663 for (int i = 0; i < c1_count; ++i) {
3664 const QSpan *cs = c1_spans + i;
3665 for (int j=cs->x; j<cs->x + cs->len; ++j)
3666 buffer[j] = cs->coverage;
3669 // Fill with new spans
3670 for (int i = 0; i < c2_count; ++i) {
3671 const QSpan *cs = c2_spans + i;
3672 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3673 buffer[j] += cs->coverage;
3674 if (buffer[j] > 255)
3682 // Skip to next span
3683 while (x < max && buffer[x] == 0) ++x;
3684 if (x >= max) break;
3687 int coverage = buffer[x];
3689 // Find length of span
3690 while (x < max && buffer[x] == coverage)
3693 result->appendSpan(sx, x - sx, y, coverage);
3698 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3700 Q_Q(QRasterPaintEngine);
3701 QRasterPaintEngineState *s = q->state();
3703 rasterizer->setAntialiased(s->flags.antialiased);
3705 QRect clipRect(deviceRect);
3707 // ### get from optimized rectbased QClipData
3709 const QClipData *c = clip();
3711 const QRect r(QPoint(c->xmin, c->ymin),
3712 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3713 clipRect = clipRect.intersected(r);
3714 blend = data->blend;
3716 blend = data->unclipped_blend;
3719 rasterizer->setClipRect(clipRect);
3720 rasterizer->initialize(blend, data);
3723 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3724 ProcessSpans callback,
3725 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3727 if (!callback || !outline)
3730 Q_Q(QRasterPaintEngine);
3731 QRasterPaintEngineState *s = q->state();
3733 if (!s->flags.antialiased) {
3734 initializeRasterizer(spanData);
3736 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3740 rasterizer->rasterize(outline, fillRule);
3744 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3748 int q_gray_rendered_spans(QT_FT_Raster raster);
3751 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3752 ProcessSpans callback,
3753 void *userData, QRasterBuffer *)
3755 if (!callback || !outline)
3758 Q_Q(QRasterPaintEngine);
3759 QRasterPaintEngineState *s = q->state();
3761 if (!s->flags.antialiased) {
3762 rasterizer->setAntialiased(s->flags.antialiased);
3763 rasterizer->setClipRect(deviceRect);
3764 rasterizer->initialize(callback, userData);
3766 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3770 rasterizer->rasterize(outline, fillRule);
3774 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3775 // minimize memory reallocations. However if initial size for
3776 // raster pool is changed for lower value, reallocations will
3778 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3779 int rasterPoolSize = rasterPoolInitialSize;
3780 unsigned char *rasterPoolBase;
3781 #if defined(Q_WS_WIN64)
3783 // We make use of setjmp and longjmp in qgrayraster.c which requires
3784 // 16-byte alignment, hence we hardcode this requirement here..
3785 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3787 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3788 rasterPoolBase = rasterPoolOnStack;
3790 Q_CHECK_PTR(rasterPoolBase);
3792 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3794 void *data = userData;
3796 QT_FT_BBox clip_box = { deviceRect.x(),
3798 deviceRect.x() + deviceRect.width(),
3799 deviceRect.y() + deviceRect.height() };
3801 QT_FT_Raster_Params rasterParams;
3802 rasterParams.target = 0;
3803 rasterParams.source = outline;
3804 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3805 rasterParams.gray_spans = 0;
3806 rasterParams.black_spans = 0;
3807 rasterParams.bit_test = 0;
3808 rasterParams.bit_set = 0;
3809 rasterParams.user = data;
3810 rasterParams.clip_box = clip_box;
3815 int rendered_spans = 0;
3819 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3820 rasterParams.gray_spans = callback;
3821 rasterParams.skip_spans = rendered_spans;
3822 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3824 // Out of memory, reallocate some more and try again...
3825 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3826 int new_size = rasterPoolSize * 2;
3827 if (new_size > 1024 * 1024) {
3828 qWarning("QPainter: Rasterization of primitive failed");
3832 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3834 #if defined(Q_WS_WIN64)
3835 _aligned_free(rasterPoolBase);
3837 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3838 free(rasterPoolBase);
3841 rasterPoolSize = new_size;
3843 #if defined(Q_WS_WIN64)
3844 // We make use of setjmp and longjmp in qgrayraster.c which requires
3845 // 16-byte alignment, hence we hardcode this requirement here..
3846 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3848 (unsigned char *) malloc(rasterPoolSize);
3850 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3852 qt_ft_grays_raster.raster_done(*grayRaster.data());
3853 qt_ft_grays_raster.raster_new(grayRaster.data());
3854 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3860 #if defined(Q_WS_WIN64)
3861 _aligned_free(rasterPoolBase);
3863 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3864 free(rasterPoolBase);
3868 void QRasterPaintEnginePrivate::recalculateFastImages()
3870 Q_Q(QRasterPaintEngine);
3871 QRasterPaintEngineState *s = q->state();
3873 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3874 && s->matrix.type() <= QTransform::TxShear;
3877 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3879 Q_Q(const QRasterPaintEngine);
3880 const QRasterPaintEngineState *s = q->state();
3882 return s->flags.fast_images
3883 && (mode == QPainter::CompositionMode_SourceOver
3884 || (mode == QPainter::CompositionMode_Source
3885 && !image.hasAlphaChannel()));
3888 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3890 Q_ASSERT(image.depth() == 1);
3892 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3893 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3895 QRgb fg = PREMUL(color.rgba());
3898 int height = sourceImage.height();
3899 int width = sourceImage.width();
3900 for (int y=0; y<height; ++y) {
3901 uchar *source = sourceImage.scanLine(y);
3902 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3903 if (!source || !target)
3904 QT_THROW(std::bad_alloc()); // we must have run out of memory
3905 for (int x=0; x < width; ++x)
3906 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3911 QRasterBuffer::~QRasterBuffer()
3915 void QRasterBuffer::init()
3917 compositionMode = QPainter::CompositionMode_SourceOver;
3918 monoDestinationWithClut = false;
3923 QImage::Format QRasterBuffer::prepare(QImage *image)
3925 m_buffer = (uchar *)image->bits();
3926 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3927 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3928 bytes_per_pixel = image->depth()/8;
3929 bytes_per_line = image->bytesPerLine();
3931 format = image->format();
3932 drawHelper = qDrawHelper + format;
3933 if (image->depth() == 1 && image->colorTable().size() == 2) {
3934 monoDestinationWithClut = true;
3935 destColor0 = PREMUL(image->colorTable()[0]);
3936 destColor1 = PREMUL(image->colorTable()[1]);
3942 void QRasterBuffer::resetBuffer(int val)
3944 memset(m_buffer, val, m_height*bytes_per_line);
3948 #if defined(Q_WS_QWS)
3949 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
3951 m_buffer = reinterpret_cast<uchar*>(device->memory());
3952 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
3953 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
3954 bytes_per_pixel = device->depth() / 8;
3955 bytes_per_line = device->bytesPerLine();
3956 format = device->format();
3957 #ifndef QT_NO_RASTERCALLBACKS
3959 drawHelper = qDrawHelperCallback + format;
3962 drawHelper = qDrawHelper + format;
3965 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
3969 return widget->frameGeometry().width();
3971 return widget->frameGeometry().height();
3976 return qt_paint_device_metric(widget, m);
3979 int QCustomRasterPaintDevice::bytesPerLine() const
3981 return (width() * depth() + 7) / 8;
3984 #elif defined(Q_OS_SYMBIAN)
3986 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
3990 #endif // Q_OS_SYMBIAN
3993 \class QCustomRasterPaintDevice
3998 \brief The QCustomRasterPaintDevice class is provided to activate
3999 hardware accelerated paint engines in Qt for Embedded Linux.
4001 Note that this class is only available in \l{Qt for Embedded Linux}.
4003 In \l{Qt for Embedded Linux}, painting is a pure software
4004 implementation. But starting with Qt 4.2, it is
4005 possible to add an accelerated graphics driver to take advantage
4006 of available hardware resources.
4008 Hardware acceleration is accomplished by creating a custom screen
4009 driver, accelerating the copying from memory to the screen, and
4010 implementing a custom paint engine accelerating the various
4011 painting operations. Then a custom paint device (derived from the
4012 QCustomRasterPaintDevice class) and a custom window surface
4013 (derived from QWSWindowSurface) must be implemented to make
4014 \l{Qt for Embedded Linux} aware of the accelerated driver.
4016 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
4017 documentation for details.
4019 \sa QRasterPaintEngine, QPaintDevice
4023 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
4025 Constructs a custom raster based paint device for the given
4026 top-level \a widget.
4030 \fn int QCustomRasterPaintDevice::bytesPerLine() const
4032 Returns the number of bytes per line in the framebuffer. Note that
4033 this number might be larger than the framebuffer width.
4037 \fn int QCustomRasterPaintDevice::devType() const
4042 \fn QImage::Format QCustomRasterPaintDevice::format() const
4044 Returns the format of the device's memory buffet.
4046 The default format is QImage::Format_ARGB32_Premultiplied. The
4047 only other valid format is QImage::Format_RGB16.
4051 \fn void * QCustomRasterPaintDevice::memory () const
4053 Returns a pointer to the paint device's memory buffer, or 0 if no
4058 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4063 \fn QSize QCustomRasterPaintDevice::size () const
4068 QClipData::QClipData(int height)
4070 clipSpanHeight = height;
4075 xmin = xmax = ymin = ymax = 0;
4079 hasRectClip = hasRegionClip = false;
4082 QClipData::~QClipData()
4090 void QClipData::initialize()
4096 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4098 Q_CHECK_PTR(m_clipLines);
4100 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4101 allocated = clipSpanHeight;
4102 Q_CHECK_PTR(m_spans);
4108 m_clipLines[y].spans = 0;
4109 m_clipLines[y].count = 0;
4113 const int len = clipRect.width();
4116 QSpan *span = m_spans + count;
4120 span->coverage = 255;
4123 m_clipLines[y].spans = span;
4124 m_clipLines[y].count = 1;
4128 while (y < clipSpanHeight) {
4129 m_clipLines[y].spans = 0;
4130 m_clipLines[y].count = 0;
4133 } else if (hasRegionClip) {
4135 const QVector<QRect> rects = clipRegion.rects();
4136 const int numRects = rects.size();
4139 const int maxSpans = (ymax - ymin) * numRects;
4140 if (maxSpans > allocated) {
4141 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4142 allocated = maxSpans;
4147 int firstInBand = 0;
4149 while (firstInBand < numRects) {
4150 const int currMinY = rects.at(firstInBand).y();
4151 const int currMaxY = currMinY + rects.at(firstInBand).height();
4153 while (y < currMinY) {
4154 m_clipLines[y].spans = 0;
4155 m_clipLines[y].count = 0;
4159 int lastInBand = firstInBand;
4160 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4163 while (y < currMaxY) {
4165 m_clipLines[y].spans = m_spans + count;
4166 m_clipLines[y].count = lastInBand - firstInBand + 1;
4168 for (int r = firstInBand; r <= lastInBand; ++r) {
4169 const QRect &currRect = rects.at(r);
4170 QSpan *span = m_spans + count;
4171 span->x = currRect.x();
4172 span->len = currRect.width();
4174 span->coverage = 255;
4180 firstInBand = lastInBand + 1;
4183 Q_ASSERT(count <= allocated);
4185 while (y < clipSpanHeight) {
4186 m_clipLines[y].spans = 0;
4187 m_clipLines[y].count = 0;
4193 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4198 free(m_clipLines); // same for clipLines
4204 void QClipData::fixup()
4209 ymin = ymax = xmin = xmax = 0;
4214 ymin = m_spans[0].y;
4215 ymax = m_spans[count-1].y + 1;
4219 const int firstLeft = m_spans[0].x;
4220 const int firstRight = m_spans[0].x + m_spans[0].len;
4223 for (int i = 0; i < count; ++i) {
4224 QT_FT_Span_& span = m_spans[i];
4227 if (span.y != y + 1 && y != -1)
4230 m_clipLines[y].spans = &span;
4231 m_clipLines[y].count = 1;
4233 ++m_clipLines[y].count;
4235 const int spanLeft = span.x;
4236 const int spanRight = spanLeft + span.len;
4238 if (spanLeft < xmin)
4241 if (spanRight > xmax)
4244 if (spanLeft != firstLeft || spanRight != firstRight)
4250 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4255 Convert \a rect to clip spans.
4257 void QClipData::setClipRect(const QRect &rect)
4259 if (hasRectClip && rect == clipRect)
4262 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4264 hasRegionClip = false;
4268 xmax = rect.x() + rect.width();
4269 ymin = qMin(rect.y(), clipSpanHeight);
4270 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4277 // qDebug() << xmin << xmax << ymin << ymax;
4281 Convert \a region to clip spans.
4283 void QClipData::setClipRegion(const QRegion ®ion)
4285 if (region.rectCount() == 1) {
4286 setClipRect(region.rects().at(0));
4290 hasRegionClip = true;
4291 hasRectClip = false;
4292 clipRegion = region;
4294 { // set bounding rect
4295 const QRect rect = region.boundingRect();
4297 xmax = rect.x() + rect.width();
4299 ymax = rect.y() + rect.height();
4311 spans must be sorted on y
4313 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4314 const QSpan *spans, const QSpan *end,
4315 QSpan **outSpans, int available)
4317 const_cast<QClipData *>(clip)->initialize();
4319 QSpan *out = *outSpans;
4321 const QSpan *clipSpans = clip->m_spans + *currentClip;
4322 const QSpan *clipEnd = clip->m_spans + clip->count;
4324 while (available && spans < end ) {
4325 if (clipSpans >= clipEnd) {
4329 if (clipSpans->y > spans->y) {
4333 if (spans->y != clipSpans->y) {
4334 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4335 clipSpans = clip->m_clipLines[spans->y].spans;
4340 Q_ASSERT(spans->y == clipSpans->y);
4343 int sx2 = sx1 + spans->len;
4344 int cx1 = clipSpans->x;
4345 int cx2 = cx1 + clipSpans->len;
4347 if (cx1 < sx1 && cx2 < sx1) {
4350 } else if (sx1 < cx1 && sx2 < cx1) {
4354 int x = qMax(sx1, cx1);
4355 int len = qMin(sx2, cx2) - x;
4357 out->x = qMax(sx1, cx1);
4358 out->len = qMin(sx2, cx2) - out->x;
4360 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4372 *currentClip = clipSpans - clip->m_spans;
4376 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4378 // qDebug() << "qt_span_fill_clipped" << spanCount;
4379 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4381 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4383 const int NSPANS = 256;
4384 QSpan cspans[NSPANS];
4385 int currentClip = 0;
4386 const QSpan *end = spans + spanCount;
4387 while (spans < end) {
4388 QSpan *clipped = cspans;
4389 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4390 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4391 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4393 if (clipped - cspans)
4394 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4400 Clip spans to \a{clip}-rectangle.
4401 Returns number of unclipped spans
4403 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4406 const short minx = clip.left();
4407 const short miny = clip.top();
4408 const short maxx = clip.right();
4409 const short maxy = clip.bottom();
4412 for (int i = 0; i < numSpans; ++i) {
4413 if (spans[i].y > maxy)
4415 if (spans[i].y < miny
4416 || spans[i].x > maxx
4417 || spans[i].x + spans[i].len <= minx) {
4420 if (spans[i].x < minx) {
4421 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4424 spans[n].x = spans[i].x;
4425 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4427 if (spans[n].len == 0)
4429 spans[n].y = spans[i].y;
4430 spans[n].coverage = spans[i].coverage;
4437 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4440 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4441 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4443 Q_ASSERT(fillData->clip);
4444 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4446 // hw: check if this const_cast<> is safe!!!
4447 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4448 fillData->clip->clipRect);
4450 fillData->unclipped_blend(count, spans, fillData);
4453 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4455 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4457 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4458 // for (int i = 0; i < qMin(count, 10); ++i) {
4459 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4462 switch (clipData->operation) {
4464 case Qt::IntersectClip:
4466 QClipData *newClip = clipData->newClip;
4467 newClip->initialize();
4469 int currentClip = 0;
4470 const QSpan *end = spans + count;
4471 while (spans < end) {
4472 QSpan *newspans = newClip->m_spans + newClip->count;
4473 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4474 &newspans, newClip->allocated - newClip->count);
4475 newClip->count = newspans - newClip->m_spans;
4477 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4478 newClip->allocated *= 2;
4485 case Qt::ReplaceClip:
4486 clipData->newClip->appendSpans(spans, count);
4494 QImage QRasterBuffer::bufferImage() const
4496 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4498 for (int y = 0; y < m_height; ++y) {
4499 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4501 for (int x=0; x<m_width; ++x) {
4502 uint argb = span[x];
4503 image.setPixel(x, y, argb);
4511 void QRasterBuffer::flushToARGBImage(QImage *target) const
4513 int w = qMin(m_width, target->width());
4514 int h = qMin(m_height, target->height());
4516 for (int y=0; y<h; ++y) {
4517 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4518 QRgb *dest = (QRgb *) target->scanLine(y);
4519 for (int x=0; x<w; ++x) {
4520 QRgb pixel = sourceLine[x];
4521 int alpha = qAlpha(pixel);
4525 dest[x] = (alpha << 24)
4526 | ((255*qRed(pixel)/alpha) << 16)
4527 | ((255*qGreen(pixel)/alpha) << 8)
4528 | ((255*qBlue(pixel)/alpha) << 0);
4535 class QGradientCache
4539 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4540 stops(s), opacity(op), interpolationMode(mode) {}
4541 uint buffer[GRADIENT_STOPTABLE_SIZE];
4542 QGradientStops stops;
4544 QGradient::InterpolationMode interpolationMode;
4547 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4550 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4551 quint64 hash_val = 0;
4553 QGradientStops stops = gradient.stops();
4554 for (int i = 0; i < stops.size() && i <= 2; i++)
4555 hash_val += stops[i].second.rgba();
4557 QMutexLocker lock(&mutex);
4558 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4560 if (it == cache.constEnd())
4561 return addCacheElement(hash_val, gradient, opacity);
4564 const CacheInfo &cache_info = it.value();
4565 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4566 return cache_info.buffer;
4568 } while (it != cache.constEnd() && it.key() == hash_val);
4569 // an exact match for these stops and opacity was not found, create new cache
4570 return addCacheElement(hash_val, gradient, opacity);
4574 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4576 inline int maxCacheSize() const { return 60; }
4577 inline void generateGradientColorTable(const QGradient& g,
4579 int size, int opacity) const;
4580 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4581 if (cache.size() == maxCacheSize()) {
4582 // may remove more than 1, but OK
4583 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4585 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4586 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4587 return cache.insert(hash_val, cache_entry).value().buffer;
4590 QGradientColorTableHash cache;
4594 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4596 QGradientStops stops = gradient.stops();
4597 int stopCount = stops.count();
4598 Q_ASSERT(stopCount > 0);
4600 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4602 if (stopCount == 2) {
4603 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4604 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4606 qreal first_stop = stops[0].first;
4607 qreal second_stop = stops[1].first;
4609 if (second_stop < first_stop) {
4610 qSwap(first_color, second_color);
4611 qSwap(first_stop, second_stop);
4614 if (colorInterpolation) {
4615 first_color = PREMUL(first_color);
4616 second_color = PREMUL(second_color);
4619 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4620 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4622 uint red_first = qRed(first_color) << 16;
4623 uint green_first = qGreen(first_color) << 16;
4624 uint blue_first = qBlue(first_color) << 16;
4625 uint alpha_first = qAlpha(first_color) << 16;
4627 uint red_second = qRed(second_color) << 16;
4628 uint green_second = qGreen(second_color) << 16;
4629 uint blue_second = qBlue(second_color) << 16;
4630 uint alpha_second = qAlpha(second_color) << 16;
4633 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4634 if (colorInterpolation)
4635 colorTable[i] = first_color;
4637 colorTable[i] = PREMUL(first_color);
4640 if (i < second_index) {
4641 qreal reciprocal = qreal(1) / (second_index - first_index);
4643 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4644 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4645 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4646 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4649 red_first += 1 << 15;
4650 green_first += 1 << 15;
4651 blue_first += 1 << 15;
4652 alpha_first += 1 << 15;
4654 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4655 red_first += red_delta;
4656 green_first += green_delta;
4657 blue_first += blue_delta;
4658 alpha_first += alpha_delta;
4660 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4661 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4663 if (colorInterpolation)
4664 colorTable[i] = color;
4666 colorTable[i] = PREMUL(color);
4670 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4671 if (colorInterpolation)
4672 colorTable[i] = second_color;
4674 colorTable[i] = PREMUL(second_color);
4680 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4681 if (stopCount == 1) {
4682 current_color = PREMUL(current_color);
4683 for (int i = 0; i < size; ++i)
4684 colorTable[i] = current_color;
4688 // The position where the gradient begins and ends
4689 qreal begin_pos = stops[0].first;
4690 qreal end_pos = stops[stopCount-1].first;
4692 int pos = 0; // The position in the color table.
4695 qreal incr = 1 / qreal(size); // the double increment.
4696 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4698 // Up to first point
4699 colorTable[pos++] = PREMUL(current_color);
4700 while (dpos <= begin_pos) {
4701 colorTable[pos] = colorTable[pos - 1];
4706 int current_stop = 0; // We always interpolate between current and current + 1.
4708 qreal t; // position between current left and right stops
4709 qreal t_delta; // the t increment per entry in the color table
4711 if (dpos < end_pos) {
4713 while (dpos > stops[current_stop+1].first)
4716 if (current_stop != 0)
4717 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4718 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4720 if (colorInterpolation) {
4721 current_color = PREMUL(current_color);
4722 next_color = PREMUL(next_color);
4725 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4726 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4727 t = (dpos - stops[current_stop].first) * c;
4731 Q_ASSERT(current_stop < stopCount);
4733 int dist = qRound(t);
4734 int idist = 256 - dist;
4736 if (colorInterpolation)
4737 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4739 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4744 if (dpos >= end_pos)
4750 while (dpos > stops[current_stop+skip+1].first)
4754 current_stop += skip;
4756 current_color = next_color;
4758 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4759 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4761 if (colorInterpolation) {
4763 current_color = PREMUL(current_color);
4764 next_color = PREMUL(next_color);
4767 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4768 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4769 t = (dpos - stops[current_stop].first) * c;
4776 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4777 while (pos < size - 1) {
4778 colorTable[pos] = current_color;
4782 // Make sure the last color stop is represented at the end of the table
4783 colorTable[size - 1] = current_color;
4786 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4789 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4793 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
4798 m11 = m22 = m33 = 1.;
4799 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4800 clip = pe ? pe->d_func()->clip() : 0;
4803 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4805 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4807 Qt::BrushStyle brushStyle = qbrush_style(brush);
4808 switch (brushStyle) {
4809 case Qt::SolidPattern: {
4811 QColor c = qbrush_color(brush);
4812 QRgb rgba = c.rgba();
4813 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4814 if ((solid.color & 0xff000000) == 0
4815 && compositionMode == QPainter::CompositionMode_SourceOver) {
4821 case Qt::LinearGradientPattern:
4823 type = LinearGradient;
4824 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4825 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4826 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4827 gradient.spread = g->spread();
4829 QLinearGradientData &linearData = gradient.linear;
4831 linearData.origin.x = g->start().x();
4832 linearData.origin.y = g->start().y();
4833 linearData.end.x = g->finalStop().x();
4834 linearData.end.y = g->finalStop().y();
4838 case Qt::RadialGradientPattern:
4840 type = RadialGradient;
4841 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4842 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4843 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4844 gradient.spread = g->spread();
4846 QRadialGradientData &radialData = gradient.radial;
4848 QPointF center = g->center();
4849 radialData.center.x = center.x();
4850 radialData.center.y = center.y();
4851 radialData.center.radius = g->centerRadius();
4852 QPointF focal = g->focalPoint();
4853 radialData.focal.x = focal.x();
4854 radialData.focal.y = focal.y();
4855 radialData.focal.radius = g->focalRadius();
4859 case Qt::ConicalGradientPattern:
4861 type = ConicalGradient;
4862 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4863 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4864 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4865 gradient.spread = QGradient::RepeatSpread;
4867 QConicalGradientData &conicalData = gradient.conical;
4869 QPointF center = g->center();
4870 conicalData.center.x = center.x();
4871 conicalData.center.y = center.y();
4872 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4876 case Qt::Dense1Pattern:
4877 case Qt::Dense2Pattern:
4878 case Qt::Dense3Pattern:
4879 case Qt::Dense4Pattern:
4880 case Qt::Dense5Pattern:
4881 case Qt::Dense6Pattern:
4882 case Qt::Dense7Pattern:
4883 case Qt::HorPattern:
4884 case Qt::VerPattern:
4885 case Qt::CrossPattern:
4886 case Qt::BDiagPattern:
4887 case Qt::FDiagPattern:
4888 case Qt::DiagCrossPattern:
4891 tempImage = new QImage();
4892 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4893 initTexture(tempImage, alpha, QTextureData::Tiled);
4895 case Qt::TexturePattern:
4898 tempImage = new QImage();
4900 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4901 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4903 *tempImage = brush.textureImage();
4904 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4912 adjustSpanMethods();
4915 void QSpanData::adjustSpanMethods()
4925 unclipped_blend = 0;
4928 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4929 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4930 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4931 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4932 fillRect = rasterBuffer->drawHelper->fillRect;
4934 case LinearGradient:
4935 case RadialGradient:
4936 case ConicalGradient:
4937 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4941 #ifndef QT_NO_RASTERCALLBACKS
4942 if (!rasterBuffer->buffer())
4943 unclipped_blend = qBlendTextureCallback;
4946 unclipped_blend = qBlendTexture;
4948 unclipped_blend = qBlendTexture;
4950 if (!texture.imageData)
4951 unclipped_blend = 0;
4956 if (!unclipped_blend) {
4959 blend = unclipped_blend;
4960 } else if (clip->hasRectClip) {
4961 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4963 blend = qt_span_fill_clipped;
4967 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4970 // make sure we round off correctly in qdrawhelper.cpp
4971 delta.translate(1.0 / 65536, 1.0 / 65536);
4973 QTransform inv = (delta * matrix).inverted();
4986 const bool affine = !m13 && !m23;
4987 fast_matrix = affine
4988 && m11 * m11 + m21 * m21 < 1e4
4989 && m12 * m12 + m22 * m22 < 1e4
4993 adjustSpanMethods();
4996 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4998 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
5000 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
5001 if (!d || d->height == 0) {
5002 texture.imageData = 0;
5009 texture.bytesPerLine = 0;
5010 texture.format = QImage::Format_Invalid;
5011 texture.colorTable = 0;
5012 texture.hasAlpha = alpha != 256;
5014 texture.imageData = d->data;
5015 texture.width = d->width;
5016 texture.height = d->height;
5018 if (sourceRect.isNull()) {
5021 texture.x2 = texture.width;
5022 texture.y2 = texture.height;
5024 texture.x1 = sourceRect.x();
5025 texture.y1 = sourceRect.y();
5026 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
5027 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
5030 texture.bytesPerLine = d->bytes_per_line;
5032 texture.format = d->format;
5033 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5034 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5036 texture.const_alpha = alpha;
5037 texture.type = _type;
5039 adjustSpanMethods();
5044 \a x and \a y is relative to the midpoint of \a rect.
5046 static inline void drawEllipsePoints(int x, int y, int length,
5049 ProcessSpans pen_func, ProcessSpans brush_func,
5050 QSpanData *pen_data, QSpanData *brush_data)
5055 QT_FT_Span outline[4];
5056 const int midx = rect.x() + (rect.width() + 1) / 2;
5057 const int midy = rect.y() + (rect.height() + 1) / 2;
5063 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
5064 outline[0].len = qMin(length, x - outline[0].x);
5066 outline[0].coverage = 255;
5070 outline[1].len = length;
5072 outline[1].coverage = 255;
5075 outline[2].x = outline[0].x;
5076 outline[2].len = outline[0].len;
5077 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
5078 outline[2].coverage = 255;
5082 outline[3].len = length;
5083 outline[3].y = outline[2].y;
5084 outline[3].coverage = 255;
5086 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
5090 fill[0].x = outline[0].x + outline[0].len - 1;
5091 fill[0].len = qMax(0, outline[1].x - fill[0].x);
5092 fill[0].y = outline[1].y;
5093 fill[0].coverage = 255;
5096 fill[1].x = outline[2].x + outline[2].len - 1;
5097 fill[1].len = qMax(0, outline[3].x - fill[1].x);
5098 fill[1].y = outline[3].y;
5099 fill[1].coverage = 255;
5101 int n = (fill[0].y >= fill[1].y ? 1 : 2);
5102 n = qt_intersect_spans(fill, n, clip);
5104 brush_func(n, fill, brush_data);
5107 int n = (outline[1].y >= outline[2].y ? 2 : 4);
5108 n = qt_intersect_spans(outline, n, clip);
5110 pen_func(n, outline, pen_data);
5116 Draws an ellipse using the integer point midpoint algorithm.
5118 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
5119 ProcessSpans pen_func, ProcessSpans brush_func,
5120 QSpanData *pen_data, QSpanData *brush_data)
5122 const qreal a = qreal(rect.width()) / 2;
5123 const qreal b = qreal(rect.height()) / 2;
5124 qreal d = b*b - (a*a*b) + 0.25*a*a;
5127 int y = (rect.height() + 1) / 2;
5131 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
5132 if (d < 0) { // select E
5135 } else { // select SE
5136 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
5137 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5138 pen_func, brush_func, pen_data, brush_data);
5143 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5144 pen_func, brush_func, pen_data, brush_data);
5147 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
5148 const int miny = rect.height() & 0x1;
5150 if (d < 0) { // select SE
5151 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
5153 } else { // select S
5154 d += a*a*(-2*y + 3);
5157 drawEllipsePoints(x, y, 1, rect, clip,
5158 pen_func, brush_func, pen_data, brush_data);
5163 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
5166 Draws the first \a pointCount points in the buffer \a points
5168 The default implementation converts the first \a pointCount QPoints in \a points
5169 to QPointFs and calls the floating point version of drawPoints.
5173 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
5176 Reimplement this function to draw the largest ellipse that can be
5177 contained within rectangle \a rect.
5180 #ifdef QT_DEBUG_DRAW
5181 void dumpClip(int width, int height, const QClipData *clip)
5183 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
5184 clipImg.fill(0xffff0000);
5191 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
5193 for (int i = 0; i < clip->count; ++i) {
5194 const QSpan *span = ((QClipData *) clip)->spans() + i;
5195 for (int j = 0; j < span->len; ++j)
5196 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
5197 x0 = qMin(x0, int(span->x));
5198 x1 = qMax(x1, int(span->x + span->len - 1));
5200 y0 = qMin(y0, int(span->y));
5201 y1 = qMax(y1, int(span->y));
5204 static int counter = 0;
5211 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
5212 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));