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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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>
57 #if defined (Q_WS_X11)
58 # include <private/qfontengine_ft_p.h>
61 // #include <private/qdatabuffer_p.h>
62 // #include <private/qpainter_p.h>
63 #include <private/qmath_p.h>
64 #include <private/qtextengine_p.h>
65 #include <private/qfontengine_p.h>
66 #include <private/qpixmap_raster_p.h>
67 // #include <private/qpolygonclipper_p.h>
68 // #include <private/qrasterizer_p.h>
69 #include <private/qimage_p.h>
70 #include <private/qstatictext_p.h>
71 #include <private/qcosmeticstroker_p.h>
72 #include "qmemrotate_p.h"
74 #include "qpaintengine_raster_p.h"
75 // #include "qbezier_p.h"
76 #include "qoutlinemapper_p.h"
79 # include <qt_windows.h>
80 # include <qvarlengtharray.h>
81 # include <private/qfontengine_p.h>
82 # if defined(Q_OS_WINCE)
83 # include "qguifunctions_wince.h"
85 #elif defined(Q_WS_MAC)
86 # include <private/qt_mac_p.h>
87 # include <private/qpixmap_mac_p.h>
88 # include <private/qpaintengine_mac_p.h>
89 #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
90 # include <private/qfontengine_s60_p.h>
91 #elif defined(Q_WS_QPA)
92 # include <private/qfontengine_ft_p.h>
95 #if defined(Q_WS_WIN64)
102 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
104 #define qreal_to_fixed_26_6(f) (int(f * 64))
105 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
106 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
108 // #define QT_DEBUG_DRAW
110 void dumpClip(int width, int height, const QClipData *clip);
113 #define QT_FAST_SPANS
116 // A little helper macro to get a better approximation of dimensions.
117 // If we have a rect that starting at 0.5 of width 3.5 it should span
119 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
122 extern bool qt_cleartype_enabled;
126 extern bool qt_applefontsmoothing_enabled;
130 /********************************************************************************
133 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
134 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
135 static void qt_span_clip(int count, const QSpan *spans, void *userData);
136 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
142 Qt::ClipOperation operation;
148 LineDrawIncludeLastPixel
151 struct QRasterFloatPoint {
157 static const QRectF boundingRect(const QPointF *points, int pointCount)
159 const QPointF *e = points;
160 const QPointF *last = points + pointCount;
161 qreal minx, maxx, miny, maxy;
162 minx = maxx = e->x();
163 miny = maxy = e->y();
167 else if (e->x() > maxx)
171 else if (e->y() > maxy)
174 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
178 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
179 return (elementCount == 5 // 5-point polygon, check for closed rect
180 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
181 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
182 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
183 && pts[0] < pts[4] && pts[1] < pts[5]
185 (elementCount == 4 // 4-point polygon, check for unclosed rect
186 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
187 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
188 && pts[0] < pts[4] && pts[1] < pts[5]
193 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
195 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
198 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
200 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
203 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
204 qfixed c2x, qfixed c2y,
205 qfixed ex, qfixed ey,
208 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
209 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
210 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
214 #if !defined(QT_NO_DEBUG) && 0
215 static void qt_debug_path(const QPainterPath &path)
217 const char *names[] = {
224 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
225 for (int i=0; i<path.elementCount(); ++i) {
226 const QPainterPath::Element &e = path.elementAt(i);
227 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
228 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
233 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
234 QPaintEngineExPrivate(),
241 \class QRasterPaintEngine
246 \brief The QRasterPaintEngine class enables hardware acceleration
247 of painting operations in Qt for Embedded Linux.
249 Note that this functionality is only available in
250 \l{Qt for Embedded Linux}.
252 In \l{Qt for Embedded Linux}, painting is a pure software
253 implementation. But starting with Qt 4.2, it is
254 possible to add an accelerated graphics driver to take advantage
255 of available hardware resources.
257 Hardware acceleration is accomplished by creating a custom screen
258 driver, accelerating the copying from memory to the screen, and
259 implementing a custom paint engine accelerating the various
260 painting operations. Then a custom paint device (derived from the
261 QCustomRasterPaintDevice class) and a custom window surface
262 (derived from QWSWindowSurface) must be implemented to make
263 \l{Qt for Embedded Linux} aware of the accelerated driver.
265 \note The QRasterPaintEngine class does not support 8-bit images.
266 Instead, they need to be converted to a supported format, such as
267 QImage::Format_ARGB32_Premultiplied.
269 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
270 documentation for details.
272 \sa QCustomRasterPaintDevice, QPaintEngine
276 \fn Type QRasterPaintEngine::type() const
282 \relates QRasterPaintEngine
284 A struct equivalent to QT_FT_Span, containing a position (x,
285 y), the span's length in pixels and its color/coverage (a value
286 ranging from 0 to 255).
292 Creates a raster based paint engine for operating on the given
293 \a device, with the complete set of \l
294 {QPaintEngine::PaintEngineFeature}{paint engine features and
297 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
298 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
300 d_func()->device = device;
307 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
310 d_func()->device = device;
314 void QRasterPaintEngine::init()
316 Q_D(QRasterPaintEngine);
323 // The antialiasing raster.
324 d->grayRaster.reset(new QT_FT_Raster);
325 Q_CHECK_PTR(d->grayRaster.data());
326 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
327 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
330 d->rasterizer.reset(new QRasterizer);
331 d->rasterBuffer.reset(new QRasterBuffer());
332 d->outlineMapper.reset(new QOutlineMapper);
333 d->outlinemapper_xform_dirty = true;
335 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
336 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
337 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
339 d->baseClip.reset(new QClipData(d->device->height()));
340 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
342 d->image_filler.init(d->rasterBuffer.data(), this);
343 d->image_filler.type = QSpanData::Texture;
345 d->image_filler_xform.init(d->rasterBuffer.data(), this);
346 d->image_filler_xform.type = QSpanData::Texture;
348 d->solid_color_filler.init(d->rasterBuffer.data(), this);
349 d->solid_color_filler.type = QSpanData::Solid;
351 d->deviceDepth = d->device->depth();
353 d->mono_surface = false;
354 gccaps &= ~PorterDuff;
356 QImage::Format format = QImage::Format_Invalid;
358 switch (d->device->devType()) {
359 case QInternal::Pixmap:
360 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
362 case QInternal::Image:
363 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
366 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
372 case QImage::Format_MonoLSB:
373 case QImage::Format_Mono:
374 d->mono_surface = true;
376 case QImage::Format_ARGB8565_Premultiplied:
377 case QImage::Format_ARGB8555_Premultiplied:
378 case QImage::Format_ARGB6666_Premultiplied:
379 case QImage::Format_ARGB4444_Premultiplied:
380 case QImage::Format_ARGB32_Premultiplied:
381 case QImage::Format_ARGB32:
382 gccaps |= PorterDuff;
384 case QImage::Format_RGB32:
385 case QImage::Format_RGB444:
386 case QImage::Format_RGB555:
387 case QImage::Format_RGB666:
388 case QImage::Format_RGB888:
389 case QImage::Format_RGB16:
400 Destroys this paint engine.
402 QRasterPaintEngine::~QRasterPaintEngine()
404 Q_D(QRasterPaintEngine);
406 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
412 bool QRasterPaintEngine::begin(QPaintDevice *device)
414 Q_D(QRasterPaintEngine);
416 if (device->devType() == QInternal::Pixmap) {
417 QPixmap *pixmap = static_cast<QPixmap *>(device);
418 QPixmapData *pd = pixmap->pixmapData();
419 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
420 d->device = pd->buffer();
425 // Make sure QPaintEngine::paintDevice() returns the proper device.
428 Q_ASSERT(d->device->devType() == QInternal::Image
429 || d->device->devType() == QInternal::CustomRaster);
431 d->systemStateChanged();
433 QRasterPaintEngineState *s = state();
434 ensureOutlineMapper();
435 d->outlineMapper->m_clip_rect = d->deviceRect;
437 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
438 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
439 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
440 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
442 d->rasterizer->setClipRect(d->deviceRect);
444 s->penData.init(d->rasterBuffer.data(), this);
445 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
446 s->stroker = &d->basicStroker;
447 d->basicStroker.setClipRect(d->deviceRect);
449 s->brushData.init(d->rasterBuffer.data(), this);
450 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
452 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
454 setDirty(DirtyBrushOrigin);
457 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
458 << ") devType:" << device->devType()
459 << "devRect:" << d->deviceRect;
461 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
465 #if defined(Q_WS_WIN)
466 d->isPlain45DegreeRotation = true;
470 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
471 #if defined(Q_WS_WIN)
472 else if (qt_cleartype_enabled)
473 #elif defined (Q_WS_MAC)
474 else if (qt_applefontsmoothing_enabled)
479 QImage::Format format = static_cast<QImage *>(d->device)->format();
480 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
481 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
483 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
485 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
494 bool QRasterPaintEngine::end()
497 Q_D(QRasterPaintEngine);
498 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
500 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
510 void QRasterPaintEngine::releaseBuffer()
512 Q_D(QRasterPaintEngine);
513 d->rasterBuffer.reset(new QRasterBuffer);
519 QSize QRasterPaintEngine::size() const
521 Q_D(const QRasterPaintEngine);
522 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
529 void QRasterPaintEngine::saveBuffer(const QString &s) const
531 Q_D(const QRasterPaintEngine);
532 d->rasterBuffer->bufferImage().save(s, "PNG");
539 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
541 QRasterPaintEngineState *s = state();
542 // FALCON: get rid of this line, see drawImage call below.
544 QTransform::TransformationType txop = s->matrix.type();
548 case QTransform::TxNone:
549 s->flags.int_xform = true;
552 case QTransform::TxTranslate:
553 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
554 && qreal(int(s->matrix.dy())) == s->matrix.dy();
557 case QTransform::TxScale:
558 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
559 && qreal(int(s->matrix.dy())) == s->matrix.dy()
560 && qreal(int(s->matrix.m11())) == s->matrix.m11()
561 && qreal(int(s->matrix.m22())) == s->matrix.m22();
564 default: // shear / perspective...
565 s->flags.int_xform = false;
569 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
571 ensureOutlineMapper();
574 Q_D(QRasterPaintEngine);
575 d->isPlain45DegreeRotation = false;
576 if (txop >= QTransform::TxRotate) {
577 d->isPlain45DegreeRotation =
578 (qFuzzyIsNull(matrix.m11())
579 && qFuzzyIsNull(matrix.m12() - qreal(1))
580 && qFuzzyIsNull(matrix.m21() + qreal(1))
581 && qFuzzyIsNull(matrix.m22())
584 (qFuzzyIsNull(matrix.m11() + qreal(1))
585 && qFuzzyIsNull(matrix.m12())
586 && qFuzzyIsNull(matrix.m21())
587 && qFuzzyIsNull(matrix.m22() + qreal(1))
590 (qFuzzyIsNull(matrix.m11())
591 && qFuzzyIsNull(matrix.m12() + qreal(1))
592 && qFuzzyIsNull(matrix.m21() - qreal(1))
593 && qFuzzyIsNull(matrix.m22())
603 QRasterPaintEngineState::~QRasterPaintEngineState()
605 if (flags.has_clip_ownership)
610 QRasterPaintEngineState::QRasterPaintEngineState()
622 flags.fast_pen = true;
623 flags.antialiased = false;
624 flags.bilinear = false;
625 flags.fast_text = true;
626 flags.int_xform = true;
627 flags.tx_noshear = true;
628 flags.fast_images = true;
631 flags.has_clip_ownership = false;
636 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
641 , strokeFlags(s.strokeFlags)
642 , lastBrush(s.lastBrush)
643 , brushData(s.brushData)
644 , fillFlags(s.fillFlags)
645 , pixmapFlags(s.pixmapFlags)
646 , intOpacity(s.intOpacity)
650 , flag_bits(s.flag_bits)
652 brushData.tempImage = 0;
653 penData.tempImage = 0;
654 flags.has_clip_ownership = false;
660 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
662 QRasterPaintEngineState *s;
664 s = new QRasterPaintEngineState();
666 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
674 void QRasterPaintEngine::setState(QPainterState *s)
676 Q_D(QRasterPaintEngine);
677 QPaintEngineEx::setState(s);
678 d->rasterBuffer->compositionMode = s->composition_mode;
682 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
687 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
694 void QRasterPaintEngine::penChanged()
697 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
699 QRasterPaintEngineState *s = state();
700 s->strokeFlags |= DirtyPen;
701 s->dirty |= DirtyPen;
707 void QRasterPaintEngine::updatePen(const QPen &pen)
709 Q_D(QRasterPaintEngine);
710 QRasterPaintEngineState *s = state();
712 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
715 Qt::PenStyle pen_style = qpen_style(pen);
720 s->penData.clip = d->clip();
721 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
723 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
724 || pen.brush().transform().type() >= QTransform::TxNone) {
725 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
728 // Slightly ugly handling of an uncommon case... We need to change
729 // the pen because it is reused in draw_midpoint to decide dashed
731 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
732 pen_style = Qt::SolidLine;
733 s->lastPen.setStyle(Qt::SolidLine);
736 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
737 d->basicStroker.setCapStyle(qpen_capStyle(pen));
738 d->basicStroker.setMiterLimit(pen.miterLimit());
740 qreal penWidth = qpen_widthf(pen);
742 d->basicStroker.setStrokeWidth(1);
744 d->basicStroker.setStrokeWidth(penWidth);
746 if(pen_style == Qt::SolidLine) {
747 s->stroker = &d->basicStroker;
748 } else if (pen_style != Qt::NoPen) {
750 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
751 if (pen.isCosmetic()) {
752 d->dashStroker->setClipRect(d->deviceRect);
754 // ### I've seen this inverted devrect multiple places now...
755 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
756 d->dashStroker->setClipRect(clipRect);
758 d->dashStroker->setDashPattern(pen.dashPattern());
759 d->dashStroker->setDashOffset(pen.dashOffset());
760 s->stroker = d->dashStroker.data();
765 ensureState(); // needed because of tx_noshear...
766 s->flags.fast_pen = pen_style > Qt::NoPen
768 && ((pen.isCosmetic() && penWidth <= 1)
769 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
771 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
781 void QRasterPaintEngine::brushOriginChanged()
783 QRasterPaintEngineState *s = state();
785 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
788 s->fillFlags |= DirtyBrushOrigin;
795 void QRasterPaintEngine::brushChanged()
797 QRasterPaintEngineState *s = state();
799 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
801 s->fillFlags |= DirtyBrush;
810 void QRasterPaintEngine::updateBrush(const QBrush &brush)
813 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
815 Q_D(QRasterPaintEngine);
816 QRasterPaintEngineState *s = state();
817 // must set clip prior to setup, as setup uses it...
818 s->brushData.clip = d->clip();
819 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
820 if (s->fillFlags & DirtyTransform
821 || brush.transform().type() >= QTransform::TxNone)
822 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
823 s->lastBrush = brush;
827 void QRasterPaintEngine::updateOutlineMapper()
829 Q_D(QRasterPaintEngine);
830 d->outlineMapper->setMatrix(state()->matrix);
833 void QRasterPaintEngine::updateState()
835 QRasterPaintEngineState *s = state();
837 if (s->dirty & DirtyTransform)
838 updateMatrix(s->matrix);
840 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
841 const QPainter::CompositionMode mode = s->composition_mode;
842 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
843 && s->intOpacity == 256
844 && (mode == QPainter::CompositionMode_Source
845 || (mode == QPainter::CompositionMode_SourceOver
846 && qAlpha(s->penData.solid.color) == 255));
856 void QRasterPaintEngine::opacityChanged()
858 QRasterPaintEngineState *s = state();
861 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
864 s->fillFlags |= DirtyOpacity;
865 s->strokeFlags |= DirtyOpacity;
866 s->pixmapFlags |= DirtyOpacity;
867 s->dirty |= DirtyOpacity;
868 s->intOpacity = (int) (s->opacity * 256);
874 void QRasterPaintEngine::compositionModeChanged()
876 Q_D(QRasterPaintEngine);
877 QRasterPaintEngineState *s = state();
880 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
883 s->fillFlags |= DirtyCompositionMode;
884 s->dirty |= DirtyCompositionMode;
886 s->strokeFlags |= DirtyCompositionMode;
887 d->rasterBuffer->compositionMode = s->composition_mode;
889 d->recalculateFastImages();
895 void QRasterPaintEngine::renderHintsChanged()
897 QRasterPaintEngineState *s = state();
900 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
903 bool was_aa = s->flags.antialiased;
904 bool was_bilinear = s->flags.bilinear;
906 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
907 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
909 if (was_aa != s->flags.antialiased)
910 s->strokeFlags |= DirtyHints;
912 if (was_bilinear != s->flags.bilinear) {
913 s->strokeFlags |= DirtyPen;
914 s->fillFlags |= DirtyBrush;
917 Q_D(QRasterPaintEngine);
918 d->recalculateFastImages();
924 void QRasterPaintEngine::transformChanged()
926 QRasterPaintEngineState *s = state();
929 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
932 s->fillFlags |= DirtyTransform;
933 s->strokeFlags |= DirtyTransform;
935 s->dirty |= DirtyTransform;
937 Q_D(QRasterPaintEngine);
938 d->recalculateFastImages();
944 void QRasterPaintEngine::clipEnabledChanged()
946 QRasterPaintEngineState *s = state();
949 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
953 s->clip->enabled = s->clipEnabled;
954 s->fillFlags |= DirtyClipEnabled;
955 s->strokeFlags |= DirtyClipEnabled;
956 s->pixmapFlags |= DirtyClipEnabled;
960 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
962 SrcOverBlendFunc func,
967 if (alpha == 0 || !clip.isValid())
970 Q_ASSERT(img.depth() >= 8);
972 int srcBPL = img.bytesPerLine();
973 const uchar *srcBits = img.bits();
974 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
975 int iw = img.width();
976 int ih = img.height();
981 // Adjust the image according to the source offset...
982 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
985 // adapt the x parameters
986 int x = qRound(pt.x());
988 int cx2 = clip.x() + clip.width();
991 srcBits += srcSize * d;
996 int d = x + iw - cx2;
1002 // adapt the y paremeters...
1004 int cy2 = clip.y() + clip.height();
1005 int y = qRound(pt.y());
1008 srcBits += srcBPL * d;
1013 int d = y + ih - cy2;
1019 // call the blend function...
1020 int dstSize = rasterBuffer->bytesPerPixel();
1021 int dstBPL = rasterBuffer->bytesPerLine();
1022 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1029 void QRasterPaintEnginePrivate::systemStateChanged()
1031 QRect clipRect(0, 0,
1032 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1033 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1035 if (!systemClip.isEmpty()) {
1036 QRegion clippedDeviceRgn = systemClip & clipRect;
1037 deviceRect = clippedDeviceRgn.boundingRect();
1038 baseClip->setClipRegion(clippedDeviceRgn);
1040 deviceRect = clipRect;
1041 baseClip->setClipRect(deviceRect);
1043 #ifdef QT_DEBUG_DRAW
1044 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1047 exDeviceRect = deviceRect;
1049 Q_Q(QRasterPaintEngine);
1050 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1051 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1052 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1055 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1057 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1060 Q_Q(QRasterPaintEngine);
1061 bool bilinear = q->state()->flags.bilinear;
1063 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1064 spanData->setupMatrix(b.transform() * m, bilinear);
1066 if (m.type() <= QTransform::TxTranslate) {
1067 // specialize setupMatrix for translation matrices
1068 // to avoid needless matrix inversion
1076 spanData->dx = -m.dx();
1077 spanData->dy = -m.dy();
1078 spanData->txop = m.type();
1079 spanData->bilinear = bilinear;
1080 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1081 spanData->adjustSpanMethods();
1083 spanData->setupMatrix(m, bilinear);
1088 // #define QT_CLIPPING_RATIOS
1090 #ifdef QT_CLIPPING_RATIOS
1095 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1097 if (d->clip()->hasRectClip)
1099 if (d->clip()->hasRegionClip)
1103 if ((totalClips % 5000) == 0) {
1104 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1105 rectClips * 100.0 / (qreal) totalClips,
1106 regionClips * 100.0 / (qreal) totalClips,
1107 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1116 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1118 if (s->flags.has_clip_ownership)
1121 s->flags.has_clip_ownership = false;
1124 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1126 s->fillFlags |= QPaintEngine::DirtyClipPath;
1127 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1128 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1130 d->solid_color_filler.clip = d->clip();
1131 d->solid_color_filler.adjustSpanMethods();
1133 #ifdef QT_DEBUG_DRAW
1134 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1143 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1145 #ifdef QT_DEBUG_DRAW
1146 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1148 if (path.elements()) {
1149 for (int i=0; i<path.elementCount(); ++i) {
1150 qDebug() << " - " << path.elements()[i]
1151 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1154 for (int i=0; i<path.elementCount(); ++i) {
1155 qDebug() << " ---- "
1156 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1161 Q_D(QRasterPaintEngine);
1162 QRasterPaintEngineState *s = state();
1164 const qreal *points = path.points();
1165 const QPainterPath::ElementType *types = path.elements();
1167 // There are some cases that are not supported by clip(QRect)
1168 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1169 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1170 if (s->matrix.type() <= QTransform::TxScale
1171 && ((path.shape() == QVectorPath::RectangleHint)
1172 || (isRect(points, path.elementCount())
1173 && (!types || (types[0] == QPainterPath::MoveToElement
1174 && types[1] == QPainterPath::LineToElement
1175 && types[2] == QPainterPath::LineToElement
1176 && types[3] == QPainterPath::LineToElement))))) {
1177 #ifdef QT_DEBUG_DRAW
1178 qDebug() << " --- optimizing vector clip to rect clip...";
1181 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1182 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1187 if (op == Qt::NoClip) {
1188 qrasterpaintengine_state_setNoClip(s);
1191 QClipData *base = d->baseClip.data();
1193 // Intersect with current clip when available...
1194 if (op == Qt::IntersectClip && s->clip)
1197 // We always intersect, except when there is nothing to
1198 // intersect with, in which case we simplify the operation to
1200 Qt::ClipOperation isectOp = Qt::IntersectClip;
1202 isectOp = Qt::ReplaceClip;
1204 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1205 newClip->initialize();
1206 ClipData clipData = { base, newClip, isectOp };
1207 ensureOutlineMapper();
1208 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1212 if (op == Qt::UniteClip) {
1214 QClipData *result = new QClipData(d->rasterBuffer->height());
1215 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1216 qt_merge_clip(current, newClip, result);
1224 if (s->flags.has_clip_ownership)
1228 s->flags.has_clip_ownership = true;
1230 qrasterpaintengine_dirty_clip(d, s);
1238 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1240 #ifdef QT_DEBUG_DRAW
1241 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1244 QRasterPaintEngineState *s = state();
1246 if (op == Qt::NoClip) {
1247 qrasterpaintengine_state_setNoClip(s);
1249 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1250 QPaintEngineEx::clip(rect, op);
1253 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1254 QPaintEngineEx::clip(rect, op);
1260 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1262 Q_D(QRasterPaintEngine);
1263 QRect clipRect = r & d->deviceRect;
1264 QRasterPaintEngineState *s = state();
1266 if (op == Qt::ReplaceClip || s->clip == 0) {
1268 // No current clip, hence we intersect with sysclip and be
1270 QRegion clipRegion = systemClip();
1271 QClipData *clip = new QClipData(d->rasterBuffer->height());
1273 if (clipRegion.isEmpty())
1274 clip->setClipRect(clipRect);
1276 clip->setClipRegion(clipRegion & clipRect);
1278 if (s->flags.has_clip_ownership)
1282 s->clip->enabled = true;
1283 s->flags.has_clip_ownership = true;
1285 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1286 QClipData *base = s->clip;
1289 if (base->hasRectClip || base->hasRegionClip) {
1290 if (!s->flags.has_clip_ownership) {
1291 s->clip = new QClipData(d->rasterBuffer->height());
1292 s->flags.has_clip_ownership = true;
1294 if (base->hasRectClip)
1295 s->clip->setClipRect(base->clipRect & clipRect);
1297 s->clip->setClipRegion(base->clipRegion & clipRect);
1298 s->clip->enabled = true;
1306 qrasterpaintengine_dirty_clip(d, s);
1314 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1316 #ifdef QT_DEBUG_DRAW
1317 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1320 Q_D(QRasterPaintEngine);
1322 if (region.rectCount() == 1) {
1323 clip(region.boundingRect(), op);
1327 QRasterPaintEngineState *s = state();
1328 const QClipData *clip = d->clip();
1329 const QClipData *baseClip = d->baseClip.data();
1331 if (op == Qt::NoClip) {
1332 qrasterpaintengine_state_setNoClip(s);
1333 } else if (s->matrix.type() > QTransform::TxScale
1334 || op == Qt::UniteClip
1335 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1336 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1337 QPaintEngineEx::clip(region, op);
1339 const QClipData *curClip;
1342 if (op == Qt::IntersectClip)
1347 if (s->flags.has_clip_ownership) {
1351 newClip = new QClipData(d->rasterBuffer->height());
1353 s->flags.has_clip_ownership = true;
1356 QRegion r = s->matrix.map(region);
1357 if (curClip->hasRectClip)
1358 newClip->setClipRegion(r & curClip->clipRect);
1359 else if (curClip->hasRegionClip)
1360 newClip->setClipRegion(r & curClip->clipRegion);
1362 qrasterpaintengine_dirty_clip(d, s);
1369 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1371 #ifdef QT_DEBUG_DRAW
1372 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1375 if (!fillData->blend)
1378 Q_D(QRasterPaintEngine);
1380 const QRectF controlPointRect = path.controlPointRect();
1382 QRasterPaintEngineState *s = state();
1383 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1384 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1385 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1386 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1387 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1388 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1390 if (!s->flags.antialiased && !do_clip) {
1391 d->initializeRasterizer(fillData);
1392 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1396 ensureOutlineMapper();
1397 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1400 static void fillRect_normalized(const QRect &r, QSpanData *data,
1401 QRasterPaintEnginePrivate *pe)
1405 bool rectClipped = true;
1408 x1 = qMax(r.x(), data->clip->xmin);
1409 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1410 y1 = qMax(r.y(), data->clip->ymin);
1411 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1412 rectClipped = data->clip->hasRectClip;
1415 x1 = qMax(r.x(), pe->deviceRect.x());
1416 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1417 y1 = qMax(r.y(), pe->deviceRect.y());
1418 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1420 x1 = qMax(r.x(), 0);
1421 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1422 y1 = qMax(r.y(), 0);
1423 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1426 if (x2 <= x1 || y2 <= y1)
1429 const int width = x2 - x1;
1430 const int height = y2 - y1;
1432 bool isUnclipped = rectClipped
1433 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1435 if (pe && isUnclipped) {
1436 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1438 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1439 || (mode == QPainter::CompositionMode_SourceOver
1440 && qAlpha(data->solid.color) == 255)))
1442 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1448 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1450 const int nspans = 256;
1451 QT_FT_Span spans[nspans];
1453 Q_ASSERT(data->blend);
1456 int n = qMin(nspans, y2 - y);
1460 spans[i].len = width;
1462 spans[i].coverage = 255;
1466 blend(n, spans, data);
1474 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1476 #ifdef QT_DEBUG_DRAW
1477 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1479 Q_D(QRasterPaintEngine);
1481 QRasterPaintEngineState *s = state();
1485 if (s->brushData.blend) {
1486 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1487 const QRect *r = rects;
1488 const QRect *lastRect = rects + rectCount;
1490 int offset_x = int(s->matrix.dx());
1491 int offset_y = int(s->matrix.dy());
1492 while (r < lastRect) {
1493 QRect rect = r->normalized();
1494 QRect rr = rect.translated(offset_x, offset_y);
1495 fillRect_normalized(rr, &s->brushData, d);
1499 QRectVectorPath path;
1500 for (int i=0; i<rectCount; ++i) {
1502 fill(path, s->brush);
1508 if (s->penData.blend) {
1509 QRectVectorPath path;
1510 if (s->flags.fast_pen) {
1511 QCosmeticStroker stroker(s, d->deviceRect);
1512 for (int i = 0; i < rectCount; ++i) {
1514 stroker.drawPath(path);
1517 for (int i = 0; i < rectCount; ++i) {
1519 stroke(path, s->pen);
1528 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1530 #ifdef QT_DEBUG_DRAW
1531 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1533 #ifdef QT_FAST_SPANS
1534 Q_D(QRasterPaintEngine);
1536 QRasterPaintEngineState *s = state();
1539 if (s->flags.tx_noshear) {
1541 if (s->brushData.blend) {
1542 d->initializeRasterizer(&s->brushData);
1543 for (int i = 0; i < rectCount; ++i) {
1544 const QRectF &rect = rects[i].normalized();
1547 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1548 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1549 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1554 if (s->penData.blend) {
1555 QRectVectorPath path;
1556 if (s->flags.fast_pen) {
1557 QCosmeticStroker stroker(s, d->deviceRect);
1558 for (int i = 0; i < rectCount; ++i) {
1560 stroker.drawPath(path);
1563 for (int i = 0; i < rectCount; ++i) {
1565 QPaintEngineEx::stroke(path, s->lastPen);
1572 #endif // QT_FAST_SPANS
1573 QPaintEngineEx::drawRects(rects, rectCount);
1580 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1582 Q_D(QRasterPaintEngine);
1583 QRasterPaintEngineState *s = state();
1586 if (!s->penData.blend)
1589 if (s->flags.fast_pen) {
1590 QCosmeticStroker stroker(s, d->deviceRect);
1591 stroker.drawPath(path);
1592 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1593 qreal width = s->lastPen.isCosmetic()
1594 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1595 : qpen_widthf(s->lastPen) * s->txscale;
1597 qreal dashOffset = s->lastPen.dashOffset();
1599 qreal patternLength = 0;
1600 const QVector<qreal> pattern = s->lastPen.dashPattern();
1601 for (int i = 0; i < pattern.size(); ++i)
1602 patternLength += pattern.at(i);
1604 if (patternLength > 0) {
1605 int n = qFloor(dashOffset / patternLength);
1606 dashOffset -= n * patternLength;
1607 while (dashOffset >= pattern.at(dashIndex)) {
1608 dashOffset -= pattern.at(dashIndex);
1609 if (++dashIndex >= pattern.size())
1615 Q_D(QRasterPaintEngine);
1616 d->initializeRasterizer(&s->penData);
1617 int lineCount = path.elementCount() / 2;
1618 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1620 for (int i = 0; i < lineCount; ++i) {
1621 if (lines[i].p1() == lines[i].p2()) {
1622 if (s->lastPen.capStyle() != Qt::FlatCap) {
1623 QPointF p = lines[i].p1();
1624 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1625 QPointF(p.x() + width*0.5, p.y())));
1626 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1631 const QLineF line = s->matrix.map(lines[i]);
1632 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1633 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1634 width / line.length(),
1635 s->lastPen.capStyle() == Qt::SquareCap);
1637 d->rasterizeLine_dashed(line, width,
1638 &dashIndex, &dashOffset, &inDash);
1643 QPaintEngineEx::stroke(path, pen);
1646 static inline QRect toNormalizedFillRect(const QRectF &rect)
1648 int x1 = qRound(rect.x());
1649 int y1 = qRound(rect.y());
1650 int x2 = qRound(rect.right());
1651 int y2 = qRound(rect.bottom());
1658 return QRect(x1, y1, x2 - x1, y2 - y1);
1664 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1668 #ifdef QT_DEBUG_DRAW
1669 QRectF rf = path.controlPointRect();
1670 qDebug() << "QRasterPaintEngine::fill(): "
1671 << "size=" << path.elementCount()
1672 << ", hints=" << hex << path.hints()
1676 Q_D(QRasterPaintEngine);
1677 QRasterPaintEngineState *s = state();
1680 if (!s->brushData.blend)
1683 if (path.shape() == QVectorPath::RectangleHint) {
1684 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1685 const qreal *p = path.points();
1686 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1687 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1688 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1692 if (s->flags.tx_noshear) {
1693 d->initializeRasterizer(&s->brushData);
1694 // ### Is normalizing really necessary here?
1695 const qreal *p = path.points();
1696 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1698 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1699 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1700 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1706 // ### Optimize for non transformed ellipses and rectangles...
1707 QRectF cpRect = path.controlPointRect();
1708 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1709 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1712 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1713 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1714 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1715 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1717 // ### Falonc: implement....
1718 // if (!s->flags.antialiased && !do_clip) {
1719 // d->initializeRasterizer(&s->brushData);
1720 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1724 ensureOutlineMapper();
1725 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1728 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1730 Q_D(QRasterPaintEngine);
1731 QRasterPaintEngineState *s = state();
1733 if (!s->flags.antialiased) {
1734 uint txop = s->matrix.type();
1735 if (txop == QTransform::TxNone) {
1736 fillRect_normalized(toNormalizedFillRect(r), data, d);
1738 } else if (txop == QTransform::TxTranslate) {
1739 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1740 fillRect_normalized(rr, data, d);
1742 } else if (txop == QTransform::TxScale) {
1743 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1744 fillRect_normalized(rr, data, d);
1749 if (s->flags.tx_noshear) {
1750 d->initializeRasterizer(data);
1751 QRectF nr = r.normalized();
1752 if (!nr.isEmpty()) {
1753 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1754 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1755 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1762 ensureOutlineMapper();
1763 fillPath(path, data);
1769 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1771 #ifdef QT_DEBUG_DRAW
1772 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1774 QRasterPaintEngineState *s = state();
1777 if (!s->brushData.blend)
1780 fillRect(r, &s->brushData);
1786 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1788 #ifdef QT_DEBUG_DRAW
1789 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1791 Q_D(QRasterPaintEngine);
1792 QRasterPaintEngineState *s = state();
1794 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1795 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1796 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1799 d->solid_color_filler.clip = d->clip();
1800 d->solid_color_filler.adjustSpanMethods();
1801 fillRect(r, &d->solid_color_filler);
1804 static inline bool isAbove(const QPointF *a, const QPointF *b)
1806 return a->y() < b->y();
1809 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1814 Q_ASSERT(pointCount >= 2);
1816 QVector<const QPointF *> sorted;
1817 sorted.reserve(pointCount);
1819 upper->reserve(pointCount * 3 / 4);
1820 lower->reserve(pointCount * 3 / 4);
1822 for (int i = 0; i < pointCount; ++i)
1823 sorted << points + i;
1825 qSort(sorted.begin(), sorted.end(), isAbove);
1827 qreal splitY = sorted.at(sorted.size() / 2)->y();
1829 const QPointF *end = points + pointCount;
1830 const QPointF *last = end - 1;
1832 QVector<QPointF> *bin[2] = { upper, lower };
1834 for (const QPointF *p = points; p < end; ++p) {
1835 int side = p->y() < splitY;
1836 int lastSide = last->y() < splitY;
1838 if (side != lastSide) {
1839 if (qFuzzyCompare(p->y(), splitY)) {
1840 bin[!side]->append(*p);
1841 } else if (qFuzzyCompare(last->y(), splitY)) {
1842 bin[side]->append(*last);
1844 QPointF delta = *p - *last;
1845 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1847 bin[0]->append(intersection);
1848 bin[1]->append(intersection);
1852 bin[side]->append(*p);
1857 // give up if we couldn't reduce the point count
1858 return upper->size() < pointCount && lower->size() < pointCount;
1864 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1866 Q_D(QRasterPaintEngine);
1867 QRasterPaintEngineState *s = state();
1869 const int maxPoints = 0xffff;
1871 // max amount of points that raster engine can reliably handle
1872 if (pointCount > maxPoints) {
1873 QVector<QPointF> upper, lower;
1875 if (splitPolygon(points, pointCount, &upper, &lower)) {
1876 fillPolygon(upper.constData(), upper.size(), mode);
1877 fillPolygon(lower.constData(), lower.size(), mode);
1879 qWarning("Polygon too complex for filling.");
1884 // Compose polygon fill..,
1885 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1886 ensureOutlineMapper();
1887 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1890 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1892 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1898 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1900 Q_D(QRasterPaintEngine);
1901 QRasterPaintEngineState *s = state();
1903 #ifdef QT_DEBUG_DRAW
1904 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1905 for (int i=0; i<pointCount; ++i)
1906 qDebug() << " - " << points[i];
1908 Q_ASSERT(pointCount >= 2);
1910 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1911 QRectF r(points[0], points[2]);
1917 if (mode != PolylineMode) {
1920 if (s->brushData.blend) {
1921 fillPolygon(points, pointCount, mode);
1925 // Do the outline...
1926 if (s->penData.blend) {
1927 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1928 if (s->flags.fast_pen) {
1929 QCosmeticStroker stroker(s, d->deviceRect);
1930 stroker.drawPath(vp);
1932 QPaintEngineEx::stroke(vp, s->lastPen);
1940 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1942 Q_D(QRasterPaintEngine);
1943 QRasterPaintEngineState *s = state();
1945 #ifdef QT_DEBUG_DRAW
1946 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1947 for (int i=0; i<pointCount; ++i)
1948 qDebug() << " - " << points[i];
1950 Q_ASSERT(pointCount >= 2);
1951 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1952 QRect r(points[0].x(),
1954 points[2].x() - points[0].x(),
1955 points[2].y() - points[0].y());
1963 if (mode != PolylineMode) {
1965 if (s->brushData.blend) {
1966 // Compose polygon fill..,
1967 ensureOutlineMapper();
1968 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1969 d->outlineMapper->moveTo(*points);
1970 const QPoint *p = points;
1971 const QPoint *ep = points + pointCount - 1;
1973 d->outlineMapper->lineTo(*(++p));
1975 d->outlineMapper->endOutline();
1978 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1980 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1984 // Do the outline...
1985 if (s->penData.blend) {
1986 int count = pointCount * 2;
1987 QVarLengthArray<qreal> fpoints(count);
1989 for (int i=0; i<count; i+=2) {
1990 fpoints[i] = ((int *) points)[i+1];
1991 fpoints[i+1] = ((int *) points)[i];
1994 for (int i=0; i<count; ++i)
1995 fpoints[i] = ((int *) points)[i];
1997 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1999 if (s->flags.fast_pen) {
2000 QCosmeticStroker stroker(s, d->deviceRect);
2001 stroker.drawPath(vp);
2003 QPaintEngineEx::stroke(vp, s->lastPen);
2011 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2013 #ifdef QT_DEBUG_DRAW
2014 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2017 QPixmapData *pd = pixmap.pixmapData();
2018 if (pd->classId() == QPixmapData::RasterClass) {
2019 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2020 if (image.depth() == 1) {
2021 Q_D(QRasterPaintEngine);
2022 QRasterPaintEngineState *s = state();
2023 if (s->matrix.type() <= QTransform::TxTranslate) {
2025 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2027 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2030 QRasterPaintEngine::drawImage(pos, image);
2033 const QImage image = pixmap.toImage();
2034 if (pixmap.depth() == 1) {
2035 Q_D(QRasterPaintEngine);
2036 QRasterPaintEngineState *s = state();
2037 if (s->matrix.type() <= QTransform::TxTranslate) {
2039 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2041 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2044 QRasterPaintEngine::drawImage(pos, image);
2052 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2054 #ifdef QT_DEBUG_DRAW
2055 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2058 QPixmapData* pd = pixmap.pixmapData();
2059 if (pd->classId() == QPixmapData::RasterClass) {
2060 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2061 if (image.depth() == 1) {
2062 Q_D(QRasterPaintEngine);
2063 QRasterPaintEngineState *s = state();
2064 if (s->matrix.type() <= QTransform::TxTranslate
2065 && r.size() == sr.size()
2066 && r.size() == pixmap.size()) {
2068 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2071 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2074 drawImage(r, image, sr);
2077 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2078 const QImage image = pd->toImage(clippedSource);
2079 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2080 if (image.depth() == 1) {
2081 Q_D(QRasterPaintEngine);
2082 QRasterPaintEngineState *s = state();
2083 if (s->matrix.type() <= QTransform::TxTranslate
2084 && r.size() == sr.size()
2085 && r.size() == pixmap.size()) {
2087 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2090 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2093 drawImage(r, image, translatedSource);
2098 // assumes that rect has positive width and height
2099 static inline const QRect toRect_normalized(const QRectF &rect)
2101 const int x = qRound(rect.x());
2102 const int y = qRound(rect.y());
2103 const int w = int(rect.width() + qreal(0.5));
2104 const int h = int(rect.height() + qreal(0.5));
2106 return QRect(x, y, w, h);
2109 static inline int fast_ceil_positive(const qreal &v)
2111 const int iv = int(v);
2118 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2120 const int xmin = int(rect.x());
2121 const int xmax = int(fast_ceil_positive(rect.right()));
2122 const int ymin = int(rect.y());
2123 const int ymax = int(fast_ceil_positive(rect.bottom()));
2124 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2130 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2132 #ifdef QT_DEBUG_DRAW
2133 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2136 Q_D(QRasterPaintEngine);
2137 QRasterPaintEngineState *s = state();
2139 if (s->matrix.type() > QTransform::TxTranslate) {
2140 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2142 QRectF(0, 0, img.width(), img.height()));
2145 const QClipData *clip = d->clip();
2146 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2148 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2149 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2152 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2154 } else if (clip->hasRectClip) {
2155 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2163 d->image_filler.clip = clip;
2164 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2165 if (!d->image_filler.blend)
2167 d->image_filler.dx = -pt.x();
2168 d->image_filler.dy = -pt.y();
2169 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2171 fillRect_normalized(rr, &d->image_filler, d);
2176 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2178 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2189 inline RotationType qRotationType(const QTransform &transform)
2191 QTransform::TransformationType type = transform.type();
2193 if (type > QTransform::TxRotate)
2196 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2197 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2200 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2201 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2204 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2205 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2211 inline bool isPixelAligned(const QRectF &rect) {
2212 return QRectF(rect.toRect()) == rect;
2219 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2220 Qt::ImageConversionFlags)
2222 #ifdef QT_DEBUG_DRAW
2223 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2229 Q_D(QRasterPaintEngine);
2230 QRasterPaintEngineState *s = state();
2231 int sr_l = qFloor(sr.left());
2232 int sr_r = qCeil(sr.right()) - 1;
2233 int sr_t = qFloor(sr.top());
2234 int sr_b = qCeil(sr.bottom()) - 1;
2236 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2237 QTransform old = s->matrix;
2239 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2240 QRgb color = img.pixel(sr_l, sr_t);
2241 switch (img.format()) {
2242 case QImage::Format_ARGB32_Premultiplied:
2243 case QImage::Format_ARGB8565_Premultiplied:
2244 case QImage::Format_ARGB6666_Premultiplied:
2245 case QImage::Format_ARGB8555_Premultiplied:
2246 case QImage::Format_ARGB4444_Premultiplied:
2247 // Combine premultiplied color with the opacity set on the painter.
2248 d->solid_color_filler.solid.color =
2249 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2250 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2253 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2257 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2258 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2262 d->solid_color_filler.clip = d->clip();
2263 d->solid_color_filler.adjustSpanMethods();
2264 fillRect(r, &d->solid_color_filler);
2270 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2272 const QClipData *clip = d->clip();
2274 if (s->matrix.type() > QTransform::TxTranslate
2276 && (!clip || clip->hasRectClip)
2277 && s->intOpacity == 256
2278 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2279 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2280 && d->rasterBuffer->format == img.format()
2281 && (d->rasterBuffer->format == QImage::Format_RGB16
2282 || d->rasterBuffer->format == QImage::Format_RGB32
2283 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2284 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2286 RotationType rotationType = qRotationType(s->matrix);
2288 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2289 QRectF transformedTargetRect = s->matrix.mapRect(r);
2291 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2292 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2294 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2295 if (clippedTransformedTargetRect.isNull())
2298 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2300 QRect clippedSourceRect
2301 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2302 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2304 uint dbpl = d->rasterBuffer->bytesPerLine();
2305 uint sbpl = img.bytesPerLine();
2307 uchar *dst = d->rasterBuffer->buffer();
2308 uint bpp = img.depth() >> 3;
2310 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2311 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2313 uint cw = clippedSourceRect.width();
2314 uint ch = clippedSourceRect.height();
2316 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2323 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2325 QRectF targetBounds = s->matrix.mapRect(r);
2326 bool exceedsPrecision = targetBounds.width() > 0xffff
2327 || targetBounds.height() > 0xffff;
2329 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2330 if (s->matrix.type() > QTransform::TxScale) {
2331 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2332 if (func && (!clip || clip->hasRectClip)) {
2333 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2334 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2335 s->matrix, s->intOpacity);
2339 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2340 if (func && (!clip || clip->hasRectClip)) {
2341 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2342 img.bits(), img.bytesPerLine(),
2343 qt_mapRect_non_normalizing(r, s->matrix), sr,
2344 !clip ? d->deviceRect : clip->clipRect,
2351 QTransform copy = s->matrix;
2352 copy.translate(r.x(), r.y());
2354 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2355 copy.translate(-sr.x(), -sr.y());
2357 d->image_filler_xform.clip = clip;
2358 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2359 if (!d->image_filler_xform.blend)
2361 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2363 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2364 QRectF rr = s->matrix.mapRect(r);
2366 const int x1 = qRound(rr.x());
2367 const int y1 = qRound(rr.y());
2368 const int x2 = qRound(rr.right());
2369 const int y2 = qRound(rr.bottom());
2371 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2375 #ifdef QT_FAST_SPANS
2377 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2378 d->initializeRasterizer(&d->image_filler_xform);
2379 d->rasterizer->setAntialiased(s->flags.antialiased);
2381 const QRectF &rect = r.normalized();
2382 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2383 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2385 if (s->flags.tx_noshear)
2386 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2388 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2394 QTransform m = s->matrix;
2395 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2396 m.m21(), m.m22(), m.m23(),
2397 m.m31(), m.m32(), m.m33());
2398 fillPath(path, &d->image_filler_xform);
2401 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2402 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2404 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2406 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2408 } else if (clip->hasRectClip) {
2409 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2415 d->image_filler.clip = clip;
2416 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2417 if (!d->image_filler.blend)
2419 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2420 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2423 rr.translate(s->matrix.dx(), s->matrix.dy());
2425 const int x1 = qRound(rr.x());
2426 const int y1 = qRound(rr.y());
2427 const int x2 = qRound(rr.right());
2428 const int y2 = qRound(rr.bottom());
2430 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2437 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2439 #ifdef QT_DEBUG_DRAW
2440 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2442 Q_D(QRasterPaintEngine);
2443 QRasterPaintEngineState *s = state();
2447 QPixmapData *pd = pixmap.pixmapData();
2448 if (pd->classId() == QPixmapData::RasterClass) {
2449 image = static_cast<QRasterPixmapData *>(pd)->image;
2451 image = pixmap.toImage();
2454 if (image.depth() == 1)
2455 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2457 if (s->matrix.type() > QTransform::TxTranslate) {
2458 QTransform copy = s->matrix;
2459 copy.translate(r.x(), r.y());
2460 copy.translate(-sr.x(), -sr.y());
2461 d->image_filler_xform.clip = d->clip();
2462 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2463 if (!d->image_filler_xform.blend)
2465 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2467 #ifdef QT_FAST_SPANS
2469 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2470 d->initializeRasterizer(&d->image_filler_xform);
2471 d->rasterizer->setAntialiased(s->flags.antialiased);
2473 const QRectF &rect = r.normalized();
2474 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2475 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2476 if (s->flags.tx_noshear)
2477 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2479 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2485 fillPath(path, &d->image_filler_xform);
2487 d->image_filler.clip = d->clip();
2489 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2490 if (!d->image_filler.blend)
2492 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2493 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2496 rr.translate(s->matrix.dx(), s->matrix.dy());
2497 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2503 static inline bool monoVal(const uchar* s, int x)
2505 return (s[x>>3] << (x&7)) & 0x80;
2511 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2513 Q_D(QRasterPaintEngine);
2514 QRasterPaintEngineState *s = state();
2516 if (!s->penData.blend)
2519 QRasterBuffer *rb = d->rasterBuffer.data();
2521 const QRect rect(rx, ry, w, h);
2522 const QClipData *clip = d->clip();
2523 bool unclipped = false;
2525 // inlined QRect::intersects
2526 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2527 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2529 if (clip->hasRectClip) {
2530 unclipped = rx > clip->xmin
2531 && rx + w < clip->xmax
2533 && ry + h < clip->ymax;
2539 // inlined QRect::intersects
2540 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2541 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2545 // inlined QRect::contains
2546 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2547 && rect.top() >= 0 && rect.bottom() < rb->height();
2549 unclipped = contains && d->isUnclipped_normalized(rect);
2552 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2553 const uchar * scanline = static_cast<const uchar *>(src);
2555 if (s->flags.fast_text) {
2558 if (s->penData.bitmapBlit) {
2559 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2560 scanline, w, h, bpl);
2563 } else if (depth == 8) {
2564 if (s->penData.alphamapBlit) {
2565 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2566 scanline, w, h, bpl, 0);
2569 } else if (depth == 32) {
2570 // (A)RGB Alpha mask where the alpha component is not used.
2571 if (s->penData.alphaRGBBlit) {
2572 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2573 (const uint *) scanline, w, h, bpl / 4, 0);
2577 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2578 // (A)RGB Alpha mask where the alpha component is not used.
2580 int nx = qMax(0, rx);
2581 int ny = qMax(0, ry);
2583 // Move scanline pointer to compensate for moved x and y
2584 int xdiff = nx - rx;
2585 int ydiff = ny - ry;
2586 scanline += ydiff * bpl;
2587 scanline += xdiff * (depth == 32 ? 4 : 1);
2592 if (nx + w > d->rasterBuffer->width())
2593 w = d->rasterBuffer->width() - nx;
2594 if (ny + h > d->rasterBuffer->height())
2595 h = d->rasterBuffer->height() - ny;
2600 if (depth == 8 && s->penData.alphamapBlit) {
2601 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2602 scanline, w, h, bpl, clip);
2603 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2604 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2605 (const uint *) scanline, w, h, bpl / 4, clip);
2620 scanline += bpl * y0;
2624 w = qMin(w, rb->width() - qMax(0, rx));
2625 h = qMin(h, rb->height() - qMax(0, ry));
2627 if (w <= 0 || h <= 0)
2630 const int NSPANS = 256;
2631 QSpan spans[NSPANS];
2634 const int x1 = x0 + w;
2635 const int y1 = y0 + h;
2638 for (int y = y0; y < y1; ++y) {
2639 for (int x = x0; x < x1; ) {
2640 if (!monoVal(scanline, x)) {
2645 if (current == NSPANS) {
2646 blend(current, spans, &s->penData);
2649 spans[current].x = x + rx;
2650 spans[current].y = y + ry;
2651 spans[current].coverage = 255;
2654 // extend span until we find a different one.
2655 while (x < x1 && monoVal(scanline, x)) {
2659 spans[current].len = len;
2664 } else if (depth == 8) {
2665 for (int y = y0; y < y1; ++y) {
2666 for (int x = x0; x < x1; ) {
2667 // Skip those with 0 coverage
2668 if (scanline[x] == 0) {
2673 if (current == NSPANS) {
2674 blend(current, spans, &s->penData);
2677 int coverage = scanline[x];
2678 spans[current].x = x + rx;
2679 spans[current].y = y + ry;
2680 spans[current].coverage = coverage;
2684 // extend span until we find a different one.
2685 while (x < x1 && scanline[x] == coverage) {
2689 spans[current].len = len;
2694 } else { // 32-bit alpha...
2695 uint *sl = (uint *) src;
2696 for (int y = y0; y < y1; ++y) {
2697 for (int x = x0; x < x1; ) {
2698 // Skip those with 0 coverage
2699 if ((sl[x] & 0x00ffffff) == 0) {
2704 if (current == NSPANS) {
2705 blend(current, spans, &s->penData);
2708 uint rgbCoverage = sl[x];
2709 int coverage = qGreen(rgbCoverage);
2710 spans[current].x = x + rx;
2711 spans[current].y = y + ry;
2712 spans[current].coverage = coverage;
2716 // extend span until we find a different one.
2717 while (x < x1 && sl[x] == rgbCoverage) {
2721 spans[current].len = len;
2724 sl += bpl / sizeof(uint);
2727 // qDebug() << "alphaPenBlt: num spans=" << current
2728 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2729 // Call span func for current set of spans.
2731 blend(current, spans, &s->penData);
2734 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2735 const QFixedPoint *positions, QFontEngine *fontEngine)
2737 Q_D(QRasterPaintEngine);
2738 QRasterPaintEngineState *s = state();
2740 #if !defined(QT_NO_FREETYPE)
2741 if (fontEngine->type() == QFontEngine::Freetype) {
2742 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2743 QFontEngineFT::GlyphFormat neededFormat =
2744 painter()->device()->devType() == QInternal::Widget
2745 ? fe->defaultGlyphFormat()
2746 : QFontEngineFT::Format_A8;
2748 if (d_func()->mono_surface
2749 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2751 neededFormat = QFontEngineFT::Format_Mono;
2753 if (neededFormat == QFontEngineFT::Format_None)
2754 neededFormat = QFontEngineFT::Format_A8;
2756 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2757 if (s->matrix.type() >= QTransform::TxScale) {
2758 if (s->matrix.isAffine())
2759 gset = fe->loadTransformedGlyphSet(s->matrix);
2764 if (!gset || gset->outline_drawing
2765 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2768 FT_Face lockedFace = 0;
2771 switch (neededFormat) {
2772 case QFontEngineFT::Format_Mono:
2775 case QFontEngineFT::Format_A8:
2778 case QFontEngineFT::Format_A32:
2786 for (int i = 0; i < numGlyphs; i++) {
2787 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2788 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2790 if (!glyph || glyph->format != neededFormat) {
2792 lockedFace = fe->lockFace();
2793 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2796 if (!glyph || !glyph->data)
2800 switch (neededFormat) {
2801 case QFontEngineFT::Format_Mono:
2802 pitch = ((glyph->width + 31) & ~31) >> 3;
2804 case QFontEngineFT::Format_A8:
2805 pitch = (glyph->width + 3) & ~3;
2807 case QFontEngineFT::Format_A32:
2808 pitch = glyph->width * 4;
2815 alphaPenBlt(glyph->data, pitch, depth,
2816 qFloor(positions[i].x) + glyph->x,
2817 qFloor(positions[i].y) - glyph->y,
2818 glyph->width, glyph->height);
2825 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2827 QImageTextureGlyphCache *cache =
2828 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2830 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2831 fontEngine->setGlyphCache(0, cache);
2834 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2835 cache->fillInPendingGlyphs();
2837 const QImage &image = cache->image();
2838 int bpl = image.bytesPerLine();
2840 int depth = image.depth();
2844 leftShift = 2; // multiply by 4
2845 else if (depth == 1)
2846 rightShift = 3; // divide by 8
2848 int margin = cache->glyphMargin();
2849 const uchar *bits = image.bits();
2850 for (int i=0; i<numGlyphs; ++i) {
2852 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2853 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2854 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2858 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2859 int y = qFloor(positions[i].y) - c.baseLineY - margin;
2861 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2864 // c.baseLineX, c.baseLineY,
2867 // positions[i].x.toInt(), positions[i].y.toInt());
2869 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2875 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2876 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2878 Q_D(QRasterPaintEngine);
2879 QRasterPaintEngineState *s = state();
2881 QFontEngine *fontEngine = ti.fontEngine;
2882 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2883 QPaintEngineEx::drawTextItem(p, ti);
2887 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2889 QVarLengthArray<QFixedPoint> positions;
2890 QVarLengthArray<glyph_t> glyphs;
2891 QTransform matrix = s->matrix;
2892 matrix.translate(p.x(), p.y());
2893 if (matrix.type() == QTransform::TxScale)
2894 fe->setFontScale(matrix.m11());
2895 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2897 for (int i=0; i<glyphs.size(); ++i) {
2898 TOpenFontCharMetrics tmetrics;
2899 const TUint8 *glyphBitmapBytes;
2900 TSize glyphBitmapSize;
2901 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2902 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
2903 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
2904 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2907 if (matrix.type() == QTransform::TxScale)
2908 fe->setFontScale(1.0);
2912 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2915 * Returns true if the rectangle is completely within the current clip
2916 * state of the paint engine.
2918 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2920 const QClipData *cl = clip();
2922 // inline contains() for performance (we know the rects are normalized)
2923 const QRect &r1 = deviceRect;
2924 return (r.left() >= r1.left() && r.right() <= r1.right()
2925 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2929 if (cl->hasRectClip) {
2930 // currently all painting functions clips to deviceRect internally
2931 if (cl->clipRect == deviceRect)
2934 // inline contains() for performance (we know the rects are normalized)
2935 const QRect &r1 = cl->clipRect;
2936 return (r.left() >= r1.left() && r.right() <= r1.right()
2937 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2939 return qt_region_strictContains(cl->clipRegion, r);
2943 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2946 Q_Q(const QRasterPaintEngine);
2947 const QRasterPaintEngineState *s = q->state();
2948 const QClipData *cl = clip();
2950 QRect r = rect.normalized();
2951 // inline contains() for performance (we know the rects are normalized)
2952 const QRect &r1 = deviceRect;
2953 return (r.left() >= r1.left() && r.right() <= r1.right()
2954 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2958 // currently all painting functions that call this function clip to deviceRect internally
2959 if (cl->hasRectClip && cl->clipRect == deviceRect)
2962 if (s->flags.antialiased)
2965 QRect r = rect.normalized();
2967 r.setX(r.x() - penWidth);
2968 r.setY(r.y() - penWidth);
2969 r.setWidth(r.width() + 2 * penWidth);
2970 r.setHeight(r.height() + 2 * penWidth);
2973 if (cl->hasRectClip) {
2974 // inline contains() for performance (we know the rects are normalized)
2975 const QRect &r1 = cl->clipRect;
2976 return (r.left() >= r1.left() && r.right() <= r1.right()
2977 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2979 return qt_region_strictContains(cl->clipRegion, r);
2983 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2986 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2990 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2991 const QSpanData *data) const
2993 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2997 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2998 const QSpanData *data) const
3000 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3006 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3011 QRasterPaintEngineState *s = state();
3013 QFontEngine *fontEngine = textItem->fontEngine();
3014 const qreal pixelSize = fontEngine->fontDef.pixelSize;
3015 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) < 64 * 64) {
3016 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3019 QPaintEngineEx::drawStaticTextItem(textItem);
3026 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3028 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3029 QRasterPaintEngineState *s = state();
3031 #ifdef QT_DEBUG_DRAW
3032 Q_D(QRasterPaintEngine);
3033 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3034 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3041 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3043 bool drawCached = true;
3045 if (s->matrix.type() >= QTransform::TxProject)
3048 // don't try to cache huge fonts
3049 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3050 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3053 // ### Remove the TestFontEngine and Box engine crap, in these
3054 // ### cases we should delegate painting to the font engine
3057 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3058 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3059 // qDebug() << "type" << fontEngineType << s->matrix.type();
3060 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3061 || (s->matrix.type() <= QTransform::TxTranslate
3062 && (fontEngineType == QFontEngine::TestFontEngine
3063 || fontEngineType == QFontEngine::Box))) {
3067 if (s->matrix.type() > QTransform::TxTranslate)
3071 QRasterPaintEngineState *s = state();
3073 QVarLengthArray<QFixedPoint> positions;
3074 QVarLengthArray<glyph_t> glyphs;
3076 QTransform matrix = s->matrix;
3077 matrix.translate(p.x(), p.y());
3079 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3081 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3085 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3086 if (s->matrix.type() <= QTransform::TxTranslate
3087 || (s->matrix.type() == QTransform::TxScale
3088 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3089 drawGlyphsS60(p, ti);
3092 #else // Q_WS_WIN || Q_WS_MAC
3094 QFontEngine *fontEngine = ti.fontEngine;
3097 if (s->matrix.type() < QTransform::TxScale) {
3099 QVarLengthArray<QFixedPoint> positions;
3100 QVarLengthArray<glyph_t> glyphs;
3101 QTransform matrix = state()->transform();
3103 qreal _x = qFloor(p.x());
3104 qreal _y = qFloor(p.y());
3105 matrix.translate(_x, _y);
3107 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3108 if (glyphs.size() == 0)
3111 for(int i = 0; i < glyphs.size(); i++) {
3112 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3113 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3114 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3115 qRound(positions[i].x + metrics.x),
3116 qRound(positions[i].y + metrics.y),
3117 img.width(), img.height());
3123 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3125 if (fontEngine->type() != QFontEngine::Freetype) {
3126 QPaintEngineEx::drawTextItem(p, ti);
3130 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3132 QTransform matrix = s->matrix;
3133 matrix.translate(p.x(), p.y());
3135 QVarLengthArray<QFixedPoint> positions;
3136 QVarLengthArray<glyph_t> glyphs;
3137 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3138 if (glyphs.size() == 0)
3141 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3142 QPaintEngine::drawTextItem(p, ti);
3148 QPaintEngineEx::drawTextItem(p, ti);
3154 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3156 Q_D(QRasterPaintEngine);
3157 QRasterPaintEngineState *s = state();
3160 if (!s->penData.blend)
3163 if (!s->flags.fast_pen) {
3164 QPaintEngineEx::drawPoints(points, pointCount);
3168 QCosmeticStroker stroker(s, d->deviceRect);
3169 stroker.drawPoints(points, pointCount);
3173 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3175 Q_D(QRasterPaintEngine);
3176 QRasterPaintEngineState *s = state();
3179 if (!s->penData.blend)
3182 if (!s->flags.fast_pen) {
3183 QPaintEngineEx::drawPoints(points, pointCount);
3187 QCosmeticStroker stroker(s, d->deviceRect);
3188 stroker.drawPoints(points, pointCount);
3194 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3196 #ifdef QT_DEBUG_DRAW
3197 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3199 Q_D(QRasterPaintEngine);
3200 QRasterPaintEngineState *s = state();
3203 if (!s->penData.blend)
3206 if (s->flags.fast_pen) {
3207 QCosmeticStroker stroker(s, d->deviceRect);
3208 for (int i=0; i<lineCount; ++i) {
3209 const QLine &l = lines[i];
3210 stroker.drawLine(l.p1(), l.p2());
3213 QPaintEngineEx::drawLines(lines, lineCount);
3217 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3223 Q_Q(QRasterPaintEngine);
3224 QRasterPaintEngineState *s = q->state();
3226 const QPen &pen = s->lastPen;
3227 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3228 const QVector<qreal> pattern = pen.dashPattern();
3230 qreal patternLength = 0;
3231 for (int i = 0; i < pattern.size(); ++i)
3232 patternLength += pattern.at(i);
3234 if (patternLength <= 0)
3237 qreal length = line.length();
3238 Q_ASSERT(length > 0);
3239 while (length > 0) {
3240 const bool rasterize = *inDash;
3241 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3244 if (dash >= length) {
3246 *dashOffset += dash / width;
3250 *inDash = !(*inDash);
3251 if (++*dashIndex >= pattern.size())
3258 if (rasterize && dash > 0)
3259 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3266 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3268 #ifdef QT_DEBUG_DRAW
3269 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3271 Q_D(QRasterPaintEngine);
3272 QRasterPaintEngineState *s = state();
3275 if (!s->penData.blend)
3277 if (s->flags.fast_pen) {
3278 QCosmeticStroker stroker(s, d->deviceRect);
3279 for (int i=0; i<lineCount; ++i) {
3280 QLineF line = lines[i];
3281 stroker.drawLine(line.p1(), line.p2());
3284 QPaintEngineEx::drawLines(lines, lineCount);
3292 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3294 QPaintEngineEx::drawEllipse(rect);
3301 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3303 Q_D(QRasterPaintEngine);
3310 CGContextRef QRasterPaintEngine::getCGContext() const
3312 Q_D(const QRasterPaintEngine);
3313 return d->cgContext;
3321 void QRasterPaintEngine::setDC(HDC hdc) {
3322 Q_D(QRasterPaintEngine);
3329 HDC QRasterPaintEngine::getDC() const
3331 Q_D(const QRasterPaintEngine);
3338 void QRasterPaintEngine::releaseDC(HDC) const
3347 QPoint QRasterPaintEngine::coordinateOffset() const
3349 return QPoint(0, 0);
3352 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3357 Q_D(QRasterPaintEngine);
3359 Q_ASSERT(image.depth() == 1);
3361 const int spanCount = 256;
3362 QT_FT_Span spans[spanCount];
3366 int w = image.width();
3367 int h = image.height();
3368 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3369 int ymin = qMax(qRound(pos.y()), 0);
3370 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3371 int xmin = qMax(qRound(pos.x()), 0);
3373 int x_offset = xmin - qRound(pos.x());
3375 QImage::Format format = image.format();
3376 for (int y = ymin; y < ymax; ++y) {
3377 const uchar *src = image.scanLine(y - qRound(pos.y()));
3378 if (format == QImage::Format_MonoLSB) {
3379 for (int x = 0; x < xmax - xmin; ++x) {
3380 int src_x = x + x_offset;
3381 uchar pixel = src[src_x >> 3];
3386 if (pixel & (0x1 << (src_x & 7))) {
3387 spans[n].x = xmin + x;
3389 spans[n].coverage = 255;
3391 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3395 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3398 if (n == spanCount) {
3399 fg->blend(n, spans, fg);
3405 for (int x = 0; x < xmax - xmin; ++x) {
3406 int src_x = x + x_offset;
3407 uchar pixel = src[src_x >> 3];
3412 if (pixel & (0x80 >> (x & 7))) {
3413 spans[n].x = xmin + x;
3415 spans[n].coverage = 255;
3417 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3421 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3424 if (n == spanCount) {
3425 fg->blend(n, spans, fg);
3433 fg->blend(n, spans, fg);
3439 \enum QRasterPaintEngine::ClipType
3442 \value RectClip Indicates that the currently set clip is a single rectangle.
3443 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3448 Returns the type of the clip currently set.
3450 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3452 Q_D(const QRasterPaintEngine);
3454 const QClipData *clip = d->clip();
3455 if (!clip || clip->hasRectClip)
3463 Returns the bounding rect of the currently set clip.
3465 QRect QRasterPaintEngine::clipBoundingRect() const
3467 Q_D(const QRasterPaintEngine);
3469 const QClipData *clip = d->clip();
3472 return d->deviceRect;
3474 if (clip->hasRectClip)
3475 return clip->clipRect;
3477 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3480 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3482 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3484 QVarLengthArray<short, 4096> buffer;
3486 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3487 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3488 result->initialize();
3490 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3491 const QSpan *c1_spans = c1ClipLines[y].spans;
3492 int c1_count = c1ClipLines[y].count;
3493 const QSpan *c2_spans = c2ClipLines[y].spans;
3494 int c2_count = c2ClipLines[y].count;
3496 if (c1_count == 0 && c2_count == 0)
3498 if (c1_count == 0) {
3499 result->appendSpans(c2_spans, c2_count);
3501 } else if (c2_count == 0) {
3502 result->appendSpans(c1_spans, c1_count);
3506 // we need to merge the two
3508 // find required length
3509 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3510 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3512 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3514 // Fill with old spans.
3515 for (int i = 0; i < c1_count; ++i) {
3516 const QSpan *cs = c1_spans + i;
3517 for (int j=cs->x; j<cs->x + cs->len; ++j)
3518 buffer[j] = cs->coverage;
3521 // Fill with new spans
3522 for (int i = 0; i < c2_count; ++i) {
3523 const QSpan *cs = c2_spans + i;
3524 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3525 buffer[j] += cs->coverage;
3526 if (buffer[j] > 255)
3534 // Skip to next span
3535 while (x < max && buffer[x] == 0) ++x;
3536 if (x >= max) break;
3539 int coverage = buffer[x];
3541 // Find length of span
3542 while (x < max && buffer[x] == coverage)
3545 result->appendSpan(sx, x - sx, y, coverage);
3550 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3552 Q_Q(QRasterPaintEngine);
3553 QRasterPaintEngineState *s = q->state();
3555 rasterizer->setAntialiased(s->flags.antialiased);
3557 QRect clipRect(deviceRect);
3559 // ### get from optimized rectbased QClipData
3561 const QClipData *c = clip();
3563 const QRect r(QPoint(c->xmin, c->ymin),
3564 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3565 clipRect = clipRect.intersected(r);
3566 blend = data->blend;
3568 blend = data->unclipped_blend;
3571 rasterizer->setClipRect(clipRect);
3572 rasterizer->initialize(blend, data);
3575 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3576 ProcessSpans callback,
3577 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3579 if (!callback || !outline)
3582 Q_Q(QRasterPaintEngine);
3583 QRasterPaintEngineState *s = q->state();
3585 if (!s->flags.antialiased) {
3586 initializeRasterizer(spanData);
3588 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3592 rasterizer->rasterize(outline, fillRule);
3596 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3600 int q_gray_rendered_spans(QT_FT_Raster raster);
3603 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3604 ProcessSpans callback,
3605 void *userData, QRasterBuffer *)
3607 if (!callback || !outline)
3610 Q_Q(QRasterPaintEngine);
3611 QRasterPaintEngineState *s = q->state();
3613 if (!s->flags.antialiased) {
3614 rasterizer->setAntialiased(s->flags.antialiased);
3615 rasterizer->setClipRect(deviceRect);
3616 rasterizer->initialize(callback, userData);
3618 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3622 rasterizer->rasterize(outline, fillRule);
3626 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3627 // minimize memory reallocations. However if initial size for
3628 // raster pool is changed for lower value, reallocations will
3630 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3631 int rasterPoolSize = rasterPoolInitialSize;
3632 unsigned char *rasterPoolBase;
3633 #if defined(Q_WS_WIN64)
3635 // We make use of setjmp and longjmp in qgrayraster.c which requires
3636 // 16-byte alignment, hence we hardcode this requirement here..
3637 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3639 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3640 rasterPoolBase = rasterPoolOnStack;
3642 Q_CHECK_PTR(rasterPoolBase);
3644 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3646 void *data = userData;
3648 QT_FT_BBox clip_box = { deviceRect.x(),
3650 deviceRect.x() + deviceRect.width(),
3651 deviceRect.y() + deviceRect.height() };
3653 QT_FT_Raster_Params rasterParams;
3654 rasterParams.target = 0;
3655 rasterParams.source = outline;
3656 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3657 rasterParams.gray_spans = 0;
3658 rasterParams.black_spans = 0;
3659 rasterParams.bit_test = 0;
3660 rasterParams.bit_set = 0;
3661 rasterParams.user = data;
3662 rasterParams.clip_box = clip_box;
3667 int rendered_spans = 0;
3671 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3672 rasterParams.gray_spans = callback;
3673 rasterParams.skip_spans = rendered_spans;
3674 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3676 // Out of memory, reallocate some more and try again...
3677 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3678 int new_size = rasterPoolSize * 2;
3679 if (new_size > 1024 * 1024) {
3680 qWarning("QPainter: Rasterization of primitive failed");
3684 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3686 #if defined(Q_WS_WIN64)
3687 _aligned_free(rasterPoolBase);
3689 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3690 free(rasterPoolBase);
3693 rasterPoolSize = new_size;
3695 #if defined(Q_WS_WIN64)
3696 // We make use of setjmp and longjmp in qgrayraster.c which requires
3697 // 16-byte alignment, hence we hardcode this requirement here..
3698 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3700 (unsigned char *) malloc(rasterPoolSize);
3702 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3704 qt_ft_grays_raster.raster_done(*grayRaster.data());
3705 qt_ft_grays_raster.raster_new(grayRaster.data());
3706 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3712 #if defined(Q_WS_WIN64)
3713 _aligned_free(rasterPoolBase);
3715 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3716 free(rasterPoolBase);
3720 void QRasterPaintEnginePrivate::recalculateFastImages()
3722 Q_Q(QRasterPaintEngine);
3723 QRasterPaintEngineState *s = q->state();
3725 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3726 && s->matrix.type() <= QTransform::TxShear;
3729 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3731 Q_Q(const QRasterPaintEngine);
3732 const QRasterPaintEngineState *s = q->state();
3734 return s->flags.fast_images
3735 && (mode == QPainter::CompositionMode_SourceOver
3736 || (mode == QPainter::CompositionMode_Source
3737 && !image.hasAlphaChannel()));
3740 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3742 Q_ASSERT(image.depth() == 1);
3744 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3745 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3747 QRgb fg = PREMUL(color.rgba());
3750 int height = sourceImage.height();
3751 int width = sourceImage.width();
3752 for (int y=0; y<height; ++y) {
3753 uchar *source = sourceImage.scanLine(y);
3754 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3755 if (!source || !target)
3756 QT_THROW(std::bad_alloc()); // we must have run out of memory
3757 for (int x=0; x < width; ++x)
3758 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3763 QRasterBuffer::~QRasterBuffer()
3767 void QRasterBuffer::init()
3769 compositionMode = QPainter::CompositionMode_SourceOver;
3770 monoDestinationWithClut = false;
3775 QImage::Format QRasterBuffer::prepare(QImage *image)
3777 m_buffer = (uchar *)image->bits();
3778 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3779 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3780 bytes_per_pixel = image->depth()/8;
3781 bytes_per_line = image->bytesPerLine();
3783 format = image->format();
3784 drawHelper = qDrawHelper + format;
3785 if (image->depth() == 1 && image->colorTable().size() == 2) {
3786 monoDestinationWithClut = true;
3787 destColor0 = PREMUL(image->colorTable()[0]);
3788 destColor1 = PREMUL(image->colorTable()[1]);
3794 void QRasterBuffer::resetBuffer(int val)
3796 memset(m_buffer, val, m_height*bytes_per_line);
3799 QClipData::QClipData(int height)
3801 clipSpanHeight = height;
3806 xmin = xmax = ymin = ymax = 0;
3810 hasRectClip = hasRegionClip = false;
3813 QClipData::~QClipData()
3821 void QClipData::initialize()
3827 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3829 Q_CHECK_PTR(m_clipLines);
3831 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3832 allocated = clipSpanHeight;
3833 Q_CHECK_PTR(m_spans);
3839 m_clipLines[y].spans = 0;
3840 m_clipLines[y].count = 0;
3844 const int len = clipRect.width();
3847 QSpan *span = m_spans + count;
3851 span->coverage = 255;
3854 m_clipLines[y].spans = span;
3855 m_clipLines[y].count = 1;
3859 while (y < clipSpanHeight) {
3860 m_clipLines[y].spans = 0;
3861 m_clipLines[y].count = 0;
3864 } else if (hasRegionClip) {
3866 const QVector<QRect> rects = clipRegion.rects();
3867 const int numRects = rects.size();
3870 const int maxSpans = (ymax - ymin) * numRects;
3871 if (maxSpans > allocated) {
3872 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3873 allocated = maxSpans;
3878 int firstInBand = 0;
3880 while (firstInBand < numRects) {
3881 const int currMinY = rects.at(firstInBand).y();
3882 const int currMaxY = currMinY + rects.at(firstInBand).height();
3884 while (y < currMinY) {
3885 m_clipLines[y].spans = 0;
3886 m_clipLines[y].count = 0;
3890 int lastInBand = firstInBand;
3891 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3894 while (y < currMaxY) {
3896 m_clipLines[y].spans = m_spans + count;
3897 m_clipLines[y].count = lastInBand - firstInBand + 1;
3899 for (int r = firstInBand; r <= lastInBand; ++r) {
3900 const QRect &currRect = rects.at(r);
3901 QSpan *span = m_spans + count;
3902 span->x = currRect.x();
3903 span->len = currRect.width();
3905 span->coverage = 255;
3911 firstInBand = lastInBand + 1;
3914 Q_ASSERT(count <= allocated);
3916 while (y < clipSpanHeight) {
3917 m_clipLines[y].spans = 0;
3918 m_clipLines[y].count = 0;
3924 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3929 free(m_clipLines); // same for clipLines
3935 void QClipData::fixup()
3940 ymin = ymax = xmin = xmax = 0;
3945 ymin = m_spans[0].y;
3946 ymax = m_spans[count-1].y + 1;
3950 const int firstLeft = m_spans[0].x;
3951 const int firstRight = m_spans[0].x + m_spans[0].len;
3954 for (int i = 0; i < count; ++i) {
3955 QT_FT_Span_& span = m_spans[i];
3958 if (span.y != y + 1 && y != -1)
3961 m_clipLines[y].spans = &span;
3962 m_clipLines[y].count = 1;
3964 ++m_clipLines[y].count;
3966 const int spanLeft = span.x;
3967 const int spanRight = spanLeft + span.len;
3969 if (spanLeft < xmin)
3972 if (spanRight > xmax)
3975 if (spanLeft != firstLeft || spanRight != firstRight)
3981 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3986 Convert \a rect to clip spans.
3988 void QClipData::setClipRect(const QRect &rect)
3990 if (hasRectClip && rect == clipRect)
3993 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3995 hasRegionClip = false;
3999 xmax = rect.x() + rect.width();
4000 ymin = qMin(rect.y(), clipSpanHeight);
4001 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4008 // qDebug() << xmin << xmax << ymin << ymax;
4012 Convert \a region to clip spans.
4014 void QClipData::setClipRegion(const QRegion ®ion)
4016 if (region.rectCount() == 1) {
4017 setClipRect(region.rects().at(0));
4021 hasRegionClip = true;
4022 hasRectClip = false;
4023 clipRegion = region;
4025 { // set bounding rect
4026 const QRect rect = region.boundingRect();
4028 xmax = rect.x() + rect.width();
4030 ymax = rect.y() + rect.height();
4042 spans must be sorted on y
4044 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4045 const QSpan *spans, const QSpan *end,
4046 QSpan **outSpans, int available)
4048 const_cast<QClipData *>(clip)->initialize();
4050 QSpan *out = *outSpans;
4052 const QSpan *clipSpans = clip->m_spans + *currentClip;
4053 const QSpan *clipEnd = clip->m_spans + clip->count;
4055 while (available && spans < end ) {
4056 if (clipSpans >= clipEnd) {
4060 if (clipSpans->y > spans->y) {
4064 if (spans->y != clipSpans->y) {
4065 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4066 clipSpans = clip->m_clipLines[spans->y].spans;
4071 Q_ASSERT(spans->y == clipSpans->y);
4074 int sx2 = sx1 + spans->len;
4075 int cx1 = clipSpans->x;
4076 int cx2 = cx1 + clipSpans->len;
4078 if (cx1 < sx1 && cx2 < sx1) {
4081 } else if (sx1 < cx1 && sx2 < cx1) {
4085 int x = qMax(sx1, cx1);
4086 int len = qMin(sx2, cx2) - x;
4088 out->x = qMax(sx1, cx1);
4089 out->len = qMin(sx2, cx2) - out->x;
4091 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4103 *currentClip = clipSpans - clip->m_spans;
4107 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4109 // qDebug() << "qt_span_fill_clipped" << spanCount;
4110 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4112 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4114 const int NSPANS = 256;
4115 QSpan cspans[NSPANS];
4116 int currentClip = 0;
4117 const QSpan *end = spans + spanCount;
4118 while (spans < end) {
4119 QSpan *clipped = cspans;
4120 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4121 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4122 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4124 if (clipped - cspans)
4125 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4131 Clip spans to \a{clip}-rectangle.
4132 Returns number of unclipped spans
4134 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4137 const short minx = clip.left();
4138 const short miny = clip.top();
4139 const short maxx = clip.right();
4140 const short maxy = clip.bottom();
4143 for (int i = 0; i < numSpans; ++i) {
4144 if (spans[i].y > maxy)
4146 if (spans[i].y < miny
4147 || spans[i].x > maxx
4148 || spans[i].x + spans[i].len <= minx) {
4151 if (spans[i].x < minx) {
4152 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4155 spans[n].x = spans[i].x;
4156 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4158 if (spans[n].len == 0)
4160 spans[n].y = spans[i].y;
4161 spans[n].coverage = spans[i].coverage;
4168 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4171 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4172 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4174 Q_ASSERT(fillData->clip);
4175 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4177 // hw: check if this const_cast<> is safe!!!
4178 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4179 fillData->clip->clipRect);
4181 fillData->unclipped_blend(count, spans, fillData);
4184 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4186 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4188 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4189 // for (int i = 0; i < qMin(count, 10); ++i) {
4190 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4193 switch (clipData->operation) {
4195 case Qt::IntersectClip:
4197 QClipData *newClip = clipData->newClip;
4198 newClip->initialize();
4200 int currentClip = 0;
4201 const QSpan *end = spans + count;
4202 while (spans < end) {
4203 QSpan *newspans = newClip->m_spans + newClip->count;
4204 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4205 &newspans, newClip->allocated - newClip->count);
4206 newClip->count = newspans - newClip->m_spans;
4208 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4209 newClip->allocated *= 2;
4216 case Qt::ReplaceClip:
4217 clipData->newClip->appendSpans(spans, count);
4225 QImage QRasterBuffer::bufferImage() const
4227 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4229 for (int y = 0; y < m_height; ++y) {
4230 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4232 for (int x=0; x<m_width; ++x) {
4233 uint argb = span[x];
4234 image.setPixel(x, y, argb);
4242 void QRasterBuffer::flushToARGBImage(QImage *target) const
4244 int w = qMin(m_width, target->width());
4245 int h = qMin(m_height, target->height());
4247 for (int y=0; y<h; ++y) {
4248 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4249 QRgb *dest = (QRgb *) target->scanLine(y);
4250 for (int x=0; x<w; ++x) {
4251 QRgb pixel = sourceLine[x];
4252 int alpha = qAlpha(pixel);
4256 dest[x] = (alpha << 24)
4257 | ((255*qRed(pixel)/alpha) << 16)
4258 | ((255*qGreen(pixel)/alpha) << 8)
4259 | ((255*qBlue(pixel)/alpha) << 0);
4266 class QGradientCache
4270 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4271 stops(s), opacity(op), interpolationMode(mode) {}
4272 uint buffer[GRADIENT_STOPTABLE_SIZE];
4273 QGradientStops stops;
4275 QGradient::InterpolationMode interpolationMode;
4278 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4281 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4282 quint64 hash_val = 0;
4284 QGradientStops stops = gradient.stops();
4285 for (int i = 0; i < stops.size() && i <= 2; i++)
4286 hash_val += stops[i].second.rgba();
4288 QMutexLocker lock(&mutex);
4289 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4291 if (it == cache.constEnd())
4292 return addCacheElement(hash_val, gradient, opacity);
4295 const CacheInfo &cache_info = it.value();
4296 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4297 return cache_info.buffer;
4299 } while (it != cache.constEnd() && it.key() == hash_val);
4300 // an exact match for these stops and opacity was not found, create new cache
4301 return addCacheElement(hash_val, gradient, opacity);
4305 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4307 inline int maxCacheSize() const { return 60; }
4308 inline void generateGradientColorTable(const QGradient& g,
4310 int size, int opacity) const;
4311 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4312 if (cache.size() == maxCacheSize()) {
4313 // may remove more than 1, but OK
4314 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4316 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4317 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4318 return cache.insert(hash_val, cache_entry).value().buffer;
4321 QGradientColorTableHash cache;
4325 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4327 QGradientStops stops = gradient.stops();
4328 int stopCount = stops.count();
4329 Q_ASSERT(stopCount > 0);
4331 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4333 if (stopCount == 2) {
4334 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4335 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4337 qreal first_stop = stops[0].first;
4338 qreal second_stop = stops[1].first;
4340 if (second_stop < first_stop) {
4341 qSwap(first_color, second_color);
4342 qSwap(first_stop, second_stop);
4345 if (colorInterpolation) {
4346 first_color = PREMUL(first_color);
4347 second_color = PREMUL(second_color);
4350 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4351 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4353 uint red_first = qRed(first_color) << 16;
4354 uint green_first = qGreen(first_color) << 16;
4355 uint blue_first = qBlue(first_color) << 16;
4356 uint alpha_first = qAlpha(first_color) << 16;
4358 uint red_second = qRed(second_color) << 16;
4359 uint green_second = qGreen(second_color) << 16;
4360 uint blue_second = qBlue(second_color) << 16;
4361 uint alpha_second = qAlpha(second_color) << 16;
4364 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4365 if (colorInterpolation)
4366 colorTable[i] = first_color;
4368 colorTable[i] = PREMUL(first_color);
4371 if (i < second_index) {
4372 qreal reciprocal = qreal(1) / (second_index - first_index);
4374 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4375 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4376 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4377 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4380 red_first += 1 << 15;
4381 green_first += 1 << 15;
4382 blue_first += 1 << 15;
4383 alpha_first += 1 << 15;
4385 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4386 red_first += red_delta;
4387 green_first += green_delta;
4388 blue_first += blue_delta;
4389 alpha_first += alpha_delta;
4391 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4392 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4394 if (colorInterpolation)
4395 colorTable[i] = color;
4397 colorTable[i] = PREMUL(color);
4401 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4402 if (colorInterpolation)
4403 colorTable[i] = second_color;
4405 colorTable[i] = PREMUL(second_color);
4411 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4412 if (stopCount == 1) {
4413 current_color = PREMUL(current_color);
4414 for (int i = 0; i < size; ++i)
4415 colorTable[i] = current_color;
4419 // The position where the gradient begins and ends
4420 qreal begin_pos = stops[0].first;
4421 qreal end_pos = stops[stopCount-1].first;
4423 int pos = 0; // The position in the color table.
4426 qreal incr = 1 / qreal(size); // the double increment.
4427 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4429 // Up to first point
4430 colorTable[pos++] = PREMUL(current_color);
4431 while (dpos <= begin_pos) {
4432 colorTable[pos] = colorTable[pos - 1];
4437 int current_stop = 0; // We always interpolate between current and current + 1.
4439 qreal t; // position between current left and right stops
4440 qreal t_delta; // the t increment per entry in the color table
4442 if (dpos < end_pos) {
4444 while (dpos > stops[current_stop+1].first)
4447 if (current_stop != 0)
4448 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4449 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4451 if (colorInterpolation) {
4452 current_color = PREMUL(current_color);
4453 next_color = PREMUL(next_color);
4456 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4457 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4458 t = (dpos - stops[current_stop].first) * c;
4462 Q_ASSERT(current_stop < stopCount);
4464 int dist = qRound(t);
4465 int idist = 256 - dist;
4467 if (colorInterpolation)
4468 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4470 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4475 if (dpos >= end_pos)
4481 while (dpos > stops[current_stop+skip+1].first)
4485 current_stop += skip;
4487 current_color = next_color;
4489 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4490 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4492 if (colorInterpolation) {
4494 current_color = PREMUL(current_color);
4495 next_color = PREMUL(next_color);
4498 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4499 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4500 t = (dpos - stops[current_stop].first) * c;
4507 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4508 while (pos < size - 1) {
4509 colorTable[pos] = current_color;
4513 // Make sure the last color stop is represented at the end of the table
4514 colorTable[size - 1] = current_color;
4517 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4520 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4526 m11 = m22 = m33 = 1.;
4527 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4528 clip = pe ? pe->d_func()->clip() : 0;
4531 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4533 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4535 Qt::BrushStyle brushStyle = qbrush_style(brush);
4536 switch (brushStyle) {
4537 case Qt::SolidPattern: {
4539 QColor c = qbrush_color(brush);
4540 QRgb rgba = c.rgba();
4541 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4542 if ((solid.color & 0xff000000) == 0
4543 && compositionMode == QPainter::CompositionMode_SourceOver) {
4549 case Qt::LinearGradientPattern:
4551 type = LinearGradient;
4552 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4553 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4554 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4555 gradient.spread = g->spread();
4557 QLinearGradientData &linearData = gradient.linear;
4559 linearData.origin.x = g->start().x();
4560 linearData.origin.y = g->start().y();
4561 linearData.end.x = g->finalStop().x();
4562 linearData.end.y = g->finalStop().y();
4566 case Qt::RadialGradientPattern:
4568 type = RadialGradient;
4569 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4570 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4571 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4572 gradient.spread = g->spread();
4574 QRadialGradientData &radialData = gradient.radial;
4576 QPointF center = g->center();
4577 radialData.center.x = center.x();
4578 radialData.center.y = center.y();
4579 radialData.center.radius = g->centerRadius();
4580 QPointF focal = g->focalPoint();
4581 radialData.focal.x = focal.x();
4582 radialData.focal.y = focal.y();
4583 radialData.focal.radius = g->focalRadius();
4587 case Qt::ConicalGradientPattern:
4589 type = ConicalGradient;
4590 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4591 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4592 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4593 gradient.spread = QGradient::RepeatSpread;
4595 QConicalGradientData &conicalData = gradient.conical;
4597 QPointF center = g->center();
4598 conicalData.center.x = center.x();
4599 conicalData.center.y = center.y();
4600 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4604 case Qt::Dense1Pattern:
4605 case Qt::Dense2Pattern:
4606 case Qt::Dense3Pattern:
4607 case Qt::Dense4Pattern:
4608 case Qt::Dense5Pattern:
4609 case Qt::Dense6Pattern:
4610 case Qt::Dense7Pattern:
4611 case Qt::HorPattern:
4612 case Qt::VerPattern:
4613 case Qt::CrossPattern:
4614 case Qt::BDiagPattern:
4615 case Qt::FDiagPattern:
4616 case Qt::DiagCrossPattern:
4619 tempImage = new QImage();
4620 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4621 initTexture(tempImage, alpha, QTextureData::Tiled);
4623 case Qt::TexturePattern:
4626 tempImage = new QImage();
4628 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4629 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4631 *tempImage = brush.textureImage();
4632 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4640 adjustSpanMethods();
4643 void QSpanData::adjustSpanMethods()
4653 unclipped_blend = 0;
4656 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4657 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4658 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4659 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4660 fillRect = rasterBuffer->drawHelper->fillRect;
4662 case LinearGradient:
4663 case RadialGradient:
4664 case ConicalGradient:
4665 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4668 unclipped_blend = qBlendTexture;
4669 if (!texture.imageData)
4670 unclipped_blend = 0;
4675 if (!unclipped_blend) {
4678 blend = unclipped_blend;
4679 } else if (clip->hasRectClip) {
4680 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4682 blend = qt_span_fill_clipped;
4686 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4689 // make sure we round off correctly in qdrawhelper.cpp
4690 delta.translate(1.0 / 65536, 1.0 / 65536);
4692 QTransform inv = (delta * matrix).inverted();
4705 const bool affine = !m13 && !m23;
4706 fast_matrix = affine
4707 && m11 * m11 + m21 * m21 < 1e4
4708 && m12 * m12 + m22 * m22 < 1e4
4712 adjustSpanMethods();
4715 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4717 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4719 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4720 if (!d || d->height == 0) {
4721 texture.imageData = 0;
4728 texture.bytesPerLine = 0;
4729 texture.format = QImage::Format_Invalid;
4730 texture.colorTable = 0;
4731 texture.hasAlpha = alpha != 256;
4733 texture.imageData = d->data;
4734 texture.width = d->width;
4735 texture.height = d->height;
4737 if (sourceRect.isNull()) {
4740 texture.x2 = texture.width;
4741 texture.y2 = texture.height;
4743 texture.x1 = sourceRect.x();
4744 texture.y1 = sourceRect.y();
4745 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4746 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4749 texture.bytesPerLine = d->bytes_per_line;
4751 texture.format = d->format;
4752 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4753 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4755 texture.const_alpha = alpha;
4756 texture.type = _type;
4758 adjustSpanMethods();
4763 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4766 Draws the first \a pointCount points in the buffer \a points
4768 The default implementation converts the first \a pointCount QPoints in \a points
4769 to QPointFs and calls the floating point version of drawPoints.
4773 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4776 Reimplement this function to draw the largest ellipse that can be
4777 contained within rectangle \a rect.
4780 #ifdef QT_DEBUG_DRAW
4781 void dumpClip(int width, int height, const QClipData *clip)
4783 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4784 clipImg.fill(0xffff0000);
4791 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4793 for (int i = 0; i < clip->count; ++i) {
4794 const QSpan *span = ((QClipData *) clip)->spans() + i;
4795 for (int j = 0; j < span->len; ++j)
4796 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4797 x0 = qMin(x0, int(span->x));
4798 x1 = qMax(x1, int(span->x + span->len - 1));
4800 y0 = qMin(y0, int(span->y));
4801 y1 = qMax(y1, int(span->y));
4804 static int counter = 0;
4811 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4812 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));