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 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3012 textItem->fontEngine());
3018 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3020 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3021 QRasterPaintEngineState *s = state();
3023 #ifdef QT_DEBUG_DRAW
3024 Q_D(QRasterPaintEngine);
3025 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3026 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3033 #if defined (Q_WS_WIN) || defined(Q_WS_MAC)
3035 bool drawCached = true;
3037 if (s->matrix.type() >= QTransform::TxProject)
3040 // don't try to cache huge fonts
3041 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
3042 if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
3045 // ### Remove the TestFontEngine and Box engine crap, in these
3046 // ### cases we should delegate painting to the font engine
3049 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3050 QFontEngine::Type fontEngineType = ti.fontEngine->type();
3051 // qDebug() << "type" << fontEngineType << s->matrix.type();
3052 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
3053 || (s->matrix.type() <= QTransform::TxTranslate
3054 && (fontEngineType == QFontEngine::TestFontEngine
3055 || fontEngineType == QFontEngine::Box))) {
3059 if (s->matrix.type() > QTransform::TxTranslate)
3063 QRasterPaintEngineState *s = state();
3065 QVarLengthArray<QFixedPoint> positions;
3066 QVarLengthArray<glyph_t> glyphs;
3068 QTransform matrix = s->matrix;
3069 matrix.translate(p.x(), p.y());
3071 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3073 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3077 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
3078 if (s->matrix.type() <= QTransform::TxTranslate
3079 || (s->matrix.type() == QTransform::TxScale
3080 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3081 drawGlyphsS60(p, ti);
3084 #else // Q_WS_WIN || Q_WS_MAC
3086 QFontEngine *fontEngine = ti.fontEngine;
3089 if (s->matrix.type() < QTransform::TxScale) {
3091 QVarLengthArray<QFixedPoint> positions;
3092 QVarLengthArray<glyph_t> glyphs;
3093 QTransform matrix = state()->transform();
3095 qreal _x = qFloor(p.x());
3096 qreal _y = qFloor(p.y());
3097 matrix.translate(_x, _y);
3099 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3100 if (glyphs.size() == 0)
3103 for(int i = 0; i < glyphs.size(); i++) {
3104 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3105 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3106 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3107 qRound(positions[i].x + metrics.x),
3108 qRound(positions[i].y + metrics.y),
3109 img.width(), img.height());
3115 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3117 if (fontEngine->type() != QFontEngine::Freetype) {
3118 QPaintEngineEx::drawTextItem(p, ti);
3122 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3124 QTransform matrix = s->matrix;
3125 matrix.translate(p.x(), p.y());
3127 QVarLengthArray<QFixedPoint> positions;
3128 QVarLengthArray<glyph_t> glyphs;
3129 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3130 if (glyphs.size() == 0)
3133 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3134 QPaintEngine::drawTextItem(p, ti);
3140 QPaintEngineEx::drawTextItem(p, ti);
3146 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3148 Q_D(QRasterPaintEngine);
3149 QRasterPaintEngineState *s = state();
3152 if (!s->penData.blend)
3155 if (!s->flags.fast_pen) {
3156 QPaintEngineEx::drawPoints(points, pointCount);
3160 QCosmeticStroker stroker(s, d->deviceRect);
3161 stroker.drawPoints(points, pointCount);
3165 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3167 Q_D(QRasterPaintEngine);
3168 QRasterPaintEngineState *s = state();
3171 if (!s->penData.blend)
3174 if (!s->flags.fast_pen) {
3175 QPaintEngineEx::drawPoints(points, pointCount);
3179 QCosmeticStroker stroker(s, d->deviceRect);
3180 stroker.drawPoints(points, pointCount);
3186 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3188 #ifdef QT_DEBUG_DRAW
3189 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3191 Q_D(QRasterPaintEngine);
3192 QRasterPaintEngineState *s = state();
3195 if (!s->penData.blend)
3198 if (s->flags.fast_pen) {
3199 QCosmeticStroker stroker(s, d->deviceRect);
3200 for (int i=0; i<lineCount; ++i) {
3201 const QLine &l = lines[i];
3202 stroker.drawLine(l.p1(), l.p2());
3205 QPaintEngineEx::drawLines(lines, lineCount);
3209 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3215 Q_Q(QRasterPaintEngine);
3216 QRasterPaintEngineState *s = q->state();
3218 const QPen &pen = s->lastPen;
3219 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3220 const QVector<qreal> pattern = pen.dashPattern();
3222 qreal patternLength = 0;
3223 for (int i = 0; i < pattern.size(); ++i)
3224 patternLength += pattern.at(i);
3226 if (patternLength <= 0)
3229 qreal length = line.length();
3230 Q_ASSERT(length > 0);
3231 while (length > 0) {
3232 const bool rasterize = *inDash;
3233 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3236 if (dash >= length) {
3238 *dashOffset += dash / width;
3242 *inDash = !(*inDash);
3243 if (++*dashIndex >= pattern.size())
3250 if (rasterize && dash > 0)
3251 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3258 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3260 #ifdef QT_DEBUG_DRAW
3261 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3263 Q_D(QRasterPaintEngine);
3264 QRasterPaintEngineState *s = state();
3267 if (!s->penData.blend)
3269 if (s->flags.fast_pen) {
3270 QCosmeticStroker stroker(s, d->deviceRect);
3271 for (int i=0; i<lineCount; ++i) {
3272 QLineF line = lines[i];
3273 stroker.drawLine(line.p1(), line.p2());
3276 QPaintEngineEx::drawLines(lines, lineCount);
3284 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3286 QPaintEngineEx::drawEllipse(rect);
3293 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3295 Q_D(QRasterPaintEngine);
3302 CGContextRef QRasterPaintEngine::getCGContext() const
3304 Q_D(const QRasterPaintEngine);
3305 return d->cgContext;
3313 void QRasterPaintEngine::setDC(HDC hdc) {
3314 Q_D(QRasterPaintEngine);
3321 HDC QRasterPaintEngine::getDC() const
3323 Q_D(const QRasterPaintEngine);
3330 void QRasterPaintEngine::releaseDC(HDC) const
3339 QPoint QRasterPaintEngine::coordinateOffset() const
3341 return QPoint(0, 0);
3344 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3349 Q_D(QRasterPaintEngine);
3351 Q_ASSERT(image.depth() == 1);
3353 const int spanCount = 256;
3354 QT_FT_Span spans[spanCount];
3358 int w = image.width();
3359 int h = image.height();
3360 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3361 int ymin = qMax(qRound(pos.y()), 0);
3362 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3363 int xmin = qMax(qRound(pos.x()), 0);
3365 int x_offset = xmin - qRound(pos.x());
3367 QImage::Format format = image.format();
3368 for (int y = ymin; y < ymax; ++y) {
3369 const uchar *src = image.scanLine(y - qRound(pos.y()));
3370 if (format == QImage::Format_MonoLSB) {
3371 for (int x = 0; x < xmax - xmin; ++x) {
3372 int src_x = x + x_offset;
3373 uchar pixel = src[src_x >> 3];
3378 if (pixel & (0x1 << (src_x & 7))) {
3379 spans[n].x = xmin + x;
3381 spans[n].coverage = 255;
3383 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3387 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3390 if (n == spanCount) {
3391 fg->blend(n, spans, fg);
3397 for (int x = 0; x < xmax - xmin; ++x) {
3398 int src_x = x + x_offset;
3399 uchar pixel = src[src_x >> 3];
3404 if (pixel & (0x80 >> (x & 7))) {
3405 spans[n].x = xmin + x;
3407 spans[n].coverage = 255;
3409 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3413 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3416 if (n == spanCount) {
3417 fg->blend(n, spans, fg);
3425 fg->blend(n, spans, fg);
3431 \enum QRasterPaintEngine::ClipType
3434 \value RectClip Indicates that the currently set clip is a single rectangle.
3435 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3440 Returns the type of the clip currently set.
3442 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3444 Q_D(const QRasterPaintEngine);
3446 const QClipData *clip = d->clip();
3447 if (!clip || clip->hasRectClip)
3455 Returns the bounding rect of the currently set clip.
3457 QRect QRasterPaintEngine::clipBoundingRect() const
3459 Q_D(const QRasterPaintEngine);
3461 const QClipData *clip = d->clip();
3464 return d->deviceRect;
3466 if (clip->hasRectClip)
3467 return clip->clipRect;
3469 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3472 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3474 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3476 QVarLengthArray<short, 4096> buffer;
3478 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3479 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3480 result->initialize();
3482 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3483 const QSpan *c1_spans = c1ClipLines[y].spans;
3484 int c1_count = c1ClipLines[y].count;
3485 const QSpan *c2_spans = c2ClipLines[y].spans;
3486 int c2_count = c2ClipLines[y].count;
3488 if (c1_count == 0 && c2_count == 0)
3490 if (c1_count == 0) {
3491 result->appendSpans(c2_spans, c2_count);
3493 } else if (c2_count == 0) {
3494 result->appendSpans(c1_spans, c1_count);
3498 // we need to merge the two
3500 // find required length
3501 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3502 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3504 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3506 // Fill with old spans.
3507 for (int i = 0; i < c1_count; ++i) {
3508 const QSpan *cs = c1_spans + i;
3509 for (int j=cs->x; j<cs->x + cs->len; ++j)
3510 buffer[j] = cs->coverage;
3513 // Fill with new spans
3514 for (int i = 0; i < c2_count; ++i) {
3515 const QSpan *cs = c2_spans + i;
3516 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3517 buffer[j] += cs->coverage;
3518 if (buffer[j] > 255)
3526 // Skip to next span
3527 while (x < max && buffer[x] == 0) ++x;
3528 if (x >= max) break;
3531 int coverage = buffer[x];
3533 // Find length of span
3534 while (x < max && buffer[x] == coverage)
3537 result->appendSpan(sx, x - sx, y, coverage);
3542 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3544 Q_Q(QRasterPaintEngine);
3545 QRasterPaintEngineState *s = q->state();
3547 rasterizer->setAntialiased(s->flags.antialiased);
3549 QRect clipRect(deviceRect);
3551 // ### get from optimized rectbased QClipData
3553 const QClipData *c = clip();
3555 const QRect r(QPoint(c->xmin, c->ymin),
3556 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3557 clipRect = clipRect.intersected(r);
3558 blend = data->blend;
3560 blend = data->unclipped_blend;
3563 rasterizer->setClipRect(clipRect);
3564 rasterizer->initialize(blend, data);
3567 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3568 ProcessSpans callback,
3569 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3571 if (!callback || !outline)
3574 Q_Q(QRasterPaintEngine);
3575 QRasterPaintEngineState *s = q->state();
3577 if (!s->flags.antialiased) {
3578 initializeRasterizer(spanData);
3580 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3584 rasterizer->rasterize(outline, fillRule);
3588 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3592 int q_gray_rendered_spans(QT_FT_Raster raster);
3595 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3596 ProcessSpans callback,
3597 void *userData, QRasterBuffer *)
3599 if (!callback || !outline)
3602 Q_Q(QRasterPaintEngine);
3603 QRasterPaintEngineState *s = q->state();
3605 if (!s->flags.antialiased) {
3606 rasterizer->setAntialiased(s->flags.antialiased);
3607 rasterizer->setClipRect(deviceRect);
3608 rasterizer->initialize(callback, userData);
3610 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3614 rasterizer->rasterize(outline, fillRule);
3618 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3619 // minimize memory reallocations. However if initial size for
3620 // raster pool is changed for lower value, reallocations will
3622 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3623 int rasterPoolSize = rasterPoolInitialSize;
3624 unsigned char *rasterPoolBase;
3625 #if defined(Q_WS_WIN64)
3627 // We make use of setjmp and longjmp in qgrayraster.c which requires
3628 // 16-byte alignment, hence we hardcode this requirement here..
3629 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3631 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3632 rasterPoolBase = rasterPoolOnStack;
3634 Q_CHECK_PTR(rasterPoolBase);
3636 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3638 void *data = userData;
3640 QT_FT_BBox clip_box = { deviceRect.x(),
3642 deviceRect.x() + deviceRect.width(),
3643 deviceRect.y() + deviceRect.height() };
3645 QT_FT_Raster_Params rasterParams;
3646 rasterParams.target = 0;
3647 rasterParams.source = outline;
3648 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3649 rasterParams.gray_spans = 0;
3650 rasterParams.black_spans = 0;
3651 rasterParams.bit_test = 0;
3652 rasterParams.bit_set = 0;
3653 rasterParams.user = data;
3654 rasterParams.clip_box = clip_box;
3659 int rendered_spans = 0;
3663 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3664 rasterParams.gray_spans = callback;
3665 rasterParams.skip_spans = rendered_spans;
3666 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3668 // Out of memory, reallocate some more and try again...
3669 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3670 int new_size = rasterPoolSize * 2;
3671 if (new_size > 1024 * 1024) {
3672 qWarning("QPainter: Rasterization of primitive failed");
3676 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3678 #if defined(Q_WS_WIN64)
3679 _aligned_free(rasterPoolBase);
3681 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3682 free(rasterPoolBase);
3685 rasterPoolSize = new_size;
3687 #if defined(Q_WS_WIN64)
3688 // We make use of setjmp and longjmp in qgrayraster.c which requires
3689 // 16-byte alignment, hence we hardcode this requirement here..
3690 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3692 (unsigned char *) malloc(rasterPoolSize);
3694 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3696 qt_ft_grays_raster.raster_done(*grayRaster.data());
3697 qt_ft_grays_raster.raster_new(grayRaster.data());
3698 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3704 #if defined(Q_WS_WIN64)
3705 _aligned_free(rasterPoolBase);
3707 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3708 free(rasterPoolBase);
3712 void QRasterPaintEnginePrivate::recalculateFastImages()
3714 Q_Q(QRasterPaintEngine);
3715 QRasterPaintEngineState *s = q->state();
3717 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3718 && s->matrix.type() <= QTransform::TxShear;
3721 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3723 Q_Q(const QRasterPaintEngine);
3724 const QRasterPaintEngineState *s = q->state();
3726 return s->flags.fast_images
3727 && (mode == QPainter::CompositionMode_SourceOver
3728 || (mode == QPainter::CompositionMode_Source
3729 && !image.hasAlphaChannel()));
3732 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3734 Q_ASSERT(image.depth() == 1);
3736 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3737 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3739 QRgb fg = PREMUL(color.rgba());
3742 int height = sourceImage.height();
3743 int width = sourceImage.width();
3744 for (int y=0; y<height; ++y) {
3745 uchar *source = sourceImage.scanLine(y);
3746 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3747 if (!source || !target)
3748 QT_THROW(std::bad_alloc()); // we must have run out of memory
3749 for (int x=0; x < width; ++x)
3750 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3755 QRasterBuffer::~QRasterBuffer()
3759 void QRasterBuffer::init()
3761 compositionMode = QPainter::CompositionMode_SourceOver;
3762 monoDestinationWithClut = false;
3767 QImage::Format QRasterBuffer::prepare(QImage *image)
3769 m_buffer = (uchar *)image->bits();
3770 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3771 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3772 bytes_per_pixel = image->depth()/8;
3773 bytes_per_line = image->bytesPerLine();
3775 format = image->format();
3776 drawHelper = qDrawHelper + format;
3777 if (image->depth() == 1 && image->colorTable().size() == 2) {
3778 monoDestinationWithClut = true;
3779 destColor0 = PREMUL(image->colorTable()[0]);
3780 destColor1 = PREMUL(image->colorTable()[1]);
3786 void QRasterBuffer::resetBuffer(int val)
3788 memset(m_buffer, val, m_height*bytes_per_line);
3791 QClipData::QClipData(int height)
3793 clipSpanHeight = height;
3798 xmin = xmax = ymin = ymax = 0;
3802 hasRectClip = hasRegionClip = false;
3805 QClipData::~QClipData()
3813 void QClipData::initialize()
3819 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3821 Q_CHECK_PTR(m_clipLines);
3823 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3824 allocated = clipSpanHeight;
3825 Q_CHECK_PTR(m_spans);
3831 m_clipLines[y].spans = 0;
3832 m_clipLines[y].count = 0;
3836 const int len = clipRect.width();
3839 QSpan *span = m_spans + count;
3843 span->coverage = 255;
3846 m_clipLines[y].spans = span;
3847 m_clipLines[y].count = 1;
3851 while (y < clipSpanHeight) {
3852 m_clipLines[y].spans = 0;
3853 m_clipLines[y].count = 0;
3856 } else if (hasRegionClip) {
3858 const QVector<QRect> rects = clipRegion.rects();
3859 const int numRects = rects.size();
3862 const int maxSpans = (ymax - ymin) * numRects;
3863 if (maxSpans > allocated) {
3864 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3865 allocated = maxSpans;
3870 int firstInBand = 0;
3872 while (firstInBand < numRects) {
3873 const int currMinY = rects.at(firstInBand).y();
3874 const int currMaxY = currMinY + rects.at(firstInBand).height();
3876 while (y < currMinY) {
3877 m_clipLines[y].spans = 0;
3878 m_clipLines[y].count = 0;
3882 int lastInBand = firstInBand;
3883 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3886 while (y < currMaxY) {
3888 m_clipLines[y].spans = m_spans + count;
3889 m_clipLines[y].count = lastInBand - firstInBand + 1;
3891 for (int r = firstInBand; r <= lastInBand; ++r) {
3892 const QRect &currRect = rects.at(r);
3893 QSpan *span = m_spans + count;
3894 span->x = currRect.x();
3895 span->len = currRect.width();
3897 span->coverage = 255;
3903 firstInBand = lastInBand + 1;
3906 Q_ASSERT(count <= allocated);
3908 while (y < clipSpanHeight) {
3909 m_clipLines[y].spans = 0;
3910 m_clipLines[y].count = 0;
3916 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3921 free(m_clipLines); // same for clipLines
3927 void QClipData::fixup()
3932 ymin = ymax = xmin = xmax = 0;
3937 ymin = m_spans[0].y;
3938 ymax = m_spans[count-1].y + 1;
3942 const int firstLeft = m_spans[0].x;
3943 const int firstRight = m_spans[0].x + m_spans[0].len;
3946 for (int i = 0; i < count; ++i) {
3947 QT_FT_Span_& span = m_spans[i];
3950 if (span.y != y + 1 && y != -1)
3953 m_clipLines[y].spans = &span;
3954 m_clipLines[y].count = 1;
3956 ++m_clipLines[y].count;
3958 const int spanLeft = span.x;
3959 const int spanRight = spanLeft + span.len;
3961 if (spanLeft < xmin)
3964 if (spanRight > xmax)
3967 if (spanLeft != firstLeft || spanRight != firstRight)
3973 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3978 Convert \a rect to clip spans.
3980 void QClipData::setClipRect(const QRect &rect)
3982 if (hasRectClip && rect == clipRect)
3985 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3987 hasRegionClip = false;
3991 xmax = rect.x() + rect.width();
3992 ymin = qMin(rect.y(), clipSpanHeight);
3993 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4000 // qDebug() << xmin << xmax << ymin << ymax;
4004 Convert \a region to clip spans.
4006 void QClipData::setClipRegion(const QRegion ®ion)
4008 if (region.rectCount() == 1) {
4009 setClipRect(region.rects().at(0));
4013 hasRegionClip = true;
4014 hasRectClip = false;
4015 clipRegion = region;
4017 { // set bounding rect
4018 const QRect rect = region.boundingRect();
4020 xmax = rect.x() + rect.width();
4022 ymax = rect.y() + rect.height();
4034 spans must be sorted on y
4036 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4037 const QSpan *spans, const QSpan *end,
4038 QSpan **outSpans, int available)
4040 const_cast<QClipData *>(clip)->initialize();
4042 QSpan *out = *outSpans;
4044 const QSpan *clipSpans = clip->m_spans + *currentClip;
4045 const QSpan *clipEnd = clip->m_spans + clip->count;
4047 while (available && spans < end ) {
4048 if (clipSpans >= clipEnd) {
4052 if (clipSpans->y > spans->y) {
4056 if (spans->y != clipSpans->y) {
4057 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4058 clipSpans = clip->m_clipLines[spans->y].spans;
4063 Q_ASSERT(spans->y == clipSpans->y);
4066 int sx2 = sx1 + spans->len;
4067 int cx1 = clipSpans->x;
4068 int cx2 = cx1 + clipSpans->len;
4070 if (cx1 < sx1 && cx2 < sx1) {
4073 } else if (sx1 < cx1 && sx2 < cx1) {
4077 int x = qMax(sx1, cx1);
4078 int len = qMin(sx2, cx2) - x;
4080 out->x = qMax(sx1, cx1);
4081 out->len = qMin(sx2, cx2) - out->x;
4083 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4095 *currentClip = clipSpans - clip->m_spans;
4099 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4101 // qDebug() << "qt_span_fill_clipped" << spanCount;
4102 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4104 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4106 const int NSPANS = 256;
4107 QSpan cspans[NSPANS];
4108 int currentClip = 0;
4109 const QSpan *end = spans + spanCount;
4110 while (spans < end) {
4111 QSpan *clipped = cspans;
4112 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4113 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4114 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4116 if (clipped - cspans)
4117 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4123 Clip spans to \a{clip}-rectangle.
4124 Returns number of unclipped spans
4126 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4129 const short minx = clip.left();
4130 const short miny = clip.top();
4131 const short maxx = clip.right();
4132 const short maxy = clip.bottom();
4135 for (int i = 0; i < numSpans; ++i) {
4136 if (spans[i].y > maxy)
4138 if (spans[i].y < miny
4139 || spans[i].x > maxx
4140 || spans[i].x + spans[i].len <= minx) {
4143 if (spans[i].x < minx) {
4144 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4147 spans[n].x = spans[i].x;
4148 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4150 if (spans[n].len == 0)
4152 spans[n].y = spans[i].y;
4153 spans[n].coverage = spans[i].coverage;
4160 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4163 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4164 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4166 Q_ASSERT(fillData->clip);
4167 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4169 // hw: check if this const_cast<> is safe!!!
4170 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4171 fillData->clip->clipRect);
4173 fillData->unclipped_blend(count, spans, fillData);
4176 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4178 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4180 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4181 // for (int i = 0; i < qMin(count, 10); ++i) {
4182 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4185 switch (clipData->operation) {
4187 case Qt::IntersectClip:
4189 QClipData *newClip = clipData->newClip;
4190 newClip->initialize();
4192 int currentClip = 0;
4193 const QSpan *end = spans + count;
4194 while (spans < end) {
4195 QSpan *newspans = newClip->m_spans + newClip->count;
4196 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4197 &newspans, newClip->allocated - newClip->count);
4198 newClip->count = newspans - newClip->m_spans;
4200 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4201 newClip->allocated *= 2;
4208 case Qt::ReplaceClip:
4209 clipData->newClip->appendSpans(spans, count);
4217 QImage QRasterBuffer::bufferImage() const
4219 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4221 for (int y = 0; y < m_height; ++y) {
4222 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4224 for (int x=0; x<m_width; ++x) {
4225 uint argb = span[x];
4226 image.setPixel(x, y, argb);
4234 void QRasterBuffer::flushToARGBImage(QImage *target) const
4236 int w = qMin(m_width, target->width());
4237 int h = qMin(m_height, target->height());
4239 for (int y=0; y<h; ++y) {
4240 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4241 QRgb *dest = (QRgb *) target->scanLine(y);
4242 for (int x=0; x<w; ++x) {
4243 QRgb pixel = sourceLine[x];
4244 int alpha = qAlpha(pixel);
4248 dest[x] = (alpha << 24)
4249 | ((255*qRed(pixel)/alpha) << 16)
4250 | ((255*qGreen(pixel)/alpha) << 8)
4251 | ((255*qBlue(pixel)/alpha) << 0);
4258 class QGradientCache
4262 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4263 stops(s), opacity(op), interpolationMode(mode) {}
4264 uint buffer[GRADIENT_STOPTABLE_SIZE];
4265 QGradientStops stops;
4267 QGradient::InterpolationMode interpolationMode;
4270 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4273 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4274 quint64 hash_val = 0;
4276 QGradientStops stops = gradient.stops();
4277 for (int i = 0; i < stops.size() && i <= 2; i++)
4278 hash_val += stops[i].second.rgba();
4280 QMutexLocker lock(&mutex);
4281 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4283 if (it == cache.constEnd())
4284 return addCacheElement(hash_val, gradient, opacity);
4287 const CacheInfo &cache_info = it.value();
4288 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4289 return cache_info.buffer;
4291 } while (it != cache.constEnd() && it.key() == hash_val);
4292 // an exact match for these stops and opacity was not found, create new cache
4293 return addCacheElement(hash_val, gradient, opacity);
4297 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4299 inline int maxCacheSize() const { return 60; }
4300 inline void generateGradientColorTable(const QGradient& g,
4302 int size, int opacity) const;
4303 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4304 if (cache.size() == maxCacheSize()) {
4305 // may remove more than 1, but OK
4306 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4308 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4309 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4310 return cache.insert(hash_val, cache_entry).value().buffer;
4313 QGradientColorTableHash cache;
4317 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4319 QGradientStops stops = gradient.stops();
4320 int stopCount = stops.count();
4321 Q_ASSERT(stopCount > 0);
4323 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4325 if (stopCount == 2) {
4326 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4327 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4329 qreal first_stop = stops[0].first;
4330 qreal second_stop = stops[1].first;
4332 if (second_stop < first_stop) {
4333 qSwap(first_color, second_color);
4334 qSwap(first_stop, second_stop);
4337 if (colorInterpolation) {
4338 first_color = PREMUL(first_color);
4339 second_color = PREMUL(second_color);
4342 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4343 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4345 uint red_first = qRed(first_color) << 16;
4346 uint green_first = qGreen(first_color) << 16;
4347 uint blue_first = qBlue(first_color) << 16;
4348 uint alpha_first = qAlpha(first_color) << 16;
4350 uint red_second = qRed(second_color) << 16;
4351 uint green_second = qGreen(second_color) << 16;
4352 uint blue_second = qBlue(second_color) << 16;
4353 uint alpha_second = qAlpha(second_color) << 16;
4356 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4357 if (colorInterpolation)
4358 colorTable[i] = first_color;
4360 colorTable[i] = PREMUL(first_color);
4363 if (i < second_index) {
4364 qreal reciprocal = qreal(1) / (second_index - first_index);
4366 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4367 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4368 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4369 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4372 red_first += 1 << 15;
4373 green_first += 1 << 15;
4374 blue_first += 1 << 15;
4375 alpha_first += 1 << 15;
4377 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4378 red_first += red_delta;
4379 green_first += green_delta;
4380 blue_first += blue_delta;
4381 alpha_first += alpha_delta;
4383 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4384 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4386 if (colorInterpolation)
4387 colorTable[i] = color;
4389 colorTable[i] = PREMUL(color);
4393 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4394 if (colorInterpolation)
4395 colorTable[i] = second_color;
4397 colorTable[i] = PREMUL(second_color);
4403 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4404 if (stopCount == 1) {
4405 current_color = PREMUL(current_color);
4406 for (int i = 0; i < size; ++i)
4407 colorTable[i] = current_color;
4411 // The position where the gradient begins and ends
4412 qreal begin_pos = stops[0].first;
4413 qreal end_pos = stops[stopCount-1].first;
4415 int pos = 0; // The position in the color table.
4418 qreal incr = 1 / qreal(size); // the double increment.
4419 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4421 // Up to first point
4422 colorTable[pos++] = PREMUL(current_color);
4423 while (dpos <= begin_pos) {
4424 colorTable[pos] = colorTable[pos - 1];
4429 int current_stop = 0; // We always interpolate between current and current + 1.
4431 qreal t; // position between current left and right stops
4432 qreal t_delta; // the t increment per entry in the color table
4434 if (dpos < end_pos) {
4436 while (dpos > stops[current_stop+1].first)
4439 if (current_stop != 0)
4440 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4441 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4443 if (colorInterpolation) {
4444 current_color = PREMUL(current_color);
4445 next_color = PREMUL(next_color);
4448 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4449 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4450 t = (dpos - stops[current_stop].first) * c;
4454 Q_ASSERT(current_stop < stopCount);
4456 int dist = qRound(t);
4457 int idist = 256 - dist;
4459 if (colorInterpolation)
4460 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4462 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4467 if (dpos >= end_pos)
4473 while (dpos > stops[current_stop+skip+1].first)
4477 current_stop += skip;
4479 current_color = next_color;
4481 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4482 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4484 if (colorInterpolation) {
4486 current_color = PREMUL(current_color);
4487 next_color = PREMUL(next_color);
4490 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4491 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4492 t = (dpos - stops[current_stop].first) * c;
4499 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4500 while (pos < size - 1) {
4501 colorTable[pos] = current_color;
4505 // Make sure the last color stop is represented at the end of the table
4506 colorTable[size - 1] = current_color;
4509 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4512 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4518 m11 = m22 = m33 = 1.;
4519 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4520 clip = pe ? pe->d_func()->clip() : 0;
4523 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4525 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4527 Qt::BrushStyle brushStyle = qbrush_style(brush);
4528 switch (brushStyle) {
4529 case Qt::SolidPattern: {
4531 QColor c = qbrush_color(brush);
4532 QRgb rgba = c.rgba();
4533 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4534 if ((solid.color & 0xff000000) == 0
4535 && compositionMode == QPainter::CompositionMode_SourceOver) {
4541 case Qt::LinearGradientPattern:
4543 type = LinearGradient;
4544 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4545 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4546 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4547 gradient.spread = g->spread();
4549 QLinearGradientData &linearData = gradient.linear;
4551 linearData.origin.x = g->start().x();
4552 linearData.origin.y = g->start().y();
4553 linearData.end.x = g->finalStop().x();
4554 linearData.end.y = g->finalStop().y();
4558 case Qt::RadialGradientPattern:
4560 type = RadialGradient;
4561 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4562 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4563 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4564 gradient.spread = g->spread();
4566 QRadialGradientData &radialData = gradient.radial;
4568 QPointF center = g->center();
4569 radialData.center.x = center.x();
4570 radialData.center.y = center.y();
4571 radialData.center.radius = g->centerRadius();
4572 QPointF focal = g->focalPoint();
4573 radialData.focal.x = focal.x();
4574 radialData.focal.y = focal.y();
4575 radialData.focal.radius = g->focalRadius();
4579 case Qt::ConicalGradientPattern:
4581 type = ConicalGradient;
4582 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4583 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4584 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4585 gradient.spread = QGradient::RepeatSpread;
4587 QConicalGradientData &conicalData = gradient.conical;
4589 QPointF center = g->center();
4590 conicalData.center.x = center.x();
4591 conicalData.center.y = center.y();
4592 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4596 case Qt::Dense1Pattern:
4597 case Qt::Dense2Pattern:
4598 case Qt::Dense3Pattern:
4599 case Qt::Dense4Pattern:
4600 case Qt::Dense5Pattern:
4601 case Qt::Dense6Pattern:
4602 case Qt::Dense7Pattern:
4603 case Qt::HorPattern:
4604 case Qt::VerPattern:
4605 case Qt::CrossPattern:
4606 case Qt::BDiagPattern:
4607 case Qt::FDiagPattern:
4608 case Qt::DiagCrossPattern:
4611 tempImage = new QImage();
4612 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4613 initTexture(tempImage, alpha, QTextureData::Tiled);
4615 case Qt::TexturePattern:
4618 tempImage = new QImage();
4620 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4621 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4623 *tempImage = brush.textureImage();
4624 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4632 adjustSpanMethods();
4635 void QSpanData::adjustSpanMethods()
4645 unclipped_blend = 0;
4648 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4649 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4650 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4651 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4652 fillRect = rasterBuffer->drawHelper->fillRect;
4654 case LinearGradient:
4655 case RadialGradient:
4656 case ConicalGradient:
4657 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4660 unclipped_blend = qBlendTexture;
4661 if (!texture.imageData)
4662 unclipped_blend = 0;
4667 if (!unclipped_blend) {
4670 blend = unclipped_blend;
4671 } else if (clip->hasRectClip) {
4672 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4674 blend = qt_span_fill_clipped;
4678 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4681 // make sure we round off correctly in qdrawhelper.cpp
4682 delta.translate(1.0 / 65536, 1.0 / 65536);
4684 QTransform inv = (delta * matrix).inverted();
4697 const bool affine = !m13 && !m23;
4698 fast_matrix = affine
4699 && m11 * m11 + m21 * m21 < 1e4
4700 && m12 * m12 + m22 * m22 < 1e4
4704 adjustSpanMethods();
4707 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4709 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4711 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4712 if (!d || d->height == 0) {
4713 texture.imageData = 0;
4720 texture.bytesPerLine = 0;
4721 texture.format = QImage::Format_Invalid;
4722 texture.colorTable = 0;
4723 texture.hasAlpha = alpha != 256;
4725 texture.imageData = d->data;
4726 texture.width = d->width;
4727 texture.height = d->height;
4729 if (sourceRect.isNull()) {
4732 texture.x2 = texture.width;
4733 texture.y2 = texture.height;
4735 texture.x1 = sourceRect.x();
4736 texture.y1 = sourceRect.y();
4737 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4738 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4741 texture.bytesPerLine = d->bytes_per_line;
4743 texture.format = d->format;
4744 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4745 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4747 texture.const_alpha = alpha;
4748 texture.type = _type;
4750 adjustSpanMethods();
4755 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4758 Draws the first \a pointCount points in the buffer \a points
4760 The default implementation converts the first \a pointCount QPoints in \a points
4761 to QPointFs and calls the floating point version of drawPoints.
4765 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4768 Reimplement this function to draw the largest ellipse that can be
4769 contained within rectangle \a rect.
4772 #ifdef QT_DEBUG_DRAW
4773 void dumpClip(int width, int height, const QClipData *clip)
4775 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4776 clipImg.fill(0xffff0000);
4783 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4785 for (int i = 0; i < clip->count; ++i) {
4786 const QSpan *span = ((QClipData *) clip)->spans() + i;
4787 for (int j = 0; j < span->len; ++j)
4788 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4789 x0 = qMin(x0, int(span->x));
4790 x1 = qMax(x1, int(span->x + span->len - 1));
4792 y0 = qMin(y0, int(span->y));
4793 y1 = qMax(y1, int(span->y));
4796 static int counter = 0;
4803 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4804 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));