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))
131 extern bool qt_cleartype_enabled;
135 extern bool qt_applefontsmoothing_enabled;
139 /********************************************************************************
142 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
143 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
144 static void qt_span_clip(int count, const QSpan *spans, void *userData);
145 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
151 Qt::ClipOperation operation;
157 LineDrawIncludeLastPixel
160 struct QRasterFloatPoint {
166 static const QRectF boundingRect(const QPointF *points, int pointCount)
168 const QPointF *e = points;
169 const QPointF *last = points + pointCount;
170 qreal minx, maxx, miny, maxy;
171 minx = maxx = e->x();
172 miny = maxy = e->y();
176 else if (e->x() > maxx)
180 else if (e->y() > maxy)
183 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
187 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
188 return (elementCount == 5 // 5-point polygon, check for closed rect
189 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
190 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
191 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
192 && pts[0] < pts[4] && pts[1] < pts[5]
194 (elementCount == 4 // 4-point polygon, check for unclosed rect
195 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
196 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
197 && pts[0] < pts[4] && pts[1] < pts[5]
202 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
204 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
207 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
209 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
212 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
213 qfixed c2x, qfixed c2y,
214 qfixed ex, qfixed ey,
217 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
218 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
219 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
223 #if !defined(QT_NO_DEBUG) && 0
224 static void qt_debug_path(const QPainterPath &path)
226 const char *names[] = {
233 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
234 for (int i=0; i<path.elementCount(); ++i) {
235 const QPainterPath::Element &e = path.elementAt(i);
236 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
237 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
242 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
243 QPaintEngineExPrivate(),
250 \class QRasterPaintEngine
255 \brief The QRasterPaintEngine class enables hardware acceleration
256 of painting operations in Qt for Embedded Linux.
258 Note that this functionality is only available in
259 \l{Qt for Embedded Linux}.
261 In \l{Qt for Embedded Linux}, painting is a pure software
262 implementation. But starting with Qt 4.2, it is
263 possible to add an accelerated graphics driver to take advantage
264 of available hardware resources.
266 Hardware acceleration is accomplished by creating a custom screen
267 driver, accelerating the copying from memory to the screen, and
268 implementing a custom paint engine accelerating the various
269 painting operations. Then a custom paint device (derived from the
270 QCustomRasterPaintDevice class) and a custom window surface
271 (derived from QWSWindowSurface) must be implemented to make
272 \l{Qt for Embedded Linux} aware of the accelerated driver.
274 \note The QRasterPaintEngine class does not support 8-bit images.
275 Instead, they need to be converted to a supported format, such as
276 QImage::Format_ARGB32_Premultiplied.
278 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
279 documentation for details.
281 \sa QCustomRasterPaintDevice, QPaintEngine
285 \fn Type QRasterPaintEngine::type() const
291 \relates QRasterPaintEngine
293 A struct equivalent to QT_FT_Span, containing a position (x,
294 y), the span's length in pixels and its color/coverage (a value
295 ranging from 0 to 255).
301 Creates a raster based paint engine for operating on the given
302 \a device, with the complete set of \l
303 {QPaintEngine::PaintEngineFeature}{paint engine features and
306 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
307 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
309 d_func()->device = device;
316 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
319 d_func()->device = device;
323 void QRasterPaintEngine::init()
325 Q_D(QRasterPaintEngine);
332 // The antialiasing raster.
333 d->grayRaster.reset(new QT_FT_Raster);
334 Q_CHECK_PTR(d->grayRaster.data());
335 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
336 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
339 d->rasterizer.reset(new QRasterizer);
340 d->rasterBuffer.reset(new QRasterBuffer());
341 d->outlineMapper.reset(new QOutlineMapper);
342 d->outlinemapper_xform_dirty = true;
344 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
345 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
346 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
348 d->baseClip.reset(new QClipData(d->device->height()));
349 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
351 d->image_filler.init(d->rasterBuffer.data(), this);
352 d->image_filler.type = QSpanData::Texture;
354 d->image_filler_xform.init(d->rasterBuffer.data(), this);
355 d->image_filler_xform.type = QSpanData::Texture;
357 d->solid_color_filler.init(d->rasterBuffer.data(), this);
358 d->solid_color_filler.type = QSpanData::Solid;
360 d->deviceDepth = d->device->depth();
362 d->mono_surface = false;
363 gccaps &= ~PorterDuff;
365 QImage::Format format = QImage::Format_Invalid;
367 switch (d->device->devType()) {
368 case QInternal::Pixmap:
369 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
371 case QInternal::Image:
372 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
375 case QInternal::CustomRaster:
376 d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
380 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
386 case QImage::Format_MonoLSB:
387 case QImage::Format_Mono:
388 d->mono_surface = true;
390 case QImage::Format_ARGB8565_Premultiplied:
391 case QImage::Format_ARGB8555_Premultiplied:
392 case QImage::Format_ARGB6666_Premultiplied:
393 case QImage::Format_ARGB4444_Premultiplied:
394 case QImage::Format_ARGB32_Premultiplied:
395 case QImage::Format_ARGB32:
396 gccaps |= PorterDuff;
398 case QImage::Format_RGB32:
399 case QImage::Format_RGB444:
400 case QImage::Format_RGB555:
401 case QImage::Format_RGB666:
402 case QImage::Format_RGB888:
403 case QImage::Format_RGB16:
414 Destroys this paint engine.
416 QRasterPaintEngine::~QRasterPaintEngine()
418 Q_D(QRasterPaintEngine);
420 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
426 bool QRasterPaintEngine::begin(QPaintDevice *device)
428 Q_D(QRasterPaintEngine);
430 if (device->devType() == QInternal::Pixmap) {
431 QPixmap *pixmap = static_cast<QPixmap *>(device);
432 QPixmapData *pd = pixmap->pixmapData();
433 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
434 d->device = pd->buffer();
439 // Make sure QPaintEngine::paintDevice() returns the proper device.
442 Q_ASSERT(d->device->devType() == QInternal::Image
443 || d->device->devType() == QInternal::CustomRaster);
445 d->systemStateChanged();
447 QRasterPaintEngineState *s = state();
448 ensureOutlineMapper();
449 d->outlineMapper->m_clip_rect = d->deviceRect;
451 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
452 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
453 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
454 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
456 d->rasterizer->setClipRect(d->deviceRect);
458 s->penData.init(d->rasterBuffer.data(), this);
459 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
460 s->stroker = &d->basicStroker;
461 d->basicStroker.setClipRect(d->deviceRect);
463 s->brushData.init(d->rasterBuffer.data(), this);
464 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
466 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
468 setDirty(DirtyBrushOrigin);
471 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
472 << ") devType:" << device->devType()
473 << "devRect:" << d->deviceRect;
475 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
479 #if defined(Q_WS_WIN)
480 d->isPlain45DegreeRotation = true;
484 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
485 #if defined(Q_WS_WIN)
486 else if (qt_cleartype_enabled)
487 #elif defined (Q_WS_MAC)
488 else if (qt_applefontsmoothing_enabled)
493 QImage::Format format = static_cast<QImage *>(d->device)->format();
494 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
495 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
497 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
499 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
508 bool QRasterPaintEngine::end()
511 Q_D(QRasterPaintEngine);
512 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
514 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
524 void QRasterPaintEngine::releaseBuffer()
526 Q_D(QRasterPaintEngine);
527 d->rasterBuffer.reset(new QRasterBuffer);
533 QSize QRasterPaintEngine::size() const
535 Q_D(const QRasterPaintEngine);
536 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
543 void QRasterPaintEngine::saveBuffer(const QString &s) const
545 Q_D(const QRasterPaintEngine);
546 d->rasterBuffer->bufferImage().save(s, "PNG");
553 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
555 QRasterPaintEngineState *s = state();
556 // FALCON: get rid of this line, see drawImage call below.
558 QTransform::TransformationType txop = s->matrix.type();
562 case QTransform::TxNone:
563 s->flags.int_xform = true;
566 case QTransform::TxTranslate:
567 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
568 && qreal(int(s->matrix.dy())) == s->matrix.dy();
571 case QTransform::TxScale:
572 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
573 && qreal(int(s->matrix.dy())) == s->matrix.dy()
574 && qreal(int(s->matrix.m11())) == s->matrix.m11()
575 && qreal(int(s->matrix.m22())) == s->matrix.m22();
578 default: // shear / perspective...
579 s->flags.int_xform = false;
583 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
585 ensureOutlineMapper();
588 Q_D(QRasterPaintEngine);
589 d->isPlain45DegreeRotation = false;
590 if (txop >= QTransform::TxRotate) {
591 d->isPlain45DegreeRotation =
592 (qFuzzyIsNull(matrix.m11())
593 && qFuzzyIsNull(matrix.m12() - qreal(1))
594 && qFuzzyIsNull(matrix.m21() + qreal(1))
595 && qFuzzyIsNull(matrix.m22())
598 (qFuzzyIsNull(matrix.m11() + qreal(1))
599 && qFuzzyIsNull(matrix.m12())
600 && qFuzzyIsNull(matrix.m21())
601 && qFuzzyIsNull(matrix.m22() + qreal(1))
604 (qFuzzyIsNull(matrix.m11())
605 && qFuzzyIsNull(matrix.m12() + qreal(1))
606 && qFuzzyIsNull(matrix.m21() - qreal(1))
607 && qFuzzyIsNull(matrix.m22())
617 QRasterPaintEngineState::~QRasterPaintEngineState()
619 if (flags.has_clip_ownership)
624 QRasterPaintEngineState::QRasterPaintEngineState()
636 flags.fast_pen = true;
637 flags.antialiased = false;
638 flags.bilinear = false;
639 flags.fast_text = true;
640 flags.int_xform = true;
641 flags.tx_noshear = true;
642 flags.fast_images = true;
645 flags.has_clip_ownership = false;
650 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
655 , strokeFlags(s.strokeFlags)
656 , lastBrush(s.lastBrush)
657 , brushData(s.brushData)
658 , fillFlags(s.fillFlags)
659 , pixmapFlags(s.pixmapFlags)
660 , intOpacity(s.intOpacity)
664 , flag_bits(s.flag_bits)
666 brushData.tempImage = 0;
667 penData.tempImage = 0;
668 flags.has_clip_ownership = false;
674 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
676 QRasterPaintEngineState *s;
678 s = new QRasterPaintEngineState();
680 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
688 void QRasterPaintEngine::setState(QPainterState *s)
690 Q_D(QRasterPaintEngine);
691 QPaintEngineEx::setState(s);
692 d->rasterBuffer->compositionMode = s->composition_mode;
696 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
701 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
708 void QRasterPaintEngine::penChanged()
711 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
713 QRasterPaintEngineState *s = state();
714 s->strokeFlags |= DirtyPen;
715 s->dirty |= DirtyPen;
721 void QRasterPaintEngine::updatePen(const QPen &pen)
723 Q_D(QRasterPaintEngine);
724 QRasterPaintEngineState *s = state();
726 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
729 Qt::PenStyle pen_style = qpen_style(pen);
734 s->penData.clip = d->clip();
735 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
737 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
738 || pen.brush().transform().type() >= QTransform::TxNone) {
739 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
742 // Slightly ugly handling of an uncommon case... We need to change
743 // the pen because it is reused in draw_midpoint to decide dashed
745 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
746 pen_style = Qt::SolidLine;
747 s->lastPen.setStyle(Qt::SolidLine);
750 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
751 d->basicStroker.setCapStyle(qpen_capStyle(pen));
752 d->basicStroker.setMiterLimit(pen.miterLimit());
754 qreal penWidth = qpen_widthf(pen);
756 d->basicStroker.setStrokeWidth(1);
758 d->basicStroker.setStrokeWidth(penWidth);
760 if(pen_style == Qt::SolidLine) {
761 s->stroker = &d->basicStroker;
762 } else if (pen_style != Qt::NoPen) {
764 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
765 if (pen.isCosmetic()) {
766 d->dashStroker->setClipRect(d->deviceRect);
768 // ### I've seen this inverted devrect multiple places now...
769 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
770 d->dashStroker->setClipRect(clipRect);
772 d->dashStroker->setDashPattern(pen.dashPattern());
773 d->dashStroker->setDashOffset(pen.dashOffset());
774 s->stroker = d->dashStroker.data();
779 ensureState(); // needed because of tx_noshear...
780 s->flags.fast_pen = pen_style > Qt::NoPen
782 && ((pen.isCosmetic() && penWidth <= 1)
783 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
785 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
795 void QRasterPaintEngine::brushOriginChanged()
797 QRasterPaintEngineState *s = state();
799 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
802 s->fillFlags |= DirtyBrushOrigin;
809 void QRasterPaintEngine::brushChanged()
811 QRasterPaintEngineState *s = state();
813 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
815 s->fillFlags |= DirtyBrush;
824 void QRasterPaintEngine::updateBrush(const QBrush &brush)
827 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
829 Q_D(QRasterPaintEngine);
830 QRasterPaintEngineState *s = state();
831 // must set clip prior to setup, as setup uses it...
832 s->brushData.clip = d->clip();
833 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
834 if (s->fillFlags & DirtyTransform
835 || brush.transform().type() >= QTransform::TxNone)
836 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
837 s->lastBrush = brush;
841 void QRasterPaintEngine::updateOutlineMapper()
843 Q_D(QRasterPaintEngine);
844 d->outlineMapper->setMatrix(state()->matrix);
847 void QRasterPaintEngine::updateState()
849 QRasterPaintEngineState *s = state();
851 if (s->dirty & DirtyTransform)
852 updateMatrix(s->matrix);
854 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
855 const QPainter::CompositionMode mode = s->composition_mode;
856 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
857 && s->intOpacity == 256
858 && (mode == QPainter::CompositionMode_Source
859 || (mode == QPainter::CompositionMode_SourceOver
860 && qAlpha(s->penData.solid.color) == 255));
870 void QRasterPaintEngine::opacityChanged()
872 QRasterPaintEngineState *s = state();
875 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
878 s->fillFlags |= DirtyOpacity;
879 s->strokeFlags |= DirtyOpacity;
880 s->pixmapFlags |= DirtyOpacity;
881 s->dirty |= DirtyOpacity;
882 s->intOpacity = (int) (s->opacity * 256);
888 void QRasterPaintEngine::compositionModeChanged()
890 Q_D(QRasterPaintEngine);
891 QRasterPaintEngineState *s = state();
894 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
897 s->fillFlags |= DirtyCompositionMode;
898 s->dirty |= DirtyCompositionMode;
900 s->strokeFlags |= DirtyCompositionMode;
901 d->rasterBuffer->compositionMode = s->composition_mode;
903 d->recalculateFastImages();
909 void QRasterPaintEngine::renderHintsChanged()
911 QRasterPaintEngineState *s = state();
914 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
917 bool was_aa = s->flags.antialiased;
918 bool was_bilinear = s->flags.bilinear;
920 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
921 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
923 if (was_aa != s->flags.antialiased)
924 s->strokeFlags |= DirtyHints;
926 if (was_bilinear != s->flags.bilinear) {
927 s->strokeFlags |= DirtyPen;
928 s->fillFlags |= DirtyBrush;
931 Q_D(QRasterPaintEngine);
932 d->recalculateFastImages();
938 void QRasterPaintEngine::transformChanged()
940 QRasterPaintEngineState *s = state();
943 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
946 s->fillFlags |= DirtyTransform;
947 s->strokeFlags |= DirtyTransform;
949 s->dirty |= DirtyTransform;
951 Q_D(QRasterPaintEngine);
952 d->recalculateFastImages();
958 void QRasterPaintEngine::clipEnabledChanged()
960 QRasterPaintEngineState *s = state();
963 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
967 s->clip->enabled = s->clipEnabled;
968 s->fillFlags |= DirtyClipEnabled;
969 s->strokeFlags |= DirtyClipEnabled;
970 s->pixmapFlags |= DirtyClipEnabled;
975 void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
977 rasterBuffer->prepare(device);
981 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
983 SrcOverBlendFunc func,
988 if (alpha == 0 || !clip.isValid())
991 Q_ASSERT(img.depth() >= 8);
993 int srcBPL = img.bytesPerLine();
994 const uchar *srcBits = img.bits();
995 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
996 int iw = img.width();
997 int ih = img.height();
1002 // Adjust the image according to the source offset...
1003 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1006 // adapt the x parameters
1007 int x = qRound(pt.x());
1009 int cx2 = clip.x() + clip.width();
1012 srcBits += srcSize * d;
1017 int d = x + iw - cx2;
1023 // adapt the y paremeters...
1025 int cy2 = clip.y() + clip.height();
1026 int y = qRound(pt.y());
1029 srcBits += srcBPL * d;
1034 int d = y + ih - cy2;
1040 // call the blend function...
1041 int dstSize = rasterBuffer->bytesPerPixel();
1042 int dstBPL = rasterBuffer->bytesPerLine();
1043 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1050 void QRasterPaintEnginePrivate::systemStateChanged()
1052 QRect clipRect(0, 0,
1053 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1054 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1056 if (!systemClip.isEmpty()) {
1057 QRegion clippedDeviceRgn = systemClip & clipRect;
1058 deviceRect = clippedDeviceRgn.boundingRect();
1059 baseClip->setClipRegion(clippedDeviceRgn);
1061 deviceRect = clipRect;
1062 baseClip->setClipRect(deviceRect);
1064 #ifdef QT_DEBUG_DRAW
1065 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1068 exDeviceRect = deviceRect;
1070 Q_Q(QRasterPaintEngine);
1071 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1072 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1073 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1076 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1078 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1081 Q_Q(QRasterPaintEngine);
1082 bool bilinear = q->state()->flags.bilinear;
1084 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1085 spanData->setupMatrix(b.transform() * m, bilinear);
1087 if (m.type() <= QTransform::TxTranslate) {
1088 // specialize setupMatrix for translation matrices
1089 // to avoid needless matrix inversion
1097 spanData->dx = -m.dx();
1098 spanData->dy = -m.dy();
1099 spanData->txop = m.type();
1100 spanData->bilinear = bilinear;
1101 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1102 spanData->adjustSpanMethods();
1104 spanData->setupMatrix(m, bilinear);
1109 // #define QT_CLIPPING_RATIOS
1111 #ifdef QT_CLIPPING_RATIOS
1116 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1118 if (d->clip()->hasRectClip)
1120 if (d->clip()->hasRegionClip)
1124 if ((totalClips % 5000) == 0) {
1125 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1126 rectClips * 100.0 / (qreal) totalClips,
1127 regionClips * 100.0 / (qreal) totalClips,
1128 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1137 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1139 if (s->flags.has_clip_ownership)
1142 s->flags.has_clip_ownership = false;
1145 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1147 s->fillFlags |= QPaintEngine::DirtyClipPath;
1148 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1149 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1151 d->solid_color_filler.clip = d->clip();
1152 d->solid_color_filler.adjustSpanMethods();
1154 #ifdef QT_DEBUG_DRAW
1155 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1164 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1166 #ifdef QT_DEBUG_DRAW
1167 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1169 if (path.elements()) {
1170 for (int i=0; i<path.elementCount(); ++i) {
1171 qDebug() << " - " << path.elements()[i]
1172 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1175 for (int i=0; i<path.elementCount(); ++i) {
1176 qDebug() << " ---- "
1177 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1182 Q_D(QRasterPaintEngine);
1183 QRasterPaintEngineState *s = state();
1185 const qreal *points = path.points();
1186 const QPainterPath::ElementType *types = path.elements();
1188 // There are some cases that are not supported by clip(QRect)
1189 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1190 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1191 if (s->matrix.type() <= QTransform::TxScale
1192 && ((path.shape() == QVectorPath::RectangleHint)
1193 || (isRect(points, path.elementCount())
1194 && (!types || (types[0] == QPainterPath::MoveToElement
1195 && types[1] == QPainterPath::LineToElement
1196 && types[2] == QPainterPath::LineToElement
1197 && types[3] == QPainterPath::LineToElement))))) {
1198 #ifdef QT_DEBUG_DRAW
1199 qDebug() << " --- optimizing vector clip to rect clip...";
1202 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1203 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1208 if (op == Qt::NoClip) {
1209 qrasterpaintengine_state_setNoClip(s);
1212 QClipData *base = d->baseClip.data();
1214 // Intersect with current clip when available...
1215 if (op == Qt::IntersectClip && s->clip)
1218 // We always intersect, except when there is nothing to
1219 // intersect with, in which case we simplify the operation to
1221 Qt::ClipOperation isectOp = Qt::IntersectClip;
1223 isectOp = Qt::ReplaceClip;
1225 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1226 newClip->initialize();
1227 ClipData clipData = { base, newClip, isectOp };
1228 ensureOutlineMapper();
1229 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1233 if (op == Qt::UniteClip) {
1235 QClipData *result = new QClipData(d->rasterBuffer->height());
1236 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1237 qt_merge_clip(current, newClip, result);
1245 if (s->flags.has_clip_ownership)
1249 s->flags.has_clip_ownership = true;
1251 qrasterpaintengine_dirty_clip(d, s);
1259 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1261 #ifdef QT_DEBUG_DRAW
1262 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1265 QRasterPaintEngineState *s = state();
1267 if (op == Qt::NoClip) {
1268 qrasterpaintengine_state_setNoClip(s);
1270 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1271 QPaintEngineEx::clip(rect, op);
1274 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1275 QPaintEngineEx::clip(rect, op);
1281 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1283 Q_D(QRasterPaintEngine);
1284 QRect clipRect = r & d->deviceRect;
1285 QRasterPaintEngineState *s = state();
1287 if (op == Qt::ReplaceClip || s->clip == 0) {
1289 // No current clip, hence we intersect with sysclip and be
1291 QRegion clipRegion = systemClip();
1292 QClipData *clip = new QClipData(d->rasterBuffer->height());
1294 if (clipRegion.isEmpty())
1295 clip->setClipRect(clipRect);
1297 clip->setClipRegion(clipRegion & clipRect);
1299 if (s->flags.has_clip_ownership)
1303 s->clip->enabled = true;
1304 s->flags.has_clip_ownership = true;
1306 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1307 QClipData *base = s->clip;
1310 if (base->hasRectClip || base->hasRegionClip) {
1311 if (!s->flags.has_clip_ownership) {
1312 s->clip = new QClipData(d->rasterBuffer->height());
1313 s->flags.has_clip_ownership = true;
1315 if (base->hasRectClip)
1316 s->clip->setClipRect(base->clipRect & clipRect);
1318 s->clip->setClipRegion(base->clipRegion & clipRect);
1319 s->clip->enabled = true;
1327 qrasterpaintengine_dirty_clip(d, s);
1335 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1337 #ifdef QT_DEBUG_DRAW
1338 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1341 Q_D(QRasterPaintEngine);
1343 if (region.rectCount() == 1) {
1344 clip(region.boundingRect(), op);
1348 QRasterPaintEngineState *s = state();
1349 const QClipData *clip = d->clip();
1350 const QClipData *baseClip = d->baseClip.data();
1352 if (op == Qt::NoClip) {
1353 qrasterpaintengine_state_setNoClip(s);
1354 } else if (s->matrix.type() > QTransform::TxScale
1355 || op == Qt::UniteClip
1356 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1357 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1358 QPaintEngineEx::clip(region, op);
1360 const QClipData *curClip;
1363 if (op == Qt::IntersectClip)
1368 if (s->flags.has_clip_ownership) {
1372 newClip = new QClipData(d->rasterBuffer->height());
1374 s->flags.has_clip_ownership = true;
1377 QRegion r = s->matrix.map(region);
1378 if (curClip->hasRectClip)
1379 newClip->setClipRegion(r & curClip->clipRect);
1380 else if (curClip->hasRegionClip)
1381 newClip->setClipRegion(r & curClip->clipRegion);
1383 qrasterpaintengine_dirty_clip(d, s);
1390 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1392 #ifdef QT_DEBUG_DRAW
1393 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1396 if (!fillData->blend)
1399 Q_D(QRasterPaintEngine);
1401 const QRectF controlPointRect = path.controlPointRect();
1403 QRasterPaintEngineState *s = state();
1404 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1405 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1406 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1407 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1408 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1409 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1411 if (!s->flags.antialiased && !do_clip) {
1412 d->initializeRasterizer(fillData);
1413 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1417 ensureOutlineMapper();
1418 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1421 static void fillRect_normalized(const QRect &r, QSpanData *data,
1422 QRasterPaintEnginePrivate *pe)
1426 bool rectClipped = true;
1429 x1 = qMax(r.x(), data->clip->xmin);
1430 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1431 y1 = qMax(r.y(), data->clip->ymin);
1432 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1433 rectClipped = data->clip->hasRectClip;
1436 x1 = qMax(r.x(), pe->deviceRect.x());
1437 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1438 y1 = qMax(r.y(), pe->deviceRect.y());
1439 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1441 x1 = qMax(r.x(), 0);
1442 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1443 y1 = qMax(r.y(), 0);
1444 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1447 if (x2 <= x1 || y2 <= y1)
1450 const int width = x2 - x1;
1451 const int height = y2 - y1;
1453 bool isUnclipped = rectClipped
1454 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1456 if (pe && isUnclipped) {
1457 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1459 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1460 || (mode == QPainter::CompositionMode_SourceOver
1461 && qAlpha(data->solid.color) == 255)))
1463 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1469 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1471 const int nspans = 256;
1472 QT_FT_Span spans[nspans];
1474 Q_ASSERT(data->blend);
1477 int n = qMin(nspans, y2 - y);
1481 spans[i].len = width;
1483 spans[i].coverage = 255;
1487 blend(n, spans, data);
1495 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1497 #ifdef QT_DEBUG_DRAW
1498 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1500 Q_D(QRasterPaintEngine);
1502 QRasterPaintEngineState *s = state();
1506 if (s->brushData.blend) {
1507 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1508 const QRect *r = rects;
1509 const QRect *lastRect = rects + rectCount;
1511 int offset_x = int(s->matrix.dx());
1512 int offset_y = int(s->matrix.dy());
1513 while (r < lastRect) {
1514 QRect rect = r->normalized();
1515 QRect rr = rect.translated(offset_x, offset_y);
1516 fillRect_normalized(rr, &s->brushData, d);
1520 QRectVectorPath path;
1521 for (int i=0; i<rectCount; ++i) {
1523 fill(path, s->brush);
1529 if (s->penData.blend) {
1530 QRectVectorPath path;
1531 if (s->flags.fast_pen) {
1532 QCosmeticStroker stroker(s, d->deviceRect);
1533 for (int i = 0; i < rectCount; ++i) {
1535 stroker.drawPath(path);
1538 for (int i = 0; i < rectCount; ++i) {
1540 stroke(path, s->pen);
1549 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1551 #ifdef QT_DEBUG_DRAW
1552 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1554 #ifdef QT_FAST_SPANS
1555 Q_D(QRasterPaintEngine);
1557 QRasterPaintEngineState *s = state();
1560 if (s->flags.tx_noshear) {
1562 if (s->brushData.blend) {
1563 d->initializeRasterizer(&s->brushData);
1564 for (int i = 0; i < rectCount; ++i) {
1565 const QRectF &rect = rects[i].normalized();
1568 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1569 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1570 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1575 if (s->penData.blend) {
1576 QRectVectorPath path;
1577 if (s->flags.fast_pen) {
1578 QCosmeticStroker stroker(s, d->deviceRect);
1579 for (int i = 0; i < rectCount; ++i) {
1581 stroker.drawPath(path);
1584 for (int i = 0; i < rectCount; ++i) {
1586 QPaintEngineEx::stroke(path, s->lastPen);
1593 #endif // QT_FAST_SPANS
1594 QPaintEngineEx::drawRects(rects, rectCount);
1601 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1603 Q_D(QRasterPaintEngine);
1604 QRasterPaintEngineState *s = state();
1607 if (!s->penData.blend)
1610 if (s->flags.fast_pen) {
1611 QCosmeticStroker stroker(s, d->deviceRect);
1612 stroker.drawPath(path);
1613 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1614 qreal width = s->lastPen.isCosmetic()
1615 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1616 : qpen_widthf(s->lastPen) * s->txscale;
1618 qreal dashOffset = s->lastPen.dashOffset();
1620 qreal patternLength = 0;
1621 const QVector<qreal> pattern = s->lastPen.dashPattern();
1622 for (int i = 0; i < pattern.size(); ++i)
1623 patternLength += pattern.at(i);
1625 if (patternLength > 0) {
1626 int n = qFloor(dashOffset / patternLength);
1627 dashOffset -= n * patternLength;
1628 while (dashOffset >= pattern.at(dashIndex)) {
1629 dashOffset -= pattern.at(dashIndex);
1630 if (++dashIndex >= pattern.size())
1636 Q_D(QRasterPaintEngine);
1637 d->initializeRasterizer(&s->penData);
1638 int lineCount = path.elementCount() / 2;
1639 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1641 for (int i = 0; i < lineCount; ++i) {
1642 if (lines[i].p1() == lines[i].p2()) {
1643 if (s->lastPen.capStyle() != Qt::FlatCap) {
1644 QPointF p = lines[i].p1();
1645 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1646 QPointF(p.x() + width*0.5, p.y())));
1647 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1652 const QLineF line = s->matrix.map(lines[i]);
1653 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1654 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1655 width / line.length(),
1656 s->lastPen.capStyle() == Qt::SquareCap);
1658 d->rasterizeLine_dashed(line, width,
1659 &dashIndex, &dashOffset, &inDash);
1664 QPaintEngineEx::stroke(path, pen);
1667 static inline QRect toNormalizedFillRect(const QRectF &rect)
1669 int x1 = qRound(rect.x());
1670 int y1 = qRound(rect.y());
1671 int x2 = qRound(rect.right());
1672 int y2 = qRound(rect.bottom());
1679 return QRect(x1, y1, x2 - x1, y2 - y1);
1685 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1689 #ifdef QT_DEBUG_DRAW
1690 QRectF rf = path.controlPointRect();
1691 qDebug() << "QRasterPaintEngine::fill(): "
1692 << "size=" << path.elementCount()
1693 << ", hints=" << hex << path.hints()
1697 Q_D(QRasterPaintEngine);
1698 QRasterPaintEngineState *s = state();
1701 if (!s->brushData.blend)
1704 if (path.shape() == QVectorPath::RectangleHint) {
1705 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1706 const qreal *p = path.points();
1707 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1708 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1709 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1713 if (s->flags.tx_noshear) {
1714 d->initializeRasterizer(&s->brushData);
1715 // ### Is normalizing really necessary here?
1716 const qreal *p = path.points();
1717 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1719 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1720 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1721 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1727 // ### Optimize for non transformed ellipses and rectangles...
1728 QRectF cpRect = path.controlPointRect();
1729 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1730 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1733 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1734 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1735 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1736 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1738 // ### Falonc: implement....
1739 // if (!s->flags.antialiased && !do_clip) {
1740 // d->initializeRasterizer(&s->brushData);
1741 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1745 ensureOutlineMapper();
1746 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1749 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1751 Q_D(QRasterPaintEngine);
1752 QRasterPaintEngineState *s = state();
1754 if (!s->flags.antialiased) {
1755 uint txop = s->matrix.type();
1756 if (txop == QTransform::TxNone) {
1757 fillRect_normalized(toNormalizedFillRect(r), data, d);
1759 } else if (txop == QTransform::TxTranslate) {
1760 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1761 fillRect_normalized(rr, data, d);
1763 } else if (txop == QTransform::TxScale) {
1764 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1765 fillRect_normalized(rr, data, d);
1770 if (s->flags.tx_noshear) {
1771 d->initializeRasterizer(data);
1772 QRectF nr = r.normalized();
1773 if (!nr.isEmpty()) {
1774 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1775 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1776 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1783 ensureOutlineMapper();
1784 fillPath(path, data);
1790 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1792 #ifdef QT_DEBUG_DRAW
1793 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1795 QRasterPaintEngineState *s = state();
1798 if (!s->brushData.blend)
1801 fillRect(r, &s->brushData);
1807 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1809 #ifdef QT_DEBUG_DRAW
1810 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1812 Q_D(QRasterPaintEngine);
1813 QRasterPaintEngineState *s = state();
1815 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1816 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1817 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1820 d->solid_color_filler.clip = d->clip();
1821 d->solid_color_filler.adjustSpanMethods();
1822 fillRect(r, &d->solid_color_filler);
1825 static inline bool isAbove(const QPointF *a, const QPointF *b)
1827 return a->y() < b->y();
1830 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1835 Q_ASSERT(pointCount >= 2);
1837 QVector<const QPointF *> sorted;
1838 sorted.reserve(pointCount);
1840 upper->reserve(pointCount * 3 / 4);
1841 lower->reserve(pointCount * 3 / 4);
1843 for (int i = 0; i < pointCount; ++i)
1844 sorted << points + i;
1846 qSort(sorted.begin(), sorted.end(), isAbove);
1848 qreal splitY = sorted.at(sorted.size() / 2)->y();
1850 const QPointF *end = points + pointCount;
1851 const QPointF *last = end - 1;
1853 QVector<QPointF> *bin[2] = { upper, lower };
1855 for (const QPointF *p = points; p < end; ++p) {
1856 int side = p->y() < splitY;
1857 int lastSide = last->y() < splitY;
1859 if (side != lastSide) {
1860 if (qFuzzyCompare(p->y(), splitY)) {
1861 bin[!side]->append(*p);
1862 } else if (qFuzzyCompare(last->y(), splitY)) {
1863 bin[side]->append(*last);
1865 QPointF delta = *p - *last;
1866 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1868 bin[0]->append(intersection);
1869 bin[1]->append(intersection);
1873 bin[side]->append(*p);
1878 // give up if we couldn't reduce the point count
1879 return upper->size() < pointCount && lower->size() < pointCount;
1885 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1887 Q_D(QRasterPaintEngine);
1888 QRasterPaintEngineState *s = state();
1890 const int maxPoints = 0xffff;
1892 // max amount of points that raster engine can reliably handle
1893 if (pointCount > maxPoints) {
1894 QVector<QPointF> upper, lower;
1896 if (splitPolygon(points, pointCount, &upper, &lower)) {
1897 fillPolygon(upper.constData(), upper.size(), mode);
1898 fillPolygon(lower.constData(), lower.size(), mode);
1900 qWarning("Polygon too complex for filling.");
1905 // Compose polygon fill..,
1906 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1907 ensureOutlineMapper();
1908 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1911 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1913 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1919 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1921 Q_D(QRasterPaintEngine);
1922 QRasterPaintEngineState *s = state();
1924 #ifdef QT_DEBUG_DRAW
1925 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1926 for (int i=0; i<pointCount; ++i)
1927 qDebug() << " - " << points[i];
1929 Q_ASSERT(pointCount >= 2);
1931 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1932 QRectF r(points[0], points[2]);
1938 if (mode != PolylineMode) {
1941 if (s->brushData.blend) {
1942 fillPolygon(points, pointCount, mode);
1946 // Do the outline...
1947 if (s->penData.blend) {
1948 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1949 if (s->flags.fast_pen) {
1950 QCosmeticStroker stroker(s, d->deviceRect);
1951 stroker.drawPath(vp);
1953 QPaintEngineEx::stroke(vp, s->lastPen);
1961 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1963 Q_D(QRasterPaintEngine);
1964 QRasterPaintEngineState *s = state();
1966 #ifdef QT_DEBUG_DRAW
1967 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1968 for (int i=0; i<pointCount; ++i)
1969 qDebug() << " - " << points[i];
1971 Q_ASSERT(pointCount >= 2);
1972 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1973 QRect r(points[0].x(),
1975 points[2].x() - points[0].x(),
1976 points[2].y() - points[0].y());
1984 if (mode != PolylineMode) {
1986 if (s->brushData.blend) {
1987 // Compose polygon fill..,
1988 ensureOutlineMapper();
1989 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1990 d->outlineMapper->moveTo(*points);
1991 const QPoint *p = points;
1992 const QPoint *ep = points + pointCount - 1;
1994 d->outlineMapper->lineTo(*(++p));
1996 d->outlineMapper->endOutline();
1999 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2001 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2005 // Do the outline...
2006 if (s->penData.blend) {
2007 int count = pointCount * 2;
2008 QVarLengthArray<qreal> fpoints(count);
2010 for (int i=0; i<count; i+=2) {
2011 fpoints[i] = ((int *) points)[i+1];
2012 fpoints[i+1] = ((int *) points)[i];
2015 for (int i=0; i<count; ++i)
2016 fpoints[i] = ((int *) points)[i];
2018 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2020 if (s->flags.fast_pen) {
2021 QCosmeticStroker stroker(s, d->deviceRect);
2022 stroker.drawPath(vp);
2024 QPaintEngineEx::stroke(vp, s->lastPen);
2032 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2034 #ifdef QT_DEBUG_DRAW
2035 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " 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) {
2046 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2048 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2051 QRasterPaintEngine::drawImage(pos, image);
2054 const QImage image = pixmap.toImage();
2055 if (pixmap.depth() == 1) {
2056 Q_D(QRasterPaintEngine);
2057 QRasterPaintEngineState *s = state();
2058 if (s->matrix.type() <= QTransform::TxTranslate) {
2060 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2062 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2065 QRasterPaintEngine::drawImage(pos, image);
2073 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2075 #ifdef QT_DEBUG_DRAW
2076 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2079 QPixmapData* pd = pixmap.pixmapData();
2080 if (pd->classId() == QPixmapData::RasterClass) {
2081 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2082 if (image.depth() == 1) {
2083 Q_D(QRasterPaintEngine);
2084 QRasterPaintEngineState *s = state();
2085 if (s->matrix.type() <= QTransform::TxTranslate
2086 && r.size() == sr.size()
2087 && r.size() == pixmap.size()) {
2089 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2092 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2095 drawImage(r, image, sr);
2098 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2099 const QImage image = pd->toImage(clippedSource);
2100 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2101 if (image.depth() == 1) {
2102 Q_D(QRasterPaintEngine);
2103 QRasterPaintEngineState *s = state();
2104 if (s->matrix.type() <= QTransform::TxTranslate
2105 && r.size() == sr.size()
2106 && r.size() == pixmap.size()) {
2108 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2111 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2114 drawImage(r, image, translatedSource);
2119 // assumes that rect has positive width and height
2120 static inline const QRect toRect_normalized(const QRectF &rect)
2122 const int x = qRound(rect.x());
2123 const int y = qRound(rect.y());
2124 const int w = int(rect.width() + qreal(0.5));
2125 const int h = int(rect.height() + qreal(0.5));
2127 return QRect(x, y, w, h);
2130 static inline int fast_ceil_positive(const qreal &v)
2132 const int iv = int(v);
2139 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2141 const int xmin = int(rect.x());
2142 const int xmax = int(fast_ceil_positive(rect.right()));
2143 const int ymin = int(rect.y());
2144 const int ymax = int(fast_ceil_positive(rect.bottom()));
2145 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2151 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2153 #ifdef QT_DEBUG_DRAW
2154 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2157 Q_D(QRasterPaintEngine);
2158 QRasterPaintEngineState *s = state();
2160 if (s->matrix.type() > QTransform::TxTranslate) {
2161 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2163 QRectF(0, 0, img.width(), img.height()));
2166 const QClipData *clip = d->clip();
2167 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2169 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2170 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2173 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2175 } else if (clip->hasRectClip) {
2176 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2184 d->image_filler.clip = clip;
2185 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2186 if (!d->image_filler.blend)
2188 d->image_filler.dx = -pt.x();
2189 d->image_filler.dy = -pt.y();
2190 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2192 fillRect_normalized(rr, &d->image_filler, d);
2197 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2199 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2210 inline RotationType qRotationType(const QTransform &transform)
2212 QTransform::TransformationType type = transform.type();
2214 if (type > QTransform::TxRotate)
2217 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2218 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2221 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2222 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2225 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2226 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2232 inline bool isPixelAligned(const QRectF &rect) {
2233 return QRectF(rect.toRect()) == rect;
2240 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2241 Qt::ImageConversionFlags)
2243 #ifdef QT_DEBUG_DRAW
2244 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2250 Q_D(QRasterPaintEngine);
2251 QRasterPaintEngineState *s = state();
2252 int sr_l = qFloor(sr.left());
2253 int sr_r = qCeil(sr.right()) - 1;
2254 int sr_t = qFloor(sr.top());
2255 int sr_b = qCeil(sr.bottom()) - 1;
2257 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2258 QTransform old = s->matrix;
2260 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2261 QRgb color = img.pixel(sr_l, sr_t);
2262 switch (img.format()) {
2263 case QImage::Format_ARGB32_Premultiplied:
2264 case QImage::Format_ARGB8565_Premultiplied:
2265 case QImage::Format_ARGB6666_Premultiplied:
2266 case QImage::Format_ARGB8555_Premultiplied:
2267 case QImage::Format_ARGB4444_Premultiplied:
2268 // Combine premultiplied color with the opacity set on the painter.
2269 d->solid_color_filler.solid.color =
2270 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2271 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2274 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2278 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2279 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2283 d->solid_color_filler.clip = d->clip();
2284 d->solid_color_filler.adjustSpanMethods();
2285 fillRect(r, &d->solid_color_filler);
2291 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2293 const QClipData *clip = d->clip();
2295 if (s->matrix.type() > QTransform::TxTranslate
2297 && (!clip || clip->hasRectClip)
2298 && s->intOpacity == 256
2299 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2300 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2301 && d->rasterBuffer->format == img.format()
2302 && (d->rasterBuffer->format == QImage::Format_RGB16
2303 || d->rasterBuffer->format == QImage::Format_RGB32
2304 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2305 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2307 RotationType rotationType = qRotationType(s->matrix);
2309 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2310 QRectF transformedTargetRect = s->matrix.mapRect(r);
2312 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2313 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2315 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2316 if (clippedTransformedTargetRect.isNull())
2319 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2321 QRect clippedSourceRect
2322 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2323 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2325 uint dbpl = d->rasterBuffer->bytesPerLine();
2326 uint sbpl = img.bytesPerLine();
2328 uchar *dst = d->rasterBuffer->buffer();
2329 uint bpp = img.depth() >> 3;
2331 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2332 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2334 uint cw = clippedSourceRect.width();
2335 uint ch = clippedSourceRect.height();
2337 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2344 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2346 QRectF targetBounds = s->matrix.mapRect(r);
2347 bool exceedsPrecision = targetBounds.width() > 0xffff
2348 || targetBounds.height() > 0xffff;
2350 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2351 if (s->matrix.type() > QTransform::TxScale) {
2352 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2353 if (func && (!clip || clip->hasRectClip)) {
2354 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2355 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2356 s->matrix, s->intOpacity);
2360 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2361 if (func && (!clip || clip->hasRectClip)) {
2362 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2363 img.bits(), img.bytesPerLine(),
2364 qt_mapRect_non_normalizing(r, s->matrix), sr,
2365 !clip ? d->deviceRect : clip->clipRect,
2372 QTransform copy = s->matrix;
2373 copy.translate(r.x(), r.y());
2375 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2376 copy.translate(-sr.x(), -sr.y());
2378 d->image_filler_xform.clip = clip;
2379 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2380 if (!d->image_filler_xform.blend)
2382 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2384 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2385 QRectF rr = s->matrix.mapRect(r);
2387 const int x1 = qRound(rr.x());
2388 const int y1 = qRound(rr.y());
2389 const int x2 = qRound(rr.right());
2390 const int y2 = qRound(rr.bottom());
2392 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2396 #ifdef QT_FAST_SPANS
2398 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2399 d->initializeRasterizer(&d->image_filler_xform);
2400 d->rasterizer->setAntialiased(s->flags.antialiased);
2402 const QRectF &rect = r.normalized();
2403 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2404 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2406 if (s->flags.tx_noshear)
2407 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2409 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2415 QTransform m = s->matrix;
2416 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2417 m.m21(), m.m22(), m.m23(),
2418 m.m31(), m.m32(), m.m33());
2419 fillPath(path, &d->image_filler_xform);
2422 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2423 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2425 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2427 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2429 } else if (clip->hasRectClip) {
2430 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2436 d->image_filler.clip = clip;
2437 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2438 if (!d->image_filler.blend)
2440 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2441 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2444 rr.translate(s->matrix.dx(), s->matrix.dy());
2446 const int x1 = qRound(rr.x());
2447 const int y1 = qRound(rr.y());
2448 const int x2 = qRound(rr.right());
2449 const int y2 = qRound(rr.bottom());
2451 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2458 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2460 #ifdef QT_DEBUG_DRAW
2461 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2463 Q_D(QRasterPaintEngine);
2464 QRasterPaintEngineState *s = state();
2468 QPixmapData *pd = pixmap.pixmapData();
2469 if (pd->classId() == QPixmapData::RasterClass) {
2470 image = static_cast<QRasterPixmapData *>(pd)->image;
2472 image = pixmap.toImage();
2475 if (image.depth() == 1)
2476 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2478 if (s->matrix.type() > QTransform::TxTranslate) {
2479 QTransform copy = s->matrix;
2480 copy.translate(r.x(), r.y());
2481 copy.translate(-sr.x(), -sr.y());
2482 d->image_filler_xform.clip = d->clip();
2483 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2484 if (!d->image_filler_xform.blend)
2486 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2488 #ifdef QT_FAST_SPANS
2490 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2491 d->initializeRasterizer(&d->image_filler_xform);
2492 d->rasterizer->setAntialiased(s->flags.antialiased);
2494 const QRectF &rect = r.normalized();
2495 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2496 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2497 if (s->flags.tx_noshear)
2498 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2500 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2506 fillPath(path, &d->image_filler_xform);
2508 d->image_filler.clip = d->clip();
2510 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2511 if (!d->image_filler.blend)
2513 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2514 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2517 rr.translate(s->matrix.dx(), s->matrix.dy());
2518 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2524 static inline bool monoVal(const uchar* s, int x)
2526 return (s[x>>3] << (x&7)) & 0x80;
2532 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2534 Q_D(QRasterPaintEngine);
2535 QRasterPaintEngineState *s = state();
2537 if (!s->penData.blend)
2540 QRasterBuffer *rb = d->rasterBuffer.data();
2542 const QRect rect(rx, ry, w, h);
2543 const QClipData *clip = d->clip();
2544 bool unclipped = false;
2546 // inlined QRect::intersects
2547 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2548 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2550 if (clip->hasRectClip) {
2551 unclipped = rx > clip->xmin
2552 && rx + w < clip->xmax
2554 && ry + h < clip->ymax;
2560 // inlined QRect::intersects
2561 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2562 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2566 // inlined QRect::contains
2567 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2568 && rect.top() >= 0 && rect.bottom() < rb->height();
2570 unclipped = contains && d->isUnclipped_normalized(rect);
2573 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2574 const uchar * scanline = static_cast<const uchar *>(src);
2576 if (s->flags.fast_text) {
2579 if (s->penData.bitmapBlit) {
2580 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2581 scanline, w, h, bpl);
2584 } else if (depth == 8) {
2585 if (s->penData.alphamapBlit) {
2586 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2587 scanline, w, h, bpl, 0);
2590 } else if (depth == 32) {
2591 // (A)RGB Alpha mask where the alpha component is not used.
2592 if (s->penData.alphaRGBBlit) {
2593 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2594 (const uint *) scanline, w, h, bpl / 4, 0);
2598 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2599 // (A)RGB Alpha mask where the alpha component is not used.
2601 int nx = qMax(0, rx);
2602 int ny = qMax(0, ry);
2604 // Move scanline pointer to compensate for moved x and y
2605 int xdiff = nx - rx;
2606 int ydiff = ny - ry;
2607 scanline += ydiff * bpl;
2608 scanline += xdiff * (depth == 32 ? 4 : 1);
2613 if (nx + w > d->rasterBuffer->width())
2614 w = d->rasterBuffer->width() - nx;
2615 if (ny + h > d->rasterBuffer->height())
2616 h = d->rasterBuffer->height() - ny;
2621 if (depth == 8 && s->penData.alphamapBlit) {
2622 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2623 scanline, w, h, bpl, clip);
2624 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2625 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2626 (const uint *) scanline, w, h, bpl / 4, clip);
2641 scanline += bpl * y0;
2645 w = qMin(w, rb->width() - qMax(0, rx));
2646 h = qMin(h, rb->height() - qMax(0, ry));
2648 if (w <= 0 || h <= 0)
2651 const int NSPANS = 256;
2652 QSpan spans[NSPANS];
2655 const int x1 = x0 + w;
2656 const int y1 = y0 + h;
2659 for (int y = y0; y < y1; ++y) {
2660 for (int x = x0; x < x1; ) {
2661 if (!monoVal(scanline, x)) {
2666 if (current == NSPANS) {
2667 blend(current, spans, &s->penData);
2670 spans[current].x = x + rx;
2671 spans[current].y = y + ry;
2672 spans[current].coverage = 255;
2675 // extend span until we find a different one.
2676 while (x < x1 && monoVal(scanline, x)) {
2680 spans[current].len = len;
2685 } else if (depth == 8) {
2686 for (int y = y0; y < y1; ++y) {
2687 for (int x = x0; x < x1; ) {
2688 // Skip those with 0 coverage
2689 if (scanline[x] == 0) {
2694 if (current == NSPANS) {
2695 blend(current, spans, &s->penData);
2698 int coverage = scanline[x];
2699 spans[current].x = x + rx;
2700 spans[current].y = y + ry;
2701 spans[current].coverage = coverage;
2705 // extend span until we find a different one.
2706 while (x < x1 && scanline[x] == coverage) {
2710 spans[current].len = len;
2715 } else { // 32-bit alpha...
2716 uint *sl = (uint *) src;
2717 for (int y = y0; y < y1; ++y) {
2718 for (int x = x0; x < x1; ) {
2719 // Skip those with 0 coverage
2720 if ((sl[x] & 0x00ffffff) == 0) {
2725 if (current == NSPANS) {
2726 blend(current, spans, &s->penData);
2729 uint rgbCoverage = sl[x];
2730 int coverage = qGreen(rgbCoverage);
2731 spans[current].x = x + rx;
2732 spans[current].y = y + ry;
2733 spans[current].coverage = coverage;
2737 // extend span until we find a different one.
2738 while (x < x1 && sl[x] == rgbCoverage) {
2742 spans[current].len = len;
2745 sl += bpl / sizeof(uint);
2748 // qDebug() << "alphaPenBlt: num spans=" << current
2749 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2750 // Call span func for current set of spans.
2752 blend(current, spans, &s->penData);
2755 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2756 const QFixedPoint *positions, QFontEngine *fontEngine)
2758 Q_D(QRasterPaintEngine);
2759 QRasterPaintEngineState *s = state();
2761 #if !defined(QT_NO_FREETYPE)
2762 if (fontEngine->type() == QFontEngine::Freetype) {
2763 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2764 QFontEngineFT::GlyphFormat neededFormat =
2765 painter()->device()->devType() == QInternal::Widget
2766 ? fe->defaultGlyphFormat()
2767 : QFontEngineFT::Format_A8;
2769 if (d_func()->mono_surface
2770 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2772 neededFormat = QFontEngineFT::Format_Mono;
2774 if (neededFormat == QFontEngineFT::Format_None)
2775 neededFormat = QFontEngineFT::Format_A8;
2777 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2778 if (s->matrix.type() >= QTransform::TxScale) {
2779 if (s->matrix.isAffine())
2780 gset = fe->loadTransformedGlyphSet(s->matrix);
2785 if (!gset || gset->outline_drawing
2786 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2789 FT_Face lockedFace = 0;
2792 switch (neededFormat) {
2793 case QFontEngineFT::Format_Mono:
2796 case QFontEngineFT::Format_A8:
2799 case QFontEngineFT::Format_A32:
2807 for (int i = 0; i < numGlyphs; i++) {
2808 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2809 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2811 if (!glyph || glyph->format != neededFormat) {
2813 lockedFace = fe->lockFace();
2814 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2817 if (!glyph || !glyph->data)
2821 switch (neededFormat) {
2822 case QFontEngineFT::Format_Mono:
2823 pitch = ((glyph->width + 31) & ~31) >> 3;
2825 case QFontEngineFT::Format_A8:
2826 pitch = (glyph->width + 3) & ~3;
2828 case QFontEngineFT::Format_A32:
2829 pitch = glyph->width * 4;
2836 alphaPenBlt(glyph->data, pitch, depth,
2837 qFloor(positions[i].x) + glyph->x,
2838 qFloor(positions[i].y) - glyph->y,
2839 glyph->width, glyph->height);
2846 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2848 QImageTextureGlyphCache *cache =
2849 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2851 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2852 fontEngine->setGlyphCache(0, cache);
2855 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2856 cache->fillInPendingGlyphs();
2858 const QImage &image = cache->image();
2859 int bpl = image.bytesPerLine();
2861 int depth = image.depth();
2865 leftShift = 2; // multiply by 4
2866 else if (depth == 1)
2867 rightShift = 3; // divide by 8
2869 int margin = cache->glyphMargin();
2870 const uchar *bits = image.bits();
2871 for (int i=0; i<numGlyphs; ++i) {
2873 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2874 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2875 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2879 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2880 int y = qFloor(positions[i].y) - c.baseLineY - margin;
2882 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2885 // c.baseLineX, c.baseLineY,
2888 // positions[i].x.toInt(), positions[i].y.toInt());
2890 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2896 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2897 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2899 Q_D(QRasterPaintEngine);
2900 QRasterPaintEngineState *s = state();
2902 QFontEngine *fontEngine = ti.fontEngine;
2903 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2904 QPaintEngineEx::drawTextItem(p, ti);
2908 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2910 QVarLengthArray<QFixedPoint> positions;
2911 QVarLengthArray<glyph_t> glyphs;
2912 QTransform matrix = s->matrix;
2913 matrix.translate(p.x(), p.y());
2914 if (matrix.type() == QTransform::TxScale)
2915 fe->setFontScale(matrix.m11());
2916 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2918 for (int i=0; i<glyphs.size(); ++i) {
2919 TOpenFontCharMetrics tmetrics;
2920 const TUint8 *glyphBitmapBytes;
2921 TSize glyphBitmapSize;
2922 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2923 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
2924 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
2925 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2928 if (matrix.type() == QTransform::TxScale)
2929 fe->setFontScale(1.0);
2933 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2936 * Returns true if the rectangle is completely within the current clip
2937 * state of the paint engine.
2939 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2941 const QClipData *cl = clip();
2943 // inline contains() for performance (we know the rects are normalized)
2944 const QRect &r1 = deviceRect;
2945 return (r.left() >= r1.left() && r.right() <= r1.right()
2946 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2950 if (cl->hasRectClip) {
2951 // currently all painting functions clips to deviceRect internally
2952 if (cl->clipRect == deviceRect)
2955 // inline contains() for performance (we know the rects are normalized)
2956 const QRect &r1 = cl->clipRect;
2957 return (r.left() >= r1.left() && r.right() <= r1.right()
2958 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2960 return qt_region_strictContains(cl->clipRegion, r);
2964 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2967 Q_Q(const QRasterPaintEngine);
2968 const QRasterPaintEngineState *s = q->state();
2969 const QClipData *cl = clip();
2971 QRect r = rect.normalized();
2972 // inline contains() for performance (we know the rects are normalized)
2973 const QRect &r1 = deviceRect;
2974 return (r.left() >= r1.left() && r.right() <= r1.right()
2975 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2979 // currently all painting functions that call this function clip to deviceRect internally
2980 if (cl->hasRectClip && cl->clipRect == deviceRect)
2983 if (s->flags.antialiased)
2986 QRect r = rect.normalized();
2988 r.setX(r.x() - penWidth);
2989 r.setY(r.y() - penWidth);
2990 r.setWidth(r.width() + 2 * penWidth);
2991 r.setHeight(r.height() + 2 * penWidth);
2994 if (cl->hasRectClip) {
2995 // inline contains() for performance (we know the rects are normalized)
2996 const QRect &r1 = cl->clipRect;
2997 return (r.left() >= r1.left() && r.right() <= r1.right()
2998 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3000 return qt_region_strictContains(cl->clipRegion, r);
3004 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3007 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3011 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3012 const QSpanData *data) const
3014 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3018 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3019 const QSpanData *data) const
3021 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3027 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3032 QFontEngine *fontEngine = textItem->fontEngine();
3033 if (!supportsTransformations(fontEngine)) {
3034 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3037 QPaintEngineEx::drawStaticTextItem(textItem);
3044 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3046 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3047 QRasterPaintEngineState *s = state();
3049 #ifdef QT_DEBUG_DRAW
3050 Q_D(QRasterPaintEngine);
3051 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3052 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3059 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3061 if (!supportsTransformations(ti.fontEngine)) {
3062 QVarLengthArray<QFixedPoint> positions;
3063 QVarLengthArray<glyph_t> glyphs;
3065 QTransform matrix = s->matrix;
3066 matrix.translate(p.x(), p.y());
3068 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3070 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3074 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3075 if (s->matrix.type() <= QTransform::TxTranslate
3076 || (s->matrix.type() == QTransform::TxScale
3077 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3078 drawGlyphsS60(p, ti);
3081 #else // Q_WS_WIN || Q_WS_MAC
3083 QFontEngine *fontEngine = ti.fontEngine;
3085 #if defined(Q_WS_QWS)
3086 if (fontEngine->type() == QFontEngine::Box) {
3087 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3091 if (s->matrix.type() < QTransform::TxScale
3092 && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
3093 || (fontEngine->type() == QFontEngine::Proxy
3094 && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
3096 fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
3102 if (s->matrix.type() < QTransform::TxScale) {
3104 QVarLengthArray<QFixedPoint> positions;
3105 QVarLengthArray<glyph_t> glyphs;
3106 QTransform matrix = state()->transform();
3108 qreal _x = qFloor(p.x());
3109 qreal _y = qFloor(p.y());
3110 matrix.translate(_x, _y);
3112 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3113 if (glyphs.size() == 0)
3116 for(int i = 0; i < glyphs.size(); i++) {
3117 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3118 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3119 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3120 qRound(positions[i].x + metrics.x),
3121 qRound(positions[i].y + metrics.y),
3122 img.width(), img.height());
3128 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3130 #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
3131 if (fontEngine->type() == QFontEngine::QPF2) {
3132 QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
3133 if (renderingEngine)
3134 fontEngine = renderingEngine;
3138 if (fontEngine->type() != QFontEngine::Freetype) {
3139 QPaintEngineEx::drawTextItem(p, ti);
3143 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3145 QTransform matrix = s->matrix;
3146 matrix.translate(p.x(), p.y());
3148 QVarLengthArray<QFixedPoint> positions;
3149 QVarLengthArray<glyph_t> glyphs;
3150 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3151 if (glyphs.size() == 0)
3154 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3155 QPaintEngine::drawTextItem(p, ti);
3161 QPaintEngineEx::drawTextItem(p, ti);
3167 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3169 Q_D(QRasterPaintEngine);
3170 QRasterPaintEngineState *s = state();
3173 if (!s->penData.blend)
3176 if (!s->flags.fast_pen) {
3177 QPaintEngineEx::drawPoints(points, pointCount);
3181 QCosmeticStroker stroker(s, d->deviceRect);
3182 stroker.drawPoints(points, pointCount);
3186 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3188 Q_D(QRasterPaintEngine);
3189 QRasterPaintEngineState *s = state();
3192 if (!s->penData.blend)
3195 if (!s->flags.fast_pen) {
3196 QPaintEngineEx::drawPoints(points, pointCount);
3200 QCosmeticStroker stroker(s, d->deviceRect);
3201 stroker.drawPoints(points, pointCount);
3207 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3209 #ifdef QT_DEBUG_DRAW
3210 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3212 Q_D(QRasterPaintEngine);
3213 QRasterPaintEngineState *s = state();
3216 if (!s->penData.blend)
3219 if (s->flags.fast_pen) {
3220 QCosmeticStroker stroker(s, d->deviceRect);
3221 for (int i=0; i<lineCount; ++i) {
3222 const QLine &l = lines[i];
3223 stroker.drawLine(l.p1(), l.p2());
3226 QPaintEngineEx::drawLines(lines, lineCount);
3230 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3236 Q_Q(QRasterPaintEngine);
3237 QRasterPaintEngineState *s = q->state();
3239 const QPen &pen = s->lastPen;
3240 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3241 const QVector<qreal> pattern = pen.dashPattern();
3243 qreal patternLength = 0;
3244 for (int i = 0; i < pattern.size(); ++i)
3245 patternLength += pattern.at(i);
3247 if (patternLength <= 0)
3250 qreal length = line.length();
3251 Q_ASSERT(length > 0);
3252 while (length > 0) {
3253 const bool rasterize = *inDash;
3254 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3257 if (dash >= length) {
3259 *dashOffset += dash / width;
3263 *inDash = !(*inDash);
3264 if (++*dashIndex >= pattern.size())
3271 if (rasterize && dash > 0)
3272 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3279 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3281 #ifdef QT_DEBUG_DRAW
3282 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3284 Q_D(QRasterPaintEngine);
3285 QRasterPaintEngineState *s = state();
3288 if (!s->penData.blend)
3290 if (s->flags.fast_pen) {
3291 QCosmeticStroker stroker(s, d->deviceRect);
3292 for (int i=0; i<lineCount; ++i) {
3293 QLineF line = lines[i];
3294 stroker.drawLine(line.p1(), line.p2());
3297 QPaintEngineEx::drawLines(lines, lineCount);
3305 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3307 QPaintEngineEx::drawEllipse(rect);
3314 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3316 Q_D(QRasterPaintEngine);
3323 CGContextRef QRasterPaintEngine::getCGContext() const
3325 Q_D(const QRasterPaintEngine);
3326 return d->cgContext;
3334 void QRasterPaintEngine::setDC(HDC hdc) {
3335 Q_D(QRasterPaintEngine);
3342 HDC QRasterPaintEngine::getDC() const
3344 Q_D(const QRasterPaintEngine);
3351 void QRasterPaintEngine::releaseDC(HDC) const
3357 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3359 const QTransform &m = state()->matrix;
3360 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3361 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3362 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3363 || (m.type() <= QTransform::TxTranslate
3364 && (fontEngineType == QFontEngine::TestFontEngine
3365 || fontEngineType == QFontEngine::Box))) {
3369 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3372 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3374 #if defined(Q_WS_MAC)
3375 // Mac font engines don't support scaling and rotation
3376 if (m.type() > QTransform::TxTranslate)
3378 if (m.type() >= QTransform::TxProject)
3382 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3391 QPoint QRasterPaintEngine::coordinateOffset() const
3393 return QPoint(0, 0);
3397 Draws the given color \a spans with the specified \a color. The \a
3398 count parameter specifies the number of spans.
3400 The default implementation does nothing; reimplement this function
3401 to draw the given color \a spans with the specified \a color. Note
3402 that this function \e must be reimplemented if the framebuffer is
3405 \sa drawBufferSpan()
3407 #if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
3408 void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
3413 qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
3414 "a non memory-mapped device");
3418 \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
3420 Draws the given \a buffer.
3422 The default implementation does nothing; reimplement this function
3423 to draw a buffer that contains more than one color. Note that this
3424 function \e must be reimplemented if the framebuffer is not
3427 The \a size parameter specifies the total size of the given \a
3428 buffer, while the \a length parameter specifies the number of
3429 pixels to draw. The buffer's position is given by (\a x, \a
3430 y). The provided \a alpha value is added to each pixel in the
3431 buffer when drawing.
3433 \sa drawColorSpans()
3435 void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
3436 int x, int y, int length, uint const_alpha)
3443 Q_UNUSED(const_alpha);
3444 qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
3445 "a non memory-mapped device");
3449 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3454 Q_D(QRasterPaintEngine);
3456 Q_ASSERT(image.depth() == 1);
3458 const int spanCount = 256;
3459 QT_FT_Span spans[spanCount];
3463 int w = image.width();
3464 int h = image.height();
3465 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3466 int ymin = qMax(qRound(pos.y()), 0);
3467 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3468 int xmin = qMax(qRound(pos.x()), 0);
3470 int x_offset = xmin - qRound(pos.x());
3472 QImage::Format format = image.format();
3473 for (int y = ymin; y < ymax; ++y) {
3474 const uchar *src = image.scanLine(y - qRound(pos.y()));
3475 if (format == QImage::Format_MonoLSB) {
3476 for (int x = 0; x < xmax - xmin; ++x) {
3477 int src_x = x + x_offset;
3478 uchar pixel = src[src_x >> 3];
3483 if (pixel & (0x1 << (src_x & 7))) {
3484 spans[n].x = xmin + x;
3486 spans[n].coverage = 255;
3488 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3492 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3495 if (n == spanCount) {
3496 fg->blend(n, spans, fg);
3502 for (int x = 0; x < xmax - xmin; ++x) {
3503 int src_x = x + x_offset;
3504 uchar pixel = src[src_x >> 3];
3509 if (pixel & (0x80 >> (x & 7))) {
3510 spans[n].x = xmin + x;
3512 spans[n].coverage = 255;
3514 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3518 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3521 if (n == spanCount) {
3522 fg->blend(n, spans, fg);
3530 fg->blend(n, spans, fg);
3536 \enum QRasterPaintEngine::ClipType
3539 \value RectClip Indicates that the currently set clip is a single rectangle.
3540 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3545 Returns the type of the clip currently set.
3547 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3549 Q_D(const QRasterPaintEngine);
3551 const QClipData *clip = d->clip();
3552 if (!clip || clip->hasRectClip)
3560 Returns the bounding rect of the currently set clip.
3562 QRect QRasterPaintEngine::clipBoundingRect() const
3564 Q_D(const QRasterPaintEngine);
3566 const QClipData *clip = d->clip();
3569 return d->deviceRect;
3571 if (clip->hasRectClip)
3572 return clip->clipRect;
3574 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3577 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3579 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3581 QVarLengthArray<short, 4096> buffer;
3583 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3584 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3585 result->initialize();
3587 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3588 const QSpan *c1_spans = c1ClipLines[y].spans;
3589 int c1_count = c1ClipLines[y].count;
3590 const QSpan *c2_spans = c2ClipLines[y].spans;
3591 int c2_count = c2ClipLines[y].count;
3593 if (c1_count == 0 && c2_count == 0)
3595 if (c1_count == 0) {
3596 result->appendSpans(c2_spans, c2_count);
3598 } else if (c2_count == 0) {
3599 result->appendSpans(c1_spans, c1_count);
3603 // we need to merge the two
3605 // find required length
3606 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3607 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3609 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3611 // Fill with old spans.
3612 for (int i = 0; i < c1_count; ++i) {
3613 const QSpan *cs = c1_spans + i;
3614 for (int j=cs->x; j<cs->x + cs->len; ++j)
3615 buffer[j] = cs->coverage;
3618 // Fill with new spans
3619 for (int i = 0; i < c2_count; ++i) {
3620 const QSpan *cs = c2_spans + i;
3621 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3622 buffer[j] += cs->coverage;
3623 if (buffer[j] > 255)
3631 // Skip to next span
3632 while (x < max && buffer[x] == 0) ++x;
3633 if (x >= max) break;
3636 int coverage = buffer[x];
3638 // Find length of span
3639 while (x < max && buffer[x] == coverage)
3642 result->appendSpan(sx, x - sx, y, coverage);
3647 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3649 Q_Q(QRasterPaintEngine);
3650 QRasterPaintEngineState *s = q->state();
3652 rasterizer->setAntialiased(s->flags.antialiased);
3654 QRect clipRect(deviceRect);
3656 // ### get from optimized rectbased QClipData
3658 const QClipData *c = clip();
3660 const QRect r(QPoint(c->xmin, c->ymin),
3661 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3662 clipRect = clipRect.intersected(r);
3663 blend = data->blend;
3665 blend = data->unclipped_blend;
3668 rasterizer->setClipRect(clipRect);
3669 rasterizer->initialize(blend, data);
3672 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3673 ProcessSpans callback,
3674 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3676 if (!callback || !outline)
3679 Q_Q(QRasterPaintEngine);
3680 QRasterPaintEngineState *s = q->state();
3682 if (!s->flags.antialiased) {
3683 initializeRasterizer(spanData);
3685 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3689 rasterizer->rasterize(outline, fillRule);
3693 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3697 int q_gray_rendered_spans(QT_FT_Raster raster);
3700 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3701 ProcessSpans callback,
3702 void *userData, QRasterBuffer *)
3704 if (!callback || !outline)
3707 Q_Q(QRasterPaintEngine);
3708 QRasterPaintEngineState *s = q->state();
3710 if (!s->flags.antialiased) {
3711 rasterizer->setAntialiased(s->flags.antialiased);
3712 rasterizer->setClipRect(deviceRect);
3713 rasterizer->initialize(callback, userData);
3715 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3719 rasterizer->rasterize(outline, fillRule);
3723 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3724 // minimize memory reallocations. However if initial size for
3725 // raster pool is changed for lower value, reallocations will
3727 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3728 int rasterPoolSize = rasterPoolInitialSize;
3729 unsigned char *rasterPoolBase;
3730 #if defined(Q_WS_WIN64)
3732 // We make use of setjmp and longjmp in qgrayraster.c which requires
3733 // 16-byte alignment, hence we hardcode this requirement here..
3734 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3736 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3737 rasterPoolBase = rasterPoolOnStack;
3739 Q_CHECK_PTR(rasterPoolBase);
3741 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3743 void *data = userData;
3745 QT_FT_BBox clip_box = { deviceRect.x(),
3747 deviceRect.x() + deviceRect.width(),
3748 deviceRect.y() + deviceRect.height() };
3750 QT_FT_Raster_Params rasterParams;
3751 rasterParams.target = 0;
3752 rasterParams.source = outline;
3753 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3754 rasterParams.gray_spans = 0;
3755 rasterParams.black_spans = 0;
3756 rasterParams.bit_test = 0;
3757 rasterParams.bit_set = 0;
3758 rasterParams.user = data;
3759 rasterParams.clip_box = clip_box;
3764 int rendered_spans = 0;
3768 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3769 rasterParams.gray_spans = callback;
3770 rasterParams.skip_spans = rendered_spans;
3771 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3773 // Out of memory, reallocate some more and try again...
3774 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3775 int new_size = rasterPoolSize * 2;
3776 if (new_size > 1024 * 1024) {
3777 qWarning("QPainter: Rasterization of primitive failed");
3781 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3783 #if defined(Q_WS_WIN64)
3784 _aligned_free(rasterPoolBase);
3786 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3787 free(rasterPoolBase);
3790 rasterPoolSize = new_size;
3792 #if defined(Q_WS_WIN64)
3793 // We make use of setjmp and longjmp in qgrayraster.c which requires
3794 // 16-byte alignment, hence we hardcode this requirement here..
3795 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3797 (unsigned char *) malloc(rasterPoolSize);
3799 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3801 qt_ft_grays_raster.raster_done(*grayRaster.data());
3802 qt_ft_grays_raster.raster_new(grayRaster.data());
3803 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3809 #if defined(Q_WS_WIN64)
3810 _aligned_free(rasterPoolBase);
3812 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3813 free(rasterPoolBase);
3817 void QRasterPaintEnginePrivate::recalculateFastImages()
3819 Q_Q(QRasterPaintEngine);
3820 QRasterPaintEngineState *s = q->state();
3822 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3823 && s->matrix.type() <= QTransform::TxShear;
3826 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3828 Q_Q(const QRasterPaintEngine);
3829 const QRasterPaintEngineState *s = q->state();
3831 return s->flags.fast_images
3832 && (mode == QPainter::CompositionMode_SourceOver
3833 || (mode == QPainter::CompositionMode_Source
3834 && !image.hasAlphaChannel()));
3837 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3839 Q_ASSERT(image.depth() == 1);
3841 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3842 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3844 QRgb fg = PREMUL(color.rgba());
3847 int height = sourceImage.height();
3848 int width = sourceImage.width();
3849 for (int y=0; y<height; ++y) {
3850 uchar *source = sourceImage.scanLine(y);
3851 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3852 if (!source || !target)
3853 QT_THROW(std::bad_alloc()); // we must have run out of memory
3854 for (int x=0; x < width; ++x)
3855 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3860 QRasterBuffer::~QRasterBuffer()
3864 void QRasterBuffer::init()
3866 compositionMode = QPainter::CompositionMode_SourceOver;
3867 monoDestinationWithClut = false;
3872 QImage::Format QRasterBuffer::prepare(QImage *image)
3874 m_buffer = (uchar *)image->bits();
3875 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3876 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3877 bytes_per_pixel = image->depth()/8;
3878 bytes_per_line = image->bytesPerLine();
3880 format = image->format();
3881 drawHelper = qDrawHelper + format;
3882 if (image->depth() == 1 && image->colorTable().size() == 2) {
3883 monoDestinationWithClut = true;
3884 destColor0 = PREMUL(image->colorTable()[0]);
3885 destColor1 = PREMUL(image->colorTable()[1]);
3891 void QRasterBuffer::resetBuffer(int val)
3893 memset(m_buffer, val, m_height*bytes_per_line);
3897 #if defined(Q_WS_QWS)
3898 void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
3900 m_buffer = reinterpret_cast<uchar*>(device->memory());
3901 m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
3902 m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
3903 bytes_per_pixel = device->depth() / 8;
3904 bytes_per_line = device->bytesPerLine();
3905 format = device->format();
3906 #ifndef QT_NO_RASTERCALLBACKS
3908 drawHelper = qDrawHelperCallback + format;
3911 drawHelper = qDrawHelper + format;
3914 int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
3918 return widget->frameGeometry().width();
3920 return widget->frameGeometry().height();
3925 return qt_paint_device_metric(widget, m);
3928 int QCustomRasterPaintDevice::bytesPerLine() const
3930 return (width() * depth() + 7) / 8;
3933 #elif defined(Q_OS_SYMBIAN)
3935 void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
3939 #endif // Q_OS_SYMBIAN
3942 \class QCustomRasterPaintDevice
3947 \brief The QCustomRasterPaintDevice class is provided to activate
3948 hardware accelerated paint engines in Qt for Embedded Linux.
3950 Note that this class is only available in \l{Qt for Embedded Linux}.
3952 In \l{Qt for Embedded Linux}, painting is a pure software
3953 implementation. But starting with Qt 4.2, it is
3954 possible to add an accelerated graphics driver to take advantage
3955 of available hardware resources.
3957 Hardware acceleration is accomplished by creating a custom screen
3958 driver, accelerating the copying from memory to the screen, and
3959 implementing a custom paint engine accelerating the various
3960 painting operations. Then a custom paint device (derived from the
3961 QCustomRasterPaintDevice class) and a custom window surface
3962 (derived from QWSWindowSurface) must be implemented to make
3963 \l{Qt for Embedded Linux} aware of the accelerated driver.
3965 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
3966 documentation for details.
3968 \sa QRasterPaintEngine, QPaintDevice
3972 \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
3974 Constructs a custom raster based paint device for the given
3975 top-level \a widget.
3979 \fn int QCustomRasterPaintDevice::bytesPerLine() const
3981 Returns the number of bytes per line in the framebuffer. Note that
3982 this number might be larger than the framebuffer width.
3986 \fn int QCustomRasterPaintDevice::devType() const
3991 \fn QImage::Format QCustomRasterPaintDevice::format() const
3993 Returns the format of the device's memory buffet.
3995 The default format is QImage::Format_ARGB32_Premultiplied. The
3996 only other valid format is QImage::Format_RGB16.
4000 \fn void * QCustomRasterPaintDevice::memory () const
4002 Returns a pointer to the paint device's memory buffer, or 0 if no
4007 \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
4012 \fn QSize QCustomRasterPaintDevice::size () const
4017 QClipData::QClipData(int height)
4019 clipSpanHeight = height;
4024 xmin = xmax = ymin = ymax = 0;
4028 hasRectClip = hasRegionClip = false;
4031 QClipData::~QClipData()
4039 void QClipData::initialize()
4045 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
4047 Q_CHECK_PTR(m_clipLines);
4049 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
4050 allocated = clipSpanHeight;
4051 Q_CHECK_PTR(m_spans);
4057 m_clipLines[y].spans = 0;
4058 m_clipLines[y].count = 0;
4062 const int len = clipRect.width();
4065 QSpan *span = m_spans + count;
4069 span->coverage = 255;
4072 m_clipLines[y].spans = span;
4073 m_clipLines[y].count = 1;
4077 while (y < clipSpanHeight) {
4078 m_clipLines[y].spans = 0;
4079 m_clipLines[y].count = 0;
4082 } else if (hasRegionClip) {
4084 const QVector<QRect> rects = clipRegion.rects();
4085 const int numRects = rects.size();
4088 const int maxSpans = (ymax - ymin) * numRects;
4089 if (maxSpans > allocated) {
4090 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
4091 allocated = maxSpans;
4096 int firstInBand = 0;
4098 while (firstInBand < numRects) {
4099 const int currMinY = rects.at(firstInBand).y();
4100 const int currMaxY = currMinY + rects.at(firstInBand).height();
4102 while (y < currMinY) {
4103 m_clipLines[y].spans = 0;
4104 m_clipLines[y].count = 0;
4108 int lastInBand = firstInBand;
4109 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
4112 while (y < currMaxY) {
4114 m_clipLines[y].spans = m_spans + count;
4115 m_clipLines[y].count = lastInBand - firstInBand + 1;
4117 for (int r = firstInBand; r <= lastInBand; ++r) {
4118 const QRect &currRect = rects.at(r);
4119 QSpan *span = m_spans + count;
4120 span->x = currRect.x();
4121 span->len = currRect.width();
4123 span->coverage = 255;
4129 firstInBand = lastInBand + 1;
4132 Q_ASSERT(count <= allocated);
4134 while (y < clipSpanHeight) {
4135 m_clipLines[y].spans = 0;
4136 m_clipLines[y].count = 0;
4142 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
4147 free(m_clipLines); // same for clipLines
4153 void QClipData::fixup()
4158 ymin = ymax = xmin = xmax = 0;
4163 ymin = m_spans[0].y;
4164 ymax = m_spans[count-1].y + 1;
4168 const int firstLeft = m_spans[0].x;
4169 const int firstRight = m_spans[0].x + m_spans[0].len;
4172 for (int i = 0; i < count; ++i) {
4173 QT_FT_Span_& span = m_spans[i];
4176 if (span.y != y + 1 && y != -1)
4179 m_clipLines[y].spans = &span;
4180 m_clipLines[y].count = 1;
4182 ++m_clipLines[y].count;
4184 const int spanLeft = span.x;
4185 const int spanRight = spanLeft + span.len;
4187 if (spanLeft < xmin)
4190 if (spanRight > xmax)
4193 if (spanLeft != firstLeft || spanRight != firstRight)
4199 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4204 Convert \a rect to clip spans.
4206 void QClipData::setClipRect(const QRect &rect)
4208 if (hasRectClip && rect == clipRect)
4211 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4213 hasRegionClip = false;
4217 xmax = rect.x() + rect.width();
4218 ymin = qMin(rect.y(), clipSpanHeight);
4219 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4226 // qDebug() << xmin << xmax << ymin << ymax;
4230 Convert \a region to clip spans.
4232 void QClipData::setClipRegion(const QRegion ®ion)
4234 if (region.rectCount() == 1) {
4235 setClipRect(region.rects().at(0));
4239 hasRegionClip = true;
4240 hasRectClip = false;
4241 clipRegion = region;
4243 { // set bounding rect
4244 const QRect rect = region.boundingRect();
4246 xmax = rect.x() + rect.width();
4248 ymax = rect.y() + rect.height();
4260 spans must be sorted on y
4262 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4263 const QSpan *spans, const QSpan *end,
4264 QSpan **outSpans, int available)
4266 const_cast<QClipData *>(clip)->initialize();
4268 QSpan *out = *outSpans;
4270 const QSpan *clipSpans = clip->m_spans + *currentClip;
4271 const QSpan *clipEnd = clip->m_spans + clip->count;
4273 while (available && spans < end ) {
4274 if (clipSpans >= clipEnd) {
4278 if (clipSpans->y > spans->y) {
4282 if (spans->y != clipSpans->y) {
4283 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4284 clipSpans = clip->m_clipLines[spans->y].spans;
4289 Q_ASSERT(spans->y == clipSpans->y);
4292 int sx2 = sx1 + spans->len;
4293 int cx1 = clipSpans->x;
4294 int cx2 = cx1 + clipSpans->len;
4296 if (cx1 < sx1 && cx2 < sx1) {
4299 } else if (sx1 < cx1 && sx2 < cx1) {
4303 int x = qMax(sx1, cx1);
4304 int len = qMin(sx2, cx2) - x;
4306 out->x = qMax(sx1, cx1);
4307 out->len = qMin(sx2, cx2) - out->x;
4309 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4321 *currentClip = clipSpans - clip->m_spans;
4325 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4327 // qDebug() << "qt_span_fill_clipped" << spanCount;
4328 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4330 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4332 const int NSPANS = 256;
4333 QSpan cspans[NSPANS];
4334 int currentClip = 0;
4335 const QSpan *end = spans + spanCount;
4336 while (spans < end) {
4337 QSpan *clipped = cspans;
4338 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4339 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4340 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4342 if (clipped - cspans)
4343 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4349 Clip spans to \a{clip}-rectangle.
4350 Returns number of unclipped spans
4352 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4355 const short minx = clip.left();
4356 const short miny = clip.top();
4357 const short maxx = clip.right();
4358 const short maxy = clip.bottom();
4361 for (int i = 0; i < numSpans; ++i) {
4362 if (spans[i].y > maxy)
4364 if (spans[i].y < miny
4365 || spans[i].x > maxx
4366 || spans[i].x + spans[i].len <= minx) {
4369 if (spans[i].x < minx) {
4370 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4373 spans[n].x = spans[i].x;
4374 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4376 if (spans[n].len == 0)
4378 spans[n].y = spans[i].y;
4379 spans[n].coverage = spans[i].coverage;
4386 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4389 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4390 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4392 Q_ASSERT(fillData->clip);
4393 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4395 // hw: check if this const_cast<> is safe!!!
4396 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4397 fillData->clip->clipRect);
4399 fillData->unclipped_blend(count, spans, fillData);
4402 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4404 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4406 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4407 // for (int i = 0; i < qMin(count, 10); ++i) {
4408 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4411 switch (clipData->operation) {
4413 case Qt::IntersectClip:
4415 QClipData *newClip = clipData->newClip;
4416 newClip->initialize();
4418 int currentClip = 0;
4419 const QSpan *end = spans + count;
4420 while (spans < end) {
4421 QSpan *newspans = newClip->m_spans + newClip->count;
4422 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4423 &newspans, newClip->allocated - newClip->count);
4424 newClip->count = newspans - newClip->m_spans;
4426 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4427 newClip->allocated *= 2;
4434 case Qt::ReplaceClip:
4435 clipData->newClip->appendSpans(spans, count);
4443 QImage QRasterBuffer::bufferImage() const
4445 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4447 for (int y = 0; y < m_height; ++y) {
4448 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4450 for (int x=0; x<m_width; ++x) {
4451 uint argb = span[x];
4452 image.setPixel(x, y, argb);
4460 void QRasterBuffer::flushToARGBImage(QImage *target) const
4462 int w = qMin(m_width, target->width());
4463 int h = qMin(m_height, target->height());
4465 for (int y=0; y<h; ++y) {
4466 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4467 QRgb *dest = (QRgb *) target->scanLine(y);
4468 for (int x=0; x<w; ++x) {
4469 QRgb pixel = sourceLine[x];
4470 int alpha = qAlpha(pixel);
4474 dest[x] = (alpha << 24)
4475 | ((255*qRed(pixel)/alpha) << 16)
4476 | ((255*qGreen(pixel)/alpha) << 8)
4477 | ((255*qBlue(pixel)/alpha) << 0);
4484 class QGradientCache
4488 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4489 stops(s), opacity(op), interpolationMode(mode) {}
4490 uint buffer[GRADIENT_STOPTABLE_SIZE];
4491 QGradientStops stops;
4493 QGradient::InterpolationMode interpolationMode;
4496 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4499 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4500 quint64 hash_val = 0;
4502 QGradientStops stops = gradient.stops();
4503 for (int i = 0; i < stops.size() && i <= 2; i++)
4504 hash_val += stops[i].second.rgba();
4506 QMutexLocker lock(&mutex);
4507 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4509 if (it == cache.constEnd())
4510 return addCacheElement(hash_val, gradient, opacity);
4513 const CacheInfo &cache_info = it.value();
4514 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4515 return cache_info.buffer;
4517 } while (it != cache.constEnd() && it.key() == hash_val);
4518 // an exact match for these stops and opacity was not found, create new cache
4519 return addCacheElement(hash_val, gradient, opacity);
4523 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4525 inline int maxCacheSize() const { return 60; }
4526 inline void generateGradientColorTable(const QGradient& g,
4528 int size, int opacity) const;
4529 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4530 if (cache.size() == maxCacheSize()) {
4531 // may remove more than 1, but OK
4532 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4534 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4535 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4536 return cache.insert(hash_val, cache_entry).value().buffer;
4539 QGradientColorTableHash cache;
4543 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4545 QGradientStops stops = gradient.stops();
4546 int stopCount = stops.count();
4547 Q_ASSERT(stopCount > 0);
4549 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4551 if (stopCount == 2) {
4552 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4553 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4555 qreal first_stop = stops[0].first;
4556 qreal second_stop = stops[1].first;
4558 if (second_stop < first_stop) {
4559 qSwap(first_color, second_color);
4560 qSwap(first_stop, second_stop);
4563 if (colorInterpolation) {
4564 first_color = PREMUL(first_color);
4565 second_color = PREMUL(second_color);
4568 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4569 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4571 uint red_first = qRed(first_color) << 16;
4572 uint green_first = qGreen(first_color) << 16;
4573 uint blue_first = qBlue(first_color) << 16;
4574 uint alpha_first = qAlpha(first_color) << 16;
4576 uint red_second = qRed(second_color) << 16;
4577 uint green_second = qGreen(second_color) << 16;
4578 uint blue_second = qBlue(second_color) << 16;
4579 uint alpha_second = qAlpha(second_color) << 16;
4582 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4583 if (colorInterpolation)
4584 colorTable[i] = first_color;
4586 colorTable[i] = PREMUL(first_color);
4589 if (i < second_index) {
4590 qreal reciprocal = qreal(1) / (second_index - first_index);
4592 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4593 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4594 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4595 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4598 red_first += 1 << 15;
4599 green_first += 1 << 15;
4600 blue_first += 1 << 15;
4601 alpha_first += 1 << 15;
4603 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4604 red_first += red_delta;
4605 green_first += green_delta;
4606 blue_first += blue_delta;
4607 alpha_first += alpha_delta;
4609 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4610 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4612 if (colorInterpolation)
4613 colorTable[i] = color;
4615 colorTable[i] = PREMUL(color);
4619 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4620 if (colorInterpolation)
4621 colorTable[i] = second_color;
4623 colorTable[i] = PREMUL(second_color);
4629 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4630 if (stopCount == 1) {
4631 current_color = PREMUL(current_color);
4632 for (int i = 0; i < size; ++i)
4633 colorTable[i] = current_color;
4637 // The position where the gradient begins and ends
4638 qreal begin_pos = stops[0].first;
4639 qreal end_pos = stops[stopCount-1].first;
4641 int pos = 0; // The position in the color table.
4644 qreal incr = 1 / qreal(size); // the double increment.
4645 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4647 // Up to first point
4648 colorTable[pos++] = PREMUL(current_color);
4649 while (dpos <= begin_pos) {
4650 colorTable[pos] = colorTable[pos - 1];
4655 int current_stop = 0; // We always interpolate between current and current + 1.
4657 qreal t; // position between current left and right stops
4658 qreal t_delta; // the t increment per entry in the color table
4660 if (dpos < end_pos) {
4662 while (dpos > stops[current_stop+1].first)
4665 if (current_stop != 0)
4666 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4667 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4669 if (colorInterpolation) {
4670 current_color = PREMUL(current_color);
4671 next_color = PREMUL(next_color);
4674 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4675 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4676 t = (dpos - stops[current_stop].first) * c;
4680 Q_ASSERT(current_stop < stopCount);
4682 int dist = qRound(t);
4683 int idist = 256 - dist;
4685 if (colorInterpolation)
4686 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4688 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4693 if (dpos >= end_pos)
4699 while (dpos > stops[current_stop+skip+1].first)
4703 current_stop += skip;
4705 current_color = next_color;
4707 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4708 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4710 if (colorInterpolation) {
4712 current_color = PREMUL(current_color);
4713 next_color = PREMUL(next_color);
4716 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4717 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4718 t = (dpos - stops[current_stop].first) * c;
4725 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4726 while (pos < size - 1) {
4727 colorTable[pos] = current_color;
4731 // Make sure the last color stop is represented at the end of the table
4732 colorTable[size - 1] = current_color;
4735 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4738 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4742 rasterEngine = const_cast<QRasterPaintEngine *>(pe);
4747 m11 = m22 = m33 = 1.;
4748 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4749 clip = pe ? pe->d_func()->clip() : 0;
4752 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4754 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4756 Qt::BrushStyle brushStyle = qbrush_style(brush);
4757 switch (brushStyle) {
4758 case Qt::SolidPattern: {
4760 QColor c = qbrush_color(brush);
4761 QRgb rgba = c.rgba();
4762 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4763 if ((solid.color & 0xff000000) == 0
4764 && compositionMode == QPainter::CompositionMode_SourceOver) {
4770 case Qt::LinearGradientPattern:
4772 type = LinearGradient;
4773 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4774 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4775 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4776 gradient.spread = g->spread();
4778 QLinearGradientData &linearData = gradient.linear;
4780 linearData.origin.x = g->start().x();
4781 linearData.origin.y = g->start().y();
4782 linearData.end.x = g->finalStop().x();
4783 linearData.end.y = g->finalStop().y();
4787 case Qt::RadialGradientPattern:
4789 type = RadialGradient;
4790 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4791 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4792 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4793 gradient.spread = g->spread();
4795 QRadialGradientData &radialData = gradient.radial;
4797 QPointF center = g->center();
4798 radialData.center.x = center.x();
4799 radialData.center.y = center.y();
4800 radialData.center.radius = g->centerRadius();
4801 QPointF focal = g->focalPoint();
4802 radialData.focal.x = focal.x();
4803 radialData.focal.y = focal.y();
4804 radialData.focal.radius = g->focalRadius();
4808 case Qt::ConicalGradientPattern:
4810 type = ConicalGradient;
4811 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4812 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4813 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4814 gradient.spread = QGradient::RepeatSpread;
4816 QConicalGradientData &conicalData = gradient.conical;
4818 QPointF center = g->center();
4819 conicalData.center.x = center.x();
4820 conicalData.center.y = center.y();
4821 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4825 case Qt::Dense1Pattern:
4826 case Qt::Dense2Pattern:
4827 case Qt::Dense3Pattern:
4828 case Qt::Dense4Pattern:
4829 case Qt::Dense5Pattern:
4830 case Qt::Dense6Pattern:
4831 case Qt::Dense7Pattern:
4832 case Qt::HorPattern:
4833 case Qt::VerPattern:
4834 case Qt::CrossPattern:
4835 case Qt::BDiagPattern:
4836 case Qt::FDiagPattern:
4837 case Qt::DiagCrossPattern:
4840 tempImage = new QImage();
4841 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4842 initTexture(tempImage, alpha, QTextureData::Tiled);
4844 case Qt::TexturePattern:
4847 tempImage = new QImage();
4849 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4850 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4852 *tempImage = brush.textureImage();
4853 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4861 adjustSpanMethods();
4864 void QSpanData::adjustSpanMethods()
4874 unclipped_blend = 0;
4877 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4878 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4879 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4880 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4881 fillRect = rasterBuffer->drawHelper->fillRect;
4883 case LinearGradient:
4884 case RadialGradient:
4885 case ConicalGradient:
4886 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4890 #ifndef QT_NO_RASTERCALLBACKS
4891 if (!rasterBuffer->buffer())
4892 unclipped_blend = qBlendTextureCallback;
4895 unclipped_blend = qBlendTexture;
4897 unclipped_blend = qBlendTexture;
4899 if (!texture.imageData)
4900 unclipped_blend = 0;
4905 if (!unclipped_blend) {
4908 blend = unclipped_blend;
4909 } else if (clip->hasRectClip) {
4910 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4912 blend = qt_span_fill_clipped;
4916 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4919 // make sure we round off correctly in qdrawhelper.cpp
4920 delta.translate(1.0 / 65536, 1.0 / 65536);
4922 QTransform inv = (delta * matrix).inverted();
4935 const bool affine = !m13 && !m23;
4936 fast_matrix = affine
4937 && m11 * m11 + m21 * m21 < 1e4
4938 && m12 * m12 + m22 * m22 < 1e4
4942 adjustSpanMethods();
4945 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4947 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4949 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4950 if (!d || d->height == 0) {
4951 texture.imageData = 0;
4958 texture.bytesPerLine = 0;
4959 texture.format = QImage::Format_Invalid;
4960 texture.colorTable = 0;
4961 texture.hasAlpha = alpha != 256;
4963 texture.imageData = d->data;
4964 texture.width = d->width;
4965 texture.height = d->height;
4967 if (sourceRect.isNull()) {
4970 texture.x2 = texture.width;
4971 texture.y2 = texture.height;
4973 texture.x1 = sourceRect.x();
4974 texture.y1 = sourceRect.y();
4975 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4976 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4979 texture.bytesPerLine = d->bytes_per_line;
4981 texture.format = d->format;
4982 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4983 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4985 texture.const_alpha = alpha;
4986 texture.type = _type;
4988 adjustSpanMethods();
4993 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4996 Draws the first \a pointCount points in the buffer \a points
4998 The default implementation converts the first \a pointCount QPoints in \a points
4999 to QPointFs and calls the floating point version of drawPoints.
5003 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
5006 Reimplement this function to draw the largest ellipse that can be
5007 contained within rectangle \a rect.
5010 #ifdef QT_DEBUG_DRAW
5011 void dumpClip(int width, int height, const QClipData *clip)
5013 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
5014 clipImg.fill(0xffff0000);
5021 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
5023 for (int i = 0; i < clip->count; ++i) {
5024 const QSpan *span = ((QClipData *) clip)->spans() + i;
5025 for (int j = 0; j < span->len; ++j)
5026 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
5027 x0 = qMin(x0, int(span->x));
5028 x1 = qMax(x1, int(span->x + span->len - 1));
5030 y0 = qMin(y0, int(span->y));
5031 y1 = qMax(y1, int(span->y));
5034 static int counter = 0;
5041 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
5042 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));