1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
58 #if defined (Q_WS_X11)
59 # include <private/qfontengine_ft_p.h>
62 // #include <private/qdatabuffer_p.h>
63 // #include <private/qpainter_p.h>
64 #include <private/qmath_p.h>
65 #include <private/qtextengine_p.h>
66 #include <private/qfontengine_p.h>
67 #include <private/qpixmap_raster_p.h>
68 // #include <private/qpolygonclipper_p.h>
69 // #include <private/qrasterizer_p.h>
70 #include <private/qimage_p.h>
71 #include <private/qstatictext_p.h>
72 #include <private/qcosmeticstroker_p.h>
73 #include "qmemrotate_p.h"
75 #include "qpaintengine_raster_p.h"
76 // #include "qbezier_p.h"
77 #include "qoutlinemapper_p.h"
80 # include <qt_windows.h>
81 # include <qvarlengtharray.h>
82 # include <private/qfontengine_p.h>
83 # if defined(Q_OS_WINCE)
84 # include "qguifunctions_wince.h"
86 #elif defined(Q_WS_MAC)
87 # include <private/qt_mac_p.h>
88 # include <private/qpixmap_mac_p.h>
89 # include <private/qpaintengine_mac_p.h>
90 #elif defined(Q_WS_QWS)
91 # if !defined(QT_NO_FREETYPE)
92 # include <private/qfontengine_ft_p.h>
94 # if !defined(QT_NO_QWS_QPF2)
95 # include <private/qfontengine_qpf_p.h>
97 # include <private/qabstractfontengine_p.h>
98 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
99 # include <private/qfontengine_s60_p.h>
100 #elif defined(Q_WS_QPA)
101 # include <private/qfontengine_ft_p.h>
104 #if defined(Q_WS_WIN64)
111 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
113 #define qreal_to_fixed_26_6(f) (int(f * 64))
114 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
115 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
117 // #define QT_DEBUG_DRAW
119 void dumpClip(int width, int height, const QClipData *clip);
122 #define QT_FAST_SPANS
125 // A little helper macro to get a better approximation of dimensions.
126 // If we have a rect that starting at 0.5 of width 3.5 it should span
128 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
130 // use the same rounding as in qrasterizer.cpp (6 bit fixed point)
131 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
134 extern bool qt_cleartype_enabled;
138 extern bool qt_applefontsmoothing_enabled;
142 /********************************************************************************
145 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
146 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
147 static void qt_span_clip(int count, const QSpan *spans, void *userData);
148 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
154 Qt::ClipOperation operation;
160 LineDrawIncludeLastPixel
163 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
164 ProcessSpans pen_func, ProcessSpans brush_func,
165 QSpanData *pen_data, QSpanData *brush_data);
167 struct QRasterFloatPoint {
173 static const QRectF boundingRect(const QPointF *points, int pointCount)
175 const QPointF *e = points;
176 const QPointF *last = points + pointCount;
177 qreal minx, maxx, miny, maxy;
178 minx = maxx = e->x();
179 miny = maxy = e->y();
183 else if (e->x() > maxx)
187 else if (e->y() > maxy)
190 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
194 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
195 return (elementCount == 5 // 5-point polygon, check for closed rect
196 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
197 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
198 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
199 && pts[0] < pts[4] && pts[1] < pts[5]
201 (elementCount == 4 // 4-point polygon, check for unclosed rect
202 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
203 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
204 && pts[0] < pts[4] && pts[1] < pts[5]
209 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
211 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
214 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
216 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
219 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
220 qfixed c2x, qfixed c2y,
221 qfixed ex, qfixed ey,
224 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
225 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
226 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
230 #if !defined(QT_NO_DEBUG) && 0
231 static void qt_debug_path(const QPainterPath &path)
233 const char *names[] = {
240 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
241 for (int i=0; i<path.elementCount(); ++i) {
242 const QPainterPath::Element &e = path.elementAt(i);
243 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
244 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
249 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
250 QPaintEngineExPrivate(),
257 \class QRasterPaintEngine
262 \brief The QRasterPaintEngine class enables hardware acceleration
263 of painting operations in Qt for Embedded Linux.
265 Note that this functionality is only available in
266 \l{Qt for Embedded Linux}.
268 In \l{Qt for Embedded Linux}, painting is a pure software
269 implementation. But starting with Qt 4.2, it is
270 possible to add an accelerated graphics driver to take advantage
271 of available hardware resources.
273 Hardware acceleration is accomplished by creating a custom screen
274 driver, accelerating the copying from memory to the screen, and
275 implementing a custom paint engine accelerating the various
276 painting operations. Then a custom paint device (derived from the
277 QCustomRasterPaintDevice class) and a custom window surface
278 (derived from QWSWindowSurface) must be implemented to make
279 \l{Qt for Embedded Linux} aware of the accelerated driver.
281 \note The QRasterPaintEngine class does not support 8-bit images.
282 Instead, they need to be converted to a supported format, such as
283 QImage::Format_ARGB32_Premultiplied.
285 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
286 documentation for details.
288 \sa QCustomRasterPaintDevice, QPaintEngine
292 \fn Type QRasterPaintEngine::type() const
298 \relates QRasterPaintEngine
300 A struct equivalent to QT_FT_Span, containing a position (x,
301 y), the span's length in pixels and its color/coverage (a value
302 ranging from 0 to 255).
308 Creates a raster based paint engine for operating on the given
309 \a device, with the complete set of \l
310 {QPaintEngine::PaintEngineFeature}{paint engine features and
313 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
314 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
316 d_func()->device = device;
323 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
326 d_func()->device = device;
330 void QRasterPaintEngine::init()
332 Q_D(QRasterPaintEngine);
339 // The antialiasing raster.
340 d->grayRaster.reset(new QT_FT_Raster);
341 Q_CHECK_PTR(d->grayRaster.data());
342 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
343 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
346 d->rasterizer.reset(new QRasterizer);
347 d->rasterBuffer.reset(new QRasterBuffer());
348 d->outlineMapper.reset(new QOutlineMapper);
349 d->outlinemapper_xform_dirty = true;
351 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
352 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
353 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
355 d->baseClip.reset(new QClipData(d->device->height()));
356 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
358 d->image_filler.init(d->rasterBuffer.data(), this);
359 d->image_filler.type = QSpanData::Texture;
361 d->image_filler_xform.init(d->rasterBuffer.data(), this);
362 d->image_filler_xform.type = QSpanData::Texture;
364 d->solid_color_filler.init(d->rasterBuffer.data(), this);
365 d->solid_color_filler.type = QSpanData::Solid;
367 d->deviceDepth = d->device->depth();
369 d->mono_surface = false;
370 gccaps &= ~PorterDuff;
372 QImage::Format format = QImage::Format_Invalid;
374 switch (d->device->devType()) {
375 case QInternal::Pixmap:
376 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
378 case QInternal::Image:
379 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
382 case QInternal::CustomRaster:
383 d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
387 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
393 case QImage::Format_MonoLSB:
394 case QImage::Format_Mono:
395 d->mono_surface = true;
397 case QImage::Format_ARGB8565_Premultiplied:
398 case QImage::Format_ARGB8555_Premultiplied:
399 case QImage::Format_ARGB6666_Premultiplied:
400 case QImage::Format_ARGB4444_Premultiplied:
401 case QImage::Format_ARGB32_Premultiplied:
402 case QImage::Format_ARGB32:
403 gccaps |= PorterDuff;
405 case QImage::Format_RGB32:
406 case QImage::Format_RGB444:
407 case QImage::Format_RGB555:
408 case QImage::Format_RGB666:
409 case QImage::Format_RGB888:
410 case QImage::Format_RGB16:
421 Destroys this paint engine.
423 QRasterPaintEngine::~QRasterPaintEngine()
425 Q_D(QRasterPaintEngine);
427 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
433 bool QRasterPaintEngine::begin(QPaintDevice *device)
435 Q_D(QRasterPaintEngine);
437 if (device->devType() == QInternal::Pixmap) {
438 QPixmap *pixmap = static_cast<QPixmap *>(device);
439 QPixmapData *pd = pixmap->pixmapData();
440 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
441 d->device = pd->buffer();
446 // Make sure QPaintEngine::paintDevice() returns the proper device.
449 Q_ASSERT(d->device->devType() == QInternal::Image
450 || d->device->devType() == QInternal::CustomRaster);
452 d->systemStateChanged();
454 QRasterPaintEngineState *s = state();
455 ensureOutlineMapper();
456 d->outlineMapper->m_clip_rect = d->deviceRect;
458 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
459 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
460 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
461 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
463 d->rasterizer->setClipRect(d->deviceRect);
465 s->penData.init(d->rasterBuffer.data(), this);
466 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
467 s->stroker = &d->basicStroker;
468 d->basicStroker.setClipRect(d->deviceRect);
470 s->brushData.init(d->rasterBuffer.data(), this);
471 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
473 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
475 setDirty(DirtyBrushOrigin);
478 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
479 << ") devType:" << device->devType()
480 << "devRect:" << d->deviceRect;
482 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
487 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
488 #if defined(Q_WS_WIN)
489 else if (qt_cleartype_enabled)
490 #elif defined (Q_WS_MAC)
491 else if (qt_applefontsmoothing_enabled)
496 QImage::Format format = static_cast<QImage *>(d->device)->format();
497 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
500 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
502 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
511 bool QRasterPaintEngine::end()
514 Q_D(QRasterPaintEngine);
515 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
517 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
527 void QRasterPaintEngine::releaseBuffer()
529 Q_D(QRasterPaintEngine);
530 d->rasterBuffer.reset(new QRasterBuffer);
536 QSize QRasterPaintEngine::size() const
538 Q_D(const QRasterPaintEngine);
539 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
546 void QRasterPaintEngine::saveBuffer(const QString &s) const
548 Q_D(const QRasterPaintEngine);
549 d->rasterBuffer->bufferImage().save(s, "PNG");
556 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
558 QRasterPaintEngineState *s = state();
559 // FALCON: get rid of this line, see drawImage call below.
561 QTransform::TransformationType txop = s->matrix.type();
565 case QTransform::TxNone:
566 s->flags.int_xform = true;
569 case QTransform::TxTranslate:
570 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
571 && qreal(int(s->matrix.dy())) == s->matrix.dy();
574 case QTransform::TxScale:
575 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
576 && qreal(int(s->matrix.dy())) == s->matrix.dy()
577 && qreal(int(s->matrix.m11())) == s->matrix.m11()
578 && qreal(int(s->matrix.m22())) == s->matrix.m22();
581 default: // shear / perspective...
582 s->flags.int_xform = false;
586 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
588 ensureOutlineMapper();
593 QRasterPaintEngineState::~QRasterPaintEngineState()
595 if (flags.has_clip_ownership)
600 QRasterPaintEngineState::QRasterPaintEngineState()
612 flags.fast_pen = true;
613 flags.antialiased = false;
614 flags.bilinear = false;
615 flags.fast_text = true;
616 flags.int_xform = true;
617 flags.tx_noshear = true;
618 flags.fast_images = true;
621 flags.has_clip_ownership = false;
626 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
631 , strokeFlags(s.strokeFlags)
632 , lastBrush(s.lastBrush)
633 , brushData(s.brushData)
634 , fillFlags(s.fillFlags)
635 , pixmapFlags(s.pixmapFlags)
636 , intOpacity(s.intOpacity)
640 , flag_bits(s.flag_bits)
642 brushData.tempImage = 0;
643 penData.tempImage = 0;
644 flags.has_clip_ownership = false;
650 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
652 QRasterPaintEngineState *s;
654 s = new QRasterPaintEngineState();
656 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
664 void QRasterPaintEngine::setState(QPainterState *s)
666 Q_D(QRasterPaintEngine);
667 QPaintEngineEx::setState(s);
668 d->rasterBuffer->compositionMode = s->composition_mode;
672 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
677 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
684 void QRasterPaintEngine::penChanged()
687 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
689 QRasterPaintEngineState *s = state();
690 s->strokeFlags |= DirtyPen;
691 s->dirty |= DirtyPen;
697 void QRasterPaintEngine::updatePen(const QPen &pen)
699 Q_D(QRasterPaintEngine);
700 QRasterPaintEngineState *s = state();
702 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
705 Qt::PenStyle pen_style = qpen_style(pen);
710 s->penData.clip = d->clip();
711 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
713 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
714 || pen.brush().transform().type() >= QTransform::TxNone) {
715 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
718 // Slightly ugly handling of an uncommon case... We need to change
719 // the pen because it is reused in draw_midpoint to decide dashed
721 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
722 pen_style = Qt::SolidLine;
723 s->lastPen.setStyle(Qt::SolidLine);
726 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
727 d->basicStroker.setCapStyle(qpen_capStyle(pen));
728 d->basicStroker.setMiterLimit(pen.miterLimit());
730 qreal penWidth = qpen_widthf(pen);
732 d->basicStroker.setStrokeWidth(1);
734 d->basicStroker.setStrokeWidth(penWidth);
736 if(pen_style == Qt::SolidLine) {
737 s->stroker = &d->basicStroker;
738 } else if (pen_style != Qt::NoPen) {
740 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
741 if (pen.isCosmetic()) {
742 d->dashStroker->setClipRect(d->deviceRect);
744 // ### I've seen this inverted devrect multiple places now...
745 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
746 d->dashStroker->setClipRect(clipRect);
748 d->dashStroker->setDashPattern(pen.dashPattern());
749 d->dashStroker->setDashOffset(pen.dashOffset());
750 s->stroker = d->dashStroker.data();
755 ensureState(); // needed because of tx_noshear...
756 s->flags.fast_pen = pen_style > Qt::NoPen
758 && ((pen.isCosmetic() && penWidth <= 1)
759 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
761 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
771 void QRasterPaintEngine::brushOriginChanged()
773 QRasterPaintEngineState *s = state();
775 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
778 s->fillFlags |= DirtyBrushOrigin;
785 void QRasterPaintEngine::brushChanged()
787 QRasterPaintEngineState *s = state();
789 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
791 s->fillFlags |= DirtyBrush;
800 void QRasterPaintEngine::updateBrush(const QBrush &brush)
803 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
805 Q_D(QRasterPaintEngine);
806 QRasterPaintEngineState *s = state();
807 // must set clip prior to setup, as setup uses it...
808 s->brushData.clip = d->clip();
809 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
810 if (s->fillFlags & DirtyTransform
811 || brush.transform().type() >= QTransform::TxNone)
812 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
813 s->lastBrush = brush;
817 void QRasterPaintEngine::updateOutlineMapper()
819 Q_D(QRasterPaintEngine);
820 d->outlineMapper->setMatrix(state()->matrix);
823 void QRasterPaintEngine::updateState()
825 QRasterPaintEngineState *s = state();
827 if (s->dirty & DirtyTransform)
828 updateMatrix(s->matrix);
830 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
831 const QPainter::CompositionMode mode = s->composition_mode;
832 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
833 && s->intOpacity == 256
834 && (mode == QPainter::CompositionMode_Source
835 || (mode == QPainter::CompositionMode_SourceOver
836 && qAlpha(s->penData.solid.color) == 255));
846 void QRasterPaintEngine::opacityChanged()
848 QRasterPaintEngineState *s = state();
851 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
854 s->fillFlags |= DirtyOpacity;
855 s->strokeFlags |= DirtyOpacity;
856 s->pixmapFlags |= DirtyOpacity;
857 s->dirty |= DirtyOpacity;
858 s->intOpacity = (int) (s->opacity * 256);
864 void QRasterPaintEngine::compositionModeChanged()
866 Q_D(QRasterPaintEngine);
867 QRasterPaintEngineState *s = state();
870 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
873 s->fillFlags |= DirtyCompositionMode;
874 s->dirty |= DirtyCompositionMode;
876 s->strokeFlags |= DirtyCompositionMode;
877 d->rasterBuffer->compositionMode = s->composition_mode;
879 d->recalculateFastImages();
885 void QRasterPaintEngine::renderHintsChanged()
887 QRasterPaintEngineState *s = state();
890 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
893 bool was_aa = s->flags.antialiased;
894 bool was_bilinear = s->flags.bilinear;
896 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
897 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
899 if (was_aa != s->flags.antialiased)
900 s->strokeFlags |= DirtyHints;
902 if (was_bilinear != s->flags.bilinear) {
903 s->strokeFlags |= DirtyPen;
904 s->fillFlags |= DirtyBrush;
907 Q_D(QRasterPaintEngine);
908 d->recalculateFastImages();
914 void QRasterPaintEngine::transformChanged()
916 QRasterPaintEngineState *s = state();
919 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
922 s->fillFlags |= DirtyTransform;
923 s->strokeFlags |= DirtyTransform;
925 s->dirty |= DirtyTransform;
927 Q_D(QRasterPaintEngine);
928 d->recalculateFastImages();
934 void QRasterPaintEngine::clipEnabledChanged()
936 QRasterPaintEngineState *s = state();
939 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
943 s->clip->enabled = s->clipEnabled;
944 s->fillFlags |= DirtyClipEnabled;
945 s->strokeFlags |= DirtyClipEnabled;
946 s->pixmapFlags |= DirtyClipEnabled;
951 void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
953 rasterBuffer->prepare(device);
957 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
959 SrcOverBlendFunc func,
964 if (alpha == 0 || !clip.isValid())
967 Q_ASSERT(img.depth() >= 8);
969 int srcBPL = img.bytesPerLine();
970 const uchar *srcBits = img.bits();
971 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
972 int iw = img.width();
973 int ih = img.height();
978 // Adjust the image according to the source offset...
979 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
982 // adapt the x parameters
983 int x = qRound(pt.x());
985 int cx2 = clip.x() + clip.width();
988 srcBits += srcSize * d;
993 int d = x + iw - cx2;
999 // adapt the y paremeters...
1001 int cy2 = clip.y() + clip.height();
1002 int y = qRound(pt.y());
1005 srcBits += srcBPL * d;
1010 int d = y + ih - cy2;
1016 // call the blend function...
1017 int dstSize = rasterBuffer->bytesPerPixel();
1018 int dstBPL = rasterBuffer->bytesPerLine();
1019 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1026 void QRasterPaintEnginePrivate::systemStateChanged()
1028 QRect clipRect(0, 0,
1029 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1030 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1032 if (!systemClip.isEmpty()) {
1033 QRegion clippedDeviceRgn = systemClip & clipRect;
1034 deviceRect = clippedDeviceRgn.boundingRect();
1035 baseClip->setClipRegion(clippedDeviceRgn);
1037 deviceRect = clipRect;
1038 baseClip->setClipRect(deviceRect);
1040 #ifdef QT_DEBUG_DRAW
1041 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1044 exDeviceRect = deviceRect;
1046 Q_Q(QRasterPaintEngine);
1047 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1048 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1049 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1052 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1054 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1057 Q_Q(QRasterPaintEngine);
1058 bool bilinear = q->state()->flags.bilinear;
1060 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1061 spanData->setupMatrix(b.transform() * m, bilinear);
1063 if (m.type() <= QTransform::TxTranslate) {
1064 // specialize setupMatrix for translation matrices
1065 // to avoid needless matrix inversion
1073 spanData->dx = -m.dx();
1074 spanData->dy = -m.dy();
1075 spanData->txop = m.type();
1076 spanData->bilinear = bilinear;
1077 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1078 spanData->adjustSpanMethods();
1080 spanData->setupMatrix(m, bilinear);
1085 // #define QT_CLIPPING_RATIOS
1087 #ifdef QT_CLIPPING_RATIOS
1092 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1094 if (d->clip()->hasRectClip)
1096 if (d->clip()->hasRegionClip)
1100 if ((totalClips % 5000) == 0) {
1101 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1102 rectClips * 100.0 / (qreal) totalClips,
1103 regionClips * 100.0 / (qreal) totalClips,
1104 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1113 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1115 if (s->flags.has_clip_ownership)
1118 s->flags.has_clip_ownership = false;
1121 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1123 s->fillFlags |= QPaintEngine::DirtyClipPath;
1124 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1125 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1127 d->solid_color_filler.clip = d->clip();
1128 d->solid_color_filler.adjustSpanMethods();
1130 #ifdef QT_DEBUG_DRAW
1131 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1140 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1142 #ifdef QT_DEBUG_DRAW
1143 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1145 if (path.elements()) {
1146 for (int i=0; i<path.elementCount(); ++i) {
1147 qDebug() << " - " << path.elements()[i]
1148 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1151 for (int i=0; i<path.elementCount(); ++i) {
1152 qDebug() << " ---- "
1153 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1158 Q_D(QRasterPaintEngine);
1159 QRasterPaintEngineState *s = state();
1161 const qreal *points = path.points();
1162 const QPainterPath::ElementType *types = path.elements();
1164 // There are some cases that are not supported by clip(QRect)
1165 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1166 if (s->matrix.type() <= QTransform::TxScale
1167 && ((path.shape() == QVectorPath::RectangleHint)
1168 || (isRect(points, path.elementCount())
1169 && (!types || (types[0] == QPainterPath::MoveToElement
1170 && types[1] == QPainterPath::LineToElement
1171 && types[2] == QPainterPath::LineToElement
1172 && types[3] == QPainterPath::LineToElement))))) {
1173 #ifdef QT_DEBUG_DRAW
1174 qDebug() << " --- optimizing vector clip to rect clip...";
1177 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1178 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1183 if (op == Qt::NoClip) {
1184 qrasterpaintengine_state_setNoClip(s);
1187 QClipData *base = d->baseClip.data();
1189 // Intersect with current clip when available...
1190 if (op == Qt::IntersectClip && s->clip)
1193 // We always intersect, except when there is nothing to
1194 // intersect with, in which case we simplify the operation to
1196 Qt::ClipOperation isectOp = Qt::IntersectClip;
1198 isectOp = Qt::ReplaceClip;
1200 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1201 newClip->initialize();
1202 ClipData clipData = { base, newClip, isectOp };
1203 ensureOutlineMapper();
1204 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1208 if (s->flags.has_clip_ownership)
1212 s->flags.has_clip_ownership = true;
1214 qrasterpaintengine_dirty_clip(d, s);
1222 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1224 #ifdef QT_DEBUG_DRAW
1225 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1228 QRasterPaintEngineState *s = state();
1230 if (op == Qt::NoClip) {
1231 qrasterpaintengine_state_setNoClip(s);
1233 } else if (s->matrix.type() > QTransform::TxScale) {
1234 QPaintEngineEx::clip(rect, op);
1237 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1238 QPaintEngineEx::clip(rect, op);
1244 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1246 Q_D(QRasterPaintEngine);
1247 QRect clipRect = r & d->deviceRect;
1248 QRasterPaintEngineState *s = state();
1250 if (op == Qt::ReplaceClip || s->clip == 0) {
1252 // No current clip, hence we intersect with sysclip and be
1254 QRegion clipRegion = systemClip();
1255 QClipData *clip = new QClipData(d->rasterBuffer->height());
1257 if (clipRegion.isEmpty())
1258 clip->setClipRect(clipRect);
1260 clip->setClipRegion(clipRegion & clipRect);
1262 if (s->flags.has_clip_ownership)
1266 s->clip->enabled = true;
1267 s->flags.has_clip_ownership = true;
1269 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1270 QClipData *base = s->clip;
1273 if (base->hasRectClip || base->hasRegionClip) {
1274 if (!s->flags.has_clip_ownership) {
1275 s->clip = new QClipData(d->rasterBuffer->height());
1276 s->flags.has_clip_ownership = true;
1278 if (base->hasRectClip)
1279 s->clip->setClipRect(base->clipRect & clipRect);
1281 s->clip->setClipRegion(base->clipRegion & clipRect);
1282 s->clip->enabled = true;
1290 qrasterpaintengine_dirty_clip(d, s);
1298 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1300 #ifdef QT_DEBUG_DRAW
1301 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1304 Q_D(QRasterPaintEngine);
1306 if (region.rectCount() == 1) {
1307 clip(region.boundingRect(), op);
1311 QRasterPaintEngineState *s = state();
1312 const QClipData *clip = d->clip();
1313 const QClipData *baseClip = d->baseClip.data();
1315 if (op == Qt::NoClip) {
1316 qrasterpaintengine_state_setNoClip(s);
1317 } else if (s->matrix.type() > QTransform::TxScale
1318 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1319 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1320 QPaintEngineEx::clip(region, op);
1322 const QClipData *curClip;
1325 if (op == Qt::IntersectClip)
1330 if (s->flags.has_clip_ownership) {
1334 newClip = new QClipData(d->rasterBuffer->height());
1336 s->flags.has_clip_ownership = true;
1339 QRegion r = s->matrix.map(region);
1340 if (curClip->hasRectClip)
1341 newClip->setClipRegion(r & curClip->clipRect);
1342 else if (curClip->hasRegionClip)
1343 newClip->setClipRegion(r & curClip->clipRegion);
1345 qrasterpaintengine_dirty_clip(d, s);
1352 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1354 #ifdef QT_DEBUG_DRAW
1355 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1358 if (!fillData->blend)
1361 Q_D(QRasterPaintEngine);
1363 const QRectF controlPointRect = path.controlPointRect();
1365 QRasterPaintEngineState *s = state();
1366 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1367 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1368 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1369 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1370 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1371 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1373 if (!s->flags.antialiased && !do_clip) {
1374 d->initializeRasterizer(fillData);
1375 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1379 ensureOutlineMapper();
1380 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1383 static void fillRect_normalized(const QRect &r, QSpanData *data,
1384 QRasterPaintEnginePrivate *pe)
1388 bool rectClipped = true;
1391 x1 = qMax(r.x(), data->clip->xmin);
1392 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1393 y1 = qMax(r.y(), data->clip->ymin);
1394 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1395 rectClipped = data->clip->hasRectClip;
1398 x1 = qMax(r.x(), pe->deviceRect.x());
1399 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1400 y1 = qMax(r.y(), pe->deviceRect.y());
1401 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1403 x1 = qMax(r.x(), 0);
1404 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1405 y1 = qMax(r.y(), 0);
1406 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1409 if (x2 <= x1 || y2 <= y1)
1412 const int width = x2 - x1;
1413 const int height = y2 - y1;
1415 bool isUnclipped = rectClipped
1416 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1418 if (pe && isUnclipped) {
1419 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1421 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1422 || (mode == QPainter::CompositionMode_SourceOver
1423 && qAlpha(data->solid.color) == 255)))
1425 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1431 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1433 const int nspans = 256;
1434 QT_FT_Span spans[nspans];
1436 Q_ASSERT(data->blend);
1439 int n = qMin(nspans, y2 - y);
1443 spans[i].len = width;
1445 spans[i].coverage = 255;
1449 blend(n, spans, data);
1457 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1459 #ifdef QT_DEBUG_DRAW
1460 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1462 Q_D(QRasterPaintEngine);
1464 QRasterPaintEngineState *s = state();
1468 if (s->brushData.blend) {
1469 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1470 const QRect *r = rects;
1471 const QRect *lastRect = rects + rectCount;
1473 int offset_x = int(s->matrix.dx());
1474 int offset_y = int(s->matrix.dy());
1475 while (r < lastRect) {
1476 QRect rect = r->normalized();
1477 QRect rr = rect.translated(offset_x, offset_y);
1478 fillRect_normalized(rr, &s->brushData, d);
1482 QRectVectorPath path;
1483 for (int i=0; i<rectCount; ++i) {
1485 fill(path, s->brush);
1491 if (s->penData.blend) {
1492 QRectVectorPath path;
1493 if (s->flags.fast_pen) {
1494 QCosmeticStroker stroker(s, d->deviceRect);
1495 for (int i = 0; i < rectCount; ++i) {
1497 stroker.drawPath(path);
1500 for (int i = 0; i < rectCount; ++i) {
1502 stroke(path, s->pen);
1511 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1513 #ifdef QT_DEBUG_DRAW
1514 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1516 #ifdef QT_FAST_SPANS
1517 Q_D(QRasterPaintEngine);
1519 QRasterPaintEngineState *s = state();
1522 if (s->flags.tx_noshear) {
1524 if (s->brushData.blend) {
1525 d->initializeRasterizer(&s->brushData);
1526 for (int i = 0; i < rectCount; ++i) {
1527 const QRectF &rect = rects[i].normalized();
1530 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1531 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1532 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1537 if (s->penData.blend) {
1538 QRectVectorPath path;
1539 if (s->flags.fast_pen) {
1540 QCosmeticStroker stroker(s, d->deviceRect);
1541 for (int i = 0; i < rectCount; ++i) {
1543 stroker.drawPath(path);
1546 for (int i = 0; i < rectCount; ++i) {
1548 QPaintEngineEx::stroke(path, s->lastPen);
1555 #endif // QT_FAST_SPANS
1556 QPaintEngineEx::drawRects(rects, rectCount);
1563 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1565 Q_D(QRasterPaintEngine);
1566 QRasterPaintEngineState *s = state();
1569 if (!s->penData.blend)
1572 if (s->flags.fast_pen) {
1573 QCosmeticStroker stroker(s, d->deviceRect);
1574 stroker.drawPath(path);
1575 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1576 qreal width = s->lastPen.isCosmetic()
1577 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1578 : qpen_widthf(s->lastPen) * s->txscale;
1580 qreal dashOffset = s->lastPen.dashOffset();
1582 qreal patternLength = 0;
1583 const QVector<qreal> pattern = s->lastPen.dashPattern();
1584 for (int i = 0; i < pattern.size(); ++i)
1585 patternLength += pattern.at(i);
1587 if (patternLength > 0) {
1588 int n = qFloor(dashOffset / patternLength);
1589 dashOffset -= n * patternLength;
1590 while (dashOffset >= pattern.at(dashIndex)) {
1591 dashOffset -= pattern.at(dashIndex);
1592 if (++dashIndex >= pattern.size())
1598 Q_D(QRasterPaintEngine);
1599 d->initializeRasterizer(&s->penData);
1600 int lineCount = path.elementCount() / 2;
1601 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1603 for (int i = 0; i < lineCount; ++i) {
1604 if (lines[i].p1() == lines[i].p2()) {
1605 if (s->lastPen.capStyle() != Qt::FlatCap) {
1606 QPointF p = lines[i].p1();
1607 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1608 QPointF(p.x() + width*0.5, p.y())));
1609 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1614 const QLineF line = s->matrix.map(lines[i]);
1615 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1616 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1617 width / line.length(),
1618 s->lastPen.capStyle() == Qt::SquareCap);
1620 d->rasterizeLine_dashed(line, width,
1621 &dashIndex, &dashOffset, &inDash);
1626 QPaintEngineEx::stroke(path, pen);
1629 static inline QRect toNormalizedFillRect(const QRectF &rect)
1631 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1632 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1633 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1634 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1641 return QRect(x1, y1, x2 - x1, y2 - y1);
1647 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1651 #ifdef QT_DEBUG_DRAW
1652 QRectF rf = path.controlPointRect();
1653 qDebug() << "QRasterPaintEngine::fill(): "
1654 << "size=" << path.elementCount()
1655 << ", hints=" << hex << path.hints()
1659 Q_D(QRasterPaintEngine);
1660 QRasterPaintEngineState *s = state();
1663 if (!s->brushData.blend)
1666 if (path.shape() == QVectorPath::RectangleHint) {
1667 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1668 const qreal *p = path.points();
1669 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1670 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1671 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1675 if (s->flags.tx_noshear) {
1676 d->initializeRasterizer(&s->brushData);
1677 // ### Is normalizing really necessary here?
1678 const qreal *p = path.points();
1679 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1681 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1682 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1683 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1689 // ### Optimize for non transformed ellipses and rectangles...
1690 QRectF cpRect = path.controlPointRect();
1691 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1692 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1695 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1696 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1697 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1698 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1700 // ### Falonc: implement....
1701 // if (!s->flags.antialiased && !do_clip) {
1702 // d->initializeRasterizer(&s->brushData);
1703 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1707 ensureOutlineMapper();
1708 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1711 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1713 Q_D(QRasterPaintEngine);
1714 QRasterPaintEngineState *s = state();
1716 if (!s->flags.antialiased) {
1717 uint txop = s->matrix.type();
1718 if (txop == QTransform::TxNone) {
1719 fillRect_normalized(toNormalizedFillRect(r), data, d);
1721 } else if (txop == QTransform::TxTranslate) {
1722 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1723 fillRect_normalized(rr, data, d);
1725 } else if (txop == QTransform::TxScale) {
1726 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1727 fillRect_normalized(rr, data, d);
1732 if (s->flags.tx_noshear) {
1733 d->initializeRasterizer(data);
1734 QRectF nr = r.normalized();
1735 if (!nr.isEmpty()) {
1736 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1737 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1738 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1745 ensureOutlineMapper();
1746 fillPath(path, data);
1752 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1754 #ifdef QT_DEBUG_DRAW
1755 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1757 QRasterPaintEngineState *s = state();
1760 if (!s->brushData.blend)
1763 fillRect(r, &s->brushData);
1769 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1771 #ifdef QT_DEBUG_DRAW
1772 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1774 Q_D(QRasterPaintEngine);
1775 QRasterPaintEngineState *s = state();
1777 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1778 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1779 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1782 d->solid_color_filler.clip = d->clip();
1783 d->solid_color_filler.adjustSpanMethods();
1784 fillRect(r, &d->solid_color_filler);
1787 static inline bool isAbove(const QPointF *a, const QPointF *b)
1789 return a->y() < b->y();
1792 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1797 Q_ASSERT(pointCount >= 2);
1799 QVector<const QPointF *> sorted;
1800 sorted.reserve(pointCount);
1802 upper->reserve(pointCount * 3 / 4);
1803 lower->reserve(pointCount * 3 / 4);
1805 for (int i = 0; i < pointCount; ++i)
1806 sorted << points + i;
1808 qSort(sorted.begin(), sorted.end(), isAbove);
1810 qreal splitY = sorted.at(sorted.size() / 2)->y();
1812 const QPointF *end = points + pointCount;
1813 const QPointF *last = end - 1;
1815 QVector<QPointF> *bin[2] = { upper, lower };
1817 for (const QPointF *p = points; p < end; ++p) {
1818 int side = p->y() < splitY;
1819 int lastSide = last->y() < splitY;
1821 if (side != lastSide) {
1822 if (qFuzzyCompare(p->y(), splitY)) {
1823 bin[!side]->append(*p);
1824 } else if (qFuzzyCompare(last->y(), splitY)) {
1825 bin[side]->append(*last);
1827 QPointF delta = *p - *last;
1828 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1830 bin[0]->append(intersection);
1831 bin[1]->append(intersection);
1835 bin[side]->append(*p);
1840 // give up if we couldn't reduce the point count
1841 return upper->size() < pointCount && lower->size() < pointCount;
1847 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1849 Q_D(QRasterPaintEngine);
1850 QRasterPaintEngineState *s = state();
1852 const int maxPoints = 0xffff;
1854 // max amount of points that raster engine can reliably handle
1855 if (pointCount > maxPoints) {
1856 QVector<QPointF> upper, lower;
1858 if (splitPolygon(points, pointCount, &upper, &lower)) {
1859 fillPolygon(upper.constData(), upper.size(), mode);
1860 fillPolygon(lower.constData(), lower.size(), mode);
1862 qWarning("Polygon too complex for filling.");
1867 // Compose polygon fill..,
1868 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1869 ensureOutlineMapper();
1870 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1873 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1875 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1881 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1883 Q_D(QRasterPaintEngine);
1884 QRasterPaintEngineState *s = state();
1886 #ifdef QT_DEBUG_DRAW
1887 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1888 for (int i=0; i<pointCount; ++i)
1889 qDebug() << " - " << points[i];
1891 Q_ASSERT(pointCount >= 2);
1893 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1894 QRectF r(points[0], points[2]);
1900 if (mode != PolylineMode) {
1903 if (s->brushData.blend) {
1904 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1905 fillPolygon(points, pointCount, mode);
1906 d->outlineMapper->setCoordinateRounding(false);
1910 // Do the outline...
1911 if (s->penData.blend) {
1912 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1913 if (s->flags.fast_pen) {
1914 QCosmeticStroker stroker(s, d->deviceRect);
1915 stroker.drawPath(vp);
1917 QPaintEngineEx::stroke(vp, s->lastPen);
1925 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1927 Q_D(QRasterPaintEngine);
1928 QRasterPaintEngineState *s = state();
1930 #ifdef QT_DEBUG_DRAW
1931 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1932 for (int i=0; i<pointCount; ++i)
1933 qDebug() << " - " << points[i];
1935 Q_ASSERT(pointCount >= 2);
1936 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1937 QRect r(points[0].x(),
1939 points[2].x() - points[0].x(),
1940 points[2].y() - points[0].y());
1948 if (mode != PolylineMode) {
1950 if (s->brushData.blend) {
1951 // Compose polygon fill..,
1952 ensureOutlineMapper();
1953 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1954 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1955 d->outlineMapper->moveTo(*points);
1956 const QPoint *p = points;
1957 const QPoint *ep = points + pointCount - 1;
1959 d->outlineMapper->lineTo(*(++p));
1961 d->outlineMapper->endOutline();
1964 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1966 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1967 d->outlineMapper->setCoordinateRounding(false);
1971 // Do the outline...
1972 if (s->penData.blend) {
1973 int count = pointCount * 2;
1974 QVarLengthArray<qreal> fpoints(count);
1975 for (int i=0; i<count; ++i)
1976 fpoints[i] = ((int *) points)[i];
1977 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1979 if (s->flags.fast_pen) {
1980 QCosmeticStroker stroker(s, d->deviceRect);
1981 stroker.drawPath(vp);
1983 QPaintEngineEx::stroke(vp, s->lastPen);
1991 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1993 #ifdef QT_DEBUG_DRAW
1994 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1997 QPixmapData *pd = pixmap.pixmapData();
1998 if (pd->classId() == QPixmapData::RasterClass) {
1999 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2000 if (image.depth() == 1) {
2001 Q_D(QRasterPaintEngine);
2002 QRasterPaintEngineState *s = state();
2003 if (s->matrix.type() <= QTransform::TxTranslate) {
2005 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2007 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2010 QRasterPaintEngine::drawImage(pos, image);
2013 const QImage image = pixmap.toImage();
2014 if (pixmap.depth() == 1) {
2015 Q_D(QRasterPaintEngine);
2016 QRasterPaintEngineState *s = state();
2017 if (s->matrix.type() <= QTransform::TxTranslate) {
2019 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2021 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2024 QRasterPaintEngine::drawImage(pos, image);
2032 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2034 #ifdef QT_DEBUG_DRAW
2035 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2038 QPixmapData* pd = pixmap.pixmapData();
2039 if (pd->classId() == QPixmapData::RasterClass) {
2040 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2041 if (image.depth() == 1) {
2042 Q_D(QRasterPaintEngine);
2043 QRasterPaintEngineState *s = state();
2044 if (s->matrix.type() <= QTransform::TxTranslate
2045 && r.size() == sr.size()
2046 && r.size() == pixmap.size()) {
2048 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2051 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2054 drawImage(r, image, sr);
2057 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2058 const QImage image = pd->toImage(clippedSource);
2059 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2060 if (image.depth() == 1) {
2061 Q_D(QRasterPaintEngine);
2062 QRasterPaintEngineState *s = state();
2063 if (s->matrix.type() <= QTransform::TxTranslate
2064 && r.size() == sr.size()
2065 && r.size() == pixmap.size()) {
2067 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2070 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2073 drawImage(r, image, translatedSource);
2078 // assumes that rect has positive width and height
2079 static inline const QRect toRect_normalized(const QRectF &rect)
2081 const int x = qRound(rect.x());
2082 const int y = qRound(rect.y());
2083 const int w = int(rect.width() + qreal(0.5));
2084 const int h = int(rect.height() + qreal(0.5));
2086 return QRect(x, y, w, h);
2089 static inline int fast_ceil_positive(const qreal &v)
2091 const int iv = int(v);
2098 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2100 const int xmin = int(rect.x());
2101 const int xmax = int(fast_ceil_positive(rect.right()));
2102 const int ymin = int(rect.y());
2103 const int ymax = int(fast_ceil_positive(rect.bottom()));
2104 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2110 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2112 #ifdef QT_DEBUG_DRAW
2113 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2116 Q_D(QRasterPaintEngine);
2117 QRasterPaintEngineState *s = state();
2119 if (s->matrix.type() > QTransform::TxTranslate) {
2120 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2122 QRectF(0, 0, img.width(), img.height()));
2125 const QClipData *clip = d->clip();
2126 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2128 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2129 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2132 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2134 } else if (clip->hasRectClip) {
2135 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2143 d->image_filler.clip = clip;
2144 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2145 if (!d->image_filler.blend)
2147 d->image_filler.dx = -pt.x();
2148 d->image_filler.dy = -pt.y();
2149 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2151 fillRect_normalized(rr, &d->image_filler, d);
2156 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2158 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2169 inline RotationType qRotationType(const QTransform &transform)
2171 QTransform::TransformationType type = transform.type();
2173 if (type > QTransform::TxRotate)
2176 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2177 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2180 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2181 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2184 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2185 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2191 inline bool isPixelAligned(const QRectF &rect) {
2192 return QRectF(rect.toRect()) == rect;
2199 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2200 Qt::ImageConversionFlags)
2202 #ifdef QT_DEBUG_DRAW
2203 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2209 Q_D(QRasterPaintEngine);
2210 QRasterPaintEngineState *s = state();
2211 int sr_l = qFloor(sr.left());
2212 int sr_r = qCeil(sr.right()) - 1;
2213 int sr_t = qFloor(sr.top());
2214 int sr_b = qCeil(sr.bottom()) - 1;
2216 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2217 // as fillRect will apply the aliased coordinate delta we need to
2218 // subtract it here as we don't use it for image drawing
2219 QTransform old = s->matrix;
2220 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2222 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2223 QRgb color = img.pixel(sr_l, sr_t);
2224 switch (img.format()) {
2225 case QImage::Format_ARGB32_Premultiplied:
2226 case QImage::Format_ARGB8565_Premultiplied:
2227 case QImage::Format_ARGB6666_Premultiplied:
2228 case QImage::Format_ARGB8555_Premultiplied:
2229 case QImage::Format_ARGB4444_Premultiplied:
2230 // Combine premultiplied color with the opacity set on the painter.
2231 d->solid_color_filler.solid.color =
2232 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2233 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2236 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2240 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2241 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2245 d->solid_color_filler.clip = d->clip();
2246 d->solid_color_filler.adjustSpanMethods();
2247 fillRect(r, &d->solid_color_filler);
2253 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2255 const QClipData *clip = d->clip();
2257 if (s->matrix.type() > QTransform::TxTranslate
2259 && (!clip || clip->hasRectClip)
2260 && s->intOpacity == 256
2261 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2262 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2263 && d->rasterBuffer->format == img.format()
2264 && (d->rasterBuffer->format == QImage::Format_RGB16
2265 || d->rasterBuffer->format == QImage::Format_RGB32
2266 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2267 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2269 RotationType rotationType = qRotationType(s->matrix);
2271 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2272 QRectF transformedTargetRect = s->matrix.mapRect(r);
2274 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2275 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2277 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2278 if (clippedTransformedTargetRect.isNull())
2281 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2283 QRect clippedSourceRect
2284 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2285 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2287 uint dbpl = d->rasterBuffer->bytesPerLine();
2288 uint sbpl = img.bytesPerLine();
2290 uchar *dst = d->rasterBuffer->buffer();
2291 uint bpp = img.depth() >> 3;
2293 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2294 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2296 uint cw = clippedSourceRect.width();
2297 uint ch = clippedSourceRect.height();
2299 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2306 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2308 QRectF targetBounds = s->matrix.mapRect(r);
2309 bool exceedsPrecision = targetBounds.width() > 0xffff
2310 || targetBounds.height() > 0xffff;
2312 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2313 if (s->matrix.type() > QTransform::TxScale) {
2314 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2315 if (func && (!clip || clip->hasRectClip)) {
2316 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2317 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2318 s->matrix, s->intOpacity);
2322 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2323 if (func && (!clip || clip->hasRectClip)) {
2324 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2325 img.bits(), img.bytesPerLine(),
2326 qt_mapRect_non_normalizing(r, s->matrix), sr,
2327 !clip ? d->deviceRect : clip->clipRect,
2334 QTransform copy = s->matrix;
2335 copy.translate(r.x(), r.y());
2337 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2338 copy.translate(-sr.x(), -sr.y());
2340 d->image_filler_xform.clip = clip;
2341 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2342 if (!d->image_filler_xform.blend)
2344 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2346 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2347 QRectF rr = s->matrix.mapRect(r);
2349 const int x1 = qRound(rr.x());
2350 const int y1 = qRound(rr.y());
2351 const int x2 = qRound(rr.right());
2352 const int y2 = qRound(rr.bottom());
2354 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2358 #ifdef QT_FAST_SPANS
2360 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2361 d->initializeRasterizer(&d->image_filler_xform);
2362 d->rasterizer->setAntialiased(s->flags.antialiased);
2364 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2366 const QRectF &rect = r.normalized();
2367 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2368 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2370 if (s->flags.tx_noshear)
2371 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2373 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2377 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2380 QTransform m = s->matrix;
2381 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2382 m.m21(), m.m22(), m.m23(),
2383 m.m31() - offs, m.m32() - offs, m.m33());
2384 fillPath(path, &d->image_filler_xform);
2387 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2388 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2390 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2392 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2394 } else if (clip->hasRectClip) {
2395 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2401 d->image_filler.clip = clip;
2402 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2403 if (!d->image_filler.blend)
2405 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2406 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2409 rr.translate(s->matrix.dx(), s->matrix.dy());
2411 const int x1 = qRound(rr.x());
2412 const int y1 = qRound(rr.y());
2413 const int x2 = qRound(rr.right());
2414 const int y2 = qRound(rr.bottom());
2416 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2423 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2425 #ifdef QT_DEBUG_DRAW
2426 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2428 Q_D(QRasterPaintEngine);
2429 QRasterPaintEngineState *s = state();
2433 QPixmapData *pd = pixmap.pixmapData();
2434 if (pd->classId() == QPixmapData::RasterClass) {
2435 image = static_cast<QRasterPixmapData *>(pd)->image;
2437 image = pixmap.toImage();
2440 if (image.depth() == 1)
2441 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2443 if (s->matrix.type() > QTransform::TxTranslate) {
2444 QTransform copy = s->matrix;
2445 copy.translate(r.x(), r.y());
2446 copy.translate(-sr.x(), -sr.y());
2447 d->image_filler_xform.clip = d->clip();
2448 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2449 if (!d->image_filler_xform.blend)
2451 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2453 #ifdef QT_FAST_SPANS
2455 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2456 d->initializeRasterizer(&d->image_filler_xform);
2457 d->rasterizer->setAntialiased(s->flags.antialiased);
2459 const QRectF &rect = r.normalized();
2460 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2461 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2462 if (s->flags.tx_noshear)
2463 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2465 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2471 fillPath(path, &d->image_filler_xform);
2473 d->image_filler.clip = d->clip();
2475 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2476 if (!d->image_filler.blend)
2478 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2479 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2482 rr.translate(s->matrix.dx(), s->matrix.dy());
2483 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2489 static inline bool monoVal(const uchar* s, int x)
2491 return (s[x>>3] << (x&7)) & 0x80;
2497 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2499 Q_D(QRasterPaintEngine);
2500 QRasterPaintEngineState *s = state();
2502 if (!s->penData.blend)
2505 QRasterBuffer *rb = d->rasterBuffer.data();
2507 const QRect rect(rx, ry, w, h);
2508 const QClipData *clip = d->clip();
2509 bool unclipped = false;
2511 // inlined QRect::intersects
2512 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2513 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2515 if (clip->hasRectClip) {
2516 unclipped = rx > clip->xmin
2517 && rx + w < clip->xmax
2519 && ry + h < clip->ymax;
2525 // inlined QRect::intersects
2526 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2527 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2531 // inlined QRect::contains
2532 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2533 && rect.top() >= 0 && rect.bottom() < rb->height();
2535 unclipped = contains && d->isUnclipped_normalized(rect);
2538 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2539 const uchar * scanline = static_cast<const uchar *>(src);
2541 if (s->flags.fast_text) {
2544 if (s->penData.bitmapBlit) {
2545 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2546 scanline, w, h, bpl);
2549 } else if (depth == 8) {
2550 if (s->penData.alphamapBlit) {
2551 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2552 scanline, w, h, bpl, 0);
2555 } else if (depth == 32) {
2556 // (A)RGB Alpha mask where the alpha component is not used.
2557 if (s->penData.alphaRGBBlit) {
2558 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2559 (const uint *) scanline, w, h, bpl / 4, 0);
2563 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2564 // (A)RGB Alpha mask where the alpha component is not used.
2566 int nx = qMax(0, rx);
2567 int ny = qMax(0, ry);
2569 // Move scanline pointer to compensate for moved x and y
2570 int xdiff = nx - rx;
2571 int ydiff = ny - ry;
2572 scanline += ydiff * bpl;
2573 scanline += xdiff * (depth == 32 ? 4 : 1);
2578 if (nx + w > d->rasterBuffer->width())
2579 w = d->rasterBuffer->width() - nx;
2580 if (ny + h > d->rasterBuffer->height())
2581 h = d->rasterBuffer->height() - ny;
2586 if (depth == 8 && s->penData.alphamapBlit) {
2587 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2588 scanline, w, h, bpl, clip);
2589 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2590 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2591 (const uint *) scanline, w, h, bpl / 4, clip);
2606 scanline += bpl * y0;
2610 w = qMin(w, rb->width() - qMax(0, rx));
2611 h = qMin(h, rb->height() - qMax(0, ry));
2613 if (w <= 0 || h <= 0)
2616 const int NSPANS = 256;
2617 QSpan spans[NSPANS];
2620 const int x1 = x0 + w;
2621 const int y1 = y0 + h;
2624 for (int y = y0; y < y1; ++y) {
2625 for (int x = x0; x < x1; ) {
2626 if (!monoVal(scanline, x)) {
2631 if (current == NSPANS) {
2632 blend(current, spans, &s->penData);
2635 spans[current].x = x + rx;
2636 spans[current].y = y + ry;
2637 spans[current].coverage = 255;
2640 // extend span until we find a different one.
2641 while (x < x1 && monoVal(scanline, x)) {
2645 spans[current].len = len;
2650 } else if (depth == 8) {
2651 for (int y = y0; y < y1; ++y) {
2652 for (int x = x0; x < x1; ) {
2653 // Skip those with 0 coverage
2654 if (scanline[x] == 0) {
2659 if (current == NSPANS) {
2660 blend(current, spans, &s->penData);
2663 int coverage = scanline[x];
2664 spans[current].x = x + rx;
2665 spans[current].y = y + ry;
2666 spans[current].coverage = coverage;
2670 // extend span until we find a different one.
2671 while (x < x1 && scanline[x] == coverage) {
2675 spans[current].len = len;
2680 } else { // 32-bit alpha...
2681 uint *sl = (uint *) src;
2682 for (int y = y0; y < y1; ++y) {
2683 for (int x = x0; x < x1; ) {
2684 // Skip those with 0 coverage
2685 if ((sl[x] & 0x00ffffff) == 0) {
2690 if (current == NSPANS) {
2691 blend(current, spans, &s->penData);
2694 uint rgbCoverage = sl[x];
2695 int coverage = qGreen(rgbCoverage);
2696 spans[current].x = x + rx;
2697 spans[current].y = y + ry;
2698 spans[current].coverage = coverage;
2702 // extend span until we find a different one.
2703 while (x < x1 && sl[x] == rgbCoverage) {
2707 spans[current].len = len;
2710 sl += bpl / sizeof(uint);
2713 // qDebug() << "alphaPenBlt: num spans=" << current
2714 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2715 // Call span func for current set of spans.
2717 blend(current, spans, &s->penData);
2720 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2721 const QFixedPoint *positions, QFontEngine *fontEngine)
2723 Q_D(QRasterPaintEngine);
2724 QRasterPaintEngineState *s = state();
2726 #if !defined(QT_NO_FREETYPE)
2727 if (fontEngine->type() == QFontEngine::Freetype) {
2728 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2729 QFontEngineFT::GlyphFormat neededFormat =
2730 painter()->device()->devType() == QInternal::Widget
2731 ? fe->defaultGlyphFormat()
2732 : QFontEngineFT::Format_A8;
2734 if (d_func()->mono_surface
2735 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2737 neededFormat = QFontEngineFT::Format_Mono;
2739 if (neededFormat == QFontEngineFT::Format_None)
2740 neededFormat = QFontEngineFT::Format_A8;
2742 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2743 if (s->matrix.type() >= QTransform::TxScale) {
2744 if (s->matrix.isAffine())
2745 gset = fe->loadTransformedGlyphSet(s->matrix);
2750 if (!gset || gset->outline_drawing
2751 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2754 FT_Face lockedFace = 0;
2757 switch (neededFormat) {
2758 case QFontEngineFT::Format_Mono:
2761 case QFontEngineFT::Format_A8:
2764 case QFontEngineFT::Format_A32:
2772 for (int i = 0; i < numGlyphs; i++) {
2773 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2774 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2776 if (!glyph || glyph->format != neededFormat) {
2778 lockedFace = fe->lockFace();
2779 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2782 if (!glyph || !glyph->data)
2786 switch (neededFormat) {
2787 case QFontEngineFT::Format_Mono:
2788 pitch = ((glyph->width + 31) & ~31) >> 3;
2790 case QFontEngineFT::Format_A8:
2791 pitch = (glyph->width + 3) & ~3;
2793 case QFontEngineFT::Format_A32:
2794 pitch = glyph->width * 4;
2801 alphaPenBlt(glyph->data, pitch, depth,
2802 qFloor(positions[i].x) + glyph->x,
2803 qFloor(positions[i].y) - glyph->y,
2804 glyph->width, glyph->height);
2811 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2813 QImageTextureGlyphCache *cache =
2814 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2816 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2817 fontEngine->setGlyphCache(0, cache);
2820 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2821 cache->fillInPendingGlyphs();
2823 const QImage &image = cache->image();
2824 int bpl = image.bytesPerLine();
2826 int depth = image.depth();
2830 leftShift = 2; // multiply by 4
2831 else if (depth == 1)
2832 rightShift = 3; // divide by 8
2834 int margin = cache->glyphMargin();
2835 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2836 const uchar *bits = image.bits();
2837 for (int i=0; i<numGlyphs; ++i) {
2839 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2840 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2841 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2845 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2846 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2848 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2851 // c.baseLineX, c.baseLineY,
2854 // positions[i].x.toInt(), positions[i].y.toInt());
2856 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2862 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2863 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2865 Q_D(QRasterPaintEngine);
2866 QRasterPaintEngineState *s = state();
2868 QFontEngine *fontEngine = ti.fontEngine;
2869 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2870 QPaintEngineEx::drawTextItem(p, ti);
2874 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2876 QVarLengthArray<QFixedPoint> positions;
2877 QVarLengthArray<glyph_t> glyphs;
2878 QTransform matrix = s->matrix;
2879 matrix.translate(p.x(), p.y());
2880 if (matrix.type() == QTransform::TxScale)
2881 fe->setFontScale(matrix.m11());
2882 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2884 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2886 for (int i=0; i<glyphs.size(); ++i) {
2887 TOpenFontCharMetrics tmetrics;
2888 const TUint8 *glyphBitmapBytes;
2889 TSize glyphBitmapSize;
2890 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2891 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2892 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2893 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2896 if (matrix.type() == QTransform::TxScale)
2897 fe->setFontScale(1.0);
2901 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2904 * Returns true if the rectangle is completely within the current clip
2905 * state of the paint engine.
2907 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2909 const QClipData *cl = clip();
2911 // inline contains() for performance (we know the rects are normalized)
2912 const QRect &r1 = deviceRect;
2913 return (r.left() >= r1.left() && r.right() <= r1.right()
2914 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2918 if (cl->hasRectClip) {
2919 // currently all painting functions clips to deviceRect internally
2920 if (cl->clipRect == deviceRect)
2923 // inline contains() for performance (we know the rects are normalized)
2924 const QRect &r1 = cl->clipRect;
2925 return (r.left() >= r1.left() && r.right() <= r1.right()
2926 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2928 return qt_region_strictContains(cl->clipRegion, r);
2932 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2935 Q_Q(const QRasterPaintEngine);
2936 const QRasterPaintEngineState *s = q->state();
2937 const QClipData *cl = clip();
2939 QRect r = rect.normalized();
2940 // inline contains() for performance (we know the rects are normalized)
2941 const QRect &r1 = deviceRect;
2942 return (r.left() >= r1.left() && r.right() <= r1.right()
2943 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2947 // currently all painting functions that call this function clip to deviceRect internally
2948 if (cl->hasRectClip && cl->clipRect == deviceRect)
2951 if (s->flags.antialiased)
2954 QRect r = rect.normalized();
2956 r.setX(r.x() - penWidth);
2957 r.setY(r.y() - penWidth);
2958 r.setWidth(r.width() + 2 * penWidth);
2959 r.setHeight(r.height() + 2 * penWidth);
2962 if (cl->hasRectClip) {
2963 // inline contains() for performance (we know the rects are normalized)
2964 const QRect &r1 = cl->clipRect;
2965 return (r.left() >= r1.left() && r.right() <= r1.right()
2966 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2968 return qt_region_strictContains(cl->clipRegion, r);
2972 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2975 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2979 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2980 const QSpanData *data) const
2982 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2986 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2987 const QSpanData *data) const
2989 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2993 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2994 const QSpanData *data) const
2996 Q_Q(const QRasterPaintEngine);
2997 const QRasterPaintEngineState *s = q->state();
2999 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3001 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3002 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3008 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3013 QFontEngine *fontEngine = textItem->fontEngine();
3014 if (!supportsTransformations(fontEngine)) {
3015 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3018 QPaintEngineEx::drawStaticTextItem(textItem);
3025 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3027 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3028 QRasterPaintEngineState *s = state();
3030 #ifdef QT_DEBUG_DRAW
3031 Q_D(QRasterPaintEngine);
3032 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3033 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3040 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3042 if (!supportsTransformations(ti.fontEngine)) {
3043 QVarLengthArray<QFixedPoint> positions;
3044 QVarLengthArray<glyph_t> glyphs;
3046 QTransform matrix = s->matrix;
3047 matrix.translate(p.x(), p.y());
3049 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3051 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3055 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3056 if (s->matrix.type() <= QTransform::TxTranslate
3057 || (s->matrix.type() == QTransform::TxScale
3058 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3059 drawGlyphsS60(p, ti);
3062 #else // Q_WS_WIN || Q_WS_MAC
3064 QFontEngine *fontEngine = ti.fontEngine;
3066 #if defined(Q_WS_QWS)
3067 if (fontEngine->type() == QFontEngine::Box) {
3068 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3072 if (s->matrix.type() < QTransform::TxScale
3073 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3074 || (fontEngine->type() == QFontEngine::Proxy
3075 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3077 fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
3083 if (s->matrix.type() < QTransform::TxScale) {
3085 QVarLengthArray<QFixedPoint> positions;
3086 QVarLengthArray<glyph_t> glyphs;
3087 QTransform matrix = state()->transform();
3089 qreal _x = qFloor(p.x());
3090 qreal _y = qFloor(p.y());
3091 matrix.translate(_x, _y);
3093 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3094 if (glyphs.size() == 0)
3097 for(int i = 0; i < glyphs.size(); i++) {
3098 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3099 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3100 // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here?
3101 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3102 qRound(positions[i].x + metrics.x),
3103 qRound(positions[i].y + metrics.y),
3104 img.width(), img.height());
3110 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3112 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3113 if (fontEngine->type() == QFontEngine::QPF2) {
3114 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3115 if (renderingEngine)
3116 fontEngine = renderingEngine;
3120 if (fontEngine->type() != QFontEngine::Freetype) {
3121 QPaintEngineEx::drawTextItem(p, ti);
3125 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3127 QTransform matrix = s->matrix;
3128 matrix.translate(p.x(), p.y());
3130 QVarLengthArray<QFixedPoint> positions;
3131 QVarLengthArray<glyph_t> glyphs;
3132 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3133 if (glyphs.size() == 0)
3136 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3137 QPaintEngine::drawTextItem(p, ti);
3143 QPaintEngineEx::drawTextItem(p, ti);
3149 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3151 Q_D(QRasterPaintEngine);
3152 QRasterPaintEngineState *s = state();
3155 if (!s->penData.blend)
3158 if (!s->flags.fast_pen) {
3159 QPaintEngineEx::drawPoints(points, pointCount);
3163 QCosmeticStroker stroker(s, d->deviceRect);
3164 stroker.drawPoints(points, pointCount);
3168 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3170 Q_D(QRasterPaintEngine);
3171 QRasterPaintEngineState *s = state();
3174 if (!s->penData.blend)
3177 if (!s->flags.fast_pen) {
3178 QPaintEngineEx::drawPoints(points, pointCount);
3182 QCosmeticStroker stroker(s, d->deviceRect);
3183 stroker.drawPoints(points, pointCount);
3189 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3191 #ifdef QT_DEBUG_DRAW
3192 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3194 Q_D(QRasterPaintEngine);
3195 QRasterPaintEngineState *s = state();
3198 if (!s->penData.blend)
3201 if (s->flags.fast_pen) {
3202 QCosmeticStroker stroker(s, d->deviceRect);
3203 for (int i=0; i<lineCount; ++i) {
3204 const QLine &l = lines[i];
3205 stroker.drawLine(l.p1(), l.p2());
3208 QPaintEngineEx::drawLines(lines, lineCount);
3212 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3218 Q_Q(QRasterPaintEngine);
3219 QRasterPaintEngineState *s = q->state();
3221 const QPen &pen = s->lastPen;
3222 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3223 const QVector<qreal> pattern = pen.dashPattern();
3225 qreal patternLength = 0;
3226 for (int i = 0; i < pattern.size(); ++i)
3227 patternLength += pattern.at(i);
3229 if (patternLength <= 0)
3232 qreal length = line.length();
3233 Q_ASSERT(length > 0);
3234 while (length > 0) {
3235 const bool rasterize = *inDash;
3236 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3239 if (dash >= length) {
3241 *dashOffset += dash / width;
3245 *inDash = !(*inDash);
3246 if (++*dashIndex >= pattern.size())
3253 if (rasterize && dash > 0)
3254 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3261 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3263 #ifdef QT_DEBUG_DRAW
3264 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3266 Q_D(QRasterPaintEngine);
3267 QRasterPaintEngineState *s = state();
3270 if (!s->penData.blend)
3272 if (s->flags.fast_pen) {
3273 QCosmeticStroker stroker(s, d->deviceRect);
3274 for (int i=0; i<lineCount; ++i) {
3275 QLineF line = lines[i];
3276 stroker.drawLine(line.p1(), line.p2());
3279 QPaintEngineEx::drawLines(lines, lineCount);
3287 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3289 Q_D(QRasterPaintEngine);
3290 QRasterPaintEngineState *s = state();
3293 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3294 || (qpen_style(s->lastPen) == Qt::NoPen))
3295 && !s->flags.antialiased
3296 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3298 && s->matrix.type() <= QTransform::TxScale) // no shear
3301 const QRectF r = s->matrix.mapRect(rect);
3302 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3303 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3304 const QRect brect = QRect(int(r.x()), int(r.y()),
3305 int_dim(r.x(), r.width()),
3306 int_dim(r.y(), r.height()));
3308 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3309 &s->penData, &s->brushData);
3313 QPaintEngineEx::drawEllipse(rect);
3320 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3322 Q_D(QRasterPaintEngine);
3329 CGContextRef QRasterPaintEngine::getCGContext() const
3331 Q_D(const QRasterPaintEngine);
3332 return d->cgContext;
3340 void QRasterPaintEngine::setDC(HDC hdc) {
3341 Q_D(QRasterPaintEngine);
3348 HDC QRasterPaintEngine::getDC() const
3350 Q_D(const QRasterPaintEngine);
3357 void QRasterPaintEngine::releaseDC(HDC) const
3363 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3365 const QTransform &m = state()->matrix;
3366 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3367 QFontEngine::Type fontEngineType = fontEngine->type();
3368 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3369 || (m.type() <= QTransform::TxTranslate
3370 && (fontEngineType == QFontEngine::TestFontEngine
3371 || fontEngineType == QFontEngine::Box))) {
3375 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3378 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3380 #if defined(Q_WS_MAC)
3381 // Mac font engines don't support scaling and rotation
3382 if (m.type() > QTransform::TxTranslate)
3384 if (m.type() >= QTransform::TxProject)
3388 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3397 QPoint QRasterPaintEngine::coordinateOffset() const
3399 return QPoint(0, 0);
3403 Draws the given color \a spans with the specified \a color. The \a
3404 count parameter specifies the number of spans.
3406 The default implementation does nothing; reimplement this function
3407 to draw the given color \a spans with the specified \a color. Note
3408 that this function \e must be reimplemented if the framebuffer is
3411 \sa drawBufferSpan()
3413 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3414 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3419 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3420 "a non memory-mapped device");
3424 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3426 Draws the given \a buffer.
3428 The default implementation does nothing; reimplement this function
3429 to draw a buffer that contains more than one color. Note that this
3430 function \e must be reimplemented if the framebuffer is not
3433 The \a size parameter specifies the total size of the given \a
3434 buffer, while the \a length parameter specifies the number of
3435 pixels to draw. The buffer's position is given by (\a x, \a
3436 y). The provided \a alpha value is added to each pixel in the
3437 buffer when drawing.
3439 \sa drawColorSpans()
3441 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3442 int x, int y, int length, uint const_alpha)
3449 Q_UNUSED(const_alpha);
3450 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3451 "a non memory-mapped device");
3455 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3460 Q_D(QRasterPaintEngine);
3462 Q_ASSERT(image.depth() == 1);
3464 const int spanCount = 256;
3465 QT_FT_Span spans[spanCount];
3469 int w = image.width();
3470 int h = image.height();
3471 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3472 int ymin = qMax(qRound(pos.y()), 0);
3473 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3474 int xmin = qMax(qRound(pos.x()), 0);
3476 int x_offset = xmin - qRound(pos.x());
3478 QImage::Format format = image.format();
3479 for (int y = ymin; y < ymax; ++y) {
3480 const uchar *src = image.scanLine(y - qRound(pos.y()));
3481 if (format == QImage::Format_MonoLSB) {
3482 for (int x = 0; x < xmax - xmin; ++x) {
3483 int src_x = x + x_offset;
3484 uchar pixel = src[src_x >> 3];
3489 if (pixel & (0x1 << (src_x & 7))) {
3490 spans[n].x = xmin + x;
3492 spans[n].coverage = 255;
3494 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3498 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3501 if (n == spanCount) {
3502 fg->blend(n, spans, fg);
3508 for (int x = 0; x < xmax - xmin; ++x) {
3509 int src_x = x + x_offset;
3510 uchar pixel = src[src_x >> 3];
3515 if (pixel & (0x80 >> (x & 7))) {
3516 spans[n].x = xmin + x;
3518 spans[n].coverage = 255;
3520 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3524 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3527 if (n == spanCount) {
3528 fg->blend(n, spans, fg);
3536 fg->blend(n, spans, fg);
3542 \enum QRasterPaintEngine::ClipType
3545 \value RectClip Indicates that the currently set clip is a single rectangle.
3546 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3551 Returns the type of the clip currently set.
3553 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3555 Q_D(const QRasterPaintEngine);
3557 const QClipData *clip = d->clip();
3558 if (!clip || clip->hasRectClip)
3566 Returns the bounding rect of the currently set clip.
3568 QRect QRasterPaintEngine::clipBoundingRect() const
3570 Q_D(const QRasterPaintEngine);
3572 const QClipData *clip = d->clip();
3575 return d->deviceRect;
3577 if (clip->hasRectClip)
3578 return clip->clipRect;
3580 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3583 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3585 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3587 QVarLengthArray<short, 4096> buffer;
3589 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3590 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3591 result->initialize();
3593 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3594 const QSpan *c1_spans = c1ClipLines[y].spans;
3595 int c1_count = c1ClipLines[y].count;
3596 const QSpan *c2_spans = c2ClipLines[y].spans;
3597 int c2_count = c2ClipLines[y].count;
3599 if (c1_count == 0 && c2_count == 0)
3601 if (c1_count == 0) {
3602 result->appendSpans(c2_spans, c2_count);
3604 } else if (c2_count == 0) {
3605 result->appendSpans(c1_spans, c1_count);
3609 // we need to merge the two
3611 // find required length
3612 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3613 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3615 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3617 // Fill with old spans.
3618 for (int i = 0; i < c1_count; ++i) {
3619 const QSpan *cs = c1_spans + i;
3620 for (int j=cs->x; j<cs->x + cs->len; ++j)
3621 buffer[j] = cs->coverage;
3624 // Fill with new spans
3625 for (int i = 0; i < c2_count; ++i) {
3626 const QSpan *cs = c2_spans + i;
3627 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3628 buffer[j] += cs->coverage;
3629 if (buffer[j] > 255)
3637 // Skip to next span
3638 while (x < max && buffer[x] == 0) ++x;
3639 if (x >= max) break;
3642 int coverage = buffer[x];
3644 // Find length of span
3645 while (x < max && buffer[x] == coverage)
3648 result->appendSpan(sx, x - sx, y, coverage);
3653 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3655 Q_Q(QRasterPaintEngine);
3656 QRasterPaintEngineState *s = q->state();
3658 rasterizer->setAntialiased(s->flags.antialiased);
3660 QRect clipRect(deviceRect);
3662 // ### get from optimized rectbased QClipData
3664 const QClipData *c = clip();
3666 const QRect r(QPoint(c->xmin, c->ymin),
3667 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3668 clipRect = clipRect.intersected(r);
3669 blend = data->blend;
3671 blend = data->unclipped_blend;
3674 rasterizer->setClipRect(clipRect);
3675 rasterizer->initialize(blend, data);
3678 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3679 ProcessSpans callback,
3680 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3682 if (!callback || !outline)
3685 Q_Q(QRasterPaintEngine);
3686 QRasterPaintEngineState *s = q->state();
3688 if (!s->flags.antialiased) {
3689 initializeRasterizer(spanData);
3691 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3695 rasterizer->rasterize(outline, fillRule);
3699 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3703 int q_gray_rendered_spans(QT_FT_Raster raster);
3706 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3707 ProcessSpans callback,
3708 void *userData, QRasterBuffer *)
3710 if (!callback || !outline)
3713 Q_Q(QRasterPaintEngine);
3714 QRasterPaintEngineState *s = q->state();
3716 if (!s->flags.antialiased) {
3717 rasterizer->setAntialiased(s->flags.antialiased);
3718 rasterizer->setClipRect(deviceRect);
3719 rasterizer->initialize(callback, userData);
3721 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3725 rasterizer->rasterize(outline, fillRule);
3729 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3730 // minimize memory reallocations. However if initial size for
3731 // raster pool is changed for lower value, reallocations will
3733 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3734 int rasterPoolSize = rasterPoolInitialSize;
3735 unsigned char *rasterPoolBase;
3736 #if defined(Q_WS_WIN64)
3738 // We make use of setjmp and longjmp in qgrayraster.c which requires
3739 // 16-byte alignment, hence we hardcode this requirement here..
3740 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3742 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3743 rasterPoolBase = rasterPoolOnStack;
3745 Q_CHECK_PTR(rasterPoolBase);
3747 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3749 void *data = userData;
3751 QT_FT_BBox clip_box = { deviceRect.x(),
3753 deviceRect.x() + deviceRect.width(),
3754 deviceRect.y() + deviceRect.height() };
3756 QT_FT_Raster_Params rasterParams;
3757 rasterParams.target = 0;
3758 rasterParams.source = outline;
3759 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3760 rasterParams.gray_spans = 0;
3761 rasterParams.black_spans = 0;
3762 rasterParams.bit_test = 0;
3763 rasterParams.bit_set = 0;
3764 rasterParams.user = data;
3765 rasterParams.clip_box = clip_box;
3770 int rendered_spans = 0;
3774 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3775 rasterParams.gray_spans = callback;
3776 rasterParams.skip_spans = rendered_spans;
3777 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3779 // Out of memory, reallocate some more and try again...
3780 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3781 int new_size = rasterPoolSize * 2;
3782 if (new_size > 1024 * 1024) {
3783 qWarning("QPainter: Rasterization of primitive failed");
3787 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3789 #if defined(Q_WS_WIN64)
3790 _aligned_free(rasterPoolBase);
3792 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3793 free(rasterPoolBase);
3796 rasterPoolSize = new_size;
3798 #if defined(Q_WS_WIN64)
3799 // We make use of setjmp and longjmp in qgrayraster.c which requires
3800 // 16-byte alignment, hence we hardcode this requirement here..
3801 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3803 (unsigned char *) malloc(rasterPoolSize);
3805 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3807 qt_ft_grays_raster.raster_done(*grayRaster.data());
3808 qt_ft_grays_raster.raster_new(grayRaster.data());
3809 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3815 #if defined(Q_WS_WIN64)
3816 _aligned_free(rasterPoolBase);
3818 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3819 free(rasterPoolBase);
3823 void QRasterPaintEnginePrivate::recalculateFastImages()
3825 Q_Q(QRasterPaintEngine);
3826 QRasterPaintEngineState *s = q->state();
3828 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3829 && s->matrix.type() <= QTransform::TxShear;
3832 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3834 Q_Q(const QRasterPaintEngine);
3835 const QRasterPaintEngineState *s = q->state();
3837 return s->flags.fast_images
3838 && (mode == QPainter::CompositionMode_SourceOver
3839 || (mode == QPainter::CompositionMode_Source
3840 && !image.hasAlphaChannel()));
3843 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3845 Q_ASSERT(image.depth() == 1);
3847 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3848 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3850 QRgb fg = PREMUL(color.rgba());
3853 int height = sourceImage.height();
3854 int width = sourceImage.width();
3855 for (int y=0; y<height; ++y) {
3856 uchar *source = sourceImage.scanLine(y);
3857 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3858 if (!source || !target)
3859 QT_THROW(std::bad_alloc()); // we must have run out of memory
3860 for (int x=0; x < width; ++x)
3861 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3866 QRasterBuffer::~QRasterBuffer()
3870 void QRasterBuffer::init()
3872 compositionMode = QPainter::CompositionMode_SourceOver;
3873 monoDestinationWithClut = false;
3878 QImage::Format QRasterBuffer::prepare(QImage *image)
3880 m_buffer = (uchar *)image->bits();
3881 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3882 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3883 bytes_per_pixel = image->depth()/8;
3884 bytes_per_line = image->bytesPerLine();
3886 format = image->format();
3887 drawHelper = qDrawHelper + format;
3888 if (image->depth() == 1 && image->colorTable().size() == 2) {
3889 monoDestinationWithClut = true;
3890 destColor0 = PREMUL(image->colorTable()[0]);
3891 destColor1 = PREMUL(image->colorTable()[1]);
3897 void QRasterBuffer::resetBuffer(int val)
3899 memset(m_buffer, val, m_height*bytes_per_line);
3903 #if defined(Q_WS_QWS)
3904 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
3906 m_buffer = reinterpret_cast<uchar*>(device->memory());
3907 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
3908 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
3909 bytes_per_pixel = device->depth() / 8;
3910 bytes_per_line = device->bytesPerLine();
3911 format = device->format();
3912 #ifndef QT_NO_RASTERCALLBACKS
3914 drawHelper = qDrawHelperCallback + format;
3917 drawHelper = qDrawHelper + format;
3920 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
3924 return widget->frameGeometry().width();
3926 return widget->frameGeometry().height();
3931 return qt_paint_device_metric(widget, m);
3934 int QCustomRasterPaintDevice::bytesPerLine() const
3936 return (width() * depth() + 7) / 8;
3939 #elif defined(Q_OS_SYMBIAN)
3941 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
3945 #endif // Q_OS_SYMBIAN
3948 \class QCustomRasterPaintDevice
3953 \brief The QCustomRasterPaintDevice class is provided to activate
3954 hardware accelerated paint engines in Qt for Embedded Linux.
3956 Note that this class is only available in \l{Qt for Embedded Linux}.
3958 In \l{Qt for Embedded Linux}, painting is a pure software
3959 implementation. But starting with Qt 4.2, it is
3960 possible to add an accelerated graphics driver to take advantage
3961 of available hardware resources.
3963 Hardware acceleration is accomplished by creating a custom screen
3964 driver, accelerating the copying from memory to the screen, and
3965 implementing a custom paint engine accelerating the various
3966 painting operations. Then a custom paint device (derived from the
3967 QCustomRasterPaintDevice class) and a custom window surface
3968 (derived from QWSWindowSurface) must be implemented to make
3969 \l{Qt for Embedded Linux} aware of the accelerated driver.
3971 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
3972 documentation for details.
3974 \sa QRasterPaintEngine, QPaintDevice
3978 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
3980 Constructs a custom raster based paint device for the given
3981 top-level \a widget.
3985 \fn int QCustomRasterPaintDevice::bytesPerLine() const
3987 Returns the number of bytes per line in the framebuffer. Note that
3988 this number might be larger than the framebuffer width.
3992 \fn int QCustomRasterPaintDevice::devType() const
3997 \fn QImage::Format QCustomRasterPaintDevice::format() const
3999 Returns the format of the device's memory buffet.
4001 The default format is QImage::Format_ARGB32_Premultiplied. The
4002 only other valid format is QImage::Format_RGB16.
4006 \fn void * QCustomRasterPaintDevice::memory () const
4008 Returns a pointer to the paint device's memory buffer, or 0 if no
4013 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4018 \fn QSize QCustomRasterPaintDevice::size () const
4023 QClipData::QClipData(int height)
4025 clipSpanHeight = height;
4030 xmin = xmax = ymin = ymax = 0;
4034 hasRectClip = hasRegionClip = false;
4037 QClipData::~QClipData()
4045 void QClipData::initialize()
4051 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4053 Q_CHECK_PTR(m_clipLines);
4055 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4056 allocated = clipSpanHeight;
4057 Q_CHECK_PTR(m_spans);
4063 m_clipLines[y].spans = 0;
4064 m_clipLines[y].count = 0;
4068 const int len = clipRect.width();
4071 QSpan *span = m_spans + count;
4075 span->coverage = 255;
4078 m_clipLines[y].spans = span;
4079 m_clipLines[y].count = 1;
4083 while (y < clipSpanHeight) {
4084 m_clipLines[y].spans = 0;
4085 m_clipLines[y].count = 0;
4088 } else if (hasRegionClip) {
4090 const QVector<QRect> rects = clipRegion.rects();
4091 const int numRects = rects.size();
4094 const int maxSpans = (ymax - ymin) * numRects;
4095 if (maxSpans > allocated) {
4096 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4097 allocated = maxSpans;
4102 int firstInBand = 0;
4104 while (firstInBand < numRects) {
4105 const int currMinY = rects.at(firstInBand).y();
4106 const int currMaxY = currMinY + rects.at(firstInBand).height();
4108 while (y < currMinY) {
4109 m_clipLines[y].spans = 0;
4110 m_clipLines[y].count = 0;
4114 int lastInBand = firstInBand;
4115 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4118 while (y < currMaxY) {
4120 m_clipLines[y].spans = m_spans + count;
4121 m_clipLines[y].count = lastInBand - firstInBand + 1;
4123 for (int r = firstInBand; r <= lastInBand; ++r) {
4124 const QRect &currRect = rects.at(r);
4125 QSpan *span = m_spans + count;
4126 span->x = currRect.x();
4127 span->len = currRect.width();
4129 span->coverage = 255;
4135 firstInBand = lastInBand + 1;
4138 Q_ASSERT(count <= allocated);
4140 while (y < clipSpanHeight) {
4141 m_clipLines[y].spans = 0;
4142 m_clipLines[y].count = 0;
4148 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4153 free(m_clipLines); // same for clipLines
4159 void QClipData::fixup()
4164 ymin = ymax = xmin = xmax = 0;
4169 ymin = m_spans[0].y;
4170 ymax = m_spans[count-1].y + 1;
4174 const int firstLeft = m_spans[0].x;
4175 const int firstRight = m_spans[0].x + m_spans[0].len;
4178 for (int i = 0; i < count; ++i) {
4179 QT_FT_Span_& span = m_spans[i];
4182 if (span.y != y + 1 && y != -1)
4185 m_clipLines[y].spans = &span;
4186 m_clipLines[y].count = 1;
4188 ++m_clipLines[y].count;
4190 const int spanLeft = span.x;
4191 const int spanRight = spanLeft + span.len;
4193 if (spanLeft < xmin)
4196 if (spanRight > xmax)
4199 if (spanLeft != firstLeft || spanRight != firstRight)
4205 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4210 Convert \a rect to clip spans.
4212 void QClipData::setClipRect(const QRect &rect)
4214 if (hasRectClip && rect == clipRect)
4217 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4219 hasRegionClip = false;
4223 xmax = rect.x() + rect.width();
4224 ymin = qMin(rect.y(), clipSpanHeight);
4225 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4232 // qDebug() << xmin << xmax << ymin << ymax;
4236 Convert \a region to clip spans.
4238 void QClipData::setClipRegion(const QRegion ®ion)
4240 if (region.rectCount() == 1) {
4241 setClipRect(region.rects().at(0));
4245 hasRegionClip = true;
4246 hasRectClip = false;
4247 clipRegion = region;
4249 { // set bounding rect
4250 const QRect rect = region.boundingRect();
4252 xmax = rect.x() + rect.width();
4254 ymax = rect.y() + rect.height();
4266 spans must be sorted on y
4268 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4269 const QSpan *spans, const QSpan *end,
4270 QSpan **outSpans, int available)
4272 const_cast<QClipData *>(clip)->initialize();
4274 QSpan *out = *outSpans;
4276 const QSpan *clipSpans = clip->m_spans + *currentClip;
4277 const QSpan *clipEnd = clip->m_spans + clip->count;
4279 while (available && spans < end ) {
4280 if (clipSpans >= clipEnd) {
4284 if (clipSpans->y > spans->y) {
4288 if (spans->y != clipSpans->y) {
4289 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4290 clipSpans = clip->m_clipLines[spans->y].spans;
4295 Q_ASSERT(spans->y == clipSpans->y);
4298 int sx2 = sx1 + spans->len;
4299 int cx1 = clipSpans->x;
4300 int cx2 = cx1 + clipSpans->len;
4302 if (cx1 < sx1 && cx2 < sx1) {
4305 } else if (sx1 < cx1 && sx2 < cx1) {
4309 int x = qMax(sx1, cx1);
4310 int len = qMin(sx2, cx2) - x;
4312 out->x = qMax(sx1, cx1);
4313 out->len = qMin(sx2, cx2) - out->x;
4315 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4327 *currentClip = clipSpans - clip->m_spans;
4331 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4333 // qDebug() << "qt_span_fill_clipped" << spanCount;
4334 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4336 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4338 const int NSPANS = 256;
4339 QSpan cspans[NSPANS];
4340 int currentClip = 0;
4341 const QSpan *end = spans + spanCount;
4342 while (spans < end) {
4343 QSpan *clipped = cspans;
4344 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4345 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4346 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4348 if (clipped - cspans)
4349 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4355 Clip spans to \a{clip}-rectangle.
4356 Returns number of unclipped spans
4358 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4361 const short minx = clip.left();
4362 const short miny = clip.top();
4363 const short maxx = clip.right();
4364 const short maxy = clip.bottom();
4367 for (int i = 0; i < numSpans; ++i) {
4368 if (spans[i].y > maxy)
4370 if (spans[i].y < miny
4371 || spans[i].x > maxx
4372 || spans[i].x + spans[i].len <= minx) {
4375 if (spans[i].x < minx) {
4376 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4379 spans[n].x = spans[i].x;
4380 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4382 if (spans[n].len == 0)
4384 spans[n].y = spans[i].y;
4385 spans[n].coverage = spans[i].coverage;
4392 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4395 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4396 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4398 Q_ASSERT(fillData->clip);
4399 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4401 // hw: check if this const_cast<> is safe!!!
4402 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4403 fillData->clip->clipRect);
4405 fillData->unclipped_blend(count, spans, fillData);
4408 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4410 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4412 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4413 // for (int i = 0; i < qMin(count, 10); ++i) {
4414 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4417 switch (clipData->operation) {
4419 case Qt::IntersectClip:
4421 QClipData *newClip = clipData->newClip;
4422 newClip->initialize();
4424 int currentClip = 0;
4425 const QSpan *end = spans + count;
4426 while (spans < end) {
4427 QSpan *newspans = newClip->m_spans + newClip->count;
4428 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4429 &newspans, newClip->allocated - newClip->count);
4430 newClip->count = newspans - newClip->m_spans;
4432 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4433 newClip->allocated *= 2;
4439 case Qt::ReplaceClip:
4440 clipData->newClip->appendSpans(spans, count);
4448 QImage QRasterBuffer::bufferImage() const
4450 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4452 for (int y = 0; y < m_height; ++y) {
4453 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4455 for (int x=0; x<m_width; ++x) {
4456 uint argb = span[x];
4457 image.setPixel(x, y, argb);
4465 void QRasterBuffer::flushToARGBImage(QImage *target) const
4467 int w = qMin(m_width, target->width());
4468 int h = qMin(m_height, target->height());
4470 for (int y=0; y<h; ++y) {
4471 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4472 QRgb *dest = (QRgb *) target->scanLine(y);
4473 for (int x=0; x<w; ++x) {
4474 QRgb pixel = sourceLine[x];
4475 int alpha = qAlpha(pixel);
4479 dest[x] = (alpha << 24)
4480 | ((255*qRed(pixel)/alpha) << 16)
4481 | ((255*qGreen(pixel)/alpha) << 8)
4482 | ((255*qBlue(pixel)/alpha) << 0);
4489 class QGradientCache
4493 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4494 stops(s), opacity(op), interpolationMode(mode) {}
4495 uint buffer[GRADIENT_STOPTABLE_SIZE];
4496 QGradientStops stops;
4498 QGradient::InterpolationMode interpolationMode;
4501 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4504 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4505 quint64 hash_val = 0;
4507 QGradientStops stops = gradient.stops();
4508 for (int i = 0; i < stops.size() && i <= 2; i++)
4509 hash_val += stops[i].second.rgba();
4511 QMutexLocker lock(&mutex);
4512 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4514 if (it == cache.constEnd())
4515 return addCacheElement(hash_val, gradient, opacity);
4518 const CacheInfo &cache_info = it.value();
4519 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4520 return cache_info.buffer;
4522 } while (it != cache.constEnd() && it.key() == hash_val);
4523 // an exact match for these stops and opacity was not found, create new cache
4524 return addCacheElement(hash_val, gradient, opacity);
4528 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4530 inline int maxCacheSize() const { return 60; }
4531 inline void generateGradientColorTable(const QGradient& g,
4533 int size, int opacity) const;
4534 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4535 if (cache.size() == maxCacheSize()) {
4536 // may remove more than 1, but OK
4537 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4539 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4540 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4541 return cache.insert(hash_val, cache_entry).value().buffer;
4544 QGradientColorTableHash cache;
4548 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4550 QGradientStops stops = gradient.stops();
4551 int stopCount = stops.count();
4552 Q_ASSERT(stopCount > 0);
4554 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4556 if (stopCount == 2) {
4557 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4558 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4560 qreal first_stop = stops[0].first;
4561 qreal second_stop = stops[1].first;
4563 if (second_stop < first_stop) {
4564 qSwap(first_color, second_color);
4565 qSwap(first_stop, second_stop);
4568 if (colorInterpolation) {
4569 first_color = PREMUL(first_color);
4570 second_color = PREMUL(second_color);
4573 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4574 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4576 uint red_first = qRed(first_color) << 16;
4577 uint green_first = qGreen(first_color) << 16;
4578 uint blue_first = qBlue(first_color) << 16;
4579 uint alpha_first = qAlpha(first_color) << 16;
4581 uint red_second = qRed(second_color) << 16;
4582 uint green_second = qGreen(second_color) << 16;
4583 uint blue_second = qBlue(second_color) << 16;
4584 uint alpha_second = qAlpha(second_color) << 16;
4587 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4588 if (colorInterpolation)
4589 colorTable[i] = first_color;
4591 colorTable[i] = PREMUL(first_color);
4594 if (i < second_index) {
4595 qreal reciprocal = qreal(1) / (second_index - first_index);
4597 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4598 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4599 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4600 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4603 red_first += 1 << 15;
4604 green_first += 1 << 15;
4605 blue_first += 1 << 15;
4606 alpha_first += 1 << 15;
4608 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4609 red_first += red_delta;
4610 green_first += green_delta;
4611 blue_first += blue_delta;
4612 alpha_first += alpha_delta;
4614 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4615 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4617 if (colorInterpolation)
4618 colorTable[i] = color;
4620 colorTable[i] = PREMUL(color);
4624 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4625 if (colorInterpolation)
4626 colorTable[i] = second_color;
4628 colorTable[i] = PREMUL(second_color);
4634 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4635 if (stopCount == 1) {
4636 current_color = PREMUL(current_color);
4637 for (int i = 0; i < size; ++i)
4638 colorTable[i] = current_color;
4642 // The position where the gradient begins and ends
4643 qreal begin_pos = stops[0].first;
4644 qreal end_pos = stops[stopCount-1].first;
4646 int pos = 0; // The position in the color table.
4649 qreal incr = 1 / qreal(size); // the double increment.
4650 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4652 // Up to first point
4653 colorTable[pos++] = PREMUL(current_color);
4654 while (dpos <= begin_pos) {
4655 colorTable[pos] = colorTable[pos - 1];
4660 int current_stop = 0; // We always interpolate between current and current + 1.
4662 qreal t; // position between current left and right stops
4663 qreal t_delta; // the t increment per entry in the color table
4665 if (dpos < end_pos) {
4667 while (dpos > stops[current_stop+1].first)
4670 if (current_stop != 0)
4671 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4672 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4674 if (colorInterpolation) {
4675 current_color = PREMUL(current_color);
4676 next_color = PREMUL(next_color);
4679 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4680 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4681 t = (dpos - stops[current_stop].first) * c;
4685 Q_ASSERT(current_stop < stopCount);
4687 int dist = qRound(t);
4688 int idist = 256 - dist;
4690 if (colorInterpolation)
4691 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4693 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4698 if (dpos >= end_pos)
4704 while (dpos > stops[current_stop+skip+1].first)
4708 current_stop += skip;
4710 current_color = next_color;
4712 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4713 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4715 if (colorInterpolation) {
4717 current_color = PREMUL(current_color);
4718 next_color = PREMUL(next_color);
4721 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4722 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4723 t = (dpos - stops[current_stop].first) * c;
4730 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4731 while (pos < size - 1) {
4732 colorTable[pos] = current_color;
4736 // Make sure the last color stop is represented at the end of the table
4737 colorTable[size - 1] = current_color;
4740 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4743 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4747 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
4752 m11 = m22 = m33 = 1.;
4753 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4754 clip = pe ? pe->d_func()->clip() : 0;
4757 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4759 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4761 Qt::BrushStyle brushStyle = qbrush_style(brush);
4762 switch (brushStyle) {
4763 case Qt::SolidPattern: {
4765 QColor c = qbrush_color(brush);
4766 QRgb rgba = c.rgba();
4767 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4768 if ((solid.color & 0xff000000) == 0
4769 && compositionMode == QPainter::CompositionMode_SourceOver) {
4775 case Qt::LinearGradientPattern:
4777 type = LinearGradient;
4778 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4779 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4780 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4781 gradient.spread = g->spread();
4783 QLinearGradientData &linearData = gradient.linear;
4785 linearData.origin.x = g->start().x();
4786 linearData.origin.y = g->start().y();
4787 linearData.end.x = g->finalStop().x();
4788 linearData.end.y = g->finalStop().y();
4792 case Qt::RadialGradientPattern:
4794 type = RadialGradient;
4795 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4796 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4797 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4798 gradient.spread = g->spread();
4800 QRadialGradientData &radialData = gradient.radial;
4802 QPointF center = g->center();
4803 radialData.center.x = center.x();
4804 radialData.center.y = center.y();
4805 radialData.center.radius = g->centerRadius();
4806 QPointF focal = g->focalPoint();
4807 radialData.focal.x = focal.x();
4808 radialData.focal.y = focal.y();
4809 radialData.focal.radius = g->focalRadius();
4813 case Qt::ConicalGradientPattern:
4815 type = ConicalGradient;
4816 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4817 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4818 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4819 gradient.spread = QGradient::RepeatSpread;
4821 QConicalGradientData &conicalData = gradient.conical;
4823 QPointF center = g->center();
4824 conicalData.center.x = center.x();
4825 conicalData.center.y = center.y();
4826 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4830 case Qt::Dense1Pattern:
4831 case Qt::Dense2Pattern:
4832 case Qt::Dense3Pattern:
4833 case Qt::Dense4Pattern:
4834 case Qt::Dense5Pattern:
4835 case Qt::Dense6Pattern:
4836 case Qt::Dense7Pattern:
4837 case Qt::HorPattern:
4838 case Qt::VerPattern:
4839 case Qt::CrossPattern:
4840 case Qt::BDiagPattern:
4841 case Qt::FDiagPattern:
4842 case Qt::DiagCrossPattern:
4845 tempImage = new QImage();
4846 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4847 initTexture(tempImage, alpha, QTextureData::Tiled);
4849 case Qt::TexturePattern:
4852 tempImage = new QImage();
4854 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4855 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4857 *tempImage = brush.textureImage();
4858 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4866 adjustSpanMethods();
4869 void QSpanData::adjustSpanMethods()
4879 unclipped_blend = 0;
4882 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4883 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4884 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4885 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4886 fillRect = rasterBuffer->drawHelper->fillRect;
4888 case LinearGradient:
4889 case RadialGradient:
4890 case ConicalGradient:
4891 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4895 #ifndef QT_NO_RASTERCALLBACKS
4896 if (!rasterBuffer->buffer())
4897 unclipped_blend = qBlendTextureCallback;
4900 unclipped_blend = qBlendTexture;
4902 unclipped_blend = qBlendTexture;
4904 if (!texture.imageData)
4905 unclipped_blend = 0;
4910 if (!unclipped_blend) {
4913 blend = unclipped_blend;
4914 } else if (clip->hasRectClip) {
4915 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4917 blend = qt_span_fill_clipped;
4921 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4924 // make sure we round off correctly in qdrawhelper.cpp
4925 delta.translate(1.0 / 65536, 1.0 / 65536);
4927 QTransform inv = (delta * matrix).inverted();
4940 const bool affine = !m13 && !m23;
4941 fast_matrix = affine
4942 && m11 * m11 + m21 * m21 < 1e4
4943 && m12 * m12 + m22 * m22 < 1e4
4947 adjustSpanMethods();
4950 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4952 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4954 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4955 if (!d || d->height == 0) {
4956 texture.imageData = 0;
4963 texture.bytesPerLine = 0;
4964 texture.format = QImage::Format_Invalid;
4965 texture.colorTable = 0;
4966 texture.hasAlpha = alpha != 256;
4968 texture.imageData = d->data;
4969 texture.width = d->width;
4970 texture.height = d->height;
4972 if (sourceRect.isNull()) {
4975 texture.x2 = texture.width;
4976 texture.y2 = texture.height;
4978 texture.x1 = sourceRect.x();
4979 texture.y1 = sourceRect.y();
4980 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4981 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4984 texture.bytesPerLine = d->bytes_per_line;
4986 texture.format = d->format;
4987 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4988 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4990 texture.const_alpha = alpha;
4991 texture.type = _type;
4993 adjustSpanMethods();
4998 \a x and \a y is relative to the midpoint of \a rect.
5000 static inline void drawEllipsePoints(int x, int y, int length,
5003 ProcessSpans pen_func, ProcessSpans brush_func,
5004 QSpanData *pen_data, QSpanData *brush_data)
5009 QT_FT_Span outline[4];
5010 const int midx = rect.x() + (rect.width() + 1) / 2;
5011 const int midy = rect.y() + (rect.height() + 1) / 2;
5017 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
5018 outline[0].len = qMin(length, x - outline[0].x);
5020 outline[0].coverage = 255;
5024 outline[1].len = length;
5026 outline[1].coverage = 255;
5029 outline[2].x = outline[0].x;
5030 outline[2].len = outline[0].len;
5031 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
5032 outline[2].coverage = 255;
5036 outline[3].len = length;
5037 outline[3].y = outline[2].y;
5038 outline[3].coverage = 255;
5040 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
5044 fill[0].x = outline[0].x + outline[0].len - 1;
5045 fill[0].len = qMax(0, outline[1].x - fill[0].x);
5046 fill[0].y = outline[1].y;
5047 fill[0].coverage = 255;
5050 fill[1].x = outline[2].x + outline[2].len - 1;
5051 fill[1].len = qMax(0, outline[3].x - fill[1].x);
5052 fill[1].y = outline[3].y;
5053 fill[1].coverage = 255;
5055 int n = (fill[0].y >= fill[1].y ? 1 : 2);
5056 n = qt_intersect_spans(fill, n, clip);
5058 brush_func(n, fill, brush_data);
5061 int n = (outline[1].y >= outline[2].y ? 2 : 4);
5062 n = qt_intersect_spans(outline, n, clip);
5064 pen_func(n, outline, pen_data);
5070 Draws an ellipse using the integer point midpoint algorithm.
5072 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
5073 ProcessSpans pen_func, ProcessSpans brush_func,
5074 QSpanData *pen_data, QSpanData *brush_data)
5076 const qreal a = qreal(rect.width()) / 2;
5077 const qreal b = qreal(rect.height()) / 2;
5078 qreal d = b*b - (a*a*b) + 0.25*a*a;
5081 int y = (rect.height() + 1) / 2;
5085 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
5086 if (d < 0) { // select E
5089 } else { // select SE
5090 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
5091 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5092 pen_func, brush_func, pen_data, brush_data);
5097 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
5098 pen_func, brush_func, pen_data, brush_data);
5101 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
5102 const int miny = rect.height() & 0x1;
5104 if (d < 0) { // select SE
5105 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
5107 } else { // select S
5108 d += a*a*(-2*y + 3);
5111 drawEllipsePoints(x, y, 1, rect, clip,
5112 pen_func, brush_func, pen_data, brush_data);
5117 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
5120 Draws the first \a pointCount points in the buffer \a points
5122 The default implementation converts the first \a pointCount QPoints in \a points
5123 to QPointFs and calls the floating point version of drawPoints.
5127 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
5130 Reimplement this function to draw the largest ellipse that can be
5131 contained within rectangle \a rect.
5134 #ifdef QT_DEBUG_DRAW
5135 void dumpClip(int width, int height, const QClipData *clip)
5137 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
5138 clipImg.fill(0xffff0000);
5145 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
5147 for (int i = 0; i < clip->count; ++i) {
5148 const QSpan *span = ((QClipData *) clip)->spans() + i;
5149 for (int j = 0; j < span->len; ++j)
5150 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
5151 x0 = qMin(x0, int(span->x));
5152 x1 = qMax(x1, int(span->x + span->len - 1));
5154 y0 = qMin(y0, int(span->y));
5155 y1 = qMax(y1, int(span->y));
5158 static int counter = 0;
5165 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
5166 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));