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::UniteClip && (op != Qt::IntersectClip || !s->clip
1166 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1167 if (s->matrix.type() <= QTransform::TxScale
1168 && ((path.shape() == QVectorPath::RectangleHint)
1169 || (isRect(points, path.elementCount())
1170 && (!types || (types[0] == QPainterPath::MoveToElement
1171 && types[1] == QPainterPath::LineToElement
1172 && types[2] == QPainterPath::LineToElement
1173 && types[3] == QPainterPath::LineToElement))))) {
1174 #ifdef QT_DEBUG_DRAW
1175 qDebug() << " --- optimizing vector clip to rect clip...";
1178 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1179 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1184 if (op == Qt::NoClip) {
1185 qrasterpaintengine_state_setNoClip(s);
1188 QClipData *base = d->baseClip.data();
1190 // Intersect with current clip when available...
1191 if (op == Qt::IntersectClip && s->clip)
1194 // We always intersect, except when there is nothing to
1195 // intersect with, in which case we simplify the operation to
1197 Qt::ClipOperation isectOp = Qt::IntersectClip;
1199 isectOp = Qt::ReplaceClip;
1201 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1202 newClip->initialize();
1203 ClipData clipData = { base, newClip, isectOp };
1204 ensureOutlineMapper();
1205 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1209 if (op == Qt::UniteClip) {
1211 QClipData *result = new QClipData(d->rasterBuffer->height());
1212 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1213 qt_merge_clip(current, newClip, result);
1221 if (s->flags.has_clip_ownership)
1225 s->flags.has_clip_ownership = true;
1227 qrasterpaintengine_dirty_clip(d, s);
1235 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1237 #ifdef QT_DEBUG_DRAW
1238 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1241 QRasterPaintEngineState *s = state();
1243 if (op == Qt::NoClip) {
1244 qrasterpaintengine_state_setNoClip(s);
1246 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1247 QPaintEngineEx::clip(rect, op);
1250 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1251 QPaintEngineEx::clip(rect, op);
1257 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1259 Q_D(QRasterPaintEngine);
1260 QRect clipRect = r & d->deviceRect;
1261 QRasterPaintEngineState *s = state();
1263 if (op == Qt::ReplaceClip || s->clip == 0) {
1265 // No current clip, hence we intersect with sysclip and be
1267 QRegion clipRegion = systemClip();
1268 QClipData *clip = new QClipData(d->rasterBuffer->height());
1270 if (clipRegion.isEmpty())
1271 clip->setClipRect(clipRect);
1273 clip->setClipRegion(clipRegion & clipRect);
1275 if (s->flags.has_clip_ownership)
1279 s->clip->enabled = true;
1280 s->flags.has_clip_ownership = true;
1282 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1283 QClipData *base = s->clip;
1286 if (base->hasRectClip || base->hasRegionClip) {
1287 if (!s->flags.has_clip_ownership) {
1288 s->clip = new QClipData(d->rasterBuffer->height());
1289 s->flags.has_clip_ownership = true;
1291 if (base->hasRectClip)
1292 s->clip->setClipRect(base->clipRect & clipRect);
1294 s->clip->setClipRegion(base->clipRegion & clipRect);
1295 s->clip->enabled = true;
1303 qrasterpaintengine_dirty_clip(d, s);
1311 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1313 #ifdef QT_DEBUG_DRAW
1314 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1317 Q_D(QRasterPaintEngine);
1319 if (region.rectCount() == 1) {
1320 clip(region.boundingRect(), op);
1324 QRasterPaintEngineState *s = state();
1325 const QClipData *clip = d->clip();
1326 const QClipData *baseClip = d->baseClip.data();
1328 if (op == Qt::NoClip) {
1329 qrasterpaintengine_state_setNoClip(s);
1330 } else if (s->matrix.type() > QTransform::TxScale
1331 || op == Qt::UniteClip
1332 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1333 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1334 QPaintEngineEx::clip(region, op);
1336 const QClipData *curClip;
1339 if (op == Qt::IntersectClip)
1344 if (s->flags.has_clip_ownership) {
1348 newClip = new QClipData(d->rasterBuffer->height());
1350 s->flags.has_clip_ownership = true;
1353 QRegion r = s->matrix.map(region);
1354 if (curClip->hasRectClip)
1355 newClip->setClipRegion(r & curClip->clipRect);
1356 else if (curClip->hasRegionClip)
1357 newClip->setClipRegion(r & curClip->clipRegion);
1359 qrasterpaintengine_dirty_clip(d, s);
1366 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1368 #ifdef QT_DEBUG_DRAW
1369 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1372 if (!fillData->blend)
1375 Q_D(QRasterPaintEngine);
1377 const QRectF controlPointRect = path.controlPointRect();
1379 QRasterPaintEngineState *s = state();
1380 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1381 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1382 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1383 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1384 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1385 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1387 if (!s->flags.antialiased && !do_clip) {
1388 d->initializeRasterizer(fillData);
1389 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1393 ensureOutlineMapper();
1394 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1397 static void fillRect_normalized(const QRect &r, QSpanData *data,
1398 QRasterPaintEnginePrivate *pe)
1402 bool rectClipped = true;
1405 x1 = qMax(r.x(), data->clip->xmin);
1406 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1407 y1 = qMax(r.y(), data->clip->ymin);
1408 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1409 rectClipped = data->clip->hasRectClip;
1412 x1 = qMax(r.x(), pe->deviceRect.x());
1413 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1414 y1 = qMax(r.y(), pe->deviceRect.y());
1415 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1417 x1 = qMax(r.x(), 0);
1418 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1419 y1 = qMax(r.y(), 0);
1420 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1423 if (x2 <= x1 || y2 <= y1)
1426 const int width = x2 - x1;
1427 const int height = y2 - y1;
1429 bool isUnclipped = rectClipped
1430 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1432 if (pe && isUnclipped) {
1433 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1435 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1436 || (mode == QPainter::CompositionMode_SourceOver
1437 && qAlpha(data->solid.color) == 255)))
1439 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1445 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1447 const int nspans = 256;
1448 QT_FT_Span spans[nspans];
1450 Q_ASSERT(data->blend);
1453 int n = qMin(nspans, y2 - y);
1457 spans[i].len = width;
1459 spans[i].coverage = 255;
1463 blend(n, spans, data);
1471 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1473 #ifdef QT_DEBUG_DRAW
1474 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1476 Q_D(QRasterPaintEngine);
1478 QRasterPaintEngineState *s = state();
1482 if (s->brushData.blend) {
1483 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1484 const QRect *r = rects;
1485 const QRect *lastRect = rects + rectCount;
1487 int offset_x = int(s->matrix.dx());
1488 int offset_y = int(s->matrix.dy());
1489 while (r < lastRect) {
1490 QRect rect = r->normalized();
1491 QRect rr = rect.translated(offset_x, offset_y);
1492 fillRect_normalized(rr, &s->brushData, d);
1496 QRectVectorPath path;
1497 for (int i=0; i<rectCount; ++i) {
1499 fill(path, s->brush);
1505 if (s->penData.blend) {
1506 QRectVectorPath path;
1507 if (s->flags.fast_pen) {
1508 QCosmeticStroker stroker(s, d->deviceRect);
1509 for (int i = 0; i < rectCount; ++i) {
1511 stroker.drawPath(path);
1514 for (int i = 0; i < rectCount; ++i) {
1516 stroke(path, s->pen);
1525 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1527 #ifdef QT_DEBUG_DRAW
1528 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1530 #ifdef QT_FAST_SPANS
1531 Q_D(QRasterPaintEngine);
1533 QRasterPaintEngineState *s = state();
1536 if (s->flags.tx_noshear) {
1538 if (s->brushData.blend) {
1539 d->initializeRasterizer(&s->brushData);
1540 for (int i = 0; i < rectCount; ++i) {
1541 const QRectF &rect = rects[i].normalized();
1544 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1545 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1546 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1551 if (s->penData.blend) {
1552 QRectVectorPath path;
1553 if (s->flags.fast_pen) {
1554 QCosmeticStroker stroker(s, d->deviceRect);
1555 for (int i = 0; i < rectCount; ++i) {
1557 stroker.drawPath(path);
1560 for (int i = 0; i < rectCount; ++i) {
1562 QPaintEngineEx::stroke(path, s->lastPen);
1569 #endif // QT_FAST_SPANS
1570 QPaintEngineEx::drawRects(rects, rectCount);
1577 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1579 Q_D(QRasterPaintEngine);
1580 QRasterPaintEngineState *s = state();
1583 if (!s->penData.blend)
1586 if (s->flags.fast_pen) {
1587 QCosmeticStroker stroker(s, d->deviceRect);
1588 stroker.drawPath(path);
1589 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1590 qreal width = s->lastPen.isCosmetic()
1591 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1592 : qpen_widthf(s->lastPen) * s->txscale;
1594 qreal dashOffset = s->lastPen.dashOffset();
1596 qreal patternLength = 0;
1597 const QVector<qreal> pattern = s->lastPen.dashPattern();
1598 for (int i = 0; i < pattern.size(); ++i)
1599 patternLength += pattern.at(i);
1601 if (patternLength > 0) {
1602 int n = qFloor(dashOffset / patternLength);
1603 dashOffset -= n * patternLength;
1604 while (dashOffset >= pattern.at(dashIndex)) {
1605 dashOffset -= pattern.at(dashIndex);
1606 if (++dashIndex >= pattern.size())
1612 Q_D(QRasterPaintEngine);
1613 d->initializeRasterizer(&s->penData);
1614 int lineCount = path.elementCount() / 2;
1615 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1617 for (int i = 0; i < lineCount; ++i) {
1618 if (lines[i].p1() == lines[i].p2()) {
1619 if (s->lastPen.capStyle() != Qt::FlatCap) {
1620 QPointF p = lines[i].p1();
1621 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1622 QPointF(p.x() + width*0.5, p.y())));
1623 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1628 const QLineF line = s->matrix.map(lines[i]);
1629 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1630 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1631 width / line.length(),
1632 s->lastPen.capStyle() == Qt::SquareCap);
1634 d->rasterizeLine_dashed(line, width,
1635 &dashIndex, &dashOffset, &inDash);
1640 QPaintEngineEx::stroke(path, pen);
1643 static inline QRect toNormalizedFillRect(const QRectF &rect)
1645 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1646 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1647 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1648 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1655 return QRect(x1, y1, x2 - x1, y2 - y1);
1661 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1665 #ifdef QT_DEBUG_DRAW
1666 QRectF rf = path.controlPointRect();
1667 qDebug() << "QRasterPaintEngine::fill(): "
1668 << "size=" << path.elementCount()
1669 << ", hints=" << hex << path.hints()
1673 Q_D(QRasterPaintEngine);
1674 QRasterPaintEngineState *s = state();
1677 if (!s->brushData.blend)
1680 if (path.shape() == QVectorPath::RectangleHint) {
1681 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1682 const qreal *p = path.points();
1683 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1684 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1685 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1689 if (s->flags.tx_noshear) {
1690 d->initializeRasterizer(&s->brushData);
1691 // ### Is normalizing really necessary here?
1692 const qreal *p = path.points();
1693 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1695 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1696 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1697 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1703 // ### Optimize for non transformed ellipses and rectangles...
1704 QRectF cpRect = path.controlPointRect();
1705 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1706 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1709 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1710 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1711 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1712 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1714 // ### Falonc: implement....
1715 // if (!s->flags.antialiased && !do_clip) {
1716 // d->initializeRasterizer(&s->brushData);
1717 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1721 ensureOutlineMapper();
1722 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1725 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1727 Q_D(QRasterPaintEngine);
1728 QRasterPaintEngineState *s = state();
1730 if (!s->flags.antialiased) {
1731 uint txop = s->matrix.type();
1732 if (txop == QTransform::TxNone) {
1733 fillRect_normalized(toNormalizedFillRect(r), data, d);
1735 } else if (txop == QTransform::TxTranslate) {
1736 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1737 fillRect_normalized(rr, data, d);
1739 } else if (txop == QTransform::TxScale) {
1740 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1741 fillRect_normalized(rr, data, d);
1746 if (s->flags.tx_noshear) {
1747 d->initializeRasterizer(data);
1748 QRectF nr = r.normalized();
1749 if (!nr.isEmpty()) {
1750 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1751 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1752 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1759 ensureOutlineMapper();
1760 fillPath(path, data);
1766 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1768 #ifdef QT_DEBUG_DRAW
1769 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1771 QRasterPaintEngineState *s = state();
1774 if (!s->brushData.blend)
1777 fillRect(r, &s->brushData);
1783 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1785 #ifdef QT_DEBUG_DRAW
1786 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1788 Q_D(QRasterPaintEngine);
1789 QRasterPaintEngineState *s = state();
1791 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1792 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1793 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1796 d->solid_color_filler.clip = d->clip();
1797 d->solid_color_filler.adjustSpanMethods();
1798 fillRect(r, &d->solid_color_filler);
1801 static inline bool isAbove(const QPointF *a, const QPointF *b)
1803 return a->y() < b->y();
1806 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1811 Q_ASSERT(pointCount >= 2);
1813 QVector<const QPointF *> sorted;
1814 sorted.reserve(pointCount);
1816 upper->reserve(pointCount * 3 / 4);
1817 lower->reserve(pointCount * 3 / 4);
1819 for (int i = 0; i < pointCount; ++i)
1820 sorted << points + i;
1822 qSort(sorted.begin(), sorted.end(), isAbove);
1824 qreal splitY = sorted.at(sorted.size() / 2)->y();
1826 const QPointF *end = points + pointCount;
1827 const QPointF *last = end - 1;
1829 QVector<QPointF> *bin[2] = { upper, lower };
1831 for (const QPointF *p = points; p < end; ++p) {
1832 int side = p->y() < splitY;
1833 int lastSide = last->y() < splitY;
1835 if (side != lastSide) {
1836 if (qFuzzyCompare(p->y(), splitY)) {
1837 bin[!side]->append(*p);
1838 } else if (qFuzzyCompare(last->y(), splitY)) {
1839 bin[side]->append(*last);
1841 QPointF delta = *p - *last;
1842 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1844 bin[0]->append(intersection);
1845 bin[1]->append(intersection);
1849 bin[side]->append(*p);
1854 // give up if we couldn't reduce the point count
1855 return upper->size() < pointCount && lower->size() < pointCount;
1861 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1863 Q_D(QRasterPaintEngine);
1864 QRasterPaintEngineState *s = state();
1866 const int maxPoints = 0xffff;
1868 // max amount of points that raster engine can reliably handle
1869 if (pointCount > maxPoints) {
1870 QVector<QPointF> upper, lower;
1872 if (splitPolygon(points, pointCount, &upper, &lower)) {
1873 fillPolygon(upper.constData(), upper.size(), mode);
1874 fillPolygon(lower.constData(), lower.size(), mode);
1876 qWarning("Polygon too complex for filling.");
1881 // Compose polygon fill..,
1882 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1883 ensureOutlineMapper();
1884 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1887 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1889 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1895 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1897 Q_D(QRasterPaintEngine);
1898 QRasterPaintEngineState *s = state();
1900 #ifdef QT_DEBUG_DRAW
1901 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1902 for (int i=0; i<pointCount; ++i)
1903 qDebug() << " - " << points[i];
1905 Q_ASSERT(pointCount >= 2);
1907 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1908 QRectF r(points[0], points[2]);
1914 if (mode != PolylineMode) {
1917 if (s->brushData.blend) {
1918 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1919 fillPolygon(points, pointCount, mode);
1920 d->outlineMapper->setCoordinateRounding(false);
1924 // Do the outline...
1925 if (s->penData.blend) {
1926 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1927 if (s->flags.fast_pen) {
1928 QCosmeticStroker stroker(s, d->deviceRect);
1929 stroker.drawPath(vp);
1931 QPaintEngineEx::stroke(vp, s->lastPen);
1939 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1941 Q_D(QRasterPaintEngine);
1942 QRasterPaintEngineState *s = state();
1944 #ifdef QT_DEBUG_DRAW
1945 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1946 for (int i=0; i<pointCount; ++i)
1947 qDebug() << " - " << points[i];
1949 Q_ASSERT(pointCount >= 2);
1950 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1951 QRect r(points[0].x(),
1953 points[2].x() - points[0].x(),
1954 points[2].y() - points[0].y());
1962 if (mode != PolylineMode) {
1964 if (s->brushData.blend) {
1965 // Compose polygon fill..,
1966 ensureOutlineMapper();
1967 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1968 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1969 d->outlineMapper->moveTo(*points);
1970 const QPoint *p = points;
1971 const QPoint *ep = points + pointCount - 1;
1973 d->outlineMapper->lineTo(*(++p));
1975 d->outlineMapper->endOutline();
1978 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1980 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1981 d->outlineMapper->setCoordinateRounding(false);
1985 // Do the outline...
1986 if (s->penData.blend) {
1987 int count = pointCount * 2;
1988 QVarLengthArray<qreal> fpoints(count);
1989 for (int i=0; i<count; ++i)
1990 fpoints[i] = ((int *) points)[i];
1991 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1993 if (s->flags.fast_pen) {
1994 QCosmeticStroker stroker(s, d->deviceRect);
1995 stroker.drawPath(vp);
1997 QPaintEngineEx::stroke(vp, s->lastPen);
2005 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2007 #ifdef QT_DEBUG_DRAW
2008 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2011 QPixmapData *pd = pixmap.pixmapData();
2012 if (pd->classId() == QPixmapData::RasterClass) {
2013 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2014 if (image.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);
2027 const QImage image = pixmap.toImage();
2028 if (pixmap.depth() == 1) {
2029 Q_D(QRasterPaintEngine);
2030 QRasterPaintEngineState *s = state();
2031 if (s->matrix.type() <= QTransform::TxTranslate) {
2033 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2035 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2038 QRasterPaintEngine::drawImage(pos, image);
2046 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2048 #ifdef QT_DEBUG_DRAW
2049 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2052 QPixmapData* pd = pixmap.pixmapData();
2053 if (pd->classId() == QPixmapData::RasterClass) {
2054 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2055 if (image.depth() == 1) {
2056 Q_D(QRasterPaintEngine);
2057 QRasterPaintEngineState *s = state();
2058 if (s->matrix.type() <= QTransform::TxTranslate
2059 && r.size() == sr.size()
2060 && r.size() == pixmap.size()) {
2062 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2065 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2068 drawImage(r, image, sr);
2071 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2072 const QImage image = pd->toImage(clippedSource);
2073 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2074 if (image.depth() == 1) {
2075 Q_D(QRasterPaintEngine);
2076 QRasterPaintEngineState *s = state();
2077 if (s->matrix.type() <= QTransform::TxTranslate
2078 && r.size() == sr.size()
2079 && r.size() == pixmap.size()) {
2081 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2084 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2087 drawImage(r, image, translatedSource);
2092 // assumes that rect has positive width and height
2093 static inline const QRect toRect_normalized(const QRectF &rect)
2095 const int x = qRound(rect.x());
2096 const int y = qRound(rect.y());
2097 const int w = int(rect.width() + qreal(0.5));
2098 const int h = int(rect.height() + qreal(0.5));
2100 return QRect(x, y, w, h);
2103 static inline int fast_ceil_positive(const qreal &v)
2105 const int iv = int(v);
2112 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2114 const int xmin = int(rect.x());
2115 const int xmax = int(fast_ceil_positive(rect.right()));
2116 const int ymin = int(rect.y());
2117 const int ymax = int(fast_ceil_positive(rect.bottom()));
2118 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2124 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2126 #ifdef QT_DEBUG_DRAW
2127 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2130 Q_D(QRasterPaintEngine);
2131 QRasterPaintEngineState *s = state();
2133 if (s->matrix.type() > QTransform::TxTranslate) {
2134 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2136 QRectF(0, 0, img.width(), img.height()));
2139 const QClipData *clip = d->clip();
2140 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2142 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2143 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2146 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2148 } else if (clip->hasRectClip) {
2149 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2157 d->image_filler.clip = clip;
2158 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2159 if (!d->image_filler.blend)
2161 d->image_filler.dx = -pt.x();
2162 d->image_filler.dy = -pt.y();
2163 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2165 fillRect_normalized(rr, &d->image_filler, d);
2170 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2172 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2183 inline RotationType qRotationType(const QTransform &transform)
2185 QTransform::TransformationType type = transform.type();
2187 if (type > QTransform::TxRotate)
2190 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2191 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2194 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2195 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2198 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2199 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2205 inline bool isPixelAligned(const QRectF &rect) {
2206 return QRectF(rect.toRect()) == rect;
2213 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2214 Qt::ImageConversionFlags)
2216 #ifdef QT_DEBUG_DRAW
2217 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2223 Q_D(QRasterPaintEngine);
2224 QRasterPaintEngineState *s = state();
2225 int sr_l = qFloor(sr.left());
2226 int sr_r = qCeil(sr.right()) - 1;
2227 int sr_t = qFloor(sr.top());
2228 int sr_b = qCeil(sr.bottom()) - 1;
2230 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2231 // as fillRect will apply the aliased coordinate delta we need to
2232 // subtract it here as we don't use it for image drawing
2233 QTransform old = s->matrix;
2234 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2236 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2237 QRgb color = img.pixel(sr_l, sr_t);
2238 switch (img.format()) {
2239 case QImage::Format_ARGB32_Premultiplied:
2240 case QImage::Format_ARGB8565_Premultiplied:
2241 case QImage::Format_ARGB6666_Premultiplied:
2242 case QImage::Format_ARGB8555_Premultiplied:
2243 case QImage::Format_ARGB4444_Premultiplied:
2244 // Combine premultiplied color with the opacity set on the painter.
2245 d->solid_color_filler.solid.color =
2246 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2247 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2250 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2254 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2255 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2259 d->solid_color_filler.clip = d->clip();
2260 d->solid_color_filler.adjustSpanMethods();
2261 fillRect(r, &d->solid_color_filler);
2267 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2269 const QClipData *clip = d->clip();
2271 if (s->matrix.type() > QTransform::TxTranslate
2273 && (!clip || clip->hasRectClip)
2274 && s->intOpacity == 256
2275 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2276 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2277 && d->rasterBuffer->format == img.format()
2278 && (d->rasterBuffer->format == QImage::Format_RGB16
2279 || d->rasterBuffer->format == QImage::Format_RGB32
2280 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2281 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2283 RotationType rotationType = qRotationType(s->matrix);
2285 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2286 QRectF transformedTargetRect = s->matrix.mapRect(r);
2288 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2289 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2291 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2292 if (clippedTransformedTargetRect.isNull())
2295 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2297 QRect clippedSourceRect
2298 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2299 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2301 uint dbpl = d->rasterBuffer->bytesPerLine();
2302 uint sbpl = img.bytesPerLine();
2304 uchar *dst = d->rasterBuffer->buffer();
2305 uint bpp = img.depth() >> 3;
2307 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2308 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2310 uint cw = clippedSourceRect.width();
2311 uint ch = clippedSourceRect.height();
2313 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2320 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2322 QRectF targetBounds = s->matrix.mapRect(r);
2323 bool exceedsPrecision = targetBounds.width() > 0xffff
2324 || targetBounds.height() > 0xffff;
2326 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2327 if (s->matrix.type() > QTransform::TxScale) {
2328 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2329 if (func && (!clip || clip->hasRectClip)) {
2330 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2331 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2332 s->matrix, s->intOpacity);
2336 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2337 if (func && (!clip || clip->hasRectClip)) {
2338 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2339 img.bits(), img.bytesPerLine(),
2340 qt_mapRect_non_normalizing(r, s->matrix), sr,
2341 !clip ? d->deviceRect : clip->clipRect,
2348 QTransform copy = s->matrix;
2349 copy.translate(r.x(), r.y());
2351 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2352 copy.translate(-sr.x(), -sr.y());
2354 d->image_filler_xform.clip = clip;
2355 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2356 if (!d->image_filler_xform.blend)
2358 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2360 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2361 QRectF rr = s->matrix.mapRect(r);
2363 const int x1 = qRound(rr.x());
2364 const int y1 = qRound(rr.y());
2365 const int x2 = qRound(rr.right());
2366 const int y2 = qRound(rr.bottom());
2368 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2372 #ifdef QT_FAST_SPANS
2374 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2375 d->initializeRasterizer(&d->image_filler_xform);
2376 d->rasterizer->setAntialiased(s->flags.antialiased);
2378 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2380 const QRectF &rect = r.normalized();
2381 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2382 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2384 if (s->flags.tx_noshear)
2385 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2387 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2391 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2394 QTransform m = s->matrix;
2395 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2396 m.m21(), m.m22(), m.m23(),
2397 m.m31() - offs, m.m32() - offs, m.m33());
2398 fillPath(path, &d->image_filler_xform);
2401 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2402 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2404 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2406 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2408 } else if (clip->hasRectClip) {
2409 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2415 d->image_filler.clip = clip;
2416 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2417 if (!d->image_filler.blend)
2419 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2420 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2423 rr.translate(s->matrix.dx(), s->matrix.dy());
2425 const int x1 = qRound(rr.x());
2426 const int y1 = qRound(rr.y());
2427 const int x2 = qRound(rr.right());
2428 const int y2 = qRound(rr.bottom());
2430 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2437 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2439 #ifdef QT_DEBUG_DRAW
2440 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2442 Q_D(QRasterPaintEngine);
2443 QRasterPaintEngineState *s = state();
2447 QPixmapData *pd = pixmap.pixmapData();
2448 if (pd->classId() == QPixmapData::RasterClass) {
2449 image = static_cast<QRasterPixmapData *>(pd)->image;
2451 image = pixmap.toImage();
2454 if (image.depth() == 1)
2455 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2457 if (s->matrix.type() > QTransform::TxTranslate) {
2458 QTransform copy = s->matrix;
2459 copy.translate(r.x(), r.y());
2460 copy.translate(-sr.x(), -sr.y());
2461 d->image_filler_xform.clip = d->clip();
2462 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2463 if (!d->image_filler_xform.blend)
2465 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2467 #ifdef QT_FAST_SPANS
2469 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2470 d->initializeRasterizer(&d->image_filler_xform);
2471 d->rasterizer->setAntialiased(s->flags.antialiased);
2473 const QRectF &rect = r.normalized();
2474 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2475 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2476 if (s->flags.tx_noshear)
2477 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2479 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2485 fillPath(path, &d->image_filler_xform);
2487 d->image_filler.clip = d->clip();
2489 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2490 if (!d->image_filler.blend)
2492 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2493 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2496 rr.translate(s->matrix.dx(), s->matrix.dy());
2497 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2503 static inline bool monoVal(const uchar* s, int x)
2505 return (s[x>>3] << (x&7)) & 0x80;
2511 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2513 Q_D(QRasterPaintEngine);
2514 QRasterPaintEngineState *s = state();
2516 if (!s->penData.blend)
2519 QRasterBuffer *rb = d->rasterBuffer.data();
2521 const QRect rect(rx, ry, w, h);
2522 const QClipData *clip = d->clip();
2523 bool unclipped = false;
2525 // inlined QRect::intersects
2526 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2527 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2529 if (clip->hasRectClip) {
2530 unclipped = rx > clip->xmin
2531 && rx + w < clip->xmax
2533 && ry + h < clip->ymax;
2539 // inlined QRect::intersects
2540 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2541 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2545 // inlined QRect::contains
2546 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2547 && rect.top() >= 0 && rect.bottom() < rb->height();
2549 unclipped = contains && d->isUnclipped_normalized(rect);
2552 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2553 const uchar * scanline = static_cast<const uchar *>(src);
2555 if (s->flags.fast_text) {
2558 if (s->penData.bitmapBlit) {
2559 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2560 scanline, w, h, bpl);
2563 } else if (depth == 8) {
2564 if (s->penData.alphamapBlit) {
2565 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2566 scanline, w, h, bpl, 0);
2569 } else if (depth == 32) {
2570 // (A)RGB Alpha mask where the alpha component is not used.
2571 if (s->penData.alphaRGBBlit) {
2572 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2573 (const uint *) scanline, w, h, bpl / 4, 0);
2577 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2578 // (A)RGB Alpha mask where the alpha component is not used.
2580 int nx = qMax(0, rx);
2581 int ny = qMax(0, ry);
2583 // Move scanline pointer to compensate for moved x and y
2584 int xdiff = nx - rx;
2585 int ydiff = ny - ry;
2586 scanline += ydiff * bpl;
2587 scanline += xdiff * (depth == 32 ? 4 : 1);
2592 if (nx + w > d->rasterBuffer->width())
2593 w = d->rasterBuffer->width() - nx;
2594 if (ny + h > d->rasterBuffer->height())
2595 h = d->rasterBuffer->height() - ny;
2600 if (depth == 8 && s->penData.alphamapBlit) {
2601 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2602 scanline, w, h, bpl, clip);
2603 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2604 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2605 (const uint *) scanline, w, h, bpl / 4, clip);
2620 scanline += bpl * y0;
2624 w = qMin(w, rb->width() - qMax(0, rx));
2625 h = qMin(h, rb->height() - qMax(0, ry));
2627 if (w <= 0 || h <= 0)
2630 const int NSPANS = 256;
2631 QSpan spans[NSPANS];
2634 const int x1 = x0 + w;
2635 const int y1 = y0 + h;
2638 for (int y = y0; y < y1; ++y) {
2639 for (int x = x0; x < x1; ) {
2640 if (!monoVal(scanline, x)) {
2645 if (current == NSPANS) {
2646 blend(current, spans, &s->penData);
2649 spans[current].x = x + rx;
2650 spans[current].y = y + ry;
2651 spans[current].coverage = 255;
2654 // extend span until we find a different one.
2655 while (x < x1 && monoVal(scanline, x)) {
2659 spans[current].len = len;
2664 } else if (depth == 8) {
2665 for (int y = y0; y < y1; ++y) {
2666 for (int x = x0; x < x1; ) {
2667 // Skip those with 0 coverage
2668 if (scanline[x] == 0) {
2673 if (current == NSPANS) {
2674 blend(current, spans, &s->penData);
2677 int coverage = scanline[x];
2678 spans[current].x = x + rx;
2679 spans[current].y = y + ry;
2680 spans[current].coverage = coverage;
2684 // extend span until we find a different one.
2685 while (x < x1 && scanline[x] == coverage) {
2689 spans[current].len = len;
2694 } else { // 32-bit alpha...
2695 uint *sl = (uint *) src;
2696 for (int y = y0; y < y1; ++y) {
2697 for (int x = x0; x < x1; ) {
2698 // Skip those with 0 coverage
2699 if ((sl[x] & 0x00ffffff) == 0) {
2704 if (current == NSPANS) {
2705 blend(current, spans, &s->penData);
2708 uint rgbCoverage = sl[x];
2709 int coverage = qGreen(rgbCoverage);
2710 spans[current].x = x + rx;
2711 spans[current].y = y + ry;
2712 spans[current].coverage = coverage;
2716 // extend span until we find a different one.
2717 while (x < x1 && sl[x] == rgbCoverage) {
2721 spans[current].len = len;
2724 sl += bpl / sizeof(uint);
2727 // qDebug() << "alphaPenBlt: num spans=" << current
2728 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2729 // Call span func for current set of spans.
2731 blend(current, spans, &s->penData);
2734 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2735 const QFixedPoint *positions, QFontEngine *fontEngine)
2737 Q_D(QRasterPaintEngine);
2738 QRasterPaintEngineState *s = state();
2740 #if !defined(QT_NO_FREETYPE)
2741 if (fontEngine->type() == QFontEngine::Freetype) {
2742 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2743 QFontEngineFT::GlyphFormat neededFormat =
2744 painter()->device()->devType() == QInternal::Widget
2745 ? fe->defaultGlyphFormat()
2746 : QFontEngineFT::Format_A8;
2748 if (d_func()->mono_surface
2749 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2751 neededFormat = QFontEngineFT::Format_Mono;
2753 if (neededFormat == QFontEngineFT::Format_None)
2754 neededFormat = QFontEngineFT::Format_A8;
2756 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2757 if (s->matrix.type() >= QTransform::TxScale) {
2758 if (s->matrix.isAffine())
2759 gset = fe->loadTransformedGlyphSet(s->matrix);
2764 if (!gset || gset->outline_drawing
2765 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2768 FT_Face lockedFace = 0;
2771 switch (neededFormat) {
2772 case QFontEngineFT::Format_Mono:
2775 case QFontEngineFT::Format_A8:
2778 case QFontEngineFT::Format_A32:
2786 for (int i = 0; i < numGlyphs; i++) {
2787 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2788 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2790 if (!glyph || glyph->format != neededFormat) {
2792 lockedFace = fe->lockFace();
2793 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2796 if (!glyph || !glyph->data)
2800 switch (neededFormat) {
2801 case QFontEngineFT::Format_Mono:
2802 pitch = ((glyph->width + 31) & ~31) >> 3;
2804 case QFontEngineFT::Format_A8:
2805 pitch = (glyph->width + 3) & ~3;
2807 case QFontEngineFT::Format_A32:
2808 pitch = glyph->width * 4;
2815 alphaPenBlt(glyph->data, pitch, depth,
2816 qFloor(positions[i].x) + glyph->x,
2817 qFloor(positions[i].y) - glyph->y,
2818 glyph->width, glyph->height);
2825 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2827 QImageTextureGlyphCache *cache =
2828 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2830 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2831 fontEngine->setGlyphCache(0, cache);
2834 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2835 cache->fillInPendingGlyphs();
2837 const QImage &image = cache->image();
2838 int bpl = image.bytesPerLine();
2840 int depth = image.depth();
2844 leftShift = 2; // multiply by 4
2845 else if (depth == 1)
2846 rightShift = 3; // divide by 8
2848 int margin = cache->glyphMargin();
2849 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2850 const uchar *bits = image.bits();
2851 for (int i=0; i<numGlyphs; ++i) {
2853 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2854 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2855 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2859 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2860 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2862 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2865 // c.baseLineX, c.baseLineY,
2868 // positions[i].x.toInt(), positions[i].y.toInt());
2870 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2876 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2877 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2879 Q_D(QRasterPaintEngine);
2880 QRasterPaintEngineState *s = state();
2882 QFontEngine *fontEngine = ti.fontEngine;
2883 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2884 QPaintEngineEx::drawTextItem(p, ti);
2888 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2890 QVarLengthArray<QFixedPoint> positions;
2891 QVarLengthArray<glyph_t> glyphs;
2892 QTransform matrix = s->matrix;
2893 matrix.translate(p.x(), p.y());
2894 if (matrix.type() == QTransform::TxScale)
2895 fe->setFontScale(matrix.m11());
2896 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2898 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2900 for (int i=0; i<glyphs.size(); ++i) {
2901 TOpenFontCharMetrics tmetrics;
2902 const TUint8 *glyphBitmapBytes;
2903 TSize glyphBitmapSize;
2904 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2905 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2906 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2907 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2910 if (matrix.type() == QTransform::TxScale)
2911 fe->setFontScale(1.0);
2915 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2918 * Returns true if the rectangle is completely within the current clip
2919 * state of the paint engine.
2921 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2923 const QClipData *cl = clip();
2925 // inline contains() for performance (we know the rects are normalized)
2926 const QRect &r1 = deviceRect;
2927 return (r.left() >= r1.left() && r.right() <= r1.right()
2928 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2932 if (cl->hasRectClip) {
2933 // currently all painting functions clips to deviceRect internally
2934 if (cl->clipRect == deviceRect)
2937 // inline contains() for performance (we know the rects are normalized)
2938 const QRect &r1 = cl->clipRect;
2939 return (r.left() >= r1.left() && r.right() <= r1.right()
2940 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2942 return qt_region_strictContains(cl->clipRegion, r);
2946 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2949 Q_Q(const QRasterPaintEngine);
2950 const QRasterPaintEngineState *s = q->state();
2951 const QClipData *cl = clip();
2953 QRect r = rect.normalized();
2954 // inline contains() for performance (we know the rects are normalized)
2955 const QRect &r1 = deviceRect;
2956 return (r.left() >= r1.left() && r.right() <= r1.right()
2957 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2961 // currently all painting functions that call this function clip to deviceRect internally
2962 if (cl->hasRectClip && cl->clipRect == deviceRect)
2965 if (s->flags.antialiased)
2968 QRect r = rect.normalized();
2970 r.setX(r.x() - penWidth);
2971 r.setY(r.y() - penWidth);
2972 r.setWidth(r.width() + 2 * penWidth);
2973 r.setHeight(r.height() + 2 * penWidth);
2976 if (cl->hasRectClip) {
2977 // inline contains() for performance (we know the rects are normalized)
2978 const QRect &r1 = cl->clipRect;
2979 return (r.left() >= r1.left() && r.right() <= r1.right()
2980 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2982 return qt_region_strictContains(cl->clipRegion, r);
2986 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2989 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2993 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2994 const QSpanData *data) const
2996 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3000 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3001 const QSpanData *data) const
3003 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3007 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3008 const QSpanData *data) const
3010 Q_Q(const QRasterPaintEngine);
3011 const QRasterPaintEngineState *s = q->state();
3013 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3015 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3016 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3022 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3027 QFontEngine *fontEngine = textItem->fontEngine();
3028 if (!supportsTransformations(fontEngine)) {
3029 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3032 QPaintEngineEx::drawStaticTextItem(textItem);
3039 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3041 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3042 QRasterPaintEngineState *s = state();
3044 #ifdef QT_DEBUG_DRAW
3045 Q_D(QRasterPaintEngine);
3046 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3047 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3054 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3056 if (!supportsTransformations(ti.fontEngine)) {
3057 QVarLengthArray<QFixedPoint> positions;
3058 QVarLengthArray<glyph_t> glyphs;
3060 QTransform matrix = s->matrix;
3061 matrix.translate(p.x(), p.y());
3063 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3065 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3069 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3070 if (s->matrix.type() <= QTransform::TxTranslate
3071 || (s->matrix.type() == QTransform::TxScale
3072 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3073 drawGlyphsS60(p, ti);
3076 #else // Q_WS_WIN || Q_WS_MAC
3078 QFontEngine *fontEngine = ti.fontEngine;
3080 #if defined(Q_WS_QWS)
3081 if (fontEngine->type() == QFontEngine::Box) {
3082 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3086 if (s->matrix.type() < QTransform::TxScale
3087 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3088 || (fontEngine->type() == QFontEngine::Proxy
3089 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3091 fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
3097 if (s->matrix.type() < QTransform::TxScale) {
3099 QVarLengthArray<QFixedPoint> positions;
3100 QVarLengthArray<glyph_t> glyphs;
3101 QTransform matrix = state()->transform();
3103 qreal _x = qFloor(p.x());
3104 qreal _y = qFloor(p.y());
3105 matrix.translate(_x, _y);
3107 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3108 if (glyphs.size() == 0)
3111 for(int i = 0; i < glyphs.size(); i++) {
3112 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3113 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3114 // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here?
3115 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3116 qRound(positions[i].x + metrics.x),
3117 qRound(positions[i].y + metrics.y),
3118 img.width(), img.height());
3124 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3126 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3127 if (fontEngine->type() == QFontEngine::QPF2) {
3128 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3129 if (renderingEngine)
3130 fontEngine = renderingEngine;
3134 if (fontEngine->type() != QFontEngine::Freetype) {
3135 QPaintEngineEx::drawTextItem(p, ti);
3139 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3141 QTransform matrix = s->matrix;
3142 matrix.translate(p.x(), p.y());
3144 QVarLengthArray<QFixedPoint> positions;
3145 QVarLengthArray<glyph_t> glyphs;
3146 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3147 if (glyphs.size() == 0)
3150 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3151 QPaintEngine::drawTextItem(p, ti);
3157 QPaintEngineEx::drawTextItem(p, ti);
3163 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3165 Q_D(QRasterPaintEngine);
3166 QRasterPaintEngineState *s = state();
3169 if (!s->penData.blend)
3172 if (!s->flags.fast_pen) {
3173 QPaintEngineEx::drawPoints(points, pointCount);
3177 QCosmeticStroker stroker(s, d->deviceRect);
3178 stroker.drawPoints(points, pointCount);
3182 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3184 Q_D(QRasterPaintEngine);
3185 QRasterPaintEngineState *s = state();
3188 if (!s->penData.blend)
3191 if (!s->flags.fast_pen) {
3192 QPaintEngineEx::drawPoints(points, pointCount);
3196 QCosmeticStroker stroker(s, d->deviceRect);
3197 stroker.drawPoints(points, pointCount);
3203 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3205 #ifdef QT_DEBUG_DRAW
3206 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3208 Q_D(QRasterPaintEngine);
3209 QRasterPaintEngineState *s = state();
3212 if (!s->penData.blend)
3215 if (s->flags.fast_pen) {
3216 QCosmeticStroker stroker(s, d->deviceRect);
3217 for (int i=0; i<lineCount; ++i) {
3218 const QLine &l = lines[i];
3219 stroker.drawLine(l.p1(), l.p2());
3222 QPaintEngineEx::drawLines(lines, lineCount);
3226 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3232 Q_Q(QRasterPaintEngine);
3233 QRasterPaintEngineState *s = q->state();
3235 const QPen &pen = s->lastPen;
3236 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3237 const QVector<qreal> pattern = pen.dashPattern();
3239 qreal patternLength = 0;
3240 for (int i = 0; i < pattern.size(); ++i)
3241 patternLength += pattern.at(i);
3243 if (patternLength <= 0)
3246 qreal length = line.length();
3247 Q_ASSERT(length > 0);
3248 while (length > 0) {
3249 const bool rasterize = *inDash;
3250 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3253 if (dash >= length) {
3255 *dashOffset += dash / width;
3259 *inDash = !(*inDash);
3260 if (++*dashIndex >= pattern.size())
3267 if (rasterize && dash > 0)
3268 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3275 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3277 #ifdef QT_DEBUG_DRAW
3278 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3280 Q_D(QRasterPaintEngine);
3281 QRasterPaintEngineState *s = state();
3284 if (!s->penData.blend)
3286 if (s->flags.fast_pen) {
3287 QCosmeticStroker stroker(s, d->deviceRect);
3288 for (int i=0; i<lineCount; ++i) {
3289 QLineF line = lines[i];
3290 stroker.drawLine(line.p1(), line.p2());
3293 QPaintEngineEx::drawLines(lines, lineCount);
3301 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3303 Q_D(QRasterPaintEngine);
3304 QRasterPaintEngineState *s = state();
3307 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3308 || (qpen_style(s->lastPen) == Qt::NoPen))
3309 && !s->flags.antialiased
3310 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3312 && s->matrix.type() <= QTransform::TxScale) // no shear
3315 const QRectF r = s->matrix.mapRect(rect);
3316 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3317 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3318 const QRect brect = QRect(int(r.x()), int(r.y()),
3319 int_dim(r.x(), r.width()),
3320 int_dim(r.y(), r.height()));
3322 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3323 &s->penData, &s->brushData);
3327 QPaintEngineEx::drawEllipse(rect);
3334 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3336 Q_D(QRasterPaintEngine);
3343 CGContextRef QRasterPaintEngine::getCGContext() const
3345 Q_D(const QRasterPaintEngine);
3346 return d->cgContext;
3354 void QRasterPaintEngine::setDC(HDC hdc) {
3355 Q_D(QRasterPaintEngine);
3362 HDC QRasterPaintEngine::getDC() const
3364 Q_D(const QRasterPaintEngine);
3371 void QRasterPaintEngine::releaseDC(HDC) const
3377 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3379 const QTransform &m = state()->matrix;
3380 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3381 QFontEngine::Type fontEngineType = fontEngine->type();
3382 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3383 || (m.type() <= QTransform::TxTranslate
3384 && (fontEngineType == QFontEngine::TestFontEngine
3385 || fontEngineType == QFontEngine::Box))) {
3389 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3392 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3394 #if defined(Q_WS_MAC)
3395 // Mac font engines don't support scaling and rotation
3396 if (m.type() > QTransform::TxTranslate)
3398 if (m.type() >= QTransform::TxProject)
3402 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3411 QPoint QRasterPaintEngine::coordinateOffset() const
3413 return QPoint(0, 0);
3417 Draws the given color \a spans with the specified \a color. The \a
3418 count parameter specifies the number of spans.
3420 The default implementation does nothing; reimplement this function
3421 to draw the given color \a spans with the specified \a color. Note
3422 that this function \e must be reimplemented if the framebuffer is
3425 \sa drawBufferSpan()
3427 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3428 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3433 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3434 "a non memory-mapped device");
3438 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3440 Draws the given \a buffer.
3442 The default implementation does nothing; reimplement this function
3443 to draw a buffer that contains more than one color. Note that this
3444 function \e must be reimplemented if the framebuffer is not
3447 The \a size parameter specifies the total size of the given \a
3448 buffer, while the \a length parameter specifies the number of
3449 pixels to draw. The buffer's position is given by (\a x, \a
3450 y). The provided \a alpha value is added to each pixel in the
3451 buffer when drawing.
3453 \sa drawColorSpans()
3455 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3456 int x, int y, int length, uint const_alpha)
3463 Q_UNUSED(const_alpha);
3464 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3465 "a non memory-mapped device");
3469 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3474 Q_D(QRasterPaintEngine);
3476 Q_ASSERT(image.depth() == 1);
3478 const int spanCount = 256;
3479 QT_FT_Span spans[spanCount];
3483 int w = image.width();
3484 int h = image.height();
3485 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3486 int ymin = qMax(qRound(pos.y()), 0);
3487 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3488 int xmin = qMax(qRound(pos.x()), 0);
3490 int x_offset = xmin - qRound(pos.x());
3492 QImage::Format format = image.format();
3493 for (int y = ymin; y < ymax; ++y) {
3494 const uchar *src = image.scanLine(y - qRound(pos.y()));
3495 if (format == QImage::Format_MonoLSB) {
3496 for (int x = 0; x < xmax - xmin; ++x) {
3497 int src_x = x + x_offset;
3498 uchar pixel = src[src_x >> 3];
3503 if (pixel & (0x1 << (src_x & 7))) {
3504 spans[n].x = xmin + x;
3506 spans[n].coverage = 255;
3508 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3512 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3515 if (n == spanCount) {
3516 fg->blend(n, spans, fg);
3522 for (int x = 0; x < xmax - xmin; ++x) {
3523 int src_x = x + x_offset;
3524 uchar pixel = src[src_x >> 3];
3529 if (pixel & (0x80 >> (x & 7))) {
3530 spans[n].x = xmin + x;
3532 spans[n].coverage = 255;
3534 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3538 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3541 if (n == spanCount) {
3542 fg->blend(n, spans, fg);
3550 fg->blend(n, spans, fg);
3556 \enum QRasterPaintEngine::ClipType
3559 \value RectClip Indicates that the currently set clip is a single rectangle.
3560 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3565 Returns the type of the clip currently set.
3567 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3569 Q_D(const QRasterPaintEngine);
3571 const QClipData *clip = d->clip();
3572 if (!clip || clip->hasRectClip)
3580 Returns the bounding rect of the currently set clip.
3582 QRect QRasterPaintEngine::clipBoundingRect() const
3584 Q_D(const QRasterPaintEngine);
3586 const QClipData *clip = d->clip();
3589 return d->deviceRect;
3591 if (clip->hasRectClip)
3592 return clip->clipRect;
3594 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3597 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3599 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3601 QVarLengthArray<short, 4096> buffer;
3603 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3604 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3605 result->initialize();
3607 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3608 const QSpan *c1_spans = c1ClipLines[y].spans;
3609 int c1_count = c1ClipLines[y].count;
3610 const QSpan *c2_spans = c2ClipLines[y].spans;
3611 int c2_count = c2ClipLines[y].count;
3613 if (c1_count == 0 && c2_count == 0)
3615 if (c1_count == 0) {
3616 result->appendSpans(c2_spans, c2_count);
3618 } else if (c2_count == 0) {
3619 result->appendSpans(c1_spans, c1_count);
3623 // we need to merge the two
3625 // find required length
3626 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3627 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3629 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3631 // Fill with old spans.
3632 for (int i = 0; i < c1_count; ++i) {
3633 const QSpan *cs = c1_spans + i;
3634 for (int j=cs->x; j<cs->x + cs->len; ++j)
3635 buffer[j] = cs->coverage;
3638 // Fill with new spans
3639 for (int i = 0; i < c2_count; ++i) {
3640 const QSpan *cs = c2_spans + i;
3641 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3642 buffer[j] += cs->coverage;
3643 if (buffer[j] > 255)
3651 // Skip to next span
3652 while (x < max && buffer[x] == 0) ++x;
3653 if (x >= max) break;
3656 int coverage = buffer[x];
3658 // Find length of span
3659 while (x < max && buffer[x] == coverage)
3662 result->appendSpan(sx, x - sx, y, coverage);
3667 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3669 Q_Q(QRasterPaintEngine);
3670 QRasterPaintEngineState *s = q->state();
3672 rasterizer->setAntialiased(s->flags.antialiased);
3674 QRect clipRect(deviceRect);
3676 // ### get from optimized rectbased QClipData
3678 const QClipData *c = clip();
3680 const QRect r(QPoint(c->xmin, c->ymin),
3681 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3682 clipRect = clipRect.intersected(r);
3683 blend = data->blend;
3685 blend = data->unclipped_blend;
3688 rasterizer->setClipRect(clipRect);
3689 rasterizer->initialize(blend, data);
3692 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3693 ProcessSpans callback,
3694 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3696 if (!callback || !outline)
3699 Q_Q(QRasterPaintEngine);
3700 QRasterPaintEngineState *s = q->state();
3702 if (!s->flags.antialiased) {
3703 initializeRasterizer(spanData);
3705 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3709 rasterizer->rasterize(outline, fillRule);
3713 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3717 int q_gray_rendered_spans(QT_FT_Raster raster);
3720 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3721 ProcessSpans callback,
3722 void *userData, QRasterBuffer *)
3724 if (!callback || !outline)
3727 Q_Q(QRasterPaintEngine);
3728 QRasterPaintEngineState *s = q->state();
3730 if (!s->flags.antialiased) {
3731 rasterizer->setAntialiased(s->flags.antialiased);
3732 rasterizer->setClipRect(deviceRect);
3733 rasterizer->initialize(callback, userData);
3735 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3739 rasterizer->rasterize(outline, fillRule);
3743 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3744 // minimize memory reallocations. However if initial size for
3745 // raster pool is changed for lower value, reallocations will
3747 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3748 int rasterPoolSize = rasterPoolInitialSize;
3749 unsigned char *rasterPoolBase;
3750 #if defined(Q_WS_WIN64)
3752 // We make use of setjmp and longjmp in qgrayraster.c which requires
3753 // 16-byte alignment, hence we hardcode this requirement here..
3754 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3756 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3757 rasterPoolBase = rasterPoolOnStack;
3759 Q_CHECK_PTR(rasterPoolBase);
3761 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3763 void *data = userData;
3765 QT_FT_BBox clip_box = { deviceRect.x(),
3767 deviceRect.x() + deviceRect.width(),
3768 deviceRect.y() + deviceRect.height() };
3770 QT_FT_Raster_Params rasterParams;
3771 rasterParams.target = 0;
3772 rasterParams.source = outline;
3773 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3774 rasterParams.gray_spans = 0;
3775 rasterParams.black_spans = 0;
3776 rasterParams.bit_test = 0;
3777 rasterParams.bit_set = 0;
3778 rasterParams.user = data;
3779 rasterParams.clip_box = clip_box;
3784 int rendered_spans = 0;
3788 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3789 rasterParams.gray_spans = callback;
3790 rasterParams.skip_spans = rendered_spans;
3791 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3793 // Out of memory, reallocate some more and try again...
3794 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3795 int new_size = rasterPoolSize * 2;
3796 if (new_size > 1024 * 1024) {
3797 qWarning("QPainter: Rasterization of primitive failed");
3801 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3803 #if defined(Q_WS_WIN64)
3804 _aligned_free(rasterPoolBase);
3806 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3807 free(rasterPoolBase);
3810 rasterPoolSize = new_size;
3812 #if defined(Q_WS_WIN64)
3813 // We make use of setjmp and longjmp in qgrayraster.c which requires
3814 // 16-byte alignment, hence we hardcode this requirement here..
3815 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3817 (unsigned char *) malloc(rasterPoolSize);
3819 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3821 qt_ft_grays_raster.raster_done(*grayRaster.data());
3822 qt_ft_grays_raster.raster_new(grayRaster.data());
3823 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3829 #if defined(Q_WS_WIN64)
3830 _aligned_free(rasterPoolBase);
3832 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3833 free(rasterPoolBase);
3837 void QRasterPaintEnginePrivate::recalculateFastImages()
3839 Q_Q(QRasterPaintEngine);
3840 QRasterPaintEngineState *s = q->state();
3842 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3843 && s->matrix.type() <= QTransform::TxShear;
3846 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3848 Q_Q(const QRasterPaintEngine);
3849 const QRasterPaintEngineState *s = q->state();
3851 return s->flags.fast_images
3852 && (mode == QPainter::CompositionMode_SourceOver
3853 || (mode == QPainter::CompositionMode_Source
3854 && !image.hasAlphaChannel()));
3857 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3859 Q_ASSERT(image.depth() == 1);
3861 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3862 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3864 QRgb fg = PREMUL(color.rgba());
3867 int height = sourceImage.height();
3868 int width = sourceImage.width();
3869 for (int y=0; y<height; ++y) {
3870 uchar *source = sourceImage.scanLine(y);
3871 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3872 if (!source || !target)
3873 QT_THROW(std::bad_alloc()); // we must have run out of memory
3874 for (int x=0; x < width; ++x)
3875 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3880 QRasterBuffer::~QRasterBuffer()
3884 void QRasterBuffer::init()
3886 compositionMode = QPainter::CompositionMode_SourceOver;
3887 monoDestinationWithClut = false;
3892 QImage::Format QRasterBuffer::prepare(QImage *image)
3894 m_buffer = (uchar *)image->bits();
3895 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3896 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3897 bytes_per_pixel = image->depth()/8;
3898 bytes_per_line = image->bytesPerLine();
3900 format = image->format();
3901 drawHelper = qDrawHelper + format;
3902 if (image->depth() == 1 && image->colorTable().size() == 2) {
3903 monoDestinationWithClut = true;
3904 destColor0 = PREMUL(image->colorTable()[0]);
3905 destColor1 = PREMUL(image->colorTable()[1]);
3911 void QRasterBuffer::resetBuffer(int val)
3913 memset(m_buffer, val, m_height*bytes_per_line);
3917 #if defined(Q_WS_QWS)
3918 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
3920 m_buffer = reinterpret_cast<uchar*>(device->memory());
3921 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
3922 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
3923 bytes_per_pixel = device->depth() / 8;
3924 bytes_per_line = device->bytesPerLine();
3925 format = device->format();
3926 #ifndef QT_NO_RASTERCALLBACKS
3928 drawHelper = qDrawHelperCallback + format;
3931 drawHelper = qDrawHelper + format;
3934 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
3938 return widget->frameGeometry().width();
3940 return widget->frameGeometry().height();
3945 return qt_paint_device_metric(widget, m);
3948 int QCustomRasterPaintDevice::bytesPerLine() const
3950 return (width() * depth() + 7) / 8;
3953 #elif defined(Q_OS_SYMBIAN)
3955 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
3959 #endif // Q_OS_SYMBIAN
3962 \class QCustomRasterPaintDevice
3967 \brief The QCustomRasterPaintDevice class is provided to activate
3968 hardware accelerated paint engines in Qt for Embedded Linux.
3970 Note that this class is only available in \l{Qt for Embedded Linux}.
3972 In \l{Qt for Embedded Linux}, painting is a pure software
3973 implementation. But starting with Qt 4.2, it is
3974 possible to add an accelerated graphics driver to take advantage
3975 of available hardware resources.
3977 Hardware acceleration is accomplished by creating a custom screen
3978 driver, accelerating the copying from memory to the screen, and
3979 implementing a custom paint engine accelerating the various
3980 painting operations. Then a custom paint device (derived from the
3981 QCustomRasterPaintDevice class) and a custom window surface
3982 (derived from QWSWindowSurface) must be implemented to make
3983 \l{Qt for Embedded Linux} aware of the accelerated driver.
3985 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
3986 documentation for details.
3988 \sa QRasterPaintEngine, QPaintDevice
3992 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
3994 Constructs a custom raster based paint device for the given
3995 top-level \a widget.
3999 \fn int QCustomRasterPaintDevice::bytesPerLine() const
4001 Returns the number of bytes per line in the framebuffer. Note that
4002 this number might be larger than the framebuffer width.
4006 \fn int QCustomRasterPaintDevice::devType() const
4011 \fn QImage::Format QCustomRasterPaintDevice::format() const
4013 Returns the format of the device's memory buffet.
4015 The default format is QImage::Format_ARGB32_Premultiplied. The
4016 only other valid format is QImage::Format_RGB16.
4020 \fn void * QCustomRasterPaintDevice::memory () const
4022 Returns a pointer to the paint device's memory buffer, or 0 if no
4027 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4032 \fn QSize QCustomRasterPaintDevice::size () const
4037 QClipData::QClipData(int height)
4039 clipSpanHeight = height;
4044 xmin = xmax = ymin = ymax = 0;
4048 hasRectClip = hasRegionClip = false;
4051 QClipData::~QClipData()
4059 void QClipData::initialize()
4065 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4067 Q_CHECK_PTR(m_clipLines);
4069 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4070 allocated = clipSpanHeight;
4071 Q_CHECK_PTR(m_spans);
4077 m_clipLines[y].spans = 0;
4078 m_clipLines[y].count = 0;
4082 const int len = clipRect.width();
4085 QSpan *span = m_spans + count;
4089 span->coverage = 255;
4092 m_clipLines[y].spans = span;
4093 m_clipLines[y].count = 1;
4097 while (y < clipSpanHeight) {
4098 m_clipLines[y].spans = 0;
4099 m_clipLines[y].count = 0;
4102 } else if (hasRegionClip) {
4104 const QVector<QRect> rects = clipRegion.rects();
4105 const int numRects = rects.size();
4108 const int maxSpans = (ymax - ymin) * numRects;
4109 if (maxSpans > allocated) {
4110 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4111 allocated = maxSpans;
4116 int firstInBand = 0;
4118 while (firstInBand < numRects) {
4119 const int currMinY = rects.at(firstInBand).y();
4120 const int currMaxY = currMinY + rects.at(firstInBand).height();
4122 while (y < currMinY) {
4123 m_clipLines[y].spans = 0;
4124 m_clipLines[y].count = 0;
4128 int lastInBand = firstInBand;
4129 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4132 while (y < currMaxY) {
4134 m_clipLines[y].spans = m_spans + count;
4135 m_clipLines[y].count = lastInBand - firstInBand + 1;
4137 for (int r = firstInBand; r <= lastInBand; ++r) {
4138 const QRect &currRect = rects.at(r);
4139 QSpan *span = m_spans + count;
4140 span->x = currRect.x();
4141 span->len = currRect.width();
4143 span->coverage = 255;
4149 firstInBand = lastInBand + 1;
4152 Q_ASSERT(count <= allocated);
4154 while (y < clipSpanHeight) {
4155 m_clipLines[y].spans = 0;
4156 m_clipLines[y].count = 0;
4162 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4167 free(m_clipLines); // same for clipLines
4173 void QClipData::fixup()
4178 ymin = ymax = xmin = xmax = 0;
4183 ymin = m_spans[0].y;
4184 ymax = m_spans[count-1].y + 1;
4188 const int firstLeft = m_spans[0].x;
4189 const int firstRight = m_spans[0].x + m_spans[0].len;
4192 for (int i = 0; i < count; ++i) {
4193 QT_FT_Span_& span = m_spans[i];
4196 if (span.y != y + 1 && y != -1)
4199 m_clipLines[y].spans = &span;
4200 m_clipLines[y].count = 1;
4202 ++m_clipLines[y].count;
4204 const int spanLeft = span.x;
4205 const int spanRight = spanLeft + span.len;
4207 if (spanLeft < xmin)
4210 if (spanRight > xmax)
4213 if (spanLeft != firstLeft || spanRight != firstRight)
4219 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4224 Convert \a rect to clip spans.
4226 void QClipData::setClipRect(const QRect &rect)
4228 if (hasRectClip && rect == clipRect)
4231 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4233 hasRegionClip = false;
4237 xmax = rect.x() + rect.width();
4238 ymin = qMin(rect.y(), clipSpanHeight);
4239 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4246 // qDebug() << xmin << xmax << ymin << ymax;
4250 Convert \a region to clip spans.
4252 void QClipData::setClipRegion(const QRegion ®ion)
4254 if (region.rectCount() == 1) {
4255 setClipRect(region.rects().at(0));
4259 hasRegionClip = true;
4260 hasRectClip = false;
4261 clipRegion = region;
4263 { // set bounding rect
4264 const QRect rect = region.boundingRect();
4266 xmax = rect.x() + rect.width();
4268 ymax = rect.y() + rect.height();
4280 spans must be sorted on y
4282 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4283 const QSpan *spans, const QSpan *end,
4284 QSpan **outSpans, int available)
4286 const_cast<QClipData *>(clip)->initialize();
4288 QSpan *out = *outSpans;
4290 const QSpan *clipSpans = clip->m_spans + *currentClip;
4291 const QSpan *clipEnd = clip->m_spans + clip->count;
4293 while (available && spans < end ) {
4294 if (clipSpans >= clipEnd) {
4298 if (clipSpans->y > spans->y) {
4302 if (spans->y != clipSpans->y) {
4303 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4304 clipSpans = clip->m_clipLines[spans->y].spans;
4309 Q_ASSERT(spans->y == clipSpans->y);
4312 int sx2 = sx1 + spans->len;
4313 int cx1 = clipSpans->x;
4314 int cx2 = cx1 + clipSpans->len;
4316 if (cx1 < sx1 && cx2 < sx1) {
4319 } else if (sx1 < cx1 && sx2 < cx1) {
4323 int x = qMax(sx1, cx1);
4324 int len = qMin(sx2, cx2) - x;
4326 out->x = qMax(sx1, cx1);
4327 out->len = qMin(sx2, cx2) - out->x;
4329 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4341 *currentClip = clipSpans - clip->m_spans;
4345 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4347 // qDebug() << "qt_span_fill_clipped" << spanCount;
4348 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4350 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4352 const int NSPANS = 256;
4353 QSpan cspans[NSPANS];
4354 int currentClip = 0;
4355 const QSpan *end = spans + spanCount;
4356 while (spans < end) {
4357 QSpan *clipped = cspans;
4358 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4359 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4360 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4362 if (clipped - cspans)
4363 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4369 Clip spans to \a{clip}-rectangle.
4370 Returns number of unclipped spans
4372 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4375 const short minx = clip.left();
4376 const short miny = clip.top();
4377 const short maxx = clip.right();
4378 const short maxy = clip.bottom();
4381 for (int i = 0; i < numSpans; ++i) {
4382 if (spans[i].y > maxy)
4384 if (spans[i].y < miny
4385 || spans[i].x > maxx
4386 || spans[i].x + spans[i].len <= minx) {
4389 if (spans[i].x < minx) {
4390 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4393 spans[n].x = spans[i].x;
4394 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4396 if (spans[n].len == 0)
4398 spans[n].y = spans[i].y;
4399 spans[n].coverage = spans[i].coverage;
4406 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4409 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4410 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4412 Q_ASSERT(fillData->clip);
4413 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4415 // hw: check if this const_cast<> is safe!!!
4416 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4417 fillData->clip->clipRect);
4419 fillData->unclipped_blend(count, spans, fillData);
4422 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4424 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4426 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4427 // for (int i = 0; i < qMin(count, 10); ++i) {
4428 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4431 switch (clipData->operation) {
4433 case Qt::IntersectClip:
4435 QClipData *newClip = clipData->newClip;
4436 newClip->initialize();
4438 int currentClip = 0;
4439 const QSpan *end = spans + count;
4440 while (spans < end) {
4441 QSpan *newspans = newClip->m_spans + newClip->count;
4442 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4443 &newspans, newClip->allocated - newClip->count);
4444 newClip->count = newspans - newClip->m_spans;
4446 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4447 newClip->allocated *= 2;
4454 case Qt::ReplaceClip:
4455 clipData->newClip->appendSpans(spans, count);
4463 QImage QRasterBuffer::bufferImage() const
4465 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4467 for (int y = 0; y < m_height; ++y) {
4468 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4470 for (int x=0; x<m_width; ++x) {
4471 uint argb = span[x];
4472 image.setPixel(x, y, argb);
4480 void QRasterBuffer::flushToARGBImage(QImage *target) const
4482 int w = qMin(m_width, target->width());
4483 int h = qMin(m_height, target->height());
4485 for (int y=0; y<h; ++y) {
4486 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4487 QRgb *dest = (QRgb *) target->scanLine(y);
4488 for (int x=0; x<w; ++x) {
4489 QRgb pixel = sourceLine[x];
4490 int alpha = qAlpha(pixel);
4494 dest[x] = (alpha << 24)
4495 | ((255*qRed(pixel)/alpha) << 16)
4496 | ((255*qGreen(pixel)/alpha) << 8)
4497 | ((255*qBlue(pixel)/alpha) << 0);
4504 class QGradientCache
4508 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4509 stops(s), opacity(op), interpolationMode(mode) {}
4510 uint buffer[GRADIENT_STOPTABLE_SIZE];
4511 QGradientStops stops;
4513 QGradient::InterpolationMode interpolationMode;
4516 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4519 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4520 quint64 hash_val = 0;
4522 QGradientStops stops = gradient.stops();
4523 for (int i = 0; i < stops.size() && i <= 2; i++)
4524 hash_val += stops[i].second.rgba();
4526 QMutexLocker lock(&mutex);
4527 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4529 if (it == cache.constEnd())
4530 return addCacheElement(hash_val, gradient, opacity);
4533 const CacheInfo &cache_info = it.value();
4534 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4535 return cache_info.buffer;
4537 } while (it != cache.constEnd() && it.key() == hash_val);
4538 // an exact match for these stops and opacity was not found, create new cache
4539 return addCacheElement(hash_val, gradient, opacity);
4543 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4545 inline int maxCacheSize() const { return 60; }
4546 inline void generateGradientColorTable(const QGradient& g,
4548 int size, int opacity) const;
4549 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4550 if (cache.size() == maxCacheSize()) {
4551 // may remove more than 1, but OK
4552 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4554 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4555 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4556 return cache.insert(hash_val, cache_entry).value().buffer;
4559 QGradientColorTableHash cache;
4563 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4565 QGradientStops stops = gradient.stops();
4566 int stopCount = stops.count();
4567 Q_ASSERT(stopCount > 0);
4569 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4571 if (stopCount == 2) {
4572 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4573 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4575 qreal first_stop = stops[0].first;
4576 qreal second_stop = stops[1].first;
4578 if (second_stop < first_stop) {
4579 qSwap(first_color, second_color);
4580 qSwap(first_stop, second_stop);
4583 if (colorInterpolation) {
4584 first_color = PREMUL(first_color);
4585 second_color = PREMUL(second_color);
4588 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4589 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4591 uint red_first = qRed(first_color) << 16;
4592 uint green_first = qGreen(first_color) << 16;
4593 uint blue_first = qBlue(first_color) << 16;
4594 uint alpha_first = qAlpha(first_color) << 16;
4596 uint red_second = qRed(second_color) << 16;
4597 uint green_second = qGreen(second_color) << 16;
4598 uint blue_second = qBlue(second_color) << 16;
4599 uint alpha_second = qAlpha(second_color) << 16;
4602 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4603 if (colorInterpolation)
4604 colorTable[i] = first_color;
4606 colorTable[i] = PREMUL(first_color);
4609 if (i < second_index) {
4610 qreal reciprocal = qreal(1) / (second_index - first_index);
4612 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4613 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4614 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4615 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4618 red_first += 1 << 15;
4619 green_first += 1 << 15;
4620 blue_first += 1 << 15;
4621 alpha_first += 1 << 15;
4623 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4624 red_first += red_delta;
4625 green_first += green_delta;
4626 blue_first += blue_delta;
4627 alpha_first += alpha_delta;
4629 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4630 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4632 if (colorInterpolation)
4633 colorTable[i] = color;
4635 colorTable[i] = PREMUL(color);
4639 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4640 if (colorInterpolation)
4641 colorTable[i] = second_color;
4643 colorTable[i] = PREMUL(second_color);
4649 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4650 if (stopCount == 1) {
4651 current_color = PREMUL(current_color);
4652 for (int i = 0; i < size; ++i)
4653 colorTable[i] = current_color;
4657 // The position where the gradient begins and ends
4658 qreal begin_pos = stops[0].first;
4659 qreal end_pos = stops[stopCount-1].first;
4661 int pos = 0; // The position in the color table.
4664 qreal incr = 1 / qreal(size); // the double increment.
4665 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4667 // Up to first point
4668 colorTable[pos++] = PREMUL(current_color);
4669 while (dpos <= begin_pos) {
4670 colorTable[pos] = colorTable[pos - 1];
4675 int current_stop = 0; // We always interpolate between current and current + 1.
4677 qreal t; // position between current left and right stops
4678 qreal t_delta; // the t increment per entry in the color table
4680 if (dpos < end_pos) {
4682 while (dpos > stops[current_stop+1].first)
4685 if (current_stop != 0)
4686 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4687 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4689 if (colorInterpolation) {
4690 current_color = PREMUL(current_color);
4691 next_color = PREMUL(next_color);
4694 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4695 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4696 t = (dpos - stops[current_stop].first) * c;
4700 Q_ASSERT(current_stop < stopCount);
4702 int dist = qRound(t);
4703 int idist = 256 - dist;
4705 if (colorInterpolation)
4706 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4708 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4713 if (dpos >= end_pos)
4719 while (dpos > stops[current_stop+skip+1].first)
4723 current_stop += skip;
4725 current_color = next_color;
4727 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4728 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4730 if (colorInterpolation) {
4732 current_color = PREMUL(current_color);
4733 next_color = PREMUL(next_color);
4736 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4737 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4738 t = (dpos - stops[current_stop].first) * c;
4745 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4746 while (pos < size - 1) {
4747 colorTable[pos] = current_color;
4751 // Make sure the last color stop is represented at the end of the table
4752 colorTable[size - 1] = current_color;
4755 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4758 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4762 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
4767 m11 = m22 = m33 = 1.;
4768 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4769 clip = pe ? pe->d_func()->clip() : 0;
4772 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4774 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4776 Qt::BrushStyle brushStyle = qbrush_style(brush);
4777 switch (brushStyle) {
4778 case Qt::SolidPattern: {
4780 QColor c = qbrush_color(brush);
4781 QRgb rgba = c.rgba();
4782 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4783 if ((solid.color & 0xff000000) == 0
4784 && compositionMode == QPainter::CompositionMode_SourceOver) {
4790 case Qt::LinearGradientPattern:
4792 type = LinearGradient;
4793 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4794 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4795 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4796 gradient.spread = g->spread();
4798 QLinearGradientData &linearData = gradient.linear;
4800 linearData.origin.x = g->start().x();
4801 linearData.origin.y = g->start().y();
4802 linearData.end.x = g->finalStop().x();
4803 linearData.end.y = g->finalStop().y();
4807 case Qt::RadialGradientPattern:
4809 type = RadialGradient;
4810 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4811 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4812 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4813 gradient.spread = g->spread();
4815 QRadialGradientData &radialData = gradient.radial;
4817 QPointF center = g->center();
4818 radialData.center.x = center.x();
4819 radialData.center.y = center.y();
4820 radialData.center.radius = g->centerRadius();
4821 QPointF focal = g->focalPoint();
4822 radialData.focal.x = focal.x();
4823 radialData.focal.y = focal.y();
4824 radialData.focal.radius = g->focalRadius();
4828 case Qt::ConicalGradientPattern:
4830 type = ConicalGradient;
4831 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4832 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4833 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4834 gradient.spread = QGradient::RepeatSpread;
4836 QConicalGradientData &conicalData = gradient.conical;
4838 QPointF center = g->center();
4839 conicalData.center.x = center.x();
4840 conicalData.center.y = center.y();
4841 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4845 case Qt::Dense1Pattern:
4846 case Qt::Dense2Pattern:
4847 case Qt::Dense3Pattern:
4848 case Qt::Dense4Pattern:
4849 case Qt::Dense5Pattern:
4850 case Qt::Dense6Pattern:
4851 case Qt::Dense7Pattern:
4852 case Qt::HorPattern:
4853 case Qt::VerPattern:
4854 case Qt::CrossPattern:
4855 case Qt::BDiagPattern:
4856 case Qt::FDiagPattern:
4857 case Qt::DiagCrossPattern:
4860 tempImage = new QImage();
4861 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4862 initTexture(tempImage, alpha, QTextureData::Tiled);
4864 case Qt::TexturePattern:
4867 tempImage = new QImage();
4869 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4870 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4872 *tempImage = brush.textureImage();
4873 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4881 adjustSpanMethods();
4884 void QSpanData::adjustSpanMethods()
4894 unclipped_blend = 0;
4897 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4898 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4899 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4900 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4901 fillRect = rasterBuffer->drawHelper->fillRect;
4903 case LinearGradient:
4904 case RadialGradient:
4905 case ConicalGradient:
4906 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4910 #ifndef QT_NO_RASTERCALLBACKS
4911 if (!rasterBuffer->buffer())
4912 unclipped_blend = qBlendTextureCallback;
4915 unclipped_blend = qBlendTexture;
4917 unclipped_blend = qBlendTexture;
4919 if (!texture.imageData)
4920 unclipped_blend = 0;
4925 if (!unclipped_blend) {
4928 blend = unclipped_blend;
4929 } else if (clip->hasRectClip) {
4930 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4932 blend = qt_span_fill_clipped;
4936 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4939 // make sure we round off correctly in qdrawhelper.cpp
4940 delta.translate(1.0 / 65536, 1.0 / 65536);
4942 QTransform inv = (delta * matrix).inverted();
4955 const bool affine = !m13 && !m23;
4956 fast_matrix = affine
4957 && m11 * m11 + m21 * m21 < 1e4
4958 && m12 * m12 + m22 * m22 < 1e4
4962 adjustSpanMethods();
4965 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4967 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4969 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4970 if (!d || d->height == 0) {
4971 texture.imageData = 0;
4978 texture.bytesPerLine = 0;
4979 texture.format = QImage::Format_Invalid;
4980 texture.colorTable = 0;
4981 texture.hasAlpha = alpha != 256;
4983 texture.imageData = d->data;
4984 texture.width = d->width;
4985 texture.height = d->height;
4987 if (sourceRect.isNull()) {
4990 texture.x2 = texture.width;
4991 texture.y2 = texture.height;
4993 texture.x1 = sourceRect.x();
4994 texture.y1 = sourceRect.y();
4995 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4996 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4999 texture.bytesPerLine = d->bytes_per_line;
5001 texture.format = d->format;
5002 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
5003 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
5005 texture.const_alpha = alpha;
5006 texture.type = _type;
5008 adjustSpanMethods();
5013 \a x and \a y is relative to the midpoint of \a rect.
5015 static inline void drawEllipsePoints(int x, int y, int length,
5018 ProcessSpans pen_func, ProcessSpans brush_func,
5019 QSpanData *pen_data, QSpanData *brush_data)
5024 QT_FT_Span outline[4];
5025 const int midx = rect.x() + (rect.width() + 1) / 2;
5026 const int midy = rect.y() + (rect.height() + 1) / 2;
5032 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
5033 outline[0].len = qMin(length, x - outline[0].x);
5035 outline[0].coverage = 255;
5039 outline[1].len = length;
5041 outline[1].coverage = 255;
5044 outline[2].x = outline[0].x;
5045 outline[2].len = outline[0].len;
5046 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
5047 outline[2].coverage = 255;
5051 outline[3].len = length;
5052 outline[3].y = outline[2].y;
5053 outline[3].coverage = 255;
5055 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
5059 fill[0].x = outline[0].x + outline[0].len - 1;
5060 fill[0].len = qMax(0, outline[1].x - fill[0].x);
5061 fill[0].y = outline[1].y;
5062 fill[0].coverage = 255;
5065 fill[1].x = outline[2].x + outline[2].len - 1;
5066 fill[1].len = qMax(0, outline[3].x - fill[1].x);
5067 fill[1].y = outline[3].y;
5068 fill[1].coverage = 255;
5070 int n = (fill[0].y >= fill[1].y ? 1 : 2);
5071 n = qt_intersect_spans(fill, n, clip);
5073 brush_func(n, fill, brush_data);
5076 int n = (outline[1].y >= outline[2].y ? 2 : 4);
5077 n = qt_intersect_spans(outline, n, clip);
5079 pen_func(n, outline, pen_data);
5085 Draws an ellipse using the integer point midpoint algorithm.
5087 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
5088 ProcessSpans pen_func, ProcessSpans brush_func,
5089 QSpanData *pen_data, QSpanData *brush_data)
5091 const qreal a = qreal(rect.width()) / 2;
5092 const qreal b = qreal(rect.height()) / 2;
5093 qreal d = b*b - (a*a*b) + 0.25*a*a;
5096 int y = (rect.height() + 1) / 2;
5100 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
5101 if (d < 0) { // select E
5104 } else { // select SE
5105 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
5106 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5107 pen_func, brush_func, pen_data, brush_data);
5112 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5113 pen_func, brush_func, pen_data, brush_data);
5116 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
5117 const int miny = rect.height() & 0x1;
5119 if (d < 0) { // select SE
5120 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
5122 } else { // select S
5123 d += a*a*(-2*y + 3);
5126 drawEllipsePoints(x, y, 1, rect, clip,
5127 pen_func, brush_func, pen_data, brush_data);
5132 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
5135 Draws the first \a pointCount points in the buffer \a points
5137 The default implementation converts the first \a pointCount QPoints in \a points
5138 to QPointFs and calls the floating point version of drawPoints.
5142 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
5145 Reimplement this function to draw the largest ellipse that can be
5146 contained within rectangle \a rect.
5149 #ifdef QT_DEBUG_DRAW
5150 void dumpClip(int width, int height, const QClipData *clip)
5152 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
5153 clipImg.fill(0xffff0000);
5160 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
5162 for (int i = 0; i < clip->count; ++i) {
5163 const QSpan *span = ((QClipData *) clip)->spans() + i;
5164 for (int j = 0; j < span->len; ++j)
5165 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
5166 x0 = qMin(x0, int(span->x));
5167 x1 = qMax(x1, int(span->x + span->len - 1));
5169 y0 = qMin(y0, int(span->y));
5170 y1 = qMax(y1, int(span->y));
5173 static int counter = 0;
5180 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
5181 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));