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);
487 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
488 #if defined(Q_WS_WIN)
489 else if (qt_cleartype_enabled)
490 #elif defined (Q_WS_MAC)
491 else if (qt_applefontsmoothing_enabled)
496 QImage::Format format = static_cast<QImage *>(d->device)->format();
497 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
500 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
502 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
511 bool QRasterPaintEngine::end()
514 Q_D(QRasterPaintEngine);
515 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
517 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
527 void QRasterPaintEngine::releaseBuffer()
529 Q_D(QRasterPaintEngine);
530 d->rasterBuffer.reset(new QRasterBuffer);
536 QSize QRasterPaintEngine::size() const
538 Q_D(const QRasterPaintEngine);
539 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
546 void QRasterPaintEngine::saveBuffer(const QString &s) const
548 Q_D(const QRasterPaintEngine);
549 d->rasterBuffer->bufferImage().save(s, "PNG");
556 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
558 QRasterPaintEngineState *s = state();
559 // FALCON: get rid of this line, see drawImage call below.
561 QTransform::TransformationType txop = s->matrix.type();
565 case QTransform::TxNone:
566 s->flags.int_xform = true;
569 case QTransform::TxTranslate:
570 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
571 && qreal(int(s->matrix.dy())) == s->matrix.dy();
574 case QTransform::TxScale:
575 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
576 && qreal(int(s->matrix.dy())) == s->matrix.dy()
577 && qreal(int(s->matrix.m11())) == s->matrix.m11()
578 && qreal(int(s->matrix.m22())) == s->matrix.m22();
581 default: // shear / perspective...
582 s->flags.int_xform = false;
586 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
588 ensureOutlineMapper();
593 QRasterPaintEngineState::~QRasterPaintEngineState()
595 if (flags.has_clip_ownership)
600 QRasterPaintEngineState::QRasterPaintEngineState()
612 flags.fast_pen = true;
613 flags.antialiased = false;
614 flags.bilinear = false;
615 flags.fast_text = true;
616 flags.int_xform = true;
617 flags.tx_noshear = true;
618 flags.fast_images = true;
621 flags.has_clip_ownership = false;
626 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
631 , strokeFlags(s.strokeFlags)
632 , lastBrush(s.lastBrush)
633 , brushData(s.brushData)
634 , fillFlags(s.fillFlags)
635 , pixmapFlags(s.pixmapFlags)
636 , intOpacity(s.intOpacity)
640 , flag_bits(s.flag_bits)
642 brushData.tempImage = 0;
643 penData.tempImage = 0;
644 flags.has_clip_ownership = false;
650 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
652 QRasterPaintEngineState *s;
654 s = new QRasterPaintEngineState();
656 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
664 void QRasterPaintEngine::setState(QPainterState *s)
666 Q_D(QRasterPaintEngine);
667 QPaintEngineEx::setState(s);
668 d->rasterBuffer->compositionMode = s->composition_mode;
672 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
677 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
684 void QRasterPaintEngine::penChanged()
687 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
689 QRasterPaintEngineState *s = state();
690 s->strokeFlags |= DirtyPen;
691 s->dirty |= DirtyPen;
697 void QRasterPaintEngine::updatePen(const QPen &pen)
699 Q_D(QRasterPaintEngine);
700 QRasterPaintEngineState *s = state();
702 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
705 Qt::PenStyle pen_style = qpen_style(pen);
710 s->penData.clip = d->clip();
711 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
713 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
714 || pen.brush().transform().type() >= QTransform::TxNone) {
715 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
718 // Slightly ugly handling of an uncommon case... We need to change
719 // the pen because it is reused in draw_midpoint to decide dashed
721 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
722 pen_style = Qt::SolidLine;
723 s->lastPen.setStyle(Qt::SolidLine);
726 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
727 d->basicStroker.setCapStyle(qpen_capStyle(pen));
728 d->basicStroker.setMiterLimit(pen.miterLimit());
730 qreal penWidth = qpen_widthf(pen);
732 d->basicStroker.setStrokeWidth(1);
734 d->basicStroker.setStrokeWidth(penWidth);
736 if(pen_style == Qt::SolidLine) {
737 s->stroker = &d->basicStroker;
738 } else if (pen_style != Qt::NoPen) {
740 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
741 if (pen.isCosmetic()) {
742 d->dashStroker->setClipRect(d->deviceRect);
744 // ### I've seen this inverted devrect multiple places now...
745 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
746 d->dashStroker->setClipRect(clipRect);
748 d->dashStroker->setDashPattern(pen.dashPattern());
749 d->dashStroker->setDashOffset(pen.dashOffset());
750 s->stroker = d->dashStroker.data();
755 ensureState(); // needed because of tx_noshear...
756 s->flags.fast_pen = pen_style > Qt::NoPen
758 && ((pen.isCosmetic() && penWidth <= 1)
759 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
761 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
771 void QRasterPaintEngine::brushOriginChanged()
773 QRasterPaintEngineState *s = state();
775 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
778 s->fillFlags |= DirtyBrushOrigin;
785 void QRasterPaintEngine::brushChanged()
787 QRasterPaintEngineState *s = state();
789 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
791 s->fillFlags |= DirtyBrush;
800 void QRasterPaintEngine::updateBrush(const QBrush &brush)
803 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
805 Q_D(QRasterPaintEngine);
806 QRasterPaintEngineState *s = state();
807 // must set clip prior to setup, as setup uses it...
808 s->brushData.clip = d->clip();
809 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
810 if (s->fillFlags & DirtyTransform
811 || brush.transform().type() >= QTransform::TxNone)
812 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
813 s->lastBrush = brush;
817 void QRasterPaintEngine::updateOutlineMapper()
819 Q_D(QRasterPaintEngine);
820 d->outlineMapper->setMatrix(state()->matrix);
823 void QRasterPaintEngine::updateState()
825 QRasterPaintEngineState *s = state();
827 if (s->dirty & DirtyTransform)
828 updateMatrix(s->matrix);
830 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
831 const QPainter::CompositionMode mode = s->composition_mode;
832 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
833 && s->intOpacity == 256
834 && (mode == QPainter::CompositionMode_Source
835 || (mode == QPainter::CompositionMode_SourceOver
836 && qAlpha(s->penData.solid.color) == 255));
846 void QRasterPaintEngine::opacityChanged()
848 QRasterPaintEngineState *s = state();
851 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
854 s->fillFlags |= DirtyOpacity;
855 s->strokeFlags |= DirtyOpacity;
856 s->pixmapFlags |= DirtyOpacity;
857 s->dirty |= DirtyOpacity;
858 s->intOpacity = (int) (s->opacity * 256);
864 void QRasterPaintEngine::compositionModeChanged()
866 Q_D(QRasterPaintEngine);
867 QRasterPaintEngineState *s = state();
870 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
873 s->fillFlags |= DirtyCompositionMode;
874 s->dirty |= DirtyCompositionMode;
876 s->strokeFlags |= DirtyCompositionMode;
877 d->rasterBuffer->compositionMode = s->composition_mode;
879 d->recalculateFastImages();
885 void QRasterPaintEngine::renderHintsChanged()
887 QRasterPaintEngineState *s = state();
890 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
893 bool was_aa = s->flags.antialiased;
894 bool was_bilinear = s->flags.bilinear;
896 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
897 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
899 if (was_aa != s->flags.antialiased)
900 s->strokeFlags |= DirtyHints;
902 if (was_bilinear != s->flags.bilinear) {
903 s->strokeFlags |= DirtyPen;
904 s->fillFlags |= DirtyBrush;
907 Q_D(QRasterPaintEngine);
908 d->recalculateFastImages();
914 void QRasterPaintEngine::transformChanged()
916 QRasterPaintEngineState *s = state();
919 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
922 s->fillFlags |= DirtyTransform;
923 s->strokeFlags |= DirtyTransform;
925 s->dirty |= DirtyTransform;
927 Q_D(QRasterPaintEngine);
928 d->recalculateFastImages();
934 void QRasterPaintEngine::clipEnabledChanged()
936 QRasterPaintEngineState *s = state();
939 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
943 s->clip->enabled = s->clipEnabled;
944 s->fillFlags |= DirtyClipEnabled;
945 s->strokeFlags |= DirtyClipEnabled;
946 s->pixmapFlags |= DirtyClipEnabled;
951 void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
953 rasterBuffer->prepare(device);
957 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
959 SrcOverBlendFunc func,
964 if (alpha == 0 || !clip.isValid())
967 Q_ASSERT(img.depth() >= 8);
969 int srcBPL = img.bytesPerLine();
970 const uchar *srcBits = img.bits();
971 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
972 int iw = img.width();
973 int ih = img.height();
978 // Adjust the image according to the source offset...
979 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
982 // adapt the x parameters
983 int x = qRound(pt.x());
985 int cx2 = clip.x() + clip.width();
988 srcBits += srcSize * d;
993 int d = x + iw - cx2;
999 // adapt the y paremeters...
1001 int cy2 = clip.y() + clip.height();
1002 int y = qRound(pt.y());
1005 srcBits += srcBPL * d;
1010 int d = y + ih - cy2;
1016 // call the blend function...
1017 int dstSize = rasterBuffer->bytesPerPixel();
1018 int dstBPL = rasterBuffer->bytesPerLine();
1019 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1026 void QRasterPaintEnginePrivate::systemStateChanged()
1028 QRect clipRect(0, 0,
1029 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1030 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1032 if (!systemClip.isEmpty()) {
1033 QRegion clippedDeviceRgn = systemClip & clipRect;
1034 deviceRect = clippedDeviceRgn.boundingRect();
1035 baseClip->setClipRegion(clippedDeviceRgn);
1037 deviceRect = clipRect;
1038 baseClip->setClipRect(deviceRect);
1040 #ifdef QT_DEBUG_DRAW
1041 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1044 exDeviceRect = deviceRect;
1046 Q_Q(QRasterPaintEngine);
1047 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1048 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1049 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1052 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1054 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1057 Q_Q(QRasterPaintEngine);
1058 bool bilinear = q->state()->flags.bilinear;
1060 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1061 spanData->setupMatrix(b.transform() * m, bilinear);
1063 if (m.type() <= QTransform::TxTranslate) {
1064 // specialize setupMatrix for translation matrices
1065 // to avoid needless matrix inversion
1073 spanData->dx = -m.dx();
1074 spanData->dy = -m.dy();
1075 spanData->txop = m.type();
1076 spanData->bilinear = bilinear;
1077 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1078 spanData->adjustSpanMethods();
1080 spanData->setupMatrix(m, bilinear);
1085 // #define QT_CLIPPING_RATIOS
1087 #ifdef QT_CLIPPING_RATIOS
1092 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1094 if (d->clip()->hasRectClip)
1096 if (d->clip()->hasRegionClip)
1100 if ((totalClips % 5000) == 0) {
1101 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1102 rectClips * 100.0 / (qreal) totalClips,
1103 regionClips * 100.0 / (qreal) totalClips,
1104 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1113 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1115 if (s->flags.has_clip_ownership)
1118 s->flags.has_clip_ownership = false;
1121 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1123 s->fillFlags |= QPaintEngine::DirtyClipPath;
1124 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1125 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1127 d->solid_color_filler.clip = d->clip();
1128 d->solid_color_filler.adjustSpanMethods();
1130 #ifdef QT_DEBUG_DRAW
1131 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1140 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1142 #ifdef QT_DEBUG_DRAW
1143 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1145 if (path.elements()) {
1146 for (int i=0; i<path.elementCount(); ++i) {
1147 qDebug() << " - " << path.elements()[i]
1148 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1151 for (int i=0; i<path.elementCount(); ++i) {
1152 qDebug() << " ---- "
1153 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1158 Q_D(QRasterPaintEngine);
1159 QRasterPaintEngineState *s = state();
1161 const qreal *points = path.points();
1162 const QPainterPath::ElementType *types = path.elements();
1164 // There are some cases that are not supported by clip(QRect)
1165 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1166 if (s->matrix.type() <= QTransform::TxScale
1167 && ((path.shape() == QVectorPath::RectangleHint)
1168 || (isRect(points, path.elementCount())
1169 && (!types || (types[0] == QPainterPath::MoveToElement
1170 && types[1] == QPainterPath::LineToElement
1171 && types[2] == QPainterPath::LineToElement
1172 && types[3] == QPainterPath::LineToElement))))) {
1173 #ifdef QT_DEBUG_DRAW
1174 qDebug() << " --- optimizing vector clip to rect clip...";
1177 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1178 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1183 if (op == Qt::NoClip) {
1184 qrasterpaintengine_state_setNoClip(s);
1187 QClipData *base = d->baseClip.data();
1189 // Intersect with current clip when available...
1190 if (op == Qt::IntersectClip && s->clip)
1193 // We always intersect, except when there is nothing to
1194 // intersect with, in which case we simplify the operation to
1196 Qt::ClipOperation isectOp = Qt::IntersectClip;
1198 isectOp = Qt::ReplaceClip;
1200 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1201 newClip->initialize();
1202 ClipData clipData = { base, newClip, isectOp };
1203 ensureOutlineMapper();
1204 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1208 if (s->flags.has_clip_ownership)
1212 s->flags.has_clip_ownership = true;
1214 qrasterpaintengine_dirty_clip(d, s);
1222 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1224 #ifdef QT_DEBUG_DRAW
1225 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1228 QRasterPaintEngineState *s = state();
1230 if (op == Qt::NoClip) {
1231 qrasterpaintengine_state_setNoClip(s);
1233 } else if (s->matrix.type() > QTransform::TxScale) {
1234 QPaintEngineEx::clip(rect, op);
1237 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1238 QPaintEngineEx::clip(rect, op);
1244 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1246 Q_D(QRasterPaintEngine);
1247 QRect clipRect = r & d->deviceRect;
1248 QRasterPaintEngineState *s = state();
1250 if (op == Qt::ReplaceClip || s->clip == 0) {
1252 // No current clip, hence we intersect with sysclip and be
1254 QRegion clipRegion = systemClip();
1255 QClipData *clip = new QClipData(d->rasterBuffer->height());
1257 if (clipRegion.isEmpty())
1258 clip->setClipRect(clipRect);
1260 clip->setClipRegion(clipRegion & clipRect);
1262 if (s->flags.has_clip_ownership)
1266 s->clip->enabled = true;
1267 s->flags.has_clip_ownership = true;
1269 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1270 QClipData *base = s->clip;
1273 if (base->hasRectClip || base->hasRegionClip) {
1274 if (!s->flags.has_clip_ownership) {
1275 s->clip = new QClipData(d->rasterBuffer->height());
1276 s->flags.has_clip_ownership = true;
1278 if (base->hasRectClip)
1279 s->clip->setClipRect(base->clipRect & clipRect);
1281 s->clip->setClipRegion(base->clipRegion & clipRect);
1282 s->clip->enabled = true;
1290 qrasterpaintengine_dirty_clip(d, s);
1298 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1300 #ifdef QT_DEBUG_DRAW
1301 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1304 Q_D(QRasterPaintEngine);
1306 if (region.rectCount() == 1) {
1307 clip(region.boundingRect(), op);
1311 QRasterPaintEngineState *s = state();
1312 const QClipData *clip = d->clip();
1313 const QClipData *baseClip = d->baseClip.data();
1315 if (op == Qt::NoClip) {
1316 qrasterpaintengine_state_setNoClip(s);
1317 } else if (s->matrix.type() > QTransform::TxScale
1318 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1319 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1320 QPaintEngineEx::clip(region, op);
1322 const QClipData *curClip;
1325 if (op == Qt::IntersectClip)
1330 if (s->flags.has_clip_ownership) {
1334 newClip = new QClipData(d->rasterBuffer->height());
1336 s->flags.has_clip_ownership = true;
1339 QRegion r = s->matrix.map(region);
1340 if (curClip->hasRectClip)
1341 newClip->setClipRegion(r & curClip->clipRect);
1342 else if (curClip->hasRegionClip)
1343 newClip->setClipRegion(r & curClip->clipRegion);
1345 qrasterpaintengine_dirty_clip(d, s);
1352 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1354 #ifdef QT_DEBUG_DRAW
1355 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1358 if (!fillData->blend)
1361 Q_D(QRasterPaintEngine);
1363 const QRectF controlPointRect = path.controlPointRect();
1365 QRasterPaintEngineState *s = state();
1366 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1367 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1368 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1369 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1370 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1371 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1373 if (!s->flags.antialiased && !do_clip) {
1374 d->initializeRasterizer(fillData);
1375 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1379 ensureOutlineMapper();
1380 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1383 static void fillRect_normalized(const QRect &r, QSpanData *data,
1384 QRasterPaintEnginePrivate *pe)
1388 bool rectClipped = true;
1391 x1 = qMax(r.x(), data->clip->xmin);
1392 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1393 y1 = qMax(r.y(), data->clip->ymin);
1394 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1395 rectClipped = data->clip->hasRectClip;
1398 x1 = qMax(r.x(), pe->deviceRect.x());
1399 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1400 y1 = qMax(r.y(), pe->deviceRect.y());
1401 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1403 x1 = qMax(r.x(), 0);
1404 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1405 y1 = qMax(r.y(), 0);
1406 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1409 if (x2 <= x1 || y2 <= y1)
1412 const int width = x2 - x1;
1413 const int height = y2 - y1;
1415 bool isUnclipped = rectClipped
1416 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1418 if (pe && isUnclipped) {
1419 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1421 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1422 || (mode == QPainter::CompositionMode_SourceOver
1423 && qAlpha(data->solid.color) == 255)))
1425 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1431 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1433 const int nspans = 256;
1434 QT_FT_Span spans[nspans];
1436 Q_ASSERT(data->blend);
1439 int n = qMin(nspans, y2 - y);
1443 spans[i].len = width;
1445 spans[i].coverage = 255;
1449 blend(n, spans, data);
1457 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1459 #ifdef QT_DEBUG_DRAW
1460 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1462 Q_D(QRasterPaintEngine);
1464 QRasterPaintEngineState *s = state();
1468 if (s->brushData.blend) {
1469 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1470 const QRect *r = rects;
1471 const QRect *lastRect = rects + rectCount;
1473 int offset_x = int(s->matrix.dx());
1474 int offset_y = int(s->matrix.dy());
1475 while (r < lastRect) {
1476 QRect rect = r->normalized();
1477 QRect rr = rect.translated(offset_x, offset_y);
1478 fillRect_normalized(rr, &s->brushData, d);
1482 QRectVectorPath path;
1483 for (int i=0; i<rectCount; ++i) {
1485 fill(path, s->brush);
1491 if (s->penData.blend) {
1492 QRectVectorPath path;
1493 if (s->flags.fast_pen) {
1494 QCosmeticStroker stroker(s, d->deviceRect);
1495 for (int i = 0; i < rectCount; ++i) {
1497 stroker.drawPath(path);
1500 for (int i = 0; i < rectCount; ++i) {
1502 stroke(path, s->pen);
1511 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1513 #ifdef QT_DEBUG_DRAW
1514 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1516 #ifdef QT_FAST_SPANS
1517 Q_D(QRasterPaintEngine);
1519 QRasterPaintEngineState *s = state();
1522 if (s->flags.tx_noshear) {
1524 if (s->brushData.blend) {
1525 d->initializeRasterizer(&s->brushData);
1526 for (int i = 0; i < rectCount; ++i) {
1527 const QRectF &rect = rects[i].normalized();
1530 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1531 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1532 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1537 if (s->penData.blend) {
1538 QRectVectorPath path;
1539 if (s->flags.fast_pen) {
1540 QCosmeticStroker stroker(s, d->deviceRect);
1541 for (int i = 0; i < rectCount; ++i) {
1543 stroker.drawPath(path);
1546 for (int i = 0; i < rectCount; ++i) {
1548 QPaintEngineEx::stroke(path, s->lastPen);
1555 #endif // QT_FAST_SPANS
1556 QPaintEngineEx::drawRects(rects, rectCount);
1563 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1565 Q_D(QRasterPaintEngine);
1566 QRasterPaintEngineState *s = state();
1569 if (!s->penData.blend)
1572 if (s->flags.fast_pen) {
1573 QCosmeticStroker stroker(s, d->deviceRect);
1574 stroker.drawPath(path);
1575 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1576 qreal width = s->lastPen.isCosmetic()
1577 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1578 : qpen_widthf(s->lastPen) * s->txscale;
1580 qreal dashOffset = s->lastPen.dashOffset();
1582 qreal patternLength = 0;
1583 const QVector<qreal> pattern = s->lastPen.dashPattern();
1584 for (int i = 0; i < pattern.size(); ++i)
1585 patternLength += pattern.at(i);
1587 if (patternLength > 0) {
1588 int n = qFloor(dashOffset / patternLength);
1589 dashOffset -= n * patternLength;
1590 while (dashOffset >= pattern.at(dashIndex)) {
1591 dashOffset -= pattern.at(dashIndex);
1592 if (++dashIndex >= pattern.size())
1598 Q_D(QRasterPaintEngine);
1599 d->initializeRasterizer(&s->penData);
1600 int lineCount = path.elementCount() / 2;
1601 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1603 for (int i = 0; i < lineCount; ++i) {
1604 if (lines[i].p1() == lines[i].p2()) {
1605 if (s->lastPen.capStyle() != Qt::FlatCap) {
1606 QPointF p = lines[i].p1();
1607 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1608 QPointF(p.x() + width*0.5, p.y())));
1609 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1614 const QLineF line = s->matrix.map(lines[i]);
1615 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1616 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1617 width / line.length(),
1618 s->lastPen.capStyle() == Qt::SquareCap);
1620 d->rasterizeLine_dashed(line, width,
1621 &dashIndex, &dashOffset, &inDash);
1626 QPaintEngineEx::stroke(path, pen);
1629 static inline QRect toNormalizedFillRect(const QRectF &rect)
1631 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1632 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1633 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1634 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1641 return QRect(x1, y1, x2 - x1, y2 - y1);
1647 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1651 #ifdef QT_DEBUG_DRAW
1652 QRectF rf = path.controlPointRect();
1653 qDebug() << "QRasterPaintEngine::fill(): "
1654 << "size=" << path.elementCount()
1655 << ", hints=" << hex << path.hints()
1659 Q_D(QRasterPaintEngine);
1660 QRasterPaintEngineState *s = state();
1663 if (!s->brushData.blend)
1666 if (path.shape() == QVectorPath::RectangleHint) {
1667 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1668 const qreal *p = path.points();
1669 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1670 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1671 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1675 if (s->flags.tx_noshear) {
1676 d->initializeRasterizer(&s->brushData);
1677 // ### Is normalizing really necessary here?
1678 const qreal *p = path.points();
1679 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1681 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1682 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1683 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1689 // ### Optimize for non transformed ellipses and rectangles...
1690 QRectF cpRect = path.controlPointRect();
1691 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1692 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1695 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1696 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1697 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1698 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1700 // ### Falonc: implement....
1701 // if (!s->flags.antialiased && !do_clip) {
1702 // d->initializeRasterizer(&s->brushData);
1703 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1707 ensureOutlineMapper();
1708 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1711 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1713 Q_D(QRasterPaintEngine);
1714 QRasterPaintEngineState *s = state();
1716 if (!s->flags.antialiased) {
1717 uint txop = s->matrix.type();
1718 if (txop == QTransform::TxNone) {
1719 fillRect_normalized(toNormalizedFillRect(r), data, d);
1721 } else if (txop == QTransform::TxTranslate) {
1722 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1723 fillRect_normalized(rr, data, d);
1725 } else if (txop == QTransform::TxScale) {
1726 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1727 fillRect_normalized(rr, data, d);
1732 if (s->flags.tx_noshear) {
1733 d->initializeRasterizer(data);
1734 QRectF nr = r.normalized();
1735 if (!nr.isEmpty()) {
1736 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1737 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1738 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1745 ensureOutlineMapper();
1746 fillPath(path, data);
1752 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1754 #ifdef QT_DEBUG_DRAW
1755 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1757 QRasterPaintEngineState *s = state();
1760 if (!s->brushData.blend)
1763 fillRect(r, &s->brushData);
1769 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1771 #ifdef QT_DEBUG_DRAW
1772 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1774 Q_D(QRasterPaintEngine);
1775 QRasterPaintEngineState *s = state();
1777 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1778 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1779 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1782 d->solid_color_filler.clip = d->clip();
1783 d->solid_color_filler.adjustSpanMethods();
1784 fillRect(r, &d->solid_color_filler);
1787 static inline bool isAbove(const QPointF *a, const QPointF *b)
1789 return a->y() < b->y();
1792 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1797 Q_ASSERT(pointCount >= 2);
1799 QVector<const QPointF *> sorted;
1800 sorted.reserve(pointCount);
1802 upper->reserve(pointCount * 3 / 4);
1803 lower->reserve(pointCount * 3 / 4);
1805 for (int i = 0; i < pointCount; ++i)
1806 sorted << points + i;
1808 qSort(sorted.begin(), sorted.end(), isAbove);
1810 qreal splitY = sorted.at(sorted.size() / 2)->y();
1812 const QPointF *end = points + pointCount;
1813 const QPointF *last = end - 1;
1815 QVector<QPointF> *bin[2] = { upper, lower };
1817 for (const QPointF *p = points; p < end; ++p) {
1818 int side = p->y() < splitY;
1819 int lastSide = last->y() < splitY;
1821 if (side != lastSide) {
1822 if (qFuzzyCompare(p->y(), splitY)) {
1823 bin[!side]->append(*p);
1824 } else if (qFuzzyCompare(last->y(), splitY)) {
1825 bin[side]->append(*last);
1827 QPointF delta = *p - *last;
1828 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1830 bin[0]->append(intersection);
1831 bin[1]->append(intersection);
1835 bin[side]->append(*p);
1840 // give up if we couldn't reduce the point count
1841 return upper->size() < pointCount && lower->size() < pointCount;
1847 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1849 Q_D(QRasterPaintEngine);
1850 QRasterPaintEngineState *s = state();
1852 const int maxPoints = 0xffff;
1854 // max amount of points that raster engine can reliably handle
1855 if (pointCount > maxPoints) {
1856 QVector<QPointF> upper, lower;
1858 if (splitPolygon(points, pointCount, &upper, &lower)) {
1859 fillPolygon(upper.constData(), upper.size(), mode);
1860 fillPolygon(lower.constData(), lower.size(), mode);
1862 qWarning("Polygon too complex for filling.");
1867 // Compose polygon fill..,
1868 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1869 ensureOutlineMapper();
1870 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1873 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1875 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1881 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1883 Q_D(QRasterPaintEngine);
1884 QRasterPaintEngineState *s = state();
1886 #ifdef QT_DEBUG_DRAW
1887 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1888 for (int i=0; i<pointCount; ++i)
1889 qDebug() << " - " << points[i];
1891 Q_ASSERT(pointCount >= 2);
1893 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1894 QRectF r(points[0], points[2]);
1900 if (mode != PolylineMode) {
1903 if (s->brushData.blend) {
1904 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1905 fillPolygon(points, pointCount, mode);
1906 d->outlineMapper->setCoordinateRounding(false);
1910 // Do the outline...
1911 if (s->penData.blend) {
1912 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1913 if (s->flags.fast_pen) {
1914 QCosmeticStroker stroker(s, d->deviceRect);
1915 stroker.drawPath(vp);
1917 QPaintEngineEx::stroke(vp, s->lastPen);
1925 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1927 Q_D(QRasterPaintEngine);
1928 QRasterPaintEngineState *s = state();
1930 #ifdef QT_DEBUG_DRAW
1931 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1932 for (int i=0; i<pointCount; ++i)
1933 qDebug() << " - " << points[i];
1935 Q_ASSERT(pointCount >= 2);
1936 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1937 QRect r(points[0].x(),
1939 points[2].x() - points[0].x(),
1940 points[2].y() - points[0].y());
1948 if (mode != PolylineMode) {
1950 if (s->brushData.blend) {
1951 // Compose polygon fill..,
1952 ensureOutlineMapper();
1953 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1954 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1955 d->outlineMapper->moveTo(*points);
1956 const QPoint *p = points;
1957 const QPoint *ep = points + pointCount - 1;
1959 d->outlineMapper->lineTo(*(++p));
1961 d->outlineMapper->endOutline();
1964 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1966 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1967 d->outlineMapper->setCoordinateRounding(false);
1971 // Do the outline...
1972 if (s->penData.blend) {
1973 int count = pointCount * 2;
1974 QVarLengthArray<qreal> fpoints(count);
1975 for (int i=0; i<count; ++i)
1976 fpoints[i] = ((int *) points)[i];
1977 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1979 if (s->flags.fast_pen) {
1980 QCosmeticStroker stroker(s, d->deviceRect);
1981 stroker.drawPath(vp);
1983 QPaintEngineEx::stroke(vp, s->lastPen);
1991 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1993 #ifdef QT_DEBUG_DRAW
1994 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1997 QPixmapData *pd = pixmap.pixmapData();
1998 if (pd->classId() == QPixmapData::RasterClass) {
1999 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2000 if (image.depth() == 1) {
2001 Q_D(QRasterPaintEngine);
2002 QRasterPaintEngineState *s = state();
2003 if (s->matrix.type() <= QTransform::TxTranslate) {
2005 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2007 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2010 QRasterPaintEngine::drawImage(pos, image);
2013 const QImage image = pixmap.toImage();
2014 if (pixmap.depth() == 1) {
2015 Q_D(QRasterPaintEngine);
2016 QRasterPaintEngineState *s = state();
2017 if (s->matrix.type() <= QTransform::TxTranslate) {
2019 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2021 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2024 QRasterPaintEngine::drawImage(pos, image);
2032 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2034 #ifdef QT_DEBUG_DRAW
2035 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2038 QPixmapData* pd = pixmap.pixmapData();
2039 if (pd->classId() == QPixmapData::RasterClass) {
2040 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2041 if (image.depth() == 1) {
2042 Q_D(QRasterPaintEngine);
2043 QRasterPaintEngineState *s = state();
2044 if (s->matrix.type() <= QTransform::TxTranslate
2045 && r.size() == sr.size()
2046 && r.size() == pixmap.size()) {
2048 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2051 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2054 drawImage(r, image, sr);
2057 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2058 const QImage image = pd->toImage(clippedSource);
2059 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2060 if (image.depth() == 1) {
2061 Q_D(QRasterPaintEngine);
2062 QRasterPaintEngineState *s = state();
2063 if (s->matrix.type() <= QTransform::TxTranslate
2064 && r.size() == sr.size()
2065 && r.size() == pixmap.size()) {
2067 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2070 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2073 drawImage(r, image, translatedSource);
2078 // assumes that rect has positive width and height
2079 static inline const QRect toRect_normalized(const QRectF &rect)
2081 const int x = qRound(rect.x());
2082 const int y = qRound(rect.y());
2083 const int w = int(rect.width() + qreal(0.5));
2084 const int h = int(rect.height() + qreal(0.5));
2086 return QRect(x, y, w, h);
2089 static inline int fast_ceil_positive(const qreal &v)
2091 const int iv = int(v);
2098 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2100 const int xmin = int(rect.x());
2101 const int xmax = int(fast_ceil_positive(rect.right()));
2102 const int ymin = int(rect.y());
2103 const int ymax = int(fast_ceil_positive(rect.bottom()));
2104 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2110 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2112 #ifdef QT_DEBUG_DRAW
2113 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2116 Q_D(QRasterPaintEngine);
2117 QRasterPaintEngineState *s = state();
2119 if (s->matrix.type() > QTransform::TxTranslate) {
2120 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2122 QRectF(0, 0, img.width(), img.height()));
2125 const QClipData *clip = d->clip();
2126 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2128 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2129 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2132 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2134 } else if (clip->hasRectClip) {
2135 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2143 d->image_filler.clip = clip;
2144 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2145 if (!d->image_filler.blend)
2147 d->image_filler.dx = -pt.x();
2148 d->image_filler.dy = -pt.y();
2149 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2151 fillRect_normalized(rr, &d->image_filler, d);
2156 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2158 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2169 inline RotationType qRotationType(const QTransform &transform)
2171 QTransform::TransformationType type = transform.type();
2173 if (type > QTransform::TxRotate)
2176 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2177 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2180 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2181 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2184 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2185 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2191 inline bool isPixelAligned(const QRectF &rect) {
2192 return QRectF(rect.toRect()) == rect;
2199 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2200 Qt::ImageConversionFlags)
2202 #ifdef QT_DEBUG_DRAW
2203 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2209 Q_D(QRasterPaintEngine);
2210 QRasterPaintEngineState *s = state();
2211 int sr_l = qFloor(sr.left());
2212 int sr_r = qCeil(sr.right()) - 1;
2213 int sr_t = qFloor(sr.top());
2214 int sr_b = qCeil(sr.bottom()) - 1;
2216 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2217 // as fillRect will apply the aliased coordinate delta we need to
2218 // subtract it here as we don't use it for image drawing
2219 QTransform old = s->matrix;
2220 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2222 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2223 QRgb color = img.pixel(sr_l, sr_t);
2224 switch (img.format()) {
2225 case QImage::Format_ARGB32_Premultiplied:
2226 case QImage::Format_ARGB8565_Premultiplied:
2227 case QImage::Format_ARGB6666_Premultiplied:
2228 case QImage::Format_ARGB8555_Premultiplied:
2229 case QImage::Format_ARGB4444_Premultiplied:
2230 // Combine premultiplied color with the opacity set on the painter.
2231 d->solid_color_filler.solid.color =
2232 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2233 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2236 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2240 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2241 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2245 d->solid_color_filler.clip = d->clip();
2246 d->solid_color_filler.adjustSpanMethods();
2247 fillRect(r, &d->solid_color_filler);
2253 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2255 const QClipData *clip = d->clip();
2257 if (s->matrix.type() > QTransform::TxTranslate
2259 && (!clip || clip->hasRectClip)
2260 && s->intOpacity == 256
2261 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2262 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2263 && d->rasterBuffer->format == img.format()
2264 && (d->rasterBuffer->format == QImage::Format_RGB16
2265 || d->rasterBuffer->format == QImage::Format_RGB32
2266 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2267 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2269 RotationType rotationType = qRotationType(s->matrix);
2271 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2272 QRectF transformedTargetRect = s->matrix.mapRect(r);
2274 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2275 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2277 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2278 if (clippedTransformedTargetRect.isNull())
2281 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2283 QRect clippedSourceRect
2284 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2285 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2287 uint dbpl = d->rasterBuffer->bytesPerLine();
2288 uint sbpl = img.bytesPerLine();
2290 uchar *dst = d->rasterBuffer->buffer();
2291 uint bpp = img.depth() >> 3;
2293 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2294 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2296 uint cw = clippedSourceRect.width();
2297 uint ch = clippedSourceRect.height();
2299 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2306 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2308 QRectF targetBounds = s->matrix.mapRect(r);
2309 bool exceedsPrecision = targetBounds.width() > 0xffff
2310 || targetBounds.height() > 0xffff;
2312 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2313 if (s->matrix.type() > QTransform::TxScale) {
2314 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2315 if (func && (!clip || clip->hasRectClip)) {
2316 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2317 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2318 s->matrix, s->intOpacity);
2322 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2323 if (func && (!clip || clip->hasRectClip)) {
2324 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2325 img.bits(), img.bytesPerLine(),
2326 qt_mapRect_non_normalizing(r, s->matrix), sr,
2327 !clip ? d->deviceRect : clip->clipRect,
2334 QTransform copy = s->matrix;
2335 copy.translate(r.x(), r.y());
2337 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2338 copy.translate(-sr.x(), -sr.y());
2340 d->image_filler_xform.clip = clip;
2341 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2342 if (!d->image_filler_xform.blend)
2344 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2346 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2347 QRectF rr = s->matrix.mapRect(r);
2349 const int x1 = qRound(rr.x());
2350 const int y1 = qRound(rr.y());
2351 const int x2 = qRound(rr.right());
2352 const int y2 = qRound(rr.bottom());
2354 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2358 #ifdef QT_FAST_SPANS
2360 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2361 d->initializeRasterizer(&d->image_filler_xform);
2362 d->rasterizer->setAntialiased(s->flags.antialiased);
2364 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2366 const QRectF &rect = r.normalized();
2367 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2368 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2370 if (s->flags.tx_noshear)
2371 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2373 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2377 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2380 QTransform m = s->matrix;
2381 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2382 m.m21(), m.m22(), m.m23(),
2383 m.m31() - offs, m.m32() - offs, m.m33());
2384 fillPath(path, &d->image_filler_xform);
2387 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2388 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2390 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2392 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2394 } else if (clip->hasRectClip) {
2395 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2401 d->image_filler.clip = clip;
2402 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2403 if (!d->image_filler.blend)
2405 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2406 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2409 rr.translate(s->matrix.dx(), s->matrix.dy());
2411 const int x1 = qRound(rr.x());
2412 const int y1 = qRound(rr.y());
2413 const int x2 = qRound(rr.right());
2414 const int y2 = qRound(rr.bottom());
2416 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2423 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2425 #ifdef QT_DEBUG_DRAW
2426 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2428 Q_D(QRasterPaintEngine);
2429 QRasterPaintEngineState *s = state();
2433 QPixmapData *pd = pixmap.pixmapData();
2434 if (pd->classId() == QPixmapData::RasterClass) {
2435 image = static_cast<QRasterPixmapData *>(pd)->image;
2437 image = pixmap.toImage();
2440 if (image.depth() == 1)
2441 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2443 if (s->matrix.type() > QTransform::TxTranslate) {
2444 QTransform copy = s->matrix;
2445 copy.translate(r.x(), r.y());
2446 copy.translate(-sr.x(), -sr.y());
2447 d->image_filler_xform.clip = d->clip();
2448 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2449 if (!d->image_filler_xform.blend)
2451 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2453 #ifdef QT_FAST_SPANS
2455 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2456 d->initializeRasterizer(&d->image_filler_xform);
2457 d->rasterizer->setAntialiased(s->flags.antialiased);
2459 const QRectF &rect = r.normalized();
2460 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2461 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2462 if (s->flags.tx_noshear)
2463 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2465 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2471 fillPath(path, &d->image_filler_xform);
2473 d->image_filler.clip = d->clip();
2475 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2476 if (!d->image_filler.blend)
2478 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2479 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2482 rr.translate(s->matrix.dx(), s->matrix.dy());
2483 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2489 static inline bool monoVal(const uchar* s, int x)
2491 return (s[x>>3] << (x&7)) & 0x80;
2497 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2499 Q_D(QRasterPaintEngine);
2500 QRasterPaintEngineState *s = state();
2502 if (!s->penData.blend)
2505 QRasterBuffer *rb = d->rasterBuffer.data();
2507 const QRect rect(rx, ry, w, h);
2508 const QClipData *clip = d->clip();
2509 bool unclipped = false;
2511 // inlined QRect::intersects
2512 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2513 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2515 if (clip->hasRectClip) {
2516 unclipped = rx > clip->xmin
2517 && rx + w < clip->xmax
2519 && ry + h < clip->ymax;
2525 // inlined QRect::intersects
2526 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2527 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2531 // inlined QRect::contains
2532 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2533 && rect.top() >= 0 && rect.bottom() < rb->height();
2535 unclipped = contains && d->isUnclipped_normalized(rect);
2538 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2539 const uchar * scanline = static_cast<const uchar *>(src);
2541 if (s->flags.fast_text) {
2544 if (s->penData.bitmapBlit) {
2545 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2546 scanline, w, h, bpl);
2549 } else if (depth == 8) {
2550 if (s->penData.alphamapBlit) {
2551 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2552 scanline, w, h, bpl, 0);
2555 } else if (depth == 32) {
2556 // (A)RGB Alpha mask where the alpha component is not used.
2557 if (s->penData.alphaRGBBlit) {
2558 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2559 (const uint *) scanline, w, h, bpl / 4, 0);
2563 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2564 // (A)RGB Alpha mask where the alpha component is not used.
2566 int nx = qMax(0, rx);
2567 int ny = qMax(0, ry);
2569 // Move scanline pointer to compensate for moved x and y
2570 int xdiff = nx - rx;
2571 int ydiff = ny - ry;
2572 scanline += ydiff * bpl;
2573 scanline += xdiff * (depth == 32 ? 4 : 1);
2578 if (nx + w > d->rasterBuffer->width())
2579 w = d->rasterBuffer->width() - nx;
2580 if (ny + h > d->rasterBuffer->height())
2581 h = d->rasterBuffer->height() - ny;
2586 if (depth == 8 && s->penData.alphamapBlit) {
2587 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2588 scanline, w, h, bpl, clip);
2589 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2590 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2591 (const uint *) scanline, w, h, bpl / 4, clip);
2606 scanline += bpl * y0;
2610 w = qMin(w, rb->width() - qMax(0, rx));
2611 h = qMin(h, rb->height() - qMax(0, ry));
2613 if (w <= 0 || h <= 0)
2616 const int NSPANS = 256;
2617 QSpan spans[NSPANS];
2620 const int x1 = x0 + w;
2621 const int y1 = y0 + h;
2624 for (int y = y0; y < y1; ++y) {
2625 for (int x = x0; x < x1; ) {
2626 if (!monoVal(scanline, x)) {
2631 if (current == NSPANS) {
2632 blend(current, spans, &s->penData);
2635 spans[current].x = x + rx;
2636 spans[current].y = y + ry;
2637 spans[current].coverage = 255;
2640 // extend span until we find a different one.
2641 while (x < x1 && monoVal(scanline, x)) {
2645 spans[current].len = len;
2650 } else if (depth == 8) {
2651 for (int y = y0; y < y1; ++y) {
2652 for (int x = x0; x < x1; ) {
2653 // Skip those with 0 coverage
2654 if (scanline[x] == 0) {
2659 if (current == NSPANS) {
2660 blend(current, spans, &s->penData);
2663 int coverage = scanline[x];
2664 spans[current].x = x + rx;
2665 spans[current].y = y + ry;
2666 spans[current].coverage = coverage;
2670 // extend span until we find a different one.
2671 while (x < x1 && scanline[x] == coverage) {
2675 spans[current].len = len;
2680 } else { // 32-bit alpha...
2681 uint *sl = (uint *) src;
2682 for (int y = y0; y < y1; ++y) {
2683 for (int x = x0; x < x1; ) {
2684 // Skip those with 0 coverage
2685 if ((sl[x] & 0x00ffffff) == 0) {
2690 if (current == NSPANS) {
2691 blend(current, spans, &s->penData);
2694 uint rgbCoverage = sl[x];
2695 int coverage = qGreen(rgbCoverage);
2696 spans[current].x = x + rx;
2697 spans[current].y = y + ry;
2698 spans[current].coverage = coverage;
2702 // extend span until we find a different one.
2703 while (x < x1 && sl[x] == rgbCoverage) {
2707 spans[current].len = len;
2710 sl += bpl / sizeof(uint);
2713 // qDebug() << "alphaPenBlt: num spans=" << current
2714 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2715 // Call span func for current set of spans.
2717 blend(current, spans, &s->penData);
2720 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2721 const QFixedPoint *positions, QFontEngine *fontEngine)
2723 Q_D(QRasterPaintEngine);
2724 QRasterPaintEngineState *s = state();
2726 #if !defined(QT_NO_FREETYPE)
2727 if (fontEngine->type() == QFontEngine::Freetype) {
2728 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2729 QFontEngineFT::GlyphFormat neededFormat =
2730 painter()->device()->devType() == QInternal::Widget
2731 ? fe->defaultGlyphFormat()
2732 : QFontEngineFT::Format_A8;
2734 if (d_func()->mono_surface
2735 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2737 neededFormat = QFontEngineFT::Format_Mono;
2739 if (neededFormat == QFontEngineFT::Format_None)
2740 neededFormat = QFontEngineFT::Format_A8;
2742 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2743 if (s->matrix.type() >= QTransform::TxScale) {
2744 if (s->matrix.isAffine())
2745 gset = fe->loadTransformedGlyphSet(s->matrix);
2750 if (!gset || gset->outline_drawing
2751 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2754 FT_Face lockedFace = 0;
2757 switch (neededFormat) {
2758 case QFontEngineFT::Format_Mono:
2761 case QFontEngineFT::Format_A8:
2764 case QFontEngineFT::Format_A32:
2772 for (int i = 0; i < numGlyphs; i++) {
2773 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2774 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2776 if (!glyph || glyph->format != neededFormat) {
2778 lockedFace = fe->lockFace();
2779 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2782 if (!glyph || !glyph->data)
2786 switch (neededFormat) {
2787 case QFontEngineFT::Format_Mono:
2788 pitch = ((glyph->width + 31) & ~31) >> 3;
2790 case QFontEngineFT::Format_A8:
2791 pitch = (glyph->width + 3) & ~3;
2793 case QFontEngineFT::Format_A32:
2794 pitch = glyph->width * 4;
2801 alphaPenBlt(glyph->data, pitch, depth,
2802 qFloor(positions[i].x) + glyph->x,
2803 qFloor(positions[i].y) - glyph->y,
2804 glyph->width, glyph->height);
2811 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2813 QImageTextureGlyphCache *cache =
2814 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2816 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2817 fontEngine->setGlyphCache(0, cache);
2820 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2821 cache->fillInPendingGlyphs();
2823 const QImage &image = cache->image();
2824 int bpl = image.bytesPerLine();
2826 int depth = image.depth();
2830 leftShift = 2; // multiply by 4
2831 else if (depth == 1)
2832 rightShift = 3; // divide by 8
2834 int margin = cache->glyphMargin();
2835 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2836 const uchar *bits = image.bits();
2837 for (int i=0; i<numGlyphs; ++i) {
2839 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2840 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2841 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2845 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2846 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2848 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2851 // c.baseLineX, c.baseLineY,
2854 // positions[i].x.toInt(), positions[i].y.toInt());
2856 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2862 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2863 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2865 Q_D(QRasterPaintEngine);
2866 QRasterPaintEngineState *s = state();
2868 QFontEngine *fontEngine = ti.fontEngine;
2869 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2870 QPaintEngineEx::drawTextItem(p, ti);
2874 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2876 QVarLengthArray<QFixedPoint> positions;
2877 QVarLengthArray<glyph_t> glyphs;
2878 QTransform matrix = s->matrix;
2879 matrix.translate(p.x(), p.y());
2880 if (matrix.type() == QTransform::TxScale)
2881 fe->setFontScale(matrix.m11());
2882 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2884 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2886 for (int i=0; i<glyphs.size(); ++i) {
2887 TOpenFontCharMetrics tmetrics;
2888 const TUint8 *glyphBitmapBytes;
2889 TSize glyphBitmapSize;
2890 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2891 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2892 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2893 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2896 if (matrix.type() == QTransform::TxScale)
2897 fe->setFontScale(1.0);
2901 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2904 * Returns true if the rectangle is completely within the current clip
2905 * state of the paint engine.
2907 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2909 const QClipData *cl = clip();
2911 // inline contains() for performance (we know the rects are normalized)
2912 const QRect &r1 = deviceRect;
2913 return (r.left() >= r1.left() && r.right() <= r1.right()
2914 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2918 if (cl->hasRectClip) {
2919 // currently all painting functions clips to deviceRect internally
2920 if (cl->clipRect == deviceRect)
2923 // inline contains() for performance (we know the rects are normalized)
2924 const QRect &r1 = cl->clipRect;
2925 return (r.left() >= r1.left() && r.right() <= r1.right()
2926 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2928 return qt_region_strictContains(cl->clipRegion, r);
2932 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2935 Q_Q(const QRasterPaintEngine);
2936 const QRasterPaintEngineState *s = q->state();
2937 const QClipData *cl = clip();
2939 QRect r = rect.normalized();
2940 // inline contains() for performance (we know the rects are normalized)
2941 const QRect &r1 = deviceRect;
2942 return (r.left() >= r1.left() && r.right() <= r1.right()
2943 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2947 // currently all painting functions that call this function clip to deviceRect internally
2948 if (cl->hasRectClip && cl->clipRect == deviceRect)
2951 if (s->flags.antialiased)
2954 QRect r = rect.normalized();
2956 r.setX(r.x() - penWidth);
2957 r.setY(r.y() - penWidth);
2958 r.setWidth(r.width() + 2 * penWidth);
2959 r.setHeight(r.height() + 2 * penWidth);
2962 if (cl->hasRectClip) {
2963 // inline contains() for performance (we know the rects are normalized)
2964 const QRect &r1 = cl->clipRect;
2965 return (r.left() >= r1.left() && r.right() <= r1.right()
2966 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2968 return qt_region_strictContains(cl->clipRegion, r);
2972 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2975 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2979 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2980 const QSpanData *data) const
2982 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2986 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2987 const QSpanData *data) const
2989 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2993 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2994 const QSpanData *data) const
2996 Q_Q(const QRasterPaintEngine);
2997 const QRasterPaintEngineState *s = q->state();
2999 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3001 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3002 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3008 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3013 QFontEngine *fontEngine = textItem->fontEngine();
3014 if (shouldDrawCachedGlyphs(fontEngine->fontDef.pixelSize, state()->matrix)) {
3015 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3018 QPaintEngineEx::drawStaticTextItem(textItem);
3025 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3027 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3028 QRasterPaintEngineState *s = state();
3030 #ifdef QT_DEBUG_DRAW
3031 Q_D(QRasterPaintEngine);
3032 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3033 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3040 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3042 if (!supportsTransformations(ti.fontEngine)) {
3043 QVarLengthArray<QFixedPoint> positions;
3044 QVarLengthArray<glyph_t> glyphs;
3046 QTransform matrix = s->matrix;
3047 matrix.translate(p.x(), p.y());
3049 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3051 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3055 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3056 if (s->matrix.type() <= QTransform::TxTranslate
3057 || (s->matrix.type() == QTransform::TxScale
3058 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3059 drawGlyphsS60(p, ti);
3062 #else // Q_WS_WIN || Q_WS_MAC
3064 QFontEngine *fontEngine = ti.fontEngine;
3066 #if defined(Q_WS_QWS)
3067 if (fontEngine->type() == QFontEngine::Box) {
3068 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3072 if (s->matrix.type() < QTransform::TxScale
3073 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3074 || (fontEngine->type() == QFontEngine::Proxy
3075 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3077 fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
3083 if (s->matrix.type() < QTransform::TxScale) {
3085 QVarLengthArray<QFixedPoint> positions;
3086 QVarLengthArray<glyph_t> glyphs;
3087 QTransform matrix = state()->transform();
3089 qreal _x = qFloor(p.x());
3090 qreal _y = qFloor(p.y());
3091 matrix.translate(_x, _y);
3093 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3094 if (glyphs.size() == 0)
3097 for(int i = 0; i < glyphs.size(); i++) {
3098 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3099 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3100 // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here?
3101 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3102 qRound(positions[i].x + metrics.x),
3103 qRound(positions[i].y + metrics.y),
3104 img.width(), img.height());
3110 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3112 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3113 if (fontEngine->type() == QFontEngine::QPF2) {
3114 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3115 if (renderingEngine)
3116 fontEngine = renderingEngine;
3120 if (fontEngine->type() != QFontEngine::Freetype) {
3121 QPaintEngineEx::drawTextItem(p, ti);
3125 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3127 QTransform matrix = s->matrix;
3128 matrix.translate(p.x(), p.y());
3130 QVarLengthArray<QFixedPoint> positions;
3131 QVarLengthArray<glyph_t> glyphs;
3132 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3133 if (glyphs.size() == 0)
3136 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3137 QPaintEngine::drawTextItem(p, ti);
3143 QPaintEngineEx::drawTextItem(p, ti);
3149 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3151 Q_D(QRasterPaintEngine);
3152 QRasterPaintEngineState *s = state();
3155 if (!s->penData.blend)
3158 if (!s->flags.fast_pen) {
3159 QPaintEngineEx::drawPoints(points, pointCount);
3163 QCosmeticStroker stroker(s, d->deviceRect);
3164 stroker.drawPoints(points, pointCount);
3168 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3170 Q_D(QRasterPaintEngine);
3171 QRasterPaintEngineState *s = state();
3174 if (!s->penData.blend)
3177 if (!s->flags.fast_pen) {
3178 QPaintEngineEx::drawPoints(points, pointCount);
3182 QCosmeticStroker stroker(s, d->deviceRect);
3183 stroker.drawPoints(points, pointCount);
3189 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3191 #ifdef QT_DEBUG_DRAW
3192 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3194 Q_D(QRasterPaintEngine);
3195 QRasterPaintEngineState *s = state();
3198 if (!s->penData.blend)
3201 if (s->flags.fast_pen) {
3202 QCosmeticStroker stroker(s, d->deviceRect);
3203 for (int i=0; i<lineCount; ++i) {
3204 const QLine &l = lines[i];
3205 stroker.drawLine(l.p1(), l.p2());
3208 QPaintEngineEx::drawLines(lines, lineCount);
3212 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3218 Q_Q(QRasterPaintEngine);
3219 QRasterPaintEngineState *s = q->state();
3221 const QPen &pen = s->lastPen;
3222 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3223 const QVector<qreal> pattern = pen.dashPattern();
3225 qreal patternLength = 0;
3226 for (int i = 0; i < pattern.size(); ++i)
3227 patternLength += pattern.at(i);
3229 if (patternLength <= 0)
3232 qreal length = line.length();
3233 Q_ASSERT(length > 0);
3234 while (length > 0) {
3235 const bool rasterize = *inDash;
3236 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3239 if (dash >= length) {
3241 *dashOffset += dash / width;
3245 *inDash = !(*inDash);
3246 if (++*dashIndex >= pattern.size())
3253 if (rasterize && dash > 0)
3254 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3261 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3263 #ifdef QT_DEBUG_DRAW
3264 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3266 Q_D(QRasterPaintEngine);
3267 QRasterPaintEngineState *s = state();
3270 if (!s->penData.blend)
3272 if (s->flags.fast_pen) {
3273 QCosmeticStroker stroker(s, d->deviceRect);
3274 for (int i=0; i<lineCount; ++i) {
3275 QLineF line = lines[i];
3276 stroker.drawLine(line.p1(), line.p2());
3279 QPaintEngineEx::drawLines(lines, lineCount);
3287 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3289 Q_D(QRasterPaintEngine);
3290 QRasterPaintEngineState *s = state();
3293 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3294 || (qpen_style(s->lastPen) == Qt::NoPen))
3295 && !s->flags.antialiased
3296 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3298 && s->matrix.type() <= QTransform::TxScale) // no shear
3301 const QRectF r = s->matrix.mapRect(rect);
3302 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3303 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3304 const QRect brect = QRect(int(r.x()), int(r.y()),
3305 int_dim(r.x(), r.width()),
3306 int_dim(r.y(), r.height()));
3308 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3309 &s->penData, &s->brushData);
3313 QPaintEngineEx::drawEllipse(rect);
3320 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3322 Q_D(QRasterPaintEngine);
3329 CGContextRef QRasterPaintEngine::getCGContext() const
3331 Q_D(const QRasterPaintEngine);
3332 return d->cgContext;
3340 void QRasterPaintEngine::setDC(HDC hdc) {
3341 Q_D(QRasterPaintEngine);
3348 HDC QRasterPaintEngine::getDC() const
3350 Q_D(const QRasterPaintEngine);
3357 void QRasterPaintEngine::releaseDC(HDC) const
3363 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3365 const QTransform &m = state()->matrix;
3366 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3367 QFontEngine::Type fontEngineType = fontEngine->type();
3368 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3369 || (m.type() <= QTransform::TxTranslate
3370 && (fontEngineType == QFontEngine::TestFontEngine
3371 || fontEngineType == QFontEngine::Box))) {
3375 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3378 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3380 #if defined(Q_WS_MAC)
3381 // Mac font engines don't support scaling and rotation
3382 if (m.type() > QTransform::TxTranslate)
3384 if (m.type() >= QTransform::TxProject)
3388 return !shouldDrawCachedGlyphs(pixelSize, m);
3394 QPoint QRasterPaintEngine::coordinateOffset() const
3396 return QPoint(0, 0);
3400 Draws the given color \a spans with the specified \a color. The \a
3401 count parameter specifies the number of spans.
3403 The default implementation does nothing; reimplement this function
3404 to draw the given color \a spans with the specified \a color. Note
3405 that this function \e must be reimplemented if the framebuffer is
3408 \sa drawBufferSpan()
3410 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3411 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3416 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3417 "a non memory-mapped device");
3421 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3423 Draws the given \a buffer.
3425 The default implementation does nothing; reimplement this function
3426 to draw a buffer that contains more than one color. Note that this
3427 function \e must be reimplemented if the framebuffer is not
3430 The \a size parameter specifies the total size of the given \a
3431 buffer, while the \a length parameter specifies the number of
3432 pixels to draw. The buffer's position is given by (\a x, \a
3433 y). The provided \a alpha value is added to each pixel in the
3434 buffer when drawing.
3436 \sa drawColorSpans()
3438 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3439 int x, int y, int length, uint const_alpha)
3446 Q_UNUSED(const_alpha);
3447 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3448 "a non memory-mapped device");
3452 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3457 Q_D(QRasterPaintEngine);
3459 Q_ASSERT(image.depth() == 1);
3461 const int spanCount = 256;
3462 QT_FT_Span spans[spanCount];
3466 int w = image.width();
3467 int h = image.height();
3468 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3469 int ymin = qMax(qRound(pos.y()), 0);
3470 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3471 int xmin = qMax(qRound(pos.x()), 0);
3473 int x_offset = xmin - qRound(pos.x());
3475 QImage::Format format = image.format();
3476 for (int y = ymin; y < ymax; ++y) {
3477 const uchar *src = image.scanLine(y - qRound(pos.y()));
3478 if (format == QImage::Format_MonoLSB) {
3479 for (int x = 0; x < xmax - xmin; ++x) {
3480 int src_x = x + x_offset;
3481 uchar pixel = src[src_x >> 3];
3486 if (pixel & (0x1 << (src_x & 7))) {
3487 spans[n].x = xmin + x;
3489 spans[n].coverage = 255;
3491 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3495 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3498 if (n == spanCount) {
3499 fg->blend(n, spans, fg);
3505 for (int x = 0; x < xmax - xmin; ++x) {
3506 int src_x = x + x_offset;
3507 uchar pixel = src[src_x >> 3];
3512 if (pixel & (0x80 >> (x & 7))) {
3513 spans[n].x = xmin + x;
3515 spans[n].coverage = 255;
3517 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3521 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3524 if (n == spanCount) {
3525 fg->blend(n, spans, fg);
3533 fg->blend(n, spans, fg);
3539 \enum QRasterPaintEngine::ClipType
3542 \value RectClip Indicates that the currently set clip is a single rectangle.
3543 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3548 Returns the type of the clip currently set.
3550 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3552 Q_D(const QRasterPaintEngine);
3554 const QClipData *clip = d->clip();
3555 if (!clip || clip->hasRectClip)
3563 Returns the bounding rect of the currently set clip.
3565 QRect QRasterPaintEngine::clipBoundingRect() const
3567 Q_D(const QRasterPaintEngine);
3569 const QClipData *clip = d->clip();
3572 return d->deviceRect;
3574 if (clip->hasRectClip)
3575 return clip->clipRect;
3577 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3580 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3582 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3584 QVarLengthArray<short, 4096> buffer;
3586 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3587 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3588 result->initialize();
3590 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3591 const QSpan *c1_spans = c1ClipLines[y].spans;
3592 int c1_count = c1ClipLines[y].count;
3593 const QSpan *c2_spans = c2ClipLines[y].spans;
3594 int c2_count = c2ClipLines[y].count;
3596 if (c1_count == 0 && c2_count == 0)
3598 if (c1_count == 0) {
3599 result->appendSpans(c2_spans, c2_count);
3601 } else if (c2_count == 0) {
3602 result->appendSpans(c1_spans, c1_count);
3606 // we need to merge the two
3608 // find required length
3609 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3610 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3612 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3614 // Fill with old spans.
3615 for (int i = 0; i < c1_count; ++i) {
3616 const QSpan *cs = c1_spans + i;
3617 for (int j=cs->x; j<cs->x + cs->len; ++j)
3618 buffer[j] = cs->coverage;
3621 // Fill with new spans
3622 for (int i = 0; i < c2_count; ++i) {
3623 const QSpan *cs = c2_spans + i;
3624 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3625 buffer[j] += cs->coverage;
3626 if (buffer[j] > 255)
3634 // Skip to next span
3635 while (x < max && buffer[x] == 0) ++x;
3636 if (x >= max) break;
3639 int coverage = buffer[x];
3641 // Find length of span
3642 while (x < max && buffer[x] == coverage)
3645 result->appendSpan(sx, x - sx, y, coverage);
3650 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3652 Q_Q(QRasterPaintEngine);
3653 QRasterPaintEngineState *s = q->state();
3655 rasterizer->setAntialiased(s->flags.antialiased);
3657 QRect clipRect(deviceRect);
3659 // ### get from optimized rectbased QClipData
3661 const QClipData *c = clip();
3663 const QRect r(QPoint(c->xmin, c->ymin),
3664 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3665 clipRect = clipRect.intersected(r);
3666 blend = data->blend;
3668 blend = data->unclipped_blend;
3671 rasterizer->setClipRect(clipRect);
3672 rasterizer->initialize(blend, data);
3675 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3676 ProcessSpans callback,
3677 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3679 if (!callback || !outline)
3682 Q_Q(QRasterPaintEngine);
3683 QRasterPaintEngineState *s = q->state();
3685 if (!s->flags.antialiased) {
3686 initializeRasterizer(spanData);
3688 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3692 rasterizer->rasterize(outline, fillRule);
3696 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3700 int q_gray_rendered_spans(QT_FT_Raster raster);
3703 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3704 ProcessSpans callback,
3705 void *userData, QRasterBuffer *)
3707 if (!callback || !outline)
3710 Q_Q(QRasterPaintEngine);
3711 QRasterPaintEngineState *s = q->state();
3713 if (!s->flags.antialiased) {
3714 rasterizer->setAntialiased(s->flags.antialiased);
3715 rasterizer->setClipRect(deviceRect);
3716 rasterizer->initialize(callback, userData);
3718 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3722 rasterizer->rasterize(outline, fillRule);
3726 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3727 // minimize memory reallocations. However if initial size for
3728 // raster pool is changed for lower value, reallocations will
3730 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3731 int rasterPoolSize = rasterPoolInitialSize;
3732 unsigned char *rasterPoolBase;
3733 #if defined(Q_WS_WIN64)
3735 // We make use of setjmp and longjmp in qgrayraster.c which requires
3736 // 16-byte alignment, hence we hardcode this requirement here..
3737 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3739 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3740 rasterPoolBase = rasterPoolOnStack;
3742 Q_CHECK_PTR(rasterPoolBase);
3744 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3746 void *data = userData;
3748 QT_FT_BBox clip_box = { deviceRect.x(),
3750 deviceRect.x() + deviceRect.width(),
3751 deviceRect.y() + deviceRect.height() };
3753 QT_FT_Raster_Params rasterParams;
3754 rasterParams.target = 0;
3755 rasterParams.source = outline;
3756 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3757 rasterParams.gray_spans = 0;
3758 rasterParams.black_spans = 0;
3759 rasterParams.bit_test = 0;
3760 rasterParams.bit_set = 0;
3761 rasterParams.user = data;
3762 rasterParams.clip_box = clip_box;
3767 int rendered_spans = 0;
3771 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3772 rasterParams.gray_spans = callback;
3773 rasterParams.skip_spans = rendered_spans;
3774 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3776 // Out of memory, reallocate some more and try again...
3777 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3778 int new_size = rasterPoolSize * 2;
3779 if (new_size > 1024 * 1024) {
3780 qWarning("QPainter: Rasterization of primitive failed");
3784 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3786 #if defined(Q_WS_WIN64)
3787 _aligned_free(rasterPoolBase);
3789 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3790 free(rasterPoolBase);
3793 rasterPoolSize = new_size;
3795 #if defined(Q_WS_WIN64)
3796 // We make use of setjmp and longjmp in qgrayraster.c which requires
3797 // 16-byte alignment, hence we hardcode this requirement here..
3798 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3800 (unsigned char *) malloc(rasterPoolSize);
3802 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3804 qt_ft_grays_raster.raster_done(*grayRaster.data());
3805 qt_ft_grays_raster.raster_new(grayRaster.data());
3806 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3812 #if defined(Q_WS_WIN64)
3813 _aligned_free(rasterPoolBase);
3815 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3816 free(rasterPoolBase);
3820 void QRasterPaintEnginePrivate::recalculateFastImages()
3822 Q_Q(QRasterPaintEngine);
3823 QRasterPaintEngineState *s = q->state();
3825 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3826 && s->matrix.type() <= QTransform::TxShear;
3829 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3831 Q_Q(const QRasterPaintEngine);
3832 const QRasterPaintEngineState *s = q->state();
3834 return s->flags.fast_images
3835 && (mode == QPainter::CompositionMode_SourceOver
3836 || (mode == QPainter::CompositionMode_Source
3837 && !image.hasAlphaChannel()));
3840 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3842 Q_ASSERT(image.depth() == 1);
3844 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3845 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3847 QRgb fg = PREMUL(color.rgba());
3850 int height = sourceImage.height();
3851 int width = sourceImage.width();
3852 for (int y=0; y<height; ++y) {
3853 uchar *source = sourceImage.scanLine(y);
3854 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3855 if (!source || !target)
3856 QT_THROW(std::bad_alloc()); // we must have run out of memory
3857 for (int x=0; x < width; ++x)
3858 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3863 QRasterBuffer::~QRasterBuffer()
3867 void QRasterBuffer::init()
3869 compositionMode = QPainter::CompositionMode_SourceOver;
3870 monoDestinationWithClut = false;
3875 QImage::Format QRasterBuffer::prepare(QImage *image)
3877 m_buffer = (uchar *)image->bits();
3878 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3879 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3880 bytes_per_pixel = image->depth()/8;
3881 bytes_per_line = image->bytesPerLine();
3883 format = image->format();
3884 drawHelper = qDrawHelper + format;
3885 if (image->depth() == 1 && image->colorTable().size() == 2) {
3886 monoDestinationWithClut = true;
3887 destColor0 = PREMUL(image->colorTable()[0]);
3888 destColor1 = PREMUL(image->colorTable()[1]);
3894 void QRasterBuffer::resetBuffer(int val)
3896 memset(m_buffer, val, m_height*bytes_per_line);
3900 #if defined(Q_WS_QWS)
3901 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
3903 m_buffer = reinterpret_cast<uchar*>(device->memory());
3904 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
3905 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
3906 bytes_per_pixel = device->depth() / 8;
3907 bytes_per_line = device->bytesPerLine();
3908 format = device->format();
3909 #ifndef QT_NO_RASTERCALLBACKS
3911 drawHelper = qDrawHelperCallback + format;
3914 drawHelper = qDrawHelper + format;
3917 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
3921 return widget->frameGeometry().width();
3923 return widget->frameGeometry().height();
3928 return qt_paint_device_metric(widget, m);
3931 int QCustomRasterPaintDevice::bytesPerLine() const
3933 return (width() * depth() + 7) / 8;
3936 #elif defined(Q_OS_SYMBIAN)
3938 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
3942 #endif // Q_OS_SYMBIAN
3945 \class QCustomRasterPaintDevice
3950 \brief The QCustomRasterPaintDevice class is provided to activate
3951 hardware accelerated paint engines in Qt for Embedded Linux.
3953 Note that this class is only available in \l{Qt for Embedded Linux}.
3955 In \l{Qt for Embedded Linux}, painting is a pure software
3956 implementation. But starting with Qt 4.2, it is
3957 possible to add an accelerated graphics driver to take advantage
3958 of available hardware resources.
3960 Hardware acceleration is accomplished by creating a custom screen
3961 driver, accelerating the copying from memory to the screen, and
3962 implementing a custom paint engine accelerating the various
3963 painting operations. Then a custom paint device (derived from the
3964 QCustomRasterPaintDevice class) and a custom window surface
3965 (derived from QWSWindowSurface) must be implemented to make
3966 \l{Qt for Embedded Linux} aware of the accelerated driver.
3968 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
3969 documentation for details.
3971 \sa QRasterPaintEngine, QPaintDevice
3975 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
3977 Constructs a custom raster based paint device for the given
3978 top-level \a widget.
3982 \fn int QCustomRasterPaintDevice::bytesPerLine() const
3984 Returns the number of bytes per line in the framebuffer. Note that
3985 this number might be larger than the framebuffer width.
3989 \fn int QCustomRasterPaintDevice::devType() const
3994 \fn QImage::Format QCustomRasterPaintDevice::format() const
3996 Returns the format of the device's memory buffet.
3998 The default format is QImage::Format_ARGB32_Premultiplied. The
3999 only other valid format is QImage::Format_RGB16.
4003 \fn void * QCustomRasterPaintDevice::memory () const
4005 Returns a pointer to the paint device's memory buffer, or 0 if no
4010 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4015 \fn QSize QCustomRasterPaintDevice::size () const
4020 QClipData::QClipData(int height)
4022 clipSpanHeight = height;
4027 xmin = xmax = ymin = ymax = 0;
4031 hasRectClip = hasRegionClip = false;
4034 QClipData::~QClipData()
4042 void QClipData::initialize()
4048 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4050 Q_CHECK_PTR(m_clipLines);
4052 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4053 allocated = clipSpanHeight;
4054 Q_CHECK_PTR(m_spans);
4060 m_clipLines[y].spans = 0;
4061 m_clipLines[y].count = 0;
4065 const int len = clipRect.width();
4068 QSpan *span = m_spans + count;
4072 span->coverage = 255;
4075 m_clipLines[y].spans = span;
4076 m_clipLines[y].count = 1;
4080 while (y < clipSpanHeight) {
4081 m_clipLines[y].spans = 0;
4082 m_clipLines[y].count = 0;
4085 } else if (hasRegionClip) {
4087 const QVector<QRect> rects = clipRegion.rects();
4088 const int numRects = rects.size();
4091 const int maxSpans = (ymax - ymin) * numRects;
4092 if (maxSpans > allocated) {
4093 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4094 allocated = maxSpans;
4099 int firstInBand = 0;
4101 while (firstInBand < numRects) {
4102 const int currMinY = rects.at(firstInBand).y();
4103 const int currMaxY = currMinY + rects.at(firstInBand).height();
4105 while (y < currMinY) {
4106 m_clipLines[y].spans = 0;
4107 m_clipLines[y].count = 0;
4111 int lastInBand = firstInBand;
4112 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4115 while (y < currMaxY) {
4117 m_clipLines[y].spans = m_spans + count;
4118 m_clipLines[y].count = lastInBand - firstInBand + 1;
4120 for (int r = firstInBand; r <= lastInBand; ++r) {
4121 const QRect &currRect = rects.at(r);
4122 QSpan *span = m_spans + count;
4123 span->x = currRect.x();
4124 span->len = currRect.width();
4126 span->coverage = 255;
4132 firstInBand = lastInBand + 1;
4135 Q_ASSERT(count <= allocated);
4137 while (y < clipSpanHeight) {
4138 m_clipLines[y].spans = 0;
4139 m_clipLines[y].count = 0;
4145 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4150 free(m_clipLines); // same for clipLines
4156 void QClipData::fixup()
4161 ymin = ymax = xmin = xmax = 0;
4166 ymin = m_spans[0].y;
4167 ymax = m_spans[count-1].y + 1;
4171 const int firstLeft = m_spans[0].x;
4172 const int firstRight = m_spans[0].x + m_spans[0].len;
4175 for (int i = 0; i < count; ++i) {
4176 QT_FT_Span_& span = m_spans[i];
4179 if (span.y != y + 1 && y != -1)
4182 m_clipLines[y].spans = &span;
4183 m_clipLines[y].count = 1;
4185 ++m_clipLines[y].count;
4187 const int spanLeft = span.x;
4188 const int spanRight = spanLeft + span.len;
4190 if (spanLeft < xmin)
4193 if (spanRight > xmax)
4196 if (spanLeft != firstLeft || spanRight != firstRight)
4202 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4207 Convert \a rect to clip spans.
4209 void QClipData::setClipRect(const QRect &rect)
4211 if (hasRectClip && rect == clipRect)
4214 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4216 hasRegionClip = false;
4220 xmax = rect.x() + rect.width();
4221 ymin = qMin(rect.y(), clipSpanHeight);
4222 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4229 // qDebug() << xmin << xmax << ymin << ymax;
4233 Convert \a region to clip spans.
4235 void QClipData::setClipRegion(const QRegion ®ion)
4237 if (region.rectCount() == 1) {
4238 setClipRect(region.rects().at(0));
4242 hasRegionClip = true;
4243 hasRectClip = false;
4244 clipRegion = region;
4246 { // set bounding rect
4247 const QRect rect = region.boundingRect();
4249 xmax = rect.x() + rect.width();
4251 ymax = rect.y() + rect.height();
4263 spans must be sorted on y
4265 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4266 const QSpan *spans, const QSpan *end,
4267 QSpan **outSpans, int available)
4269 const_cast<QClipData *>(clip)->initialize();
4271 QSpan *out = *outSpans;
4273 const QSpan *clipSpans = clip->m_spans + *currentClip;
4274 const QSpan *clipEnd = clip->m_spans + clip->count;
4276 while (available && spans < end ) {
4277 if (clipSpans >= clipEnd) {
4281 if (clipSpans->y > spans->y) {
4285 if (spans->y != clipSpans->y) {
4286 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4287 clipSpans = clip->m_clipLines[spans->y].spans;
4292 Q_ASSERT(spans->y == clipSpans->y);
4295 int sx2 = sx1 + spans->len;
4296 int cx1 = clipSpans->x;
4297 int cx2 = cx1 + clipSpans->len;
4299 if (cx1 < sx1 && cx2 < sx1) {
4302 } else if (sx1 < cx1 && sx2 < cx1) {
4306 int x = qMax(sx1, cx1);
4307 int len = qMin(sx2, cx2) - x;
4309 out->x = qMax(sx1, cx1);
4310 out->len = qMin(sx2, cx2) - out->x;
4312 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4324 *currentClip = clipSpans - clip->m_spans;
4328 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4330 // qDebug() << "qt_span_fill_clipped" << spanCount;
4331 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4333 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4335 const int NSPANS = 256;
4336 QSpan cspans[NSPANS];
4337 int currentClip = 0;
4338 const QSpan *end = spans + spanCount;
4339 while (spans < end) {
4340 QSpan *clipped = cspans;
4341 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4342 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4343 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4345 if (clipped - cspans)
4346 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4352 Clip spans to \a{clip}-rectangle.
4353 Returns number of unclipped spans
4355 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4358 const short minx = clip.left();
4359 const short miny = clip.top();
4360 const short maxx = clip.right();
4361 const short maxy = clip.bottom();
4364 for (int i = 0; i < numSpans; ++i) {
4365 if (spans[i].y > maxy)
4367 if (spans[i].y < miny
4368 || spans[i].x > maxx
4369 || spans[i].x + spans[i].len <= minx) {
4372 if (spans[i].x < minx) {
4373 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4376 spans[n].x = spans[i].x;
4377 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4379 if (spans[n].len == 0)
4381 spans[n].y = spans[i].y;
4382 spans[n].coverage = spans[i].coverage;
4389 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4392 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4393 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4395 Q_ASSERT(fillData->clip);
4396 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4398 // hw: check if this const_cast<> is safe!!!
4399 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4400 fillData->clip->clipRect);
4402 fillData->unclipped_blend(count, spans, fillData);
4405 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4407 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4409 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4410 // for (int i = 0; i < qMin(count, 10); ++i) {
4411 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4414 switch (clipData->operation) {
4416 case Qt::IntersectClip:
4418 QClipData *newClip = clipData->newClip;
4419 newClip->initialize();
4421 int currentClip = 0;
4422 const QSpan *end = spans + count;
4423 while (spans < end) {
4424 QSpan *newspans = newClip->m_spans + newClip->count;
4425 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4426 &newspans, newClip->allocated - newClip->count);
4427 newClip->count = newspans - newClip->m_spans;
4429 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4430 newClip->allocated *= 2;
4436 case Qt::ReplaceClip:
4437 clipData->newClip->appendSpans(spans, count);
4445 QImage QRasterBuffer::bufferImage() const
4447 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4449 for (int y = 0; y < m_height; ++y) {
4450 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4452 for (int x=0; x<m_width; ++x) {
4453 uint argb = span[x];
4454 image.setPixel(x, y, argb);
4462 void QRasterBuffer::flushToARGBImage(QImage *target) const
4464 int w = qMin(m_width, target->width());
4465 int h = qMin(m_height, target->height());
4467 for (int y=0; y<h; ++y) {
4468 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4469 QRgb *dest = (QRgb *) target->scanLine(y);
4470 for (int x=0; x<w; ++x) {
4471 QRgb pixel = sourceLine[x];
4472 int alpha = qAlpha(pixel);
4476 dest[x] = (alpha << 24)
4477 | ((255*qRed(pixel)/alpha) << 16)
4478 | ((255*qGreen(pixel)/alpha) << 8)
4479 | ((255*qBlue(pixel)/alpha) << 0);
4486 class QGradientCache
4490 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4491 stops(s), opacity(op), interpolationMode(mode) {}
4492 uint buffer[GRADIENT_STOPTABLE_SIZE];
4493 QGradientStops stops;
4495 QGradient::InterpolationMode interpolationMode;
4498 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4501 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4502 quint64 hash_val = 0;
4504 QGradientStops stops = gradient.stops();
4505 for (int i = 0; i < stops.size() && i <= 2; i++)
4506 hash_val += stops[i].second.rgba();
4508 QMutexLocker lock(&mutex);
4509 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4511 if (it == cache.constEnd())
4512 return addCacheElement(hash_val, gradient, opacity);
4515 const CacheInfo &cache_info = it.value();
4516 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4517 return cache_info.buffer;
4519 } while (it != cache.constEnd() && it.key() == hash_val);
4520 // an exact match for these stops and opacity was not found, create new cache
4521 return addCacheElement(hash_val, gradient, opacity);
4525 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4527 inline int maxCacheSize() const { return 60; }
4528 inline void generateGradientColorTable(const QGradient& g,
4530 int size, int opacity) const;
4531 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4532 if (cache.size() == maxCacheSize()) {
4533 // may remove more than 1, but OK
4534 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4536 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4537 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4538 return cache.insert(hash_val, cache_entry).value().buffer;
4541 QGradientColorTableHash cache;
4545 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4547 QGradientStops stops = gradient.stops();
4548 int stopCount = stops.count();
4549 Q_ASSERT(stopCount > 0);
4551 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4553 if (stopCount == 2) {
4554 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4555 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4557 qreal first_stop = stops[0].first;
4558 qreal second_stop = stops[1].first;
4560 if (second_stop < first_stop) {
4561 qSwap(first_color, second_color);
4562 qSwap(first_stop, second_stop);
4565 if (colorInterpolation) {
4566 first_color = PREMUL(first_color);
4567 second_color = PREMUL(second_color);
4570 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4571 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4573 uint red_first = qRed(first_color) << 16;
4574 uint green_first = qGreen(first_color) << 16;
4575 uint blue_first = qBlue(first_color) << 16;
4576 uint alpha_first = qAlpha(first_color) << 16;
4578 uint red_second = qRed(second_color) << 16;
4579 uint green_second = qGreen(second_color) << 16;
4580 uint blue_second = qBlue(second_color) << 16;
4581 uint alpha_second = qAlpha(second_color) << 16;
4584 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4585 if (colorInterpolation)
4586 colorTable[i] = first_color;
4588 colorTable[i] = PREMUL(first_color);
4591 if (i < second_index) {
4592 qreal reciprocal = qreal(1) / (second_index - first_index);
4594 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4595 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4596 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4597 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4600 red_first += 1 << 15;
4601 green_first += 1 << 15;
4602 blue_first += 1 << 15;
4603 alpha_first += 1 << 15;
4605 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4606 red_first += red_delta;
4607 green_first += green_delta;
4608 blue_first += blue_delta;
4609 alpha_first += alpha_delta;
4611 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4612 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4614 if (colorInterpolation)
4615 colorTable[i] = color;
4617 colorTable[i] = PREMUL(color);
4621 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4622 if (colorInterpolation)
4623 colorTable[i] = second_color;
4625 colorTable[i] = PREMUL(second_color);
4631 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4632 if (stopCount == 1) {
4633 current_color = PREMUL(current_color);
4634 for (int i = 0; i < size; ++i)
4635 colorTable[i] = current_color;
4639 // The position where the gradient begins and ends
4640 qreal begin_pos = stops[0].first;
4641 qreal end_pos = stops[stopCount-1].first;
4643 int pos = 0; // The position in the color table.
4646 qreal incr = 1 / qreal(size); // the double increment.
4647 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4649 // Up to first point
4650 colorTable[pos++] = PREMUL(current_color);
4651 while (dpos <= begin_pos) {
4652 colorTable[pos] = colorTable[pos - 1];
4657 int current_stop = 0; // We always interpolate between current and current + 1.
4659 qreal t; // position between current left and right stops
4660 qreal t_delta; // the t increment per entry in the color table
4662 if (dpos < end_pos) {
4664 while (dpos > stops[current_stop+1].first)
4667 if (current_stop != 0)
4668 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4669 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4671 if (colorInterpolation) {
4672 current_color = PREMUL(current_color);
4673 next_color = PREMUL(next_color);
4676 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4677 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4678 t = (dpos - stops[current_stop].first) * c;
4682 Q_ASSERT(current_stop < stopCount);
4684 int dist = qRound(t);
4685 int idist = 256 - dist;
4687 if (colorInterpolation)
4688 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4690 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4695 if (dpos >= end_pos)
4701 while (dpos > stops[current_stop+skip+1].first)
4705 current_stop += skip;
4707 current_color = next_color;
4709 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4710 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4712 if (colorInterpolation) {
4714 current_color = PREMUL(current_color);
4715 next_color = PREMUL(next_color);
4718 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4719 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4720 t = (dpos - stops[current_stop].first) * c;
4727 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4728 while (pos < size - 1) {
4729 colorTable[pos] = current_color;
4733 // Make sure the last color stop is represented at the end of the table
4734 colorTable[size - 1] = current_color;
4737 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4740 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4744 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
4749 m11 = m22 = m33 = 1.;
4750 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4751 clip = pe ? pe->d_func()->clip() : 0;
4754 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4756 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4758 Qt::BrushStyle brushStyle = qbrush_style(brush);
4759 switch (brushStyle) {
4760 case Qt::SolidPattern: {
4762 QColor c = qbrush_color(brush);
4763 QRgb rgba = c.rgba();
4764 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4765 if ((solid.color & 0xff000000) == 0
4766 && compositionMode == QPainter::CompositionMode_SourceOver) {
4772 case Qt::LinearGradientPattern:
4774 type = LinearGradient;
4775 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4776 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4777 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4778 gradient.spread = g->spread();
4780 QLinearGradientData &linearData = gradient.linear;
4782 linearData.origin.x = g->start().x();
4783 linearData.origin.y = g->start().y();
4784 linearData.end.x = g->finalStop().x();
4785 linearData.end.y = g->finalStop().y();
4789 case Qt::RadialGradientPattern:
4791 type = RadialGradient;
4792 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4793 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4794 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4795 gradient.spread = g->spread();
4797 QRadialGradientData &radialData = gradient.radial;
4799 QPointF center = g->center();
4800 radialData.center.x = center.x();
4801 radialData.center.y = center.y();
4802 radialData.center.radius = g->centerRadius();
4803 QPointF focal = g->focalPoint();
4804 radialData.focal.x = focal.x();
4805 radialData.focal.y = focal.y();
4806 radialData.focal.radius = g->focalRadius();
4810 case Qt::ConicalGradientPattern:
4812 type = ConicalGradient;
4813 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4814 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4815 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4816 gradient.spread = QGradient::RepeatSpread;
4818 QConicalGradientData &conicalData = gradient.conical;
4820 QPointF center = g->center();
4821 conicalData.center.x = center.x();
4822 conicalData.center.y = center.y();
4823 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4827 case Qt::Dense1Pattern:
4828 case Qt::Dense2Pattern:
4829 case Qt::Dense3Pattern:
4830 case Qt::Dense4Pattern:
4831 case Qt::Dense5Pattern:
4832 case Qt::Dense6Pattern:
4833 case Qt::Dense7Pattern:
4834 case Qt::HorPattern:
4835 case Qt::VerPattern:
4836 case Qt::CrossPattern:
4837 case Qt::BDiagPattern:
4838 case Qt::FDiagPattern:
4839 case Qt::DiagCrossPattern:
4842 tempImage = new QImage();
4843 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4844 initTexture(tempImage, alpha, QTextureData::Tiled);
4846 case Qt::TexturePattern:
4849 tempImage = new QImage();
4851 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4852 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4854 *tempImage = brush.textureImage();
4855 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4863 adjustSpanMethods();
4866 void QSpanData::adjustSpanMethods()
4876 unclipped_blend = 0;
4879 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4880 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4881 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4882 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4883 fillRect = rasterBuffer->drawHelper->fillRect;
4885 case LinearGradient:
4886 case RadialGradient:
4887 case ConicalGradient:
4888 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4892 #ifndef QT_NO_RASTERCALLBACKS
4893 if (!rasterBuffer->buffer())
4894 unclipped_blend = qBlendTextureCallback;
4897 unclipped_blend = qBlendTexture;
4899 unclipped_blend = qBlendTexture;
4901 if (!texture.imageData)
4902 unclipped_blend = 0;
4907 if (!unclipped_blend) {
4910 blend = unclipped_blend;
4911 } else if (clip->hasRectClip) {
4912 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4914 blend = qt_span_fill_clipped;
4918 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4921 // make sure we round off correctly in qdrawhelper.cpp
4922 delta.translate(1.0 / 65536, 1.0 / 65536);
4924 QTransform inv = (delta * matrix).inverted();
4937 const bool affine = !m13 && !m23;
4938 fast_matrix = affine
4939 && m11 * m11 + m21 * m21 < 1e4
4940 && m12 * m12 + m22 * m22 < 1e4
4944 adjustSpanMethods();
4947 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4949 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4951 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4952 if (!d || d->height == 0) {
4953 texture.imageData = 0;
4960 texture.bytesPerLine = 0;
4961 texture.format = QImage::Format_Invalid;
4962 texture.colorTable = 0;
4963 texture.hasAlpha = alpha != 256;
4965 texture.imageData = d->data;
4966 texture.width = d->width;
4967 texture.height = d->height;
4969 if (sourceRect.isNull()) {
4972 texture.x2 = texture.width;
4973 texture.y2 = texture.height;
4975 texture.x1 = sourceRect.x();
4976 texture.y1 = sourceRect.y();
4977 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4978 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4981 texture.bytesPerLine = d->bytes_per_line;
4983 texture.format = d->format;
4984 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4985 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4987 texture.const_alpha = alpha;
4988 texture.type = _type;
4990 adjustSpanMethods();
4995 \a x and \a y is relative to the midpoint of \a rect.
4997 static inline void drawEllipsePoints(int x, int y, int length,
5000 ProcessSpans pen_func, ProcessSpans brush_func,
5001 QSpanData *pen_data, QSpanData *brush_data)
5006 QT_FT_Span outline[4];
5007 const int midx = rect.x() + (rect.width() + 1) / 2;
5008 const int midy = rect.y() + (rect.height() + 1) / 2;
5014 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
5015 outline[0].len = qMin(length, x - outline[0].x);
5017 outline[0].coverage = 255;
5021 outline[1].len = length;
5023 outline[1].coverage = 255;
5026 outline[2].x = outline[0].x;
5027 outline[2].len = outline[0].len;
5028 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
5029 outline[2].coverage = 255;
5033 outline[3].len = length;
5034 outline[3].y = outline[2].y;
5035 outline[3].coverage = 255;
5037 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
5041 fill[0].x = outline[0].x + outline[0].len - 1;
5042 fill[0].len = qMax(0, outline[1].x - fill[0].x);
5043 fill[0].y = outline[1].y;
5044 fill[0].coverage = 255;
5047 fill[1].x = outline[2].x + outline[2].len - 1;
5048 fill[1].len = qMax(0, outline[3].x - fill[1].x);
5049 fill[1].y = outline[3].y;
5050 fill[1].coverage = 255;
5052 int n = (fill[0].y >= fill[1].y ? 1 : 2);
5053 n = qt_intersect_spans(fill, n, clip);
5055 brush_func(n, fill, brush_data);
5058 int n = (outline[1].y >= outline[2].y ? 2 : 4);
5059 n = qt_intersect_spans(outline, n, clip);
5061 pen_func(n, outline, pen_data);
5067 Draws an ellipse using the integer point midpoint algorithm.
5069 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
5070 ProcessSpans pen_func, ProcessSpans brush_func,
5071 QSpanData *pen_data, QSpanData *brush_data)
5073 const qreal a = qreal(rect.width()) / 2;
5074 const qreal b = qreal(rect.height()) / 2;
5075 qreal d = b*b - (a*a*b) + 0.25*a*a;
5078 int y = (rect.height() + 1) / 2;
5082 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
5083 if (d < 0) { // select E
5086 } else { // select SE
5087 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
5088 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5089 pen_func, brush_func, pen_data, brush_data);
5094 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5095 pen_func, brush_func, pen_data, brush_data);
5098 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
5099 const int miny = rect.height() & 0x1;
5101 if (d < 0) { // select SE
5102 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
5104 } else { // select S
5105 d += a*a*(-2*y + 3);
5108 drawEllipsePoints(x, y, 1, rect, clip,
5109 pen_func, brush_func, pen_data, brush_data);
5114 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
5117 Draws the first \a pointCount points in the buffer \a points
5119 The default implementation converts the first \a pointCount QPoints in \a points
5120 to QPointFs and calls the floating point version of drawPoints.
5124 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
5127 Reimplement this function to draw the largest ellipse that can be
5128 contained within rectangle \a rect.
5131 #ifdef QT_DEBUG_DRAW
5132 void dumpClip(int width, int height, const QClipData *clip)
5134 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
5135 clipImg.fill(0xffff0000);
5142 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
5144 for (int i = 0; i < clip->count; ++i) {
5145 const QSpan *span = ((QClipData *) clip)->spans() + i;
5146 for (int j = 0; j < span->len; ++j)
5147 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
5148 x0 = qMin(x0, int(span->x));
5149 x1 = qMax(x1, int(span->x + span->len - 1));
5151 y0 = qMin(y0, int(span->y));
5152 y1 = qMax(y1, int(span->y));
5155 static int counter = 0;
5162 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
5163 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));