1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtCore/qglobal.h>
43 #include <QtCore/qmutex.h>
45 #define QT_FT_BEGIN_HEADER
46 #define QT_FT_END_HEADER
48 #include <private/qrasterdefs_p.h>
49 #include <private/qgrayraster_p.h>
51 #include <qpainterpath.h>
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_OS_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))
123 static inline bool winClearTypeFontsEnabled()
126 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
127 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
128 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
130 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
131 return result == FE_FONTSMOOTHINGCLEARTYPE;
134 bool QRasterPaintEngine::clearTypeFontsEnabled()
136 static const bool result = winClearTypeFontsEnabled();
143 extern bool qt_applefontsmoothing_enabled;
147 /********************************************************************************
150 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
151 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
152 static void qt_span_clip(int count, const QSpan *spans, void *userData);
153 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
159 Qt::ClipOperation operation;
165 LineDrawIncludeLastPixel
168 struct QRasterFloatPoint {
174 static const QRectF boundingRect(const QPointF *points, int pointCount)
176 const QPointF *e = points;
177 const QPointF *last = points + pointCount;
178 qreal minx, maxx, miny, maxy;
179 minx = maxx = e->x();
180 miny = maxy = e->y();
184 else if (e->x() > maxx)
188 else if (e->y() > maxy)
191 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
195 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
196 return (elementCount == 5 // 5-point polygon, check for closed rect
197 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
198 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
199 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
200 && pts[0] < pts[4] && pts[1] < pts[5]
202 (elementCount == 4 // 4-point polygon, check for unclosed rect
203 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
204 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
205 && pts[0] < pts[4] && pts[1] < pts[5]
210 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
212 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
215 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
217 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
220 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
221 qfixed c2x, qfixed c2y,
222 qfixed ex, qfixed ey,
225 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
226 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
227 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
231 #if !defined(QT_NO_DEBUG) && 0
232 static void qt_debug_path(const QPainterPath &path)
234 const char *names[] = {
241 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
242 for (int i=0; i<path.elementCount(); ++i) {
243 const QPainterPath::Element &e = path.elementAt(i);
244 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
245 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
250 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
251 QPaintEngineExPrivate(),
258 \class QRasterPaintEngine
263 \brief The QRasterPaintEngine class enables hardware acceleration
264 of painting operations in Qt for Embedded Linux.
266 Note that this functionality is only available in
267 \l{Qt for Embedded Linux}.
269 In \l{Qt for Embedded Linux}, painting is a pure software
270 implementation. But starting with Qt 4.2, it is
271 possible to add an accelerated graphics driver to take advantage
272 of available hardware resources.
274 Hardware acceleration is accomplished by creating a custom screen
275 driver, accelerating the copying from memory to the screen, and
276 implementing a custom paint engine accelerating the various
277 painting operations. Then a custom paint device (derived from the
278 QCustomRasterPaintDevice class) and a custom window surface
279 (derived from QWSWindowSurface) must be implemented to make
280 \l{Qt for Embedded Linux} aware of the accelerated driver.
282 \note The QRasterPaintEngine class does not support 8-bit images.
283 Instead, they need to be converted to a supported format, such as
284 QImage::Format_ARGB32_Premultiplied.
286 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
287 documentation for details.
289 \sa QCustomRasterPaintDevice, QPaintEngine
293 \fn Type QRasterPaintEngine::type() const
299 \relates QRasterPaintEngine
301 A struct equivalent to QT_FT_Span, containing a position (x,
302 y), the span's length in pixels and its color/coverage (a value
303 ranging from 0 to 255).
309 Creates a raster based paint engine for operating on the given
310 \a device, with the complete set of \l
311 {QPaintEngine::PaintEngineFeature}{paint engine features and
314 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
315 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
317 d_func()->device = device;
324 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
327 d_func()->device = device;
331 void QRasterPaintEngine::init()
333 Q_D(QRasterPaintEngine);
340 // The antialiasing raster.
341 d->grayRaster.reset(new QT_FT_Raster);
342 Q_CHECK_PTR(d->grayRaster.data());
343 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
344 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
347 d->rasterizer.reset(new QRasterizer);
348 d->rasterBuffer.reset(new QRasterBuffer());
349 d->outlineMapper.reset(new QOutlineMapper);
350 d->outlinemapper_xform_dirty = true;
352 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
353 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
354 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
356 d->baseClip.reset(new QClipData(d->device->height()));
357 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
359 d->image_filler.init(d->rasterBuffer.data(), this);
360 d->image_filler.type = QSpanData::Texture;
362 d->image_filler_xform.init(d->rasterBuffer.data(), this);
363 d->image_filler_xform.type = QSpanData::Texture;
365 d->solid_color_filler.init(d->rasterBuffer.data(), this);
366 d->solid_color_filler.type = QSpanData::Solid;
368 d->deviceDepth = d->device->depth();
370 d->mono_surface = false;
371 gccaps &= ~PorterDuff;
373 QImage::Format format = QImage::Format_Invalid;
375 switch (d->device->devType()) {
376 case QInternal::Pixmap:
377 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
379 case QInternal::Image:
380 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
383 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
389 case QImage::Format_MonoLSB:
390 case QImage::Format_Mono:
391 d->mono_surface = true;
393 case QImage::Format_ARGB8565_Premultiplied:
394 case QImage::Format_ARGB8555_Premultiplied:
395 case QImage::Format_ARGB6666_Premultiplied:
396 case QImage::Format_ARGB4444_Premultiplied:
397 case QImage::Format_ARGB32_Premultiplied:
398 case QImage::Format_ARGB32:
399 gccaps |= PorterDuff;
401 case QImage::Format_RGB32:
402 case QImage::Format_RGB444:
403 case QImage::Format_RGB555:
404 case QImage::Format_RGB666:
405 case QImage::Format_RGB888:
406 case QImage::Format_RGB16:
417 Destroys this paint engine.
419 QRasterPaintEngine::~QRasterPaintEngine()
421 Q_D(QRasterPaintEngine);
423 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
429 bool QRasterPaintEngine::begin(QPaintDevice *device)
431 Q_D(QRasterPaintEngine);
433 if (device->devType() == QInternal::Pixmap) {
434 QPixmap *pixmap = static_cast<QPixmap *>(device);
435 QPixmapData *pd = pixmap->pixmapData();
436 if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
437 d->device = pd->buffer();
442 // Make sure QPaintEngine::paintDevice() returns the proper device.
445 Q_ASSERT(d->device->devType() == QInternal::Image
446 || d->device->devType() == QInternal::CustomRaster);
448 d->systemStateChanged();
450 QRasterPaintEngineState *s = state();
451 ensureOutlineMapper();
452 d->outlineMapper->m_clip_rect = d->deviceRect;
454 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
455 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
456 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
457 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
459 d->rasterizer->setClipRect(d->deviceRect);
461 s->penData.init(d->rasterBuffer.data(), this);
462 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
463 s->stroker = &d->basicStroker;
464 d->basicStroker.setClipRect(d->deviceRect);
466 s->brushData.init(d->rasterBuffer.data(), this);
467 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
469 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
471 setDirty(DirtyBrushOrigin);
474 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
475 << ") devType:" << device->devType()
476 << "devRect:" << d->deviceRect;
478 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
482 #if defined(Q_OS_WIN)
483 d->isPlain45DegreeRotation = true;
487 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
488 #if defined(Q_OS_WIN)
489 else if (clearTypeFontsEnabled())
490 #elif defined (Q_WS_MAC)
491 else if (qt_applefontsmoothing_enabled)
496 QImage::Format format = static_cast<QImage *>(d->device)->format();
497 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
498 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
500 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
502 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
511 bool QRasterPaintEngine::end()
514 Q_D(QRasterPaintEngine);
515 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
517 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
527 void QRasterPaintEngine::releaseBuffer()
529 Q_D(QRasterPaintEngine);
530 d->rasterBuffer.reset(new QRasterBuffer);
536 QSize QRasterPaintEngine::size() const
538 Q_D(const QRasterPaintEngine);
539 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
546 void QRasterPaintEngine::saveBuffer(const QString &s) const
548 Q_D(const QRasterPaintEngine);
549 d->rasterBuffer->bufferImage().save(s, "PNG");
556 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
558 QRasterPaintEngineState *s = state();
559 // FALCON: get rid of this line, see drawImage call below.
561 QTransform::TransformationType txop = s->matrix.type();
565 case QTransform::TxNone:
566 s->flags.int_xform = true;
569 case QTransform::TxTranslate:
570 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
571 && qreal(int(s->matrix.dy())) == s->matrix.dy();
574 case QTransform::TxScale:
575 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
576 && qreal(int(s->matrix.dy())) == s->matrix.dy()
577 && qreal(int(s->matrix.m11())) == s->matrix.m11()
578 && qreal(int(s->matrix.m22())) == s->matrix.m22();
581 default: // shear / perspective...
582 s->flags.int_xform = false;
586 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
588 ensureOutlineMapper();
591 Q_D(QRasterPaintEngine);
592 d->isPlain45DegreeRotation = false;
593 if (txop >= QTransform::TxRotate) {
594 d->isPlain45DegreeRotation =
595 (qFuzzyIsNull(matrix.m11())
596 && qFuzzyIsNull(matrix.m12() - qreal(1))
597 && qFuzzyIsNull(matrix.m21() + qreal(1))
598 && qFuzzyIsNull(matrix.m22())
601 (qFuzzyIsNull(matrix.m11() + qreal(1))
602 && qFuzzyIsNull(matrix.m12())
603 && qFuzzyIsNull(matrix.m21())
604 && qFuzzyIsNull(matrix.m22() + qreal(1))
607 (qFuzzyIsNull(matrix.m11())
608 && qFuzzyIsNull(matrix.m12() + qreal(1))
609 && qFuzzyIsNull(matrix.m21() - qreal(1))
610 && qFuzzyIsNull(matrix.m22())
620 QRasterPaintEngineState::~QRasterPaintEngineState()
622 if (flags.has_clip_ownership)
627 QRasterPaintEngineState::QRasterPaintEngineState()
639 flags.fast_pen = true;
640 flags.antialiased = false;
641 flags.bilinear = false;
642 flags.fast_text = true;
643 flags.int_xform = true;
644 flags.tx_noshear = true;
645 flags.fast_images = true;
648 flags.has_clip_ownership = false;
653 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
658 , strokeFlags(s.strokeFlags)
659 , lastBrush(s.lastBrush)
660 , brushData(s.brushData)
661 , fillFlags(s.fillFlags)
662 , pixmapFlags(s.pixmapFlags)
663 , intOpacity(s.intOpacity)
667 , flag_bits(s.flag_bits)
669 brushData.tempImage = 0;
670 penData.tempImage = 0;
671 flags.has_clip_ownership = false;
677 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
679 QRasterPaintEngineState *s;
681 s = new QRasterPaintEngineState();
683 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
691 void QRasterPaintEngine::setState(QPainterState *s)
693 Q_D(QRasterPaintEngine);
694 QPaintEngineEx::setState(s);
695 d->rasterBuffer->compositionMode = s->composition_mode;
699 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
704 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
711 void QRasterPaintEngine::penChanged()
714 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
716 QRasterPaintEngineState *s = state();
717 s->strokeFlags |= DirtyPen;
718 s->dirty |= DirtyPen;
724 void QRasterPaintEngine::updatePen(const QPen &pen)
726 Q_D(QRasterPaintEngine);
727 QRasterPaintEngineState *s = state();
729 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
732 Qt::PenStyle pen_style = qpen_style(pen);
737 s->penData.clip = d->clip();
738 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
740 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
741 || pen.brush().transform().type() >= QTransform::TxNone) {
742 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
745 // Slightly ugly handling of an uncommon case... We need to change
746 // the pen because it is reused in draw_midpoint to decide dashed
748 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
749 pen_style = Qt::SolidLine;
750 s->lastPen.setStyle(Qt::SolidLine);
753 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
754 d->basicStroker.setCapStyle(qpen_capStyle(pen));
755 d->basicStroker.setMiterLimit(pen.miterLimit());
757 qreal penWidth = qpen_widthf(pen);
759 d->basicStroker.setStrokeWidth(1);
761 d->basicStroker.setStrokeWidth(penWidth);
763 if(pen_style == Qt::SolidLine) {
764 s->stroker = &d->basicStroker;
765 } else if (pen_style != Qt::NoPen) {
767 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
768 if (pen.isCosmetic()) {
769 d->dashStroker->setClipRect(d->deviceRect);
771 // ### I've seen this inverted devrect multiple places now...
772 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
773 d->dashStroker->setClipRect(clipRect);
775 d->dashStroker->setDashPattern(pen.dashPattern());
776 d->dashStroker->setDashOffset(pen.dashOffset());
777 s->stroker = d->dashStroker.data();
782 ensureState(); // needed because of tx_noshear...
783 s->flags.fast_pen = pen_style > Qt::NoPen
785 && ((pen.isCosmetic() && penWidth <= 1)
786 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
788 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
798 void QRasterPaintEngine::brushOriginChanged()
800 QRasterPaintEngineState *s = state();
802 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
805 s->fillFlags |= DirtyBrushOrigin;
812 void QRasterPaintEngine::brushChanged()
814 QRasterPaintEngineState *s = state();
816 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
818 s->fillFlags |= DirtyBrush;
827 void QRasterPaintEngine::updateBrush(const QBrush &brush)
830 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
832 Q_D(QRasterPaintEngine);
833 QRasterPaintEngineState *s = state();
834 // must set clip prior to setup, as setup uses it...
835 s->brushData.clip = d->clip();
836 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
837 if (s->fillFlags & DirtyTransform
838 || brush.transform().type() >= QTransform::TxNone)
839 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
840 s->lastBrush = brush;
844 void QRasterPaintEngine::updateOutlineMapper()
846 Q_D(QRasterPaintEngine);
847 d->outlineMapper->setMatrix(state()->matrix);
850 void QRasterPaintEngine::updateState()
852 QRasterPaintEngineState *s = state();
854 if (s->dirty & DirtyTransform)
855 updateMatrix(s->matrix);
857 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
858 const QPainter::CompositionMode mode = s->composition_mode;
859 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
860 && s->intOpacity == 256
861 && (mode == QPainter::CompositionMode_Source
862 || (mode == QPainter::CompositionMode_SourceOver
863 && qAlpha(s->penData.solid.color) == 255));
873 void QRasterPaintEngine::opacityChanged()
875 QRasterPaintEngineState *s = state();
878 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
881 s->fillFlags |= DirtyOpacity;
882 s->strokeFlags |= DirtyOpacity;
883 s->pixmapFlags |= DirtyOpacity;
884 s->dirty |= DirtyOpacity;
885 s->intOpacity = (int) (s->opacity * 256);
891 void QRasterPaintEngine::compositionModeChanged()
893 Q_D(QRasterPaintEngine);
894 QRasterPaintEngineState *s = state();
897 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
900 s->fillFlags |= DirtyCompositionMode;
901 s->dirty |= DirtyCompositionMode;
903 s->strokeFlags |= DirtyCompositionMode;
904 d->rasterBuffer->compositionMode = s->composition_mode;
906 d->recalculateFastImages();
912 void QRasterPaintEngine::renderHintsChanged()
914 QRasterPaintEngineState *s = state();
917 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
920 bool was_aa = s->flags.antialiased;
921 bool was_bilinear = s->flags.bilinear;
923 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
924 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
926 if (was_aa != s->flags.antialiased)
927 s->strokeFlags |= DirtyHints;
929 if (was_bilinear != s->flags.bilinear) {
930 s->strokeFlags |= DirtyPen;
931 s->fillFlags |= DirtyBrush;
934 Q_D(QRasterPaintEngine);
935 d->recalculateFastImages();
941 void QRasterPaintEngine::transformChanged()
943 QRasterPaintEngineState *s = state();
946 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
949 s->fillFlags |= DirtyTransform;
950 s->strokeFlags |= DirtyTransform;
952 s->dirty |= DirtyTransform;
954 Q_D(QRasterPaintEngine);
955 d->recalculateFastImages();
961 void QRasterPaintEngine::clipEnabledChanged()
963 QRasterPaintEngineState *s = state();
966 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
970 s->clip->enabled = s->clipEnabled;
971 s->fillFlags |= DirtyClipEnabled;
972 s->strokeFlags |= DirtyClipEnabled;
973 s->pixmapFlags |= DirtyClipEnabled;
977 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
979 SrcOverBlendFunc func,
984 if (alpha == 0 || !clip.isValid())
987 Q_ASSERT(img.depth() >= 8);
989 int srcBPL = img.bytesPerLine();
990 const uchar *srcBits = img.bits();
991 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
992 int iw = img.width();
993 int ih = img.height();
998 // Adjust the image according to the source offset...
999 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1002 // adapt the x parameters
1003 int x = qRound(pt.x());
1005 int cx2 = clip.x() + clip.width();
1008 srcBits += srcSize * d;
1013 int d = x + iw - cx2;
1019 // adapt the y paremeters...
1021 int cy2 = clip.y() + clip.height();
1022 int y = qRound(pt.y());
1025 srcBits += srcBPL * d;
1030 int d = y + ih - cy2;
1036 // call the blend function...
1037 int dstSize = rasterBuffer->bytesPerPixel();
1038 int dstBPL = rasterBuffer->bytesPerLine();
1039 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1046 void QRasterPaintEnginePrivate::systemStateChanged()
1048 QRect clipRect(0, 0,
1049 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1050 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1052 if (!systemClip.isEmpty()) {
1053 QRegion clippedDeviceRgn = systemClip & clipRect;
1054 deviceRect = clippedDeviceRgn.boundingRect();
1055 baseClip->setClipRegion(clippedDeviceRgn);
1057 deviceRect = clipRect;
1058 baseClip->setClipRect(deviceRect);
1060 #ifdef QT_DEBUG_DRAW
1061 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1064 exDeviceRect = deviceRect;
1066 Q_Q(QRasterPaintEngine);
1067 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1068 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1069 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1072 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1074 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1077 Q_Q(QRasterPaintEngine);
1078 bool bilinear = q->state()->flags.bilinear;
1080 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1081 spanData->setupMatrix(b.transform() * m, bilinear);
1083 if (m.type() <= QTransform::TxTranslate) {
1084 // specialize setupMatrix for translation matrices
1085 // to avoid needless matrix inversion
1093 spanData->dx = -m.dx();
1094 spanData->dy = -m.dy();
1095 spanData->txop = m.type();
1096 spanData->bilinear = bilinear;
1097 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1098 spanData->adjustSpanMethods();
1100 spanData->setupMatrix(m, bilinear);
1105 // #define QT_CLIPPING_RATIOS
1107 #ifdef QT_CLIPPING_RATIOS
1112 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1114 if (d->clip()->hasRectClip)
1116 if (d->clip()->hasRegionClip)
1120 if ((totalClips % 5000) == 0) {
1121 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1122 rectClips * 100.0 / (qreal) totalClips,
1123 regionClips * 100.0 / (qreal) totalClips,
1124 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1133 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1135 if (s->flags.has_clip_ownership)
1138 s->flags.has_clip_ownership = false;
1141 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1143 s->fillFlags |= QPaintEngine::DirtyClipPath;
1144 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1145 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1147 d->solid_color_filler.clip = d->clip();
1148 d->solid_color_filler.adjustSpanMethods();
1150 #ifdef QT_DEBUG_DRAW
1151 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1160 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1162 #ifdef QT_DEBUG_DRAW
1163 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1165 if (path.elements()) {
1166 for (int i=0; i<path.elementCount(); ++i) {
1167 qDebug() << " - " << path.elements()[i]
1168 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1171 for (int i=0; i<path.elementCount(); ++i) {
1172 qDebug() << " ---- "
1173 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1178 Q_D(QRasterPaintEngine);
1179 QRasterPaintEngineState *s = state();
1181 const qreal *points = path.points();
1182 const QPainterPath::ElementType *types = path.elements();
1184 // There are some cases that are not supported by clip(QRect)
1185 if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
1186 || s->clip->hasRectClip || s->clip->hasRegionClip)) {
1187 if (s->matrix.type() <= QTransform::TxScale
1188 && ((path.shape() == QVectorPath::RectangleHint)
1189 || (isRect(points, path.elementCount())
1190 && (!types || (types[0] == QPainterPath::MoveToElement
1191 && types[1] == QPainterPath::LineToElement
1192 && types[2] == QPainterPath::LineToElement
1193 && types[3] == QPainterPath::LineToElement))))) {
1194 #ifdef QT_DEBUG_DRAW
1195 qDebug() << " --- optimizing vector clip to rect clip...";
1198 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1199 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1204 if (op == Qt::NoClip) {
1205 qrasterpaintengine_state_setNoClip(s);
1208 QClipData *base = d->baseClip.data();
1210 // Intersect with current clip when available...
1211 if (op == Qt::IntersectClip && s->clip)
1214 // We always intersect, except when there is nothing to
1215 // intersect with, in which case we simplify the operation to
1217 Qt::ClipOperation isectOp = Qt::IntersectClip;
1219 isectOp = Qt::ReplaceClip;
1221 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1222 newClip->initialize();
1223 ClipData clipData = { base, newClip, isectOp };
1224 ensureOutlineMapper();
1225 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1229 if (op == Qt::UniteClip) {
1231 QClipData *result = new QClipData(d->rasterBuffer->height());
1232 QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
1233 qt_merge_clip(current, newClip, result);
1241 if (s->flags.has_clip_ownership)
1245 s->flags.has_clip_ownership = true;
1247 qrasterpaintengine_dirty_clip(d, s);
1255 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1257 #ifdef QT_DEBUG_DRAW
1258 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1261 QRasterPaintEngineState *s = state();
1263 if (op == Qt::NoClip) {
1264 qrasterpaintengine_state_setNoClip(s);
1266 } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
1267 QPaintEngineEx::clip(rect, op);
1270 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1271 QPaintEngineEx::clip(rect, op);
1277 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1279 Q_D(QRasterPaintEngine);
1280 QRect clipRect = r & d->deviceRect;
1281 QRasterPaintEngineState *s = state();
1283 if (op == Qt::ReplaceClip || s->clip == 0) {
1285 // No current clip, hence we intersect with sysclip and be
1287 QRegion clipRegion = systemClip();
1288 QClipData *clip = new QClipData(d->rasterBuffer->height());
1290 if (clipRegion.isEmpty())
1291 clip->setClipRect(clipRect);
1293 clip->setClipRegion(clipRegion & clipRect);
1295 if (s->flags.has_clip_ownership)
1299 s->clip->enabled = true;
1300 s->flags.has_clip_ownership = true;
1302 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1303 QClipData *base = s->clip;
1306 if (base->hasRectClip || base->hasRegionClip) {
1307 if (!s->flags.has_clip_ownership) {
1308 s->clip = new QClipData(d->rasterBuffer->height());
1309 s->flags.has_clip_ownership = true;
1311 if (base->hasRectClip)
1312 s->clip->setClipRect(base->clipRect & clipRect);
1314 s->clip->setClipRegion(base->clipRegion & clipRect);
1315 s->clip->enabled = true;
1323 qrasterpaintengine_dirty_clip(d, s);
1331 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1333 #ifdef QT_DEBUG_DRAW
1334 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1337 Q_D(QRasterPaintEngine);
1339 if (region.rectCount() == 1) {
1340 clip(region.boundingRect(), op);
1344 QRasterPaintEngineState *s = state();
1345 const QClipData *clip = d->clip();
1346 const QClipData *baseClip = d->baseClip.data();
1348 if (op == Qt::NoClip) {
1349 qrasterpaintengine_state_setNoClip(s);
1350 } else if (s->matrix.type() > QTransform::TxScale
1351 || op == Qt::UniteClip
1352 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1353 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1354 QPaintEngineEx::clip(region, op);
1356 const QClipData *curClip;
1359 if (op == Qt::IntersectClip)
1364 if (s->flags.has_clip_ownership) {
1368 newClip = new QClipData(d->rasterBuffer->height());
1370 s->flags.has_clip_ownership = true;
1373 QRegion r = s->matrix.map(region);
1374 if (curClip->hasRectClip)
1375 newClip->setClipRegion(r & curClip->clipRect);
1376 else if (curClip->hasRegionClip)
1377 newClip->setClipRegion(r & curClip->clipRegion);
1379 qrasterpaintengine_dirty_clip(d, s);
1386 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1388 #ifdef QT_DEBUG_DRAW
1389 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1392 if (!fillData->blend)
1395 Q_D(QRasterPaintEngine);
1397 const QRectF controlPointRect = path.controlPointRect();
1399 QRasterPaintEngineState *s = state();
1400 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1401 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1402 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1403 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1404 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1405 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1407 if (!s->flags.antialiased && !do_clip) {
1408 d->initializeRasterizer(fillData);
1409 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1413 ensureOutlineMapper();
1414 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1417 static void fillRect_normalized(const QRect &r, QSpanData *data,
1418 QRasterPaintEnginePrivate *pe)
1422 bool rectClipped = true;
1425 x1 = qMax(r.x(), data->clip->xmin);
1426 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1427 y1 = qMax(r.y(), data->clip->ymin);
1428 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1429 rectClipped = data->clip->hasRectClip;
1432 x1 = qMax(r.x(), pe->deviceRect.x());
1433 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1434 y1 = qMax(r.y(), pe->deviceRect.y());
1435 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1437 x1 = qMax(r.x(), 0);
1438 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1439 y1 = qMax(r.y(), 0);
1440 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1443 if (x2 <= x1 || y2 <= y1)
1446 const int width = x2 - x1;
1447 const int height = y2 - y1;
1449 bool isUnclipped = rectClipped
1450 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1452 if (pe && isUnclipped) {
1453 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1455 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1456 || (mode == QPainter::CompositionMode_SourceOver
1457 && qAlpha(data->solid.color) == 255)))
1459 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1465 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1467 const int nspans = 256;
1468 QT_FT_Span spans[nspans];
1470 Q_ASSERT(data->blend);
1473 int n = qMin(nspans, y2 - y);
1477 spans[i].len = width;
1479 spans[i].coverage = 255;
1483 blend(n, spans, data);
1491 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1493 #ifdef QT_DEBUG_DRAW
1494 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1496 Q_D(QRasterPaintEngine);
1498 QRasterPaintEngineState *s = state();
1502 if (s->brushData.blend) {
1503 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1504 const QRect *r = rects;
1505 const QRect *lastRect = rects + rectCount;
1507 int offset_x = int(s->matrix.dx());
1508 int offset_y = int(s->matrix.dy());
1509 while (r < lastRect) {
1510 QRect rect = r->normalized();
1511 QRect rr = rect.translated(offset_x, offset_y);
1512 fillRect_normalized(rr, &s->brushData, d);
1516 QRectVectorPath path;
1517 for (int i=0; i<rectCount; ++i) {
1519 fill(path, s->brush);
1525 if (s->penData.blend) {
1526 QRectVectorPath path;
1527 if (s->flags.fast_pen) {
1528 QCosmeticStroker stroker(s, d->deviceRect);
1529 for (int i = 0; i < rectCount; ++i) {
1531 stroker.drawPath(path);
1534 for (int i = 0; i < rectCount; ++i) {
1536 stroke(path, s->pen);
1545 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1547 #ifdef QT_DEBUG_DRAW
1548 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1550 #ifdef QT_FAST_SPANS
1551 Q_D(QRasterPaintEngine);
1553 QRasterPaintEngineState *s = state();
1556 if (s->flags.tx_noshear) {
1558 if (s->brushData.blend) {
1559 d->initializeRasterizer(&s->brushData);
1560 for (int i = 0; i < rectCount; ++i) {
1561 const QRectF &rect = rects[i].normalized();
1564 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1565 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1566 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1571 if (s->penData.blend) {
1572 QRectVectorPath path;
1573 if (s->flags.fast_pen) {
1574 QCosmeticStroker stroker(s, d->deviceRect);
1575 for (int i = 0; i < rectCount; ++i) {
1577 stroker.drawPath(path);
1580 for (int i = 0; i < rectCount; ++i) {
1582 QPaintEngineEx::stroke(path, s->lastPen);
1589 #endif // QT_FAST_SPANS
1590 QPaintEngineEx::drawRects(rects, rectCount);
1597 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1599 Q_D(QRasterPaintEngine);
1600 QRasterPaintEngineState *s = state();
1603 if (!s->penData.blend)
1606 if (s->flags.fast_pen) {
1607 QCosmeticStroker stroker(s, d->deviceRect);
1608 stroker.drawPath(path);
1609 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1610 qreal width = s->lastPen.isCosmetic()
1611 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1612 : qpen_widthf(s->lastPen) * s->txscale;
1614 qreal dashOffset = s->lastPen.dashOffset();
1616 qreal patternLength = 0;
1617 const QVector<qreal> pattern = s->lastPen.dashPattern();
1618 for (int i = 0; i < pattern.size(); ++i)
1619 patternLength += pattern.at(i);
1621 if (patternLength > 0) {
1622 int n = qFloor(dashOffset / patternLength);
1623 dashOffset -= n * patternLength;
1624 while (dashOffset >= pattern.at(dashIndex)) {
1625 dashOffset -= pattern.at(dashIndex);
1626 if (++dashIndex >= pattern.size())
1632 Q_D(QRasterPaintEngine);
1633 d->initializeRasterizer(&s->penData);
1634 int lineCount = path.elementCount() / 2;
1635 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1637 for (int i = 0; i < lineCount; ++i) {
1638 if (lines[i].p1() == lines[i].p2()) {
1639 if (s->lastPen.capStyle() != Qt::FlatCap) {
1640 QPointF p = lines[i].p1();
1641 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1642 QPointF(p.x() + width*0.5, p.y())));
1643 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1648 const QLineF line = s->matrix.map(lines[i]);
1649 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1650 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1651 width / line.length(),
1652 s->lastPen.capStyle() == Qt::SquareCap);
1654 d->rasterizeLine_dashed(line, width,
1655 &dashIndex, &dashOffset, &inDash);
1660 QPaintEngineEx::stroke(path, pen);
1663 static inline QRect toNormalizedFillRect(const QRectF &rect)
1665 int x1 = qRound(rect.x());
1666 int y1 = qRound(rect.y());
1667 int x2 = qRound(rect.right());
1668 int y2 = qRound(rect.bottom());
1675 return QRect(x1, y1, x2 - x1, y2 - y1);
1681 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1685 #ifdef QT_DEBUG_DRAW
1686 QRectF rf = path.controlPointRect();
1687 qDebug() << "QRasterPaintEngine::fill(): "
1688 << "size=" << path.elementCount()
1689 << ", hints=" << hex << path.hints()
1693 Q_D(QRasterPaintEngine);
1694 QRasterPaintEngineState *s = state();
1697 if (!s->brushData.blend)
1700 if (path.shape() == QVectorPath::RectangleHint) {
1701 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1702 const qreal *p = path.points();
1703 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1704 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1705 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1709 if (s->flags.tx_noshear) {
1710 d->initializeRasterizer(&s->brushData);
1711 // ### Is normalizing really necessary here?
1712 const qreal *p = path.points();
1713 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1715 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1716 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1717 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1723 // ### Optimize for non transformed ellipses and rectangles...
1724 QRectF cpRect = path.controlPointRect();
1725 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1726 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1729 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1730 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1731 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1732 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1734 // ### Falonc: implement....
1735 // if (!s->flags.antialiased && !do_clip) {
1736 // d->initializeRasterizer(&s->brushData);
1737 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1741 ensureOutlineMapper();
1742 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1745 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1747 Q_D(QRasterPaintEngine);
1748 QRasterPaintEngineState *s = state();
1750 if (!s->flags.antialiased) {
1751 uint txop = s->matrix.type();
1752 if (txop == QTransform::TxNone) {
1753 fillRect_normalized(toNormalizedFillRect(r), data, d);
1755 } else if (txop == QTransform::TxTranslate) {
1756 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1757 fillRect_normalized(rr, data, d);
1759 } else if (txop == QTransform::TxScale) {
1760 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1761 fillRect_normalized(rr, data, d);
1766 if (s->flags.tx_noshear) {
1767 d->initializeRasterizer(data);
1768 QRectF nr = r.normalized();
1769 if (!nr.isEmpty()) {
1770 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1771 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1772 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1779 ensureOutlineMapper();
1780 fillPath(path, data);
1786 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1788 #ifdef QT_DEBUG_DRAW
1789 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1791 QRasterPaintEngineState *s = state();
1794 if (!s->brushData.blend)
1797 fillRect(r, &s->brushData);
1803 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1805 #ifdef QT_DEBUG_DRAW
1806 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1808 Q_D(QRasterPaintEngine);
1809 QRasterPaintEngineState *s = state();
1811 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1812 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1813 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1816 d->solid_color_filler.clip = d->clip();
1817 d->solid_color_filler.adjustSpanMethods();
1818 fillRect(r, &d->solid_color_filler);
1821 static inline bool isAbove(const QPointF *a, const QPointF *b)
1823 return a->y() < b->y();
1826 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1831 Q_ASSERT(pointCount >= 2);
1833 QVector<const QPointF *> sorted;
1834 sorted.reserve(pointCount);
1836 upper->reserve(pointCount * 3 / 4);
1837 lower->reserve(pointCount * 3 / 4);
1839 for (int i = 0; i < pointCount; ++i)
1840 sorted << points + i;
1842 qSort(sorted.begin(), sorted.end(), isAbove);
1844 qreal splitY = sorted.at(sorted.size() / 2)->y();
1846 const QPointF *end = points + pointCount;
1847 const QPointF *last = end - 1;
1849 QVector<QPointF> *bin[2] = { upper, lower };
1851 for (const QPointF *p = points; p < end; ++p) {
1852 int side = p->y() < splitY;
1853 int lastSide = last->y() < splitY;
1855 if (side != lastSide) {
1856 if (qFuzzyCompare(p->y(), splitY)) {
1857 bin[!side]->append(*p);
1858 } else if (qFuzzyCompare(last->y(), splitY)) {
1859 bin[side]->append(*last);
1861 QPointF delta = *p - *last;
1862 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1864 bin[0]->append(intersection);
1865 bin[1]->append(intersection);
1869 bin[side]->append(*p);
1874 // give up if we couldn't reduce the point count
1875 return upper->size() < pointCount && lower->size() < pointCount;
1881 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1883 Q_D(QRasterPaintEngine);
1884 QRasterPaintEngineState *s = state();
1886 const int maxPoints = 0xffff;
1888 // max amount of points that raster engine can reliably handle
1889 if (pointCount > maxPoints) {
1890 QVector<QPointF> upper, lower;
1892 if (splitPolygon(points, pointCount, &upper, &lower)) {
1893 fillPolygon(upper.constData(), upper.size(), mode);
1894 fillPolygon(lower.constData(), lower.size(), mode);
1896 qWarning("Polygon too complex for filling.");
1901 // Compose polygon fill..,
1902 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1903 ensureOutlineMapper();
1904 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1907 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1909 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1915 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1917 Q_D(QRasterPaintEngine);
1918 QRasterPaintEngineState *s = state();
1920 #ifdef QT_DEBUG_DRAW
1921 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1922 for (int i=0; i<pointCount; ++i)
1923 qDebug() << " - " << points[i];
1925 Q_ASSERT(pointCount >= 2);
1927 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1928 QRectF r(points[0], points[2]);
1934 if (mode != PolylineMode) {
1937 if (s->brushData.blend) {
1938 fillPolygon(points, pointCount, mode);
1942 // Do the outline...
1943 if (s->penData.blend) {
1944 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1945 if (s->flags.fast_pen) {
1946 QCosmeticStroker stroker(s, d->deviceRect);
1947 stroker.drawPath(vp);
1949 QPaintEngineEx::stroke(vp, s->lastPen);
1957 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1959 Q_D(QRasterPaintEngine);
1960 QRasterPaintEngineState *s = state();
1962 #ifdef QT_DEBUG_DRAW
1963 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1964 for (int i=0; i<pointCount; ++i)
1965 qDebug() << " - " << points[i];
1967 Q_ASSERT(pointCount >= 2);
1968 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1969 QRect r(points[0].x(),
1971 points[2].x() - points[0].x(),
1972 points[2].y() - points[0].y());
1980 if (mode != PolylineMode) {
1982 if (s->brushData.blend) {
1983 // Compose polygon fill..,
1984 ensureOutlineMapper();
1985 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1986 d->outlineMapper->moveTo(*points);
1987 const QPoint *p = points;
1988 const QPoint *ep = points + pointCount - 1;
1990 d->outlineMapper->lineTo(*(++p));
1992 d->outlineMapper->endOutline();
1995 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1997 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2001 // Do the outline...
2002 if (s->penData.blend) {
2003 int count = pointCount * 2;
2004 QVarLengthArray<qreal> fpoints(count);
2006 for (int i=0; i<count; i+=2) {
2007 fpoints[i] = ((int *) points)[i+1];
2008 fpoints[i+1] = ((int *) points)[i];
2011 for (int i=0; i<count; ++i)
2012 fpoints[i] = ((int *) points)[i];
2014 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2016 if (s->flags.fast_pen) {
2017 QCosmeticStroker stroker(s, d->deviceRect);
2018 stroker.drawPath(vp);
2020 QPaintEngineEx::stroke(vp, s->lastPen);
2028 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2030 #ifdef QT_DEBUG_DRAW
2031 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2034 QPixmapData *pd = pixmap.pixmapData();
2035 if (pd->classId() == QPixmapData::RasterClass) {
2036 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2037 if (image.depth() == 1) {
2038 Q_D(QRasterPaintEngine);
2039 QRasterPaintEngineState *s = state();
2040 if (s->matrix.type() <= QTransform::TxTranslate) {
2042 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2044 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2047 QRasterPaintEngine::drawImage(pos, image);
2050 const QImage image = pixmap.toImage();
2051 if (pixmap.depth() == 1) {
2052 Q_D(QRasterPaintEngine);
2053 QRasterPaintEngineState *s = state();
2054 if (s->matrix.type() <= QTransform::TxTranslate) {
2056 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2058 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2061 QRasterPaintEngine::drawImage(pos, image);
2069 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2071 #ifdef QT_DEBUG_DRAW
2072 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2075 QPixmapData* pd = pixmap.pixmapData();
2076 if (pd->classId() == QPixmapData::RasterClass) {
2077 const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
2078 if (image.depth() == 1) {
2079 Q_D(QRasterPaintEngine);
2080 QRasterPaintEngineState *s = state();
2081 if (s->matrix.type() <= QTransform::TxTranslate
2082 && r.size() == sr.size()
2083 && r.size() == pixmap.size()) {
2085 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2088 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2091 drawImage(r, image, sr);
2094 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2095 const QImage image = pd->toImage(clippedSource);
2096 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2097 if (image.depth() == 1) {
2098 Q_D(QRasterPaintEngine);
2099 QRasterPaintEngineState *s = state();
2100 if (s->matrix.type() <= QTransform::TxTranslate
2101 && r.size() == sr.size()
2102 && r.size() == pixmap.size()) {
2104 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2107 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2110 drawImage(r, image, translatedSource);
2115 // assumes that rect has positive width and height
2116 static inline const QRect toRect_normalized(const QRectF &rect)
2118 const int x = qRound(rect.x());
2119 const int y = qRound(rect.y());
2120 const int w = int(rect.width() + qreal(0.5));
2121 const int h = int(rect.height() + qreal(0.5));
2123 return QRect(x, y, w, h);
2126 static inline int fast_ceil_positive(const qreal &v)
2128 const int iv = int(v);
2135 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2137 const int xmin = int(rect.x());
2138 const int xmax = int(fast_ceil_positive(rect.right()));
2139 const int ymin = int(rect.y());
2140 const int ymax = int(fast_ceil_positive(rect.bottom()));
2141 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2147 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2149 #ifdef QT_DEBUG_DRAW
2150 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2153 Q_D(QRasterPaintEngine);
2154 QRasterPaintEngineState *s = state();
2156 if (s->matrix.type() > QTransform::TxTranslate) {
2157 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2159 QRectF(0, 0, img.width(), img.height()));
2162 const QClipData *clip = d->clip();
2163 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2165 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2166 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2169 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2171 } else if (clip->hasRectClip) {
2172 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2180 d->image_filler.clip = clip;
2181 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2182 if (!d->image_filler.blend)
2184 d->image_filler.dx = -pt.x();
2185 d->image_filler.dy = -pt.y();
2186 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2188 fillRect_normalized(rr, &d->image_filler, d);
2193 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2195 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2206 inline RotationType qRotationType(const QTransform &transform)
2208 QTransform::TransformationType type = transform.type();
2210 if (type > QTransform::TxRotate)
2213 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2214 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2217 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2218 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2221 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2222 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2228 inline bool isPixelAligned(const QRectF &rect) {
2229 return QRectF(rect.toRect()) == rect;
2236 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2237 Qt::ImageConversionFlags)
2239 #ifdef QT_DEBUG_DRAW
2240 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2246 Q_D(QRasterPaintEngine);
2247 QRasterPaintEngineState *s = state();
2248 int sr_l = qFloor(sr.left());
2249 int sr_r = qCeil(sr.right()) - 1;
2250 int sr_t = qFloor(sr.top());
2251 int sr_b = qCeil(sr.bottom()) - 1;
2253 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2254 QTransform old = s->matrix;
2256 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2257 QRgb color = img.pixel(sr_l, sr_t);
2258 switch (img.format()) {
2259 case QImage::Format_ARGB32_Premultiplied:
2260 case QImage::Format_ARGB8565_Premultiplied:
2261 case QImage::Format_ARGB6666_Premultiplied:
2262 case QImage::Format_ARGB8555_Premultiplied:
2263 case QImage::Format_ARGB4444_Premultiplied:
2264 // Combine premultiplied color with the opacity set on the painter.
2265 d->solid_color_filler.solid.color =
2266 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2267 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2270 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2274 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2275 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2279 d->solid_color_filler.clip = d->clip();
2280 d->solid_color_filler.adjustSpanMethods();
2281 fillRect(r, &d->solid_color_filler);
2287 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2289 const QClipData *clip = d->clip();
2291 if (s->matrix.type() > QTransform::TxTranslate
2293 && (!clip || clip->hasRectClip)
2294 && s->intOpacity == 256
2295 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2296 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2297 && d->rasterBuffer->format == img.format()
2298 && (d->rasterBuffer->format == QImage::Format_RGB16
2299 || d->rasterBuffer->format == QImage::Format_RGB32
2300 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2301 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2303 RotationType rotationType = qRotationType(s->matrix);
2305 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2306 QRectF transformedTargetRect = s->matrix.mapRect(r);
2308 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2309 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2311 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2312 if (clippedTransformedTargetRect.isNull())
2315 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2317 QRect clippedSourceRect
2318 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2319 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2321 uint dbpl = d->rasterBuffer->bytesPerLine();
2322 uint sbpl = img.bytesPerLine();
2324 uchar *dst = d->rasterBuffer->buffer();
2325 uint bpp = img.depth() >> 3;
2327 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2328 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2330 uint cw = clippedSourceRect.width();
2331 uint ch = clippedSourceRect.height();
2333 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2340 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2342 QRectF targetBounds = s->matrix.mapRect(r);
2343 bool exceedsPrecision = targetBounds.width() > 0xffff
2344 || targetBounds.height() > 0xffff;
2346 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2347 if (s->matrix.type() > QTransform::TxScale) {
2348 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2349 if (func && (!clip || clip->hasRectClip)) {
2350 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2351 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2352 s->matrix, s->intOpacity);
2356 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2357 if (func && (!clip || clip->hasRectClip)) {
2358 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2359 img.bits(), img.bytesPerLine(),
2360 qt_mapRect_non_normalizing(r, s->matrix), sr,
2361 !clip ? d->deviceRect : clip->clipRect,
2368 QTransform copy = s->matrix;
2369 copy.translate(r.x(), r.y());
2371 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2372 copy.translate(-sr.x(), -sr.y());
2374 d->image_filler_xform.clip = clip;
2375 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2376 if (!d->image_filler_xform.blend)
2378 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2380 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2381 QRectF rr = s->matrix.mapRect(r);
2383 const int x1 = qRound(rr.x());
2384 const int y1 = qRound(rr.y());
2385 const int x2 = qRound(rr.right());
2386 const int y2 = qRound(rr.bottom());
2388 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2392 #ifdef QT_FAST_SPANS
2394 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2395 d->initializeRasterizer(&d->image_filler_xform);
2396 d->rasterizer->setAntialiased(s->flags.antialiased);
2398 const QRectF &rect = r.normalized();
2399 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2400 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2402 if (s->flags.tx_noshear)
2403 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2405 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2411 QTransform m = s->matrix;
2412 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2413 m.m21(), m.m22(), m.m23(),
2414 m.m31(), m.m32(), m.m33());
2415 fillPath(path, &d->image_filler_xform);
2418 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2419 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2421 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2423 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2425 } else if (clip->hasRectClip) {
2426 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2432 d->image_filler.clip = clip;
2433 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2434 if (!d->image_filler.blend)
2436 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2437 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2440 rr.translate(s->matrix.dx(), s->matrix.dy());
2442 const int x1 = qRound(rr.x());
2443 const int y1 = qRound(rr.y());
2444 const int x2 = qRound(rr.right());
2445 const int y2 = qRound(rr.bottom());
2447 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2454 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2456 #ifdef QT_DEBUG_DRAW
2457 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2459 Q_D(QRasterPaintEngine);
2460 QRasterPaintEngineState *s = state();
2464 QPixmapData *pd = pixmap.pixmapData();
2465 if (pd->classId() == QPixmapData::RasterClass) {
2466 image = static_cast<QRasterPixmapData *>(pd)->image;
2468 image = pixmap.toImage();
2471 if (image.depth() == 1)
2472 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2474 if (s->matrix.type() > QTransform::TxTranslate) {
2475 QTransform copy = s->matrix;
2476 copy.translate(r.x(), r.y());
2477 copy.translate(-sr.x(), -sr.y());
2478 d->image_filler_xform.clip = d->clip();
2479 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2480 if (!d->image_filler_xform.blend)
2482 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2484 #ifdef QT_FAST_SPANS
2486 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2487 d->initializeRasterizer(&d->image_filler_xform);
2488 d->rasterizer->setAntialiased(s->flags.antialiased);
2490 const QRectF &rect = r.normalized();
2491 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2492 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2493 if (s->flags.tx_noshear)
2494 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2496 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2502 fillPath(path, &d->image_filler_xform);
2504 d->image_filler.clip = d->clip();
2506 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2507 if (!d->image_filler.blend)
2509 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2510 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2513 rr.translate(s->matrix.dx(), s->matrix.dy());
2514 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2520 static inline bool monoVal(const uchar* s, int x)
2522 return (s[x>>3] << (x&7)) & 0x80;
2528 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2530 Q_D(QRasterPaintEngine);
2531 QRasterPaintEngineState *s = state();
2533 if (!s->penData.blend)
2536 QRasterBuffer *rb = d->rasterBuffer.data();
2538 const QRect rect(rx, ry, w, h);
2539 const QClipData *clip = d->clip();
2540 bool unclipped = false;
2542 // inlined QRect::intersects
2543 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2544 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2546 if (clip->hasRectClip) {
2547 unclipped = rx > clip->xmin
2548 && rx + w < clip->xmax
2550 && ry + h < clip->ymax;
2556 // inlined QRect::intersects
2557 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2558 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2562 // inlined QRect::contains
2563 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2564 && rect.top() >= 0 && rect.bottom() < rb->height();
2566 unclipped = contains && d->isUnclipped_normalized(rect);
2569 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2570 const uchar * scanline = static_cast<const uchar *>(src);
2572 if (s->flags.fast_text) {
2575 if (s->penData.bitmapBlit) {
2576 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2577 scanline, w, h, bpl);
2580 } else if (depth == 8) {
2581 if (s->penData.alphamapBlit) {
2582 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2583 scanline, w, h, bpl, 0);
2586 } else if (depth == 32) {
2587 // (A)RGB Alpha mask where the alpha component is not used.
2588 if (s->penData.alphaRGBBlit) {
2589 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2590 (const uint *) scanline, w, h, bpl / 4, 0);
2594 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2595 // (A)RGB Alpha mask where the alpha component is not used.
2597 int nx = qMax(0, rx);
2598 int ny = qMax(0, ry);
2600 // Move scanline pointer to compensate for moved x and y
2601 int xdiff = nx - rx;
2602 int ydiff = ny - ry;
2603 scanline += ydiff * bpl;
2604 scanline += xdiff * (depth == 32 ? 4 : 1);
2609 if (nx + w > d->rasterBuffer->width())
2610 w = d->rasterBuffer->width() - nx;
2611 if (ny + h > d->rasterBuffer->height())
2612 h = d->rasterBuffer->height() - ny;
2617 if (depth == 8 && s->penData.alphamapBlit) {
2618 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2619 scanline, w, h, bpl, clip);
2620 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2621 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2622 (const uint *) scanline, w, h, bpl / 4, clip);
2637 scanline += bpl * y0;
2641 w = qMin(w, rb->width() - qMax(0, rx));
2642 h = qMin(h, rb->height() - qMax(0, ry));
2644 if (w <= 0 || h <= 0)
2647 const int NSPANS = 256;
2648 QSpan spans[NSPANS];
2651 const int x1 = x0 + w;
2652 const int y1 = y0 + h;
2655 for (int y = y0; y < y1; ++y) {
2656 for (int x = x0; x < x1; ) {
2657 if (!monoVal(scanline, x)) {
2662 if (current == NSPANS) {
2663 blend(current, spans, &s->penData);
2666 spans[current].x = x + rx;
2667 spans[current].y = y + ry;
2668 spans[current].coverage = 255;
2671 // extend span until we find a different one.
2672 while (x < x1 && monoVal(scanline, x)) {
2676 spans[current].len = len;
2681 } else if (depth == 8) {
2682 for (int y = y0; y < y1; ++y) {
2683 for (int x = x0; x < x1; ) {
2684 // Skip those with 0 coverage
2685 if (scanline[x] == 0) {
2690 if (current == NSPANS) {
2691 blend(current, spans, &s->penData);
2694 int coverage = scanline[x];
2695 spans[current].x = x + rx;
2696 spans[current].y = y + ry;
2697 spans[current].coverage = coverage;
2701 // extend span until we find a different one.
2702 while (x < x1 && scanline[x] == coverage) {
2706 spans[current].len = len;
2711 } else { // 32-bit alpha...
2712 uint *sl = (uint *) src;
2713 for (int y = y0; y < y1; ++y) {
2714 for (int x = x0; x < x1; ) {
2715 // Skip those with 0 coverage
2716 if ((sl[x] & 0x00ffffff) == 0) {
2721 if (current == NSPANS) {
2722 blend(current, spans, &s->penData);
2725 uint rgbCoverage = sl[x];
2726 int coverage = qGreen(rgbCoverage);
2727 spans[current].x = x + rx;
2728 spans[current].y = y + ry;
2729 spans[current].coverage = coverage;
2733 // extend span until we find a different one.
2734 while (x < x1 && sl[x] == rgbCoverage) {
2738 spans[current].len = len;
2741 sl += bpl / sizeof(uint);
2744 // qDebug() << "alphaPenBlt: num spans=" << current
2745 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2746 // Call span func for current set of spans.
2748 blend(current, spans, &s->penData);
2751 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2752 const QFixedPoint *positions, QFontEngine *fontEngine)
2754 Q_D(QRasterPaintEngine);
2755 QRasterPaintEngineState *s = state();
2757 #if !defined(QT_NO_FREETYPE)
2758 if (fontEngine->type() == QFontEngine::Freetype) {
2759 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2760 QFontEngineFT::GlyphFormat neededFormat =
2761 painter()->device()->devType() == QInternal::Widget
2762 ? fe->defaultGlyphFormat()
2763 : QFontEngineFT::Format_A8;
2765 if (d_func()->mono_surface
2766 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2768 neededFormat = QFontEngineFT::Format_Mono;
2770 if (neededFormat == QFontEngineFT::Format_None)
2771 neededFormat = QFontEngineFT::Format_A8;
2773 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2774 if (s->matrix.type() >= QTransform::TxScale) {
2775 if (s->matrix.isAffine())
2776 gset = fe->loadTransformedGlyphSet(s->matrix);
2781 if (!gset || gset->outline_drawing
2782 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2785 FT_Face lockedFace = 0;
2788 switch (neededFormat) {
2789 case QFontEngineFT::Format_Mono:
2792 case QFontEngineFT::Format_A8:
2795 case QFontEngineFT::Format_A32:
2803 for (int i = 0; i < numGlyphs; i++) {
2804 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2805 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2807 if (!glyph || glyph->format != neededFormat) {
2809 lockedFace = fe->lockFace();
2810 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2813 if (!glyph || !glyph->data)
2817 switch (neededFormat) {
2818 case QFontEngineFT::Format_Mono:
2819 pitch = ((glyph->width + 31) & ~31) >> 3;
2821 case QFontEngineFT::Format_A8:
2822 pitch = (glyph->width + 3) & ~3;
2824 case QFontEngineFT::Format_A32:
2825 pitch = glyph->width * 4;
2832 alphaPenBlt(glyph->data, pitch, depth,
2833 qFloor(positions[i].x) + glyph->x,
2834 qFloor(positions[i].y) - glyph->y,
2835 glyph->width, glyph->height);
2842 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2844 QImageTextureGlyphCache *cache =
2845 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2847 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2848 fontEngine->setGlyphCache(0, cache);
2851 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2852 cache->fillInPendingGlyphs();
2854 const QImage &image = cache->image();
2855 int bpl = image.bytesPerLine();
2857 int depth = image.depth();
2861 leftShift = 2; // multiply by 4
2862 else if (depth == 1)
2863 rightShift = 3; // divide by 8
2865 int margin = cache->glyphMargin();
2866 const uchar *bits = image.bits();
2867 for (int i=0; i<numGlyphs; ++i) {
2869 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2870 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2871 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2875 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2876 int y = qFloor(positions[i].y) - c.baseLineY - margin;
2878 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2881 // c.baseLineX, c.baseLineY,
2884 // positions[i].x.toInt(), positions[i].y.toInt());
2886 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2892 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2893 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2895 Q_D(QRasterPaintEngine);
2896 QRasterPaintEngineState *s = state();
2898 QFontEngine *fontEngine = ti.fontEngine;
2899 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2900 QPaintEngineEx::drawTextItem(p, ti);
2904 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2906 QVarLengthArray<QFixedPoint> positions;
2907 QVarLengthArray<glyph_t> glyphs;
2908 QTransform matrix = s->matrix;
2909 matrix.translate(p.x(), p.y());
2910 if (matrix.type() == QTransform::TxScale)
2911 fe->setFontScale(matrix.m11());
2912 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2914 for (int i=0; i<glyphs.size(); ++i) {
2915 TOpenFontCharMetrics tmetrics;
2916 const TUint8 *glyphBitmapBytes;
2917 TSize glyphBitmapSize;
2918 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2919 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
2920 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
2921 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2924 if (matrix.type() == QTransform::TxScale)
2925 fe->setFontScale(1.0);
2929 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2932 * Returns true if the rectangle is completely within the current clip
2933 * state of the paint engine.
2935 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2937 const QClipData *cl = clip();
2939 // inline contains() for performance (we know the rects are normalized)
2940 const QRect &r1 = deviceRect;
2941 return (r.left() >= r1.left() && r.right() <= r1.right()
2942 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2946 if (cl->hasRectClip) {
2947 // currently all painting functions clips to deviceRect internally
2948 if (cl->clipRect == deviceRect)
2951 // inline contains() for performance (we know the rects are normalized)
2952 const QRect &r1 = cl->clipRect;
2953 return (r.left() >= r1.left() && r.right() <= r1.right()
2954 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2956 return qt_region_strictContains(cl->clipRegion, r);
2960 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2963 Q_Q(const QRasterPaintEngine);
2964 const QRasterPaintEngineState *s = q->state();
2965 const QClipData *cl = clip();
2967 QRect r = rect.normalized();
2968 // inline contains() for performance (we know the rects are normalized)
2969 const QRect &r1 = deviceRect;
2970 return (r.left() >= r1.left() && r.right() <= r1.right()
2971 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2975 // currently all painting functions that call this function clip to deviceRect internally
2976 if (cl->hasRectClip && cl->clipRect == deviceRect)
2979 if (s->flags.antialiased)
2982 QRect r = rect.normalized();
2984 r.setX(r.x() - penWidth);
2985 r.setY(r.y() - penWidth);
2986 r.setWidth(r.width() + 2 * penWidth);
2987 r.setHeight(r.height() + 2 * penWidth);
2990 if (cl->hasRectClip) {
2991 // inline contains() for performance (we know the rects are normalized)
2992 const QRect &r1 = cl->clipRect;
2993 return (r.left() >= r1.left() && r.right() <= r1.right()
2994 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2996 return qt_region_strictContains(cl->clipRegion, r);
3000 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3003 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
3007 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3008 const QSpanData *data) const
3010 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3014 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3015 const QSpanData *data) const
3017 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3023 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3028 QFontEngine *fontEngine = textItem->fontEngine();
3029 if (!supportsTransformations(fontEngine)) {
3030 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3033 QPaintEngineEx::drawStaticTextItem(textItem);
3040 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3042 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3043 QRasterPaintEngineState *s = state();
3045 #ifdef QT_DEBUG_DRAW
3046 Q_D(QRasterPaintEngine);
3047 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3048 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3055 #if defined (Q_OS_WIN) || defined(Q_WS_MAC)
3057 if (!supportsTransformations(ti.fontEngine)) {
3058 QVarLengthArray<QFixedPoint> positions;
3059 QVarLengthArray<glyph_t> glyphs;
3061 QTransform matrix = s->matrix;
3062 matrix.translate(p.x(), p.y());
3064 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3066 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3070 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC
3071 if (s->matrix.type() <= QTransform::TxTranslate
3072 || (s->matrix.type() == QTransform::TxScale
3073 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3074 drawGlyphsS60(p, ti);
3077 #else // Q_OS_WIN || Q_WS_MAC
3079 QFontEngine *fontEngine = ti.fontEngine;
3082 if (s->matrix.type() < QTransform::TxScale) {
3084 QVarLengthArray<QFixedPoint> positions;
3085 QVarLengthArray<glyph_t> glyphs;
3086 QTransform matrix = state()->transform();
3088 qreal _x = qFloor(p.x());
3089 qreal _y = qFloor(p.y());
3090 matrix.translate(_x, _y);
3092 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3093 if (glyphs.size() == 0)
3096 for(int i = 0; i < glyphs.size(); i++) {
3097 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3098 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3099 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3100 qRound(positions[i].x + metrics.x),
3101 qRound(positions[i].y + metrics.y),
3102 img.width(), img.height());
3108 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3110 if (fontEngine->type() != QFontEngine::Freetype) {
3111 QPaintEngineEx::drawTextItem(p, ti);
3115 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3117 QTransform matrix = s->matrix;
3118 matrix.translate(p.x(), p.y());
3120 QVarLengthArray<QFixedPoint> positions;
3121 QVarLengthArray<glyph_t> glyphs;
3122 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3123 if (glyphs.size() == 0)
3126 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3127 QPaintEngine::drawTextItem(p, ti);
3133 QPaintEngineEx::drawTextItem(p, ti);
3139 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3141 Q_D(QRasterPaintEngine);
3142 QRasterPaintEngineState *s = state();
3145 if (!s->penData.blend)
3148 if (!s->flags.fast_pen) {
3149 QPaintEngineEx::drawPoints(points, pointCount);
3153 QCosmeticStroker stroker(s, d->deviceRect);
3154 stroker.drawPoints(points, pointCount);
3158 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3160 Q_D(QRasterPaintEngine);
3161 QRasterPaintEngineState *s = state();
3164 if (!s->penData.blend)
3167 if (!s->flags.fast_pen) {
3168 QPaintEngineEx::drawPoints(points, pointCount);
3172 QCosmeticStroker stroker(s, d->deviceRect);
3173 stroker.drawPoints(points, pointCount);
3179 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3181 #ifdef QT_DEBUG_DRAW
3182 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3184 Q_D(QRasterPaintEngine);
3185 QRasterPaintEngineState *s = state();
3188 if (!s->penData.blend)
3191 if (s->flags.fast_pen) {
3192 QCosmeticStroker stroker(s, d->deviceRect);
3193 for (int i=0; i<lineCount; ++i) {
3194 const QLine &l = lines[i];
3195 stroker.drawLine(l.p1(), l.p2());
3198 QPaintEngineEx::drawLines(lines, lineCount);
3202 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3208 Q_Q(QRasterPaintEngine);
3209 QRasterPaintEngineState *s = q->state();
3211 const QPen &pen = s->lastPen;
3212 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3213 const QVector<qreal> pattern = pen.dashPattern();
3215 qreal patternLength = 0;
3216 for (int i = 0; i < pattern.size(); ++i)
3217 patternLength += pattern.at(i);
3219 if (patternLength <= 0)
3222 qreal length = line.length();
3223 Q_ASSERT(length > 0);
3224 while (length > 0) {
3225 const bool rasterize = *inDash;
3226 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3229 if (dash >= length) {
3231 *dashOffset += dash / width;
3235 *inDash = !(*inDash);
3236 if (++*dashIndex >= pattern.size())
3243 if (rasterize && dash > 0)
3244 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3251 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3253 #ifdef QT_DEBUG_DRAW
3254 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3256 Q_D(QRasterPaintEngine);
3257 QRasterPaintEngineState *s = state();
3260 if (!s->penData.blend)
3262 if (s->flags.fast_pen) {
3263 QCosmeticStroker stroker(s, d->deviceRect);
3264 for (int i=0; i<lineCount; ++i) {
3265 QLineF line = lines[i];
3266 stroker.drawLine(line.p1(), line.p2());
3269 QPaintEngineEx::drawLines(lines, lineCount);
3277 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3279 QPaintEngineEx::drawEllipse(rect);
3286 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3288 Q_D(QRasterPaintEngine);
3295 CGContextRef QRasterPaintEngine::getCGContext() const
3297 Q_D(const QRasterPaintEngine);
3298 return d->cgContext;
3306 void QRasterPaintEngine::setDC(HDC hdc) {
3307 Q_D(QRasterPaintEngine);
3314 HDC QRasterPaintEngine::getDC() const
3316 Q_D(const QRasterPaintEngine);
3323 void QRasterPaintEngine::releaseDC(HDC) const
3329 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3331 const QTransform &m = state()->matrix;
3332 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3333 QFontEngine::Type fontEngineType = fontEngine->type();
3334 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3335 || (m.type() <= QTransform::TxTranslate
3336 && (fontEngineType == QFontEngine::TestFontEngine
3337 || fontEngineType == QFontEngine::Box))) {
3341 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3344 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3346 #if defined(Q_WS_MAC)
3347 // Mac font engines don't support scaling and rotation
3348 if (m.type() > QTransform::TxTranslate)
3350 if (m.type() >= QTransform::TxProject)
3354 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3363 QPoint QRasterPaintEngine::coordinateOffset() const
3365 return QPoint(0, 0);
3368 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3373 Q_D(QRasterPaintEngine);
3375 Q_ASSERT(image.depth() == 1);
3377 const int spanCount = 256;
3378 QT_FT_Span spans[spanCount];
3382 int w = image.width();
3383 int h = image.height();
3384 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3385 int ymin = qMax(qRound(pos.y()), 0);
3386 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3387 int xmin = qMax(qRound(pos.x()), 0);
3389 int x_offset = xmin - qRound(pos.x());
3391 QImage::Format format = image.format();
3392 for (int y = ymin; y < ymax; ++y) {
3393 const uchar *src = image.scanLine(y - qRound(pos.y()));
3394 if (format == QImage::Format_MonoLSB) {
3395 for (int x = 0; x < xmax - xmin; ++x) {
3396 int src_x = x + x_offset;
3397 uchar pixel = src[src_x >> 3];
3402 if (pixel & (0x1 << (src_x & 7))) {
3403 spans[n].x = xmin + x;
3405 spans[n].coverage = 255;
3407 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3411 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3414 if (n == spanCount) {
3415 fg->blend(n, spans, fg);
3421 for (int x = 0; x < xmax - xmin; ++x) {
3422 int src_x = x + x_offset;
3423 uchar pixel = src[src_x >> 3];
3428 if (pixel & (0x80 >> (x & 7))) {
3429 spans[n].x = xmin + x;
3431 spans[n].coverage = 255;
3433 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3437 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3440 if (n == spanCount) {
3441 fg->blend(n, spans, fg);
3449 fg->blend(n, spans, fg);
3455 \enum QRasterPaintEngine::ClipType
3458 \value RectClip Indicates that the currently set clip is a single rectangle.
3459 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3464 Returns the type of the clip currently set.
3466 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3468 Q_D(const QRasterPaintEngine);
3470 const QClipData *clip = d->clip();
3471 if (!clip || clip->hasRectClip)
3479 Returns the bounding rect of the currently set clip.
3481 QRect QRasterPaintEngine::clipBoundingRect() const
3483 Q_D(const QRasterPaintEngine);
3485 const QClipData *clip = d->clip();
3488 return d->deviceRect;
3490 if (clip->hasRectClip)
3491 return clip->clipRect;
3493 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3496 static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
3498 Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
3500 QVarLengthArray<short, 4096> buffer;
3502 QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
3503 QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
3504 result->initialize();
3506 for (int y = 0; y < c1->clipSpanHeight; ++y) {
3507 const QSpan *c1_spans = c1ClipLines[y].spans;
3508 int c1_count = c1ClipLines[y].count;
3509 const QSpan *c2_spans = c2ClipLines[y].spans;
3510 int c2_count = c2ClipLines[y].count;
3512 if (c1_count == 0 && c2_count == 0)
3514 if (c1_count == 0) {
3515 result->appendSpans(c2_spans, c2_count);
3517 } else if (c2_count == 0) {
3518 result->appendSpans(c1_spans, c1_count);
3522 // we need to merge the two
3524 // find required length
3525 int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
3526 c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
3528 memset(buffer.data(), 0, buffer.size() * sizeof(short));
3530 // Fill with old spans.
3531 for (int i = 0; i < c1_count; ++i) {
3532 const QSpan *cs = c1_spans + i;
3533 for (int j=cs->x; j<cs->x + cs->len; ++j)
3534 buffer[j] = cs->coverage;
3537 // Fill with new spans
3538 for (int i = 0; i < c2_count; ++i) {
3539 const QSpan *cs = c2_spans + i;
3540 for (int j = cs->x; j < cs->x + cs->len; ++j) {
3541 buffer[j] += cs->coverage;
3542 if (buffer[j] > 255)
3550 // Skip to next span
3551 while (x < max && buffer[x] == 0) ++x;
3552 if (x >= max) break;
3555 int coverage = buffer[x];
3557 // Find length of span
3558 while (x < max && buffer[x] == coverage)
3561 result->appendSpan(sx, x - sx, y, coverage);
3566 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3568 Q_Q(QRasterPaintEngine);
3569 QRasterPaintEngineState *s = q->state();
3571 rasterizer->setAntialiased(s->flags.antialiased);
3573 QRect clipRect(deviceRect);
3575 // ### get from optimized rectbased QClipData
3577 const QClipData *c = clip();
3579 const QRect r(QPoint(c->xmin, c->ymin),
3580 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3581 clipRect = clipRect.intersected(r);
3582 blend = data->blend;
3584 blend = data->unclipped_blend;
3587 rasterizer->setClipRect(clipRect);
3588 rasterizer->initialize(blend, data);
3591 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3592 ProcessSpans callback,
3593 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3595 if (!callback || !outline)
3598 Q_Q(QRasterPaintEngine);
3599 QRasterPaintEngineState *s = q->state();
3601 if (!s->flags.antialiased) {
3602 initializeRasterizer(spanData);
3604 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3608 rasterizer->rasterize(outline, fillRule);
3612 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3616 int q_gray_rendered_spans(QT_FT_Raster raster);
3619 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3620 ProcessSpans callback,
3621 void *userData, QRasterBuffer *)
3623 if (!callback || !outline)
3626 Q_Q(QRasterPaintEngine);
3627 QRasterPaintEngineState *s = q->state();
3629 if (!s->flags.antialiased) {
3630 rasterizer->setAntialiased(s->flags.antialiased);
3631 rasterizer->setClipRect(deviceRect);
3632 rasterizer->initialize(callback, userData);
3634 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3638 rasterizer->rasterize(outline, fillRule);
3642 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3643 // minimize memory reallocations. However if initial size for
3644 // raster pool is changed for lower value, reallocations will
3646 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3647 int rasterPoolSize = rasterPoolInitialSize;
3648 unsigned char *rasterPoolBase;
3649 #if defined(Q_OS_WIN64)
3651 // We make use of setjmp and longjmp in qgrayraster.c which requires
3652 // 16-byte alignment, hence we hardcode this requirement here..
3653 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3655 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3656 rasterPoolBase = rasterPoolOnStack;
3658 Q_CHECK_PTR(rasterPoolBase);
3660 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3662 void *data = userData;
3664 QT_FT_BBox clip_box = { deviceRect.x(),
3666 deviceRect.x() + deviceRect.width(),
3667 deviceRect.y() + deviceRect.height() };
3669 QT_FT_Raster_Params rasterParams;
3670 rasterParams.target = 0;
3671 rasterParams.source = outline;
3672 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3673 rasterParams.gray_spans = 0;
3674 rasterParams.black_spans = 0;
3675 rasterParams.bit_test = 0;
3676 rasterParams.bit_set = 0;
3677 rasterParams.user = data;
3678 rasterParams.clip_box = clip_box;
3683 int rendered_spans = 0;
3687 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3688 rasterParams.gray_spans = callback;
3689 rasterParams.skip_spans = rendered_spans;
3690 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3692 // Out of memory, reallocate some more and try again...
3693 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3694 int new_size = rasterPoolSize * 2;
3695 if (new_size > 1024 * 1024) {
3696 qWarning("QPainter: Rasterization of primitive failed");
3700 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3702 #if defined(Q_OS_WIN64)
3703 _aligned_free(rasterPoolBase);
3705 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3706 free(rasterPoolBase);
3709 rasterPoolSize = new_size;
3711 #if defined(Q_OS_WIN64)
3712 // We make use of setjmp and longjmp in qgrayraster.c which requires
3713 // 16-byte alignment, hence we hardcode this requirement here..
3714 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3716 (unsigned char *) malloc(rasterPoolSize);
3718 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3720 qt_ft_grays_raster.raster_done(*grayRaster.data());
3721 qt_ft_grays_raster.raster_new(grayRaster.data());
3722 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3728 #if defined(Q_OS_WIN64)
3729 _aligned_free(rasterPoolBase);
3731 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3732 free(rasterPoolBase);
3736 void QRasterPaintEnginePrivate::recalculateFastImages()
3738 Q_Q(QRasterPaintEngine);
3739 QRasterPaintEngineState *s = q->state();
3741 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3742 && s->matrix.type() <= QTransform::TxShear;
3745 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3747 Q_Q(const QRasterPaintEngine);
3748 const QRasterPaintEngineState *s = q->state();
3750 return s->flags.fast_images
3751 && (mode == QPainter::CompositionMode_SourceOver
3752 || (mode == QPainter::CompositionMode_Source
3753 && !image.hasAlphaChannel()));
3756 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3758 Q_ASSERT(image.depth() == 1);
3760 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3761 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3763 QRgb fg = PREMUL(color.rgba());
3766 int height = sourceImage.height();
3767 int width = sourceImage.width();
3768 for (int y=0; y<height; ++y) {
3769 uchar *source = sourceImage.scanLine(y);
3770 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3771 if (!source || !target)
3772 QT_THROW(std::bad_alloc()); // we must have run out of memory
3773 for (int x=0; x < width; ++x)
3774 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3779 QRasterBuffer::~QRasterBuffer()
3783 void QRasterBuffer::init()
3785 compositionMode = QPainter::CompositionMode_SourceOver;
3786 monoDestinationWithClut = false;
3791 QImage::Format QRasterBuffer::prepare(QImage *image)
3793 m_buffer = (uchar *)image->bits();
3794 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3795 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3796 bytes_per_pixel = image->depth()/8;
3797 bytes_per_line = image->bytesPerLine();
3799 format = image->format();
3800 drawHelper = qDrawHelper + format;
3801 if (image->depth() == 1 && image->colorTable().size() == 2) {
3802 monoDestinationWithClut = true;
3803 destColor0 = PREMUL(image->colorTable()[0]);
3804 destColor1 = PREMUL(image->colorTable()[1]);
3810 void QRasterBuffer::resetBuffer(int val)
3812 memset(m_buffer, val, m_height*bytes_per_line);
3815 QClipData::QClipData(int height)
3817 clipSpanHeight = height;
3822 xmin = xmax = ymin = ymax = 0;
3826 hasRectClip = hasRegionClip = false;
3829 QClipData::~QClipData()
3837 void QClipData::initialize()
3843 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3845 Q_CHECK_PTR(m_clipLines);
3847 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3848 allocated = clipSpanHeight;
3849 Q_CHECK_PTR(m_spans);
3855 m_clipLines[y].spans = 0;
3856 m_clipLines[y].count = 0;
3860 const int len = clipRect.width();
3863 QSpan *span = m_spans + count;
3867 span->coverage = 255;
3870 m_clipLines[y].spans = span;
3871 m_clipLines[y].count = 1;
3875 while (y < clipSpanHeight) {
3876 m_clipLines[y].spans = 0;
3877 m_clipLines[y].count = 0;
3880 } else if (hasRegionClip) {
3882 const QVector<QRect> rects = clipRegion.rects();
3883 const int numRects = rects.size();
3886 const int maxSpans = (ymax - ymin) * numRects;
3887 if (maxSpans > allocated) {
3888 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3889 allocated = maxSpans;
3894 int firstInBand = 0;
3896 while (firstInBand < numRects) {
3897 const int currMinY = rects.at(firstInBand).y();
3898 const int currMaxY = currMinY + rects.at(firstInBand).height();
3900 while (y < currMinY) {
3901 m_clipLines[y].spans = 0;
3902 m_clipLines[y].count = 0;
3906 int lastInBand = firstInBand;
3907 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3910 while (y < currMaxY) {
3912 m_clipLines[y].spans = m_spans + count;
3913 m_clipLines[y].count = lastInBand - firstInBand + 1;
3915 for (int r = firstInBand; r <= lastInBand; ++r) {
3916 const QRect &currRect = rects.at(r);
3917 QSpan *span = m_spans + count;
3918 span->x = currRect.x();
3919 span->len = currRect.width();
3921 span->coverage = 255;
3927 firstInBand = lastInBand + 1;
3930 Q_ASSERT(count <= allocated);
3932 while (y < clipSpanHeight) {
3933 m_clipLines[y].spans = 0;
3934 m_clipLines[y].count = 0;
3940 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3945 free(m_clipLines); // same for clipLines
3951 void QClipData::fixup()
3956 ymin = ymax = xmin = xmax = 0;
3961 ymin = m_spans[0].y;
3962 ymax = m_spans[count-1].y + 1;
3966 const int firstLeft = m_spans[0].x;
3967 const int firstRight = m_spans[0].x + m_spans[0].len;
3970 for (int i = 0; i < count; ++i) {
3971 QT_FT_Span_& span = m_spans[i];
3974 if (span.y != y + 1 && y != -1)
3977 m_clipLines[y].spans = &span;
3978 m_clipLines[y].count = 1;
3980 ++m_clipLines[y].count;
3982 const int spanLeft = span.x;
3983 const int spanRight = spanLeft + span.len;
3985 if (spanLeft < xmin)
3988 if (spanRight > xmax)
3991 if (spanLeft != firstLeft || spanRight != firstRight)
3997 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4002 Convert \a rect to clip spans.
4004 void QClipData::setClipRect(const QRect &rect)
4006 if (hasRectClip && rect == clipRect)
4009 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4011 hasRegionClip = false;
4015 xmax = rect.x() + rect.width();
4016 ymin = qMin(rect.y(), clipSpanHeight);
4017 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4024 // qDebug() << xmin << xmax << ymin << ymax;
4028 Convert \a region to clip spans.
4030 void QClipData::setClipRegion(const QRegion ®ion)
4032 if (region.rectCount() == 1) {
4033 setClipRect(region.rects().at(0));
4037 hasRegionClip = true;
4038 hasRectClip = false;
4039 clipRegion = region;
4041 { // set bounding rect
4042 const QRect rect = region.boundingRect();
4044 xmax = rect.x() + rect.width();
4046 ymax = rect.y() + rect.height();
4058 spans must be sorted on y
4060 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4061 const QSpan *spans, const QSpan *end,
4062 QSpan **outSpans, int available)
4064 const_cast<QClipData *>(clip)->initialize();
4066 QSpan *out = *outSpans;
4068 const QSpan *clipSpans = clip->m_spans + *currentClip;
4069 const QSpan *clipEnd = clip->m_spans + clip->count;
4071 while (available && spans < end ) {
4072 if (clipSpans >= clipEnd) {
4076 if (clipSpans->y > spans->y) {
4080 if (spans->y != clipSpans->y) {
4081 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4082 clipSpans = clip->m_clipLines[spans->y].spans;
4087 Q_ASSERT(spans->y == clipSpans->y);
4090 int sx2 = sx1 + spans->len;
4091 int cx1 = clipSpans->x;
4092 int cx2 = cx1 + clipSpans->len;
4094 if (cx1 < sx1 && cx2 < sx1) {
4097 } else if (sx1 < cx1 && sx2 < cx1) {
4101 int x = qMax(sx1, cx1);
4102 int len = qMin(sx2, cx2) - x;
4104 out->x = qMax(sx1, cx1);
4105 out->len = qMin(sx2, cx2) - out->x;
4107 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4119 *currentClip = clipSpans - clip->m_spans;
4123 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4125 // qDebug() << "qt_span_fill_clipped" << spanCount;
4126 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4128 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4130 const int NSPANS = 256;
4131 QSpan cspans[NSPANS];
4132 int currentClip = 0;
4133 const QSpan *end = spans + spanCount;
4134 while (spans < end) {
4135 QSpan *clipped = cspans;
4136 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4137 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4138 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4140 if (clipped - cspans)
4141 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4147 Clip spans to \a{clip}-rectangle.
4148 Returns number of unclipped spans
4150 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4153 const short minx = clip.left();
4154 const short miny = clip.top();
4155 const short maxx = clip.right();
4156 const short maxy = clip.bottom();
4159 for (int i = 0; i < numSpans; ++i) {
4160 if (spans[i].y > maxy)
4162 if (spans[i].y < miny
4163 || spans[i].x > maxx
4164 || spans[i].x + spans[i].len <= minx) {
4167 if (spans[i].x < minx) {
4168 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4171 spans[n].x = spans[i].x;
4172 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4174 if (spans[n].len == 0)
4176 spans[n].y = spans[i].y;
4177 spans[n].coverage = spans[i].coverage;
4184 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4187 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4188 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4190 Q_ASSERT(fillData->clip);
4191 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4193 // hw: check if this const_cast<> is safe!!!
4194 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4195 fillData->clip->clipRect);
4197 fillData->unclipped_blend(count, spans, fillData);
4200 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4202 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4204 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4205 // for (int i = 0; i < qMin(count, 10); ++i) {
4206 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4209 switch (clipData->operation) {
4211 case Qt::IntersectClip:
4213 QClipData *newClip = clipData->newClip;
4214 newClip->initialize();
4216 int currentClip = 0;
4217 const QSpan *end = spans + count;
4218 while (spans < end) {
4219 QSpan *newspans = newClip->m_spans + newClip->count;
4220 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4221 &newspans, newClip->allocated - newClip->count);
4222 newClip->count = newspans - newClip->m_spans;
4224 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4225 newClip->allocated *= 2;
4232 case Qt::ReplaceClip:
4233 clipData->newClip->appendSpans(spans, count);
4241 QImage QRasterBuffer::bufferImage() const
4243 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4245 for (int y = 0; y < m_height; ++y) {
4246 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4248 for (int x=0; x<m_width; ++x) {
4249 uint argb = span[x];
4250 image.setPixel(x, y, argb);
4258 void QRasterBuffer::flushToARGBImage(QImage *target) const
4260 int w = qMin(m_width, target->width());
4261 int h = qMin(m_height, target->height());
4263 for (int y=0; y<h; ++y) {
4264 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4265 QRgb *dest = (QRgb *) target->scanLine(y);
4266 for (int x=0; x<w; ++x) {
4267 QRgb pixel = sourceLine[x];
4268 int alpha = qAlpha(pixel);
4272 dest[x] = (alpha << 24)
4273 | ((255*qRed(pixel)/alpha) << 16)
4274 | ((255*qGreen(pixel)/alpha) << 8)
4275 | ((255*qBlue(pixel)/alpha) << 0);
4282 class QGradientCache
4286 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4287 stops(s), opacity(op), interpolationMode(mode) {}
4288 uint buffer[GRADIENT_STOPTABLE_SIZE];
4289 QGradientStops stops;
4291 QGradient::InterpolationMode interpolationMode;
4294 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4297 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4298 quint64 hash_val = 0;
4300 QGradientStops stops = gradient.stops();
4301 for (int i = 0; i < stops.size() && i <= 2; i++)
4302 hash_val += stops[i].second.rgba();
4304 QMutexLocker lock(&mutex);
4305 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4307 if (it == cache.constEnd())
4308 return addCacheElement(hash_val, gradient, opacity);
4311 const CacheInfo &cache_info = it.value();
4312 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4313 return cache_info.buffer;
4315 } while (it != cache.constEnd() && it.key() == hash_val);
4316 // an exact match for these stops and opacity was not found, create new cache
4317 return addCacheElement(hash_val, gradient, opacity);
4321 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4323 inline int maxCacheSize() const { return 60; }
4324 inline void generateGradientColorTable(const QGradient& g,
4326 int size, int opacity) const;
4327 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4328 if (cache.size() == maxCacheSize()) {
4329 // may remove more than 1, but OK
4330 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4332 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4333 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4334 return cache.insert(hash_val, cache_entry).value().buffer;
4337 QGradientColorTableHash cache;
4341 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4343 QGradientStops stops = gradient.stops();
4344 int stopCount = stops.count();
4345 Q_ASSERT(stopCount > 0);
4347 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4349 if (stopCount == 2) {
4350 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4351 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4353 qreal first_stop = stops[0].first;
4354 qreal second_stop = stops[1].first;
4356 if (second_stop < first_stop) {
4357 qSwap(first_color, second_color);
4358 qSwap(first_stop, second_stop);
4361 if (colorInterpolation) {
4362 first_color = PREMUL(first_color);
4363 second_color = PREMUL(second_color);
4366 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4367 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4369 uint red_first = qRed(first_color) << 16;
4370 uint green_first = qGreen(first_color) << 16;
4371 uint blue_first = qBlue(first_color) << 16;
4372 uint alpha_first = qAlpha(first_color) << 16;
4374 uint red_second = qRed(second_color) << 16;
4375 uint green_second = qGreen(second_color) << 16;
4376 uint blue_second = qBlue(second_color) << 16;
4377 uint alpha_second = qAlpha(second_color) << 16;
4380 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4381 if (colorInterpolation)
4382 colorTable[i] = first_color;
4384 colorTable[i] = PREMUL(first_color);
4387 if (i < second_index) {
4388 qreal reciprocal = qreal(1) / (second_index - first_index);
4390 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4391 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4392 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4393 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4396 red_first += 1 << 15;
4397 green_first += 1 << 15;
4398 blue_first += 1 << 15;
4399 alpha_first += 1 << 15;
4401 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4402 red_first += red_delta;
4403 green_first += green_delta;
4404 blue_first += blue_delta;
4405 alpha_first += alpha_delta;
4407 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4408 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4410 if (colorInterpolation)
4411 colorTable[i] = color;
4413 colorTable[i] = PREMUL(color);
4417 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4418 if (colorInterpolation)
4419 colorTable[i] = second_color;
4421 colorTable[i] = PREMUL(second_color);
4427 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4428 if (stopCount == 1) {
4429 current_color = PREMUL(current_color);
4430 for (int i = 0; i < size; ++i)
4431 colorTable[i] = current_color;
4435 // The position where the gradient begins and ends
4436 qreal begin_pos = stops[0].first;
4437 qreal end_pos = stops[stopCount-1].first;
4439 int pos = 0; // The position in the color table.
4442 qreal incr = 1 / qreal(size); // the double increment.
4443 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4445 // Up to first point
4446 colorTable[pos++] = PREMUL(current_color);
4447 while (dpos <= begin_pos) {
4448 colorTable[pos] = colorTable[pos - 1];
4453 int current_stop = 0; // We always interpolate between current and current + 1.
4455 qreal t; // position between current left and right stops
4456 qreal t_delta; // the t increment per entry in the color table
4458 if (dpos < end_pos) {
4460 while (dpos > stops[current_stop+1].first)
4463 if (current_stop != 0)
4464 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4465 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4467 if (colorInterpolation) {
4468 current_color = PREMUL(current_color);
4469 next_color = PREMUL(next_color);
4472 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4473 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4474 t = (dpos - stops[current_stop].first) * c;
4478 Q_ASSERT(current_stop < stopCount);
4480 int dist = qRound(t);
4481 int idist = 256 - dist;
4483 if (colorInterpolation)
4484 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4486 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4491 if (dpos >= end_pos)
4497 while (dpos > stops[current_stop+skip+1].first)
4501 current_stop += skip;
4503 current_color = next_color;
4505 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4506 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4508 if (colorInterpolation) {
4510 current_color = PREMUL(current_color);
4511 next_color = PREMUL(next_color);
4514 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4515 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4516 t = (dpos - stops[current_stop].first) * c;
4523 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4524 while (pos < size - 1) {
4525 colorTable[pos] = current_color;
4529 // Make sure the last color stop is represented at the end of the table
4530 colorTable[size - 1] = current_color;
4533 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4536 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4542 m11 = m22 = m33 = 1.;
4543 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4544 clip = pe ? pe->d_func()->clip() : 0;
4547 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4549 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4551 Qt::BrushStyle brushStyle = qbrush_style(brush);
4552 switch (brushStyle) {
4553 case Qt::SolidPattern: {
4555 QColor c = qbrush_color(brush);
4556 QRgb rgba = c.rgba();
4557 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4558 if ((solid.color & 0xff000000) == 0
4559 && compositionMode == QPainter::CompositionMode_SourceOver) {
4565 case Qt::LinearGradientPattern:
4567 type = LinearGradient;
4568 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4569 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4570 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4571 gradient.spread = g->spread();
4573 QLinearGradientData &linearData = gradient.linear;
4575 linearData.origin.x = g->start().x();
4576 linearData.origin.y = g->start().y();
4577 linearData.end.x = g->finalStop().x();
4578 linearData.end.y = g->finalStop().y();
4582 case Qt::RadialGradientPattern:
4584 type = RadialGradient;
4585 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4586 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4587 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4588 gradient.spread = g->spread();
4590 QRadialGradientData &radialData = gradient.radial;
4592 QPointF center = g->center();
4593 radialData.center.x = center.x();
4594 radialData.center.y = center.y();
4595 radialData.center.radius = g->centerRadius();
4596 QPointF focal = g->focalPoint();
4597 radialData.focal.x = focal.x();
4598 radialData.focal.y = focal.y();
4599 radialData.focal.radius = g->focalRadius();
4603 case Qt::ConicalGradientPattern:
4605 type = ConicalGradient;
4606 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4607 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4608 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4609 gradient.spread = QGradient::RepeatSpread;
4611 QConicalGradientData &conicalData = gradient.conical;
4613 QPointF center = g->center();
4614 conicalData.center.x = center.x();
4615 conicalData.center.y = center.y();
4616 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4620 case Qt::Dense1Pattern:
4621 case Qt::Dense2Pattern:
4622 case Qt::Dense3Pattern:
4623 case Qt::Dense4Pattern:
4624 case Qt::Dense5Pattern:
4625 case Qt::Dense6Pattern:
4626 case Qt::Dense7Pattern:
4627 case Qt::HorPattern:
4628 case Qt::VerPattern:
4629 case Qt::CrossPattern:
4630 case Qt::BDiagPattern:
4631 case Qt::FDiagPattern:
4632 case Qt::DiagCrossPattern:
4635 tempImage = new QImage();
4636 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4637 initTexture(tempImage, alpha, QTextureData::Tiled);
4639 case Qt::TexturePattern:
4642 tempImage = new QImage();
4644 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4645 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4647 *tempImage = brush.textureImage();
4648 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4656 adjustSpanMethods();
4659 void QSpanData::adjustSpanMethods()
4669 unclipped_blend = 0;
4672 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4673 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4674 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4675 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4676 fillRect = rasterBuffer->drawHelper->fillRect;
4678 case LinearGradient:
4679 case RadialGradient:
4680 case ConicalGradient:
4681 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4684 unclipped_blend = qBlendTexture;
4685 if (!texture.imageData)
4686 unclipped_blend = 0;
4691 if (!unclipped_blend) {
4694 blend = unclipped_blend;
4695 } else if (clip->hasRectClip) {
4696 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4698 blend = qt_span_fill_clipped;
4702 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4705 // make sure we round off correctly in qdrawhelper.cpp
4706 delta.translate(1.0 / 65536, 1.0 / 65536);
4708 QTransform inv = (delta * matrix).inverted();
4721 const bool affine = !m13 && !m23;
4722 fast_matrix = affine
4723 && m11 * m11 + m21 * m21 < 1e4
4724 && m12 * m12 + m22 * m22 < 1e4
4728 adjustSpanMethods();
4731 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4733 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4735 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4736 if (!d || d->height == 0) {
4737 texture.imageData = 0;
4744 texture.bytesPerLine = 0;
4745 texture.format = QImage::Format_Invalid;
4746 texture.colorTable = 0;
4747 texture.hasAlpha = alpha != 256;
4749 texture.imageData = d->data;
4750 texture.width = d->width;
4751 texture.height = d->height;
4753 if (sourceRect.isNull()) {
4756 texture.x2 = texture.width;
4757 texture.y2 = texture.height;
4759 texture.x1 = sourceRect.x();
4760 texture.y1 = sourceRect.y();
4761 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4762 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4765 texture.bytesPerLine = d->bytes_per_line;
4767 texture.format = d->format;
4768 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4769 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4771 texture.const_alpha = alpha;
4772 texture.type = _type;
4774 adjustSpanMethods();
4779 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4782 Draws the first \a pointCount points in the buffer \a points
4784 The default implementation converts the first \a pointCount QPoints in \a points
4785 to QPointFs and calls the floating point version of drawPoints.
4789 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4792 Reimplement this function to draw the largest ellipse that can be
4793 contained within rectangle \a rect.
4796 #ifdef QT_DEBUG_DRAW
4797 void dumpClip(int width, int height, const QClipData *clip)
4799 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4800 clipImg.fill(0xffff0000);
4807 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4809 for (int i = 0; i < clip->count; ++i) {
4810 const QSpan *span = ((QClipData *) clip)->spans() + i;
4811 for (int j = 0; j < span->len; ++j)
4812 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4813 x0 = qMin(x0, int(span->x));
4814 x1 = qMax(x1, int(span->x + span->len - 1));
4816 y0 = qMin(y0, int(span->y));
4817 y1 = qMax(y1, int(span->y));
4820 static int counter = 0;
4827 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4828 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));