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))
121 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
125 static inline bool winClearTypeFontsEnabled()
128 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
129 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
130 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
132 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
133 return result == FE_FONTSMOOTHINGCLEARTYPE;
136 bool QRasterPaintEngine::clearTypeFontsEnabled()
138 static const bool result = winClearTypeFontsEnabled();
145 extern bool qt_applefontsmoothing_enabled;
149 /********************************************************************************
152 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
153 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
154 static void qt_span_clip(int count, const QSpan *spans, void *userData);
160 Qt::ClipOperation operation;
166 LineDrawIncludeLastPixel
169 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
170 ProcessSpans pen_func, ProcessSpans brush_func,
171 QSpanData *pen_data, QSpanData *brush_data);
173 struct QRasterFloatPoint {
179 static const QRectF boundingRect(const QPointF *points, int pointCount)
181 const QPointF *e = points;
182 const QPointF *last = points + pointCount;
183 qreal minx, maxx, miny, maxy;
184 minx = maxx = e->x();
185 miny = maxy = e->y();
189 else if (e->x() > maxx)
193 else if (e->y() > maxy)
196 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
200 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
201 return (elementCount == 5 // 5-point polygon, check for closed rect
202 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
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]
207 (elementCount == 4 // 4-point polygon, check for unclosed rect
208 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
209 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
210 && pts[0] < pts[4] && pts[1] < pts[5]
215 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
217 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
220 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
222 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
225 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
226 qfixed c2x, qfixed c2y,
227 qfixed ex, qfixed ey,
230 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
231 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
232 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
236 #if !defined(QT_NO_DEBUG) && 0
237 static void qt_debug_path(const QPainterPath &path)
239 const char *names[] = {
246 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
247 for (int i=0; i<path.elementCount(); ++i) {
248 const QPainterPath::Element &e = path.elementAt(i);
249 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
250 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
255 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
256 QPaintEngineExPrivate(),
263 \class QRasterPaintEngine
268 \brief The QRasterPaintEngine class enables hardware acceleration
269 of painting operations in Qt for Embedded Linux.
271 Note that this functionality is only available in
272 \l{Qt for Embedded Linux}.
274 In \l{Qt for Embedded Linux}, painting is a pure software
275 implementation. But starting with Qt 4.2, it is
276 possible to add an accelerated graphics driver to take advantage
277 of available hardware resources.
279 Hardware acceleration is accomplished by creating a custom screen
280 driver, accelerating the copying from memory to the screen, and
281 implementing a custom paint engine accelerating the various
282 painting operations. Then a custom paint device (derived from the
283 QCustomRasterPaintDevice class) and a custom window surface
284 (derived from QWSWindowSurface) must be implemented to make
285 \l{Qt for Embedded Linux} aware of the accelerated driver.
287 \note The QRasterPaintEngine class does not support 8-bit images.
288 Instead, they need to be converted to a supported format, such as
289 QImage::Format_ARGB32_Premultiplied.
291 See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
292 documentation for details.
294 \sa QCustomRasterPaintDevice, QPaintEngine
298 \fn Type QRasterPaintEngine::type() const
304 \relates QRasterPaintEngine
306 A struct equivalent to QT_FT_Span, containing a position (x,
307 y), the span's length in pixels and its color/coverage (a value
308 ranging from 0 to 255).
314 Creates a raster based paint engine for operating on the given
315 \a device, with the complete set of \l
316 {QPaintEngine::PaintEngineFeature}{paint engine features and
319 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
320 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
322 d_func()->device = device;
329 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
332 d_func()->device = device;
336 void QRasterPaintEngine::init()
338 Q_D(QRasterPaintEngine);
345 // The antialiasing raster.
346 d->grayRaster.reset(new QT_FT_Raster);
347 Q_CHECK_PTR(d->grayRaster.data());
348 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
349 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
352 d->rasterizer.reset(new QRasterizer);
353 d->rasterBuffer.reset(new QRasterBuffer());
354 d->outlineMapper.reset(new QOutlineMapper);
355 d->outlinemapper_xform_dirty = true;
357 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
358 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
359 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
361 d->baseClip.reset(new QClipData(d->device->height()));
362 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
364 d->image_filler.init(d->rasterBuffer.data(), this);
365 d->image_filler.type = QSpanData::Texture;
367 d->image_filler_xform.init(d->rasterBuffer.data(), this);
368 d->image_filler_xform.type = QSpanData::Texture;
370 d->solid_color_filler.init(d->rasterBuffer.data(), this);
371 d->solid_color_filler.type = QSpanData::Solid;
373 d->deviceDepth = d->device->depth();
375 d->mono_surface = false;
376 gccaps &= ~PorterDuff;
378 QImage::Format format = QImage::Format_Invalid;
380 switch (d->device->devType()) {
381 case QInternal::Pixmap:
382 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
384 case QInternal::Image:
385 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
388 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
394 case QImage::Format_MonoLSB:
395 case QImage::Format_Mono:
396 d->mono_surface = true;
398 case QImage::Format_ARGB8565_Premultiplied:
399 case QImage::Format_ARGB8555_Premultiplied:
400 case QImage::Format_ARGB6666_Premultiplied:
401 case QImage::Format_ARGB4444_Premultiplied:
402 case QImage::Format_ARGB32_Premultiplied:
403 case QImage::Format_ARGB32:
404 gccaps |= PorterDuff;
406 case QImage::Format_RGB32:
407 case QImage::Format_RGB444:
408 case QImage::Format_RGB555:
409 case QImage::Format_RGB666:
410 case QImage::Format_RGB888:
411 case QImage::Format_RGB16:
422 Destroys this paint engine.
424 QRasterPaintEngine::~QRasterPaintEngine()
426 Q_D(QRasterPaintEngine);
428 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
434 bool QRasterPaintEngine::begin(QPaintDevice *device)
436 Q_D(QRasterPaintEngine);
438 if (device->devType() == QInternal::Pixmap) {
439 QPixmap *pixmap = static_cast<QPixmap *>(device);
440 QPlatformPixmap *pd = pixmap->handle();
441 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
442 d->device = pd->buffer();
447 // Make sure QPaintEngine::paintDevice() returns the proper device.
450 Q_ASSERT(d->device->devType() == QInternal::Image
451 || d->device->devType() == QInternal::CustomRaster);
453 d->systemStateChanged();
455 QRasterPaintEngineState *s = state();
456 ensureOutlineMapper();
457 d->outlineMapper->m_clip_rect = d->deviceRect;
459 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
460 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
461 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
462 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
464 d->rasterizer->setClipRect(d->deviceRect);
466 s->penData.init(d->rasterBuffer.data(), this);
467 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
468 s->stroker = &d->basicStroker;
469 d->basicStroker.setClipRect(d->deviceRect);
471 s->brushData.init(d->rasterBuffer.data(), this);
472 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
474 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
476 setDirty(DirtyBrushOrigin);
479 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
480 << ") devType:" << device->devType()
481 << "devRect:" << d->deviceRect;
483 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
488 d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
489 #if defined(Q_OS_WIN)
490 else if (clearTypeFontsEnabled())
491 #elif defined (Q_WS_MAC)
492 else if (qt_applefontsmoothing_enabled)
497 QImage::Format format = static_cast<QImage *>(d->device)->format();
498 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
499 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
501 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
503 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
512 bool QRasterPaintEngine::end()
515 Q_D(QRasterPaintEngine);
516 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
518 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
528 void QRasterPaintEngine::releaseBuffer()
530 Q_D(QRasterPaintEngine);
531 d->rasterBuffer.reset(new QRasterBuffer);
537 QSize QRasterPaintEngine::size() const
539 Q_D(const QRasterPaintEngine);
540 return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
547 void QRasterPaintEngine::saveBuffer(const QString &s) const
549 Q_D(const QRasterPaintEngine);
550 d->rasterBuffer->bufferImage().save(s, "PNG");
557 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
559 QRasterPaintEngineState *s = state();
560 // FALCON: get rid of this line, see drawImage call below.
562 QTransform::TransformationType txop = s->matrix.type();
566 case QTransform::TxNone:
567 s->flags.int_xform = true;
570 case QTransform::TxTranslate:
571 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
572 && qreal(int(s->matrix.dy())) == s->matrix.dy();
575 case QTransform::TxScale:
576 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
577 && qreal(int(s->matrix.dy())) == s->matrix.dy()
578 && qreal(int(s->matrix.m11())) == s->matrix.m11()
579 && qreal(int(s->matrix.m22())) == s->matrix.m22();
582 default: // shear / perspective...
583 s->flags.int_xform = false;
587 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
589 ensureOutlineMapper();
594 QRasterPaintEngineState::~QRasterPaintEngineState()
596 if (flags.has_clip_ownership)
601 QRasterPaintEngineState::QRasterPaintEngineState()
613 flags.fast_pen = true;
614 flags.antialiased = false;
615 flags.bilinear = false;
616 flags.fast_text = true;
617 flags.int_xform = true;
618 flags.tx_noshear = true;
619 flags.fast_images = true;
622 flags.has_clip_ownership = false;
627 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
632 , strokeFlags(s.strokeFlags)
633 , lastBrush(s.lastBrush)
634 , brushData(s.brushData)
635 , fillFlags(s.fillFlags)
636 , pixmapFlags(s.pixmapFlags)
637 , intOpacity(s.intOpacity)
641 , flag_bits(s.flag_bits)
643 brushData.tempImage = 0;
644 penData.tempImage = 0;
645 flags.has_clip_ownership = false;
651 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
653 QRasterPaintEngineState *s;
655 s = new QRasterPaintEngineState();
657 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
665 void QRasterPaintEngine::setState(QPainterState *s)
667 Q_D(QRasterPaintEngine);
668 QPaintEngineEx::setState(s);
669 d->rasterBuffer->compositionMode = s->composition_mode;
673 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
678 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
685 void QRasterPaintEngine::penChanged()
688 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
690 QRasterPaintEngineState *s = state();
691 s->strokeFlags |= DirtyPen;
692 s->dirty |= DirtyPen;
698 void QRasterPaintEngine::updatePen(const QPen &pen)
700 Q_D(QRasterPaintEngine);
701 QRasterPaintEngineState *s = state();
703 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
706 Qt::PenStyle pen_style = qpen_style(pen);
711 s->penData.clip = d->clip();
712 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
714 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
715 || pen.brush().transform().type() >= QTransform::TxNone) {
716 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
719 // Slightly ugly handling of an uncommon case... We need to change
720 // the pen because it is reused in draw_midpoint to decide dashed
722 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
723 pen_style = Qt::SolidLine;
724 s->lastPen.setStyle(Qt::SolidLine);
727 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
728 d->basicStroker.setCapStyle(qpen_capStyle(pen));
729 d->basicStroker.setMiterLimit(pen.miterLimit());
731 qreal penWidth = qpen_widthf(pen);
733 d->basicStroker.setStrokeWidth(1);
735 d->basicStroker.setStrokeWidth(penWidth);
737 if(pen_style == Qt::SolidLine) {
738 s->stroker = &d->basicStroker;
739 } else if (pen_style != Qt::NoPen) {
741 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
742 if (pen.isCosmetic()) {
743 d->dashStroker->setClipRect(d->deviceRect);
745 // ### I've seen this inverted devrect multiple places now...
746 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
747 d->dashStroker->setClipRect(clipRect);
749 d->dashStroker->setDashPattern(pen.dashPattern());
750 d->dashStroker->setDashOffset(pen.dashOffset());
751 s->stroker = d->dashStroker.data();
756 ensureState(); // needed because of tx_noshear...
757 s->flags.fast_pen = pen_style > Qt::NoPen
759 && ((pen.isCosmetic() && penWidth <= 1)
760 || (s->flags.tx_noshear && penWidth * s->txscale <= 1));
762 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
772 void QRasterPaintEngine::brushOriginChanged()
774 QRasterPaintEngineState *s = state();
776 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
779 s->fillFlags |= DirtyBrushOrigin;
786 void QRasterPaintEngine::brushChanged()
788 QRasterPaintEngineState *s = state();
790 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
792 s->fillFlags |= DirtyBrush;
801 void QRasterPaintEngine::updateBrush(const QBrush &brush)
804 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
806 Q_D(QRasterPaintEngine);
807 QRasterPaintEngineState *s = state();
808 // must set clip prior to setup, as setup uses it...
809 s->brushData.clip = d->clip();
810 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
811 if (s->fillFlags & DirtyTransform
812 || brush.transform().type() >= QTransform::TxNone)
813 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
814 s->lastBrush = brush;
818 void QRasterPaintEngine::updateOutlineMapper()
820 Q_D(QRasterPaintEngine);
821 d->outlineMapper->setMatrix(state()->matrix);
824 void QRasterPaintEngine::updateState()
826 QRasterPaintEngineState *s = state();
828 if (s->dirty & DirtyTransform)
829 updateMatrix(s->matrix);
831 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
832 const QPainter::CompositionMode mode = s->composition_mode;
833 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
834 && s->intOpacity == 256
835 && (mode == QPainter::CompositionMode_Source
836 || (mode == QPainter::CompositionMode_SourceOver
837 && qAlpha(s->penData.solid.color) == 255));
847 void QRasterPaintEngine::opacityChanged()
849 QRasterPaintEngineState *s = state();
852 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
855 s->fillFlags |= DirtyOpacity;
856 s->strokeFlags |= DirtyOpacity;
857 s->pixmapFlags |= DirtyOpacity;
858 s->dirty |= DirtyOpacity;
859 s->intOpacity = (int) (s->opacity * 256);
865 void QRasterPaintEngine::compositionModeChanged()
867 Q_D(QRasterPaintEngine);
868 QRasterPaintEngineState *s = state();
871 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
874 s->fillFlags |= DirtyCompositionMode;
875 s->dirty |= DirtyCompositionMode;
877 s->strokeFlags |= DirtyCompositionMode;
878 d->rasterBuffer->compositionMode = s->composition_mode;
880 d->recalculateFastImages();
886 void QRasterPaintEngine::renderHintsChanged()
888 QRasterPaintEngineState *s = state();
891 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
894 bool was_aa = s->flags.antialiased;
895 bool was_bilinear = s->flags.bilinear;
897 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
898 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
900 if (was_aa != s->flags.antialiased)
901 s->strokeFlags |= DirtyHints;
903 if (was_bilinear != s->flags.bilinear) {
904 s->strokeFlags |= DirtyPen;
905 s->fillFlags |= DirtyBrush;
908 Q_D(QRasterPaintEngine);
909 d->recalculateFastImages();
915 void QRasterPaintEngine::transformChanged()
917 QRasterPaintEngineState *s = state();
920 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
923 s->fillFlags |= DirtyTransform;
924 s->strokeFlags |= DirtyTransform;
926 s->dirty |= DirtyTransform;
928 Q_D(QRasterPaintEngine);
929 d->recalculateFastImages();
935 void QRasterPaintEngine::clipEnabledChanged()
937 QRasterPaintEngineState *s = state();
940 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
944 s->clip->enabled = s->clipEnabled;
945 s->fillFlags |= DirtyClipEnabled;
946 s->strokeFlags |= DirtyClipEnabled;
947 s->pixmapFlags |= DirtyClipEnabled;
951 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
953 SrcOverBlendFunc func,
958 if (alpha == 0 || !clip.isValid())
961 Q_ASSERT(img.depth() >= 8);
963 int srcBPL = img.bytesPerLine();
964 const uchar *srcBits = img.bits();
965 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
966 int iw = img.width();
967 int ih = img.height();
972 // Adjust the image according to the source offset...
973 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
976 // adapt the x parameters
977 int x = qRound(pt.x());
979 int cx2 = clip.x() + clip.width();
982 srcBits += srcSize * d;
987 int d = x + iw - cx2;
993 // adapt the y paremeters...
995 int cy2 = clip.y() + clip.height();
996 int y = qRound(pt.y());
999 srcBits += srcBPL * d;
1004 int d = y + ih - cy2;
1010 // call the blend function...
1011 int dstSize = rasterBuffer->bytesPerPixel();
1012 int dstBPL = rasterBuffer->bytesPerLine();
1013 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1020 void QRasterPaintEnginePrivate::systemStateChanged()
1022 QRect clipRect(0, 0,
1023 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1024 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1026 if (!systemClip.isEmpty()) {
1027 QRegion clippedDeviceRgn = systemClip & clipRect;
1028 deviceRect = clippedDeviceRgn.boundingRect();
1029 baseClip->setClipRegion(clippedDeviceRgn);
1031 deviceRect = clipRect;
1032 baseClip->setClipRect(deviceRect);
1034 #ifdef QT_DEBUG_DRAW
1035 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
1038 exDeviceRect = deviceRect;
1040 Q_Q(QRasterPaintEngine);
1041 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1042 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1043 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1046 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1048 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1051 Q_Q(QRasterPaintEngine);
1052 bool bilinear = q->state()->flags.bilinear;
1054 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1055 spanData->setupMatrix(b.transform() * m, bilinear);
1057 if (m.type() <= QTransform::TxTranslate) {
1058 // specialize setupMatrix for translation matrices
1059 // to avoid needless matrix inversion
1067 spanData->dx = -m.dx();
1068 spanData->dy = -m.dy();
1069 spanData->txop = m.type();
1070 spanData->bilinear = bilinear;
1071 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1072 spanData->adjustSpanMethods();
1074 spanData->setupMatrix(m, bilinear);
1079 // #define QT_CLIPPING_RATIOS
1081 #ifdef QT_CLIPPING_RATIOS
1086 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1088 if (d->clip()->hasRectClip)
1090 if (d->clip()->hasRegionClip)
1094 if ((totalClips % 5000) == 0) {
1095 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1096 rectClips * 100.0 / (qreal) totalClips,
1097 regionClips * 100.0 / (qreal) totalClips,
1098 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1107 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1109 if (s->flags.has_clip_ownership)
1112 s->flags.has_clip_ownership = false;
1115 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1117 s->fillFlags |= QPaintEngine::DirtyClipPath;
1118 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1119 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1121 d->solid_color_filler.clip = d->clip();
1122 d->solid_color_filler.adjustSpanMethods();
1124 #ifdef QT_DEBUG_DRAW
1125 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1134 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1136 #ifdef QT_DEBUG_DRAW
1137 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1139 if (path.elements()) {
1140 for (int i=0; i<path.elementCount(); ++i) {
1141 qDebug() << " - " << path.elements()[i]
1142 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1145 for (int i=0; i<path.elementCount(); ++i) {
1146 qDebug() << " ---- "
1147 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1152 Q_D(QRasterPaintEngine);
1153 QRasterPaintEngineState *s = state();
1155 const qreal *points = path.points();
1156 const QPainterPath::ElementType *types = path.elements();
1158 // There are some cases that are not supported by clip(QRect)
1159 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1160 if (s->matrix.type() <= QTransform::TxScale
1161 && ((path.shape() == QVectorPath::RectangleHint)
1162 || (isRect(points, path.elementCount())
1163 && (!types || (types[0] == QPainterPath::MoveToElement
1164 && types[1] == QPainterPath::LineToElement
1165 && types[2] == QPainterPath::LineToElement
1166 && types[3] == QPainterPath::LineToElement))))) {
1167 #ifdef QT_DEBUG_DRAW
1168 qDebug() << " --- optimizing vector clip to rect clip...";
1171 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1172 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1177 if (op == Qt::NoClip) {
1178 qrasterpaintengine_state_setNoClip(s);
1181 QClipData *base = d->baseClip.data();
1183 // Intersect with current clip when available...
1184 if (op == Qt::IntersectClip && s->clip)
1187 // We always intersect, except when there is nothing to
1188 // intersect with, in which case we simplify the operation to
1190 Qt::ClipOperation isectOp = Qt::IntersectClip;
1192 isectOp = Qt::ReplaceClip;
1194 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1195 newClip->initialize();
1196 ClipData clipData = { base, newClip, isectOp };
1197 ensureOutlineMapper();
1198 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1202 if (s->flags.has_clip_ownership)
1206 s->flags.has_clip_ownership = true;
1208 qrasterpaintengine_dirty_clip(d, s);
1216 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1218 #ifdef QT_DEBUG_DRAW
1219 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1222 QRasterPaintEngineState *s = state();
1224 if (op == Qt::NoClip) {
1225 qrasterpaintengine_state_setNoClip(s);
1227 } else if (s->matrix.type() > QTransform::TxScale) {
1228 QPaintEngineEx::clip(rect, op);
1231 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1232 QPaintEngineEx::clip(rect, op);
1238 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1240 Q_D(QRasterPaintEngine);
1241 QRect clipRect = r & d->deviceRect;
1242 QRasterPaintEngineState *s = state();
1244 if (op == Qt::ReplaceClip || s->clip == 0) {
1246 // No current clip, hence we intersect with sysclip and be
1248 QRegion clipRegion = systemClip();
1249 QClipData *clip = new QClipData(d->rasterBuffer->height());
1251 if (clipRegion.isEmpty())
1252 clip->setClipRect(clipRect);
1254 clip->setClipRegion(clipRegion & clipRect);
1256 if (s->flags.has_clip_ownership)
1260 s->clip->enabled = true;
1261 s->flags.has_clip_ownership = true;
1263 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1264 QClipData *base = s->clip;
1267 if (base->hasRectClip || base->hasRegionClip) {
1268 if (!s->flags.has_clip_ownership) {
1269 s->clip = new QClipData(d->rasterBuffer->height());
1270 s->flags.has_clip_ownership = true;
1272 if (base->hasRectClip)
1273 s->clip->setClipRect(base->clipRect & clipRect);
1275 s->clip->setClipRegion(base->clipRegion & clipRect);
1276 s->clip->enabled = true;
1284 qrasterpaintengine_dirty_clip(d, s);
1292 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1294 #ifdef QT_DEBUG_DRAW
1295 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1298 Q_D(QRasterPaintEngine);
1300 if (region.rectCount() == 1) {
1301 clip(region.boundingRect(), op);
1305 QRasterPaintEngineState *s = state();
1306 const QClipData *clip = d->clip();
1307 const QClipData *baseClip = d->baseClip.data();
1309 if (op == Qt::NoClip) {
1310 qrasterpaintengine_state_setNoClip(s);
1311 } else if (s->matrix.type() > QTransform::TxScale
1312 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1313 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1314 QPaintEngineEx::clip(region, op);
1316 const QClipData *curClip;
1319 if (op == Qt::IntersectClip)
1324 if (s->flags.has_clip_ownership) {
1328 newClip = new QClipData(d->rasterBuffer->height());
1330 s->flags.has_clip_ownership = true;
1333 QRegion r = s->matrix.map(region);
1334 if (curClip->hasRectClip)
1335 newClip->setClipRegion(r & curClip->clipRect);
1336 else if (curClip->hasRegionClip)
1337 newClip->setClipRegion(r & curClip->clipRegion);
1339 qrasterpaintengine_dirty_clip(d, s);
1346 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1348 #ifdef QT_DEBUG_DRAW
1349 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1352 if (!fillData->blend)
1355 Q_D(QRasterPaintEngine);
1357 const QRectF controlPointRect = path.controlPointRect();
1359 QRasterPaintEngineState *s = state();
1360 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1361 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1362 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1363 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1364 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1365 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1367 if (!s->flags.antialiased && !do_clip) {
1368 d->initializeRasterizer(fillData);
1369 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1373 ensureOutlineMapper();
1374 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1377 static void fillRect_normalized(const QRect &r, QSpanData *data,
1378 QRasterPaintEnginePrivate *pe)
1382 bool rectClipped = true;
1385 x1 = qMax(r.x(), data->clip->xmin);
1386 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1387 y1 = qMax(r.y(), data->clip->ymin);
1388 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1389 rectClipped = data->clip->hasRectClip;
1392 x1 = qMax(r.x(), pe->deviceRect.x());
1393 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1394 y1 = qMax(r.y(), pe->deviceRect.y());
1395 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1397 x1 = qMax(r.x(), 0);
1398 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1399 y1 = qMax(r.y(), 0);
1400 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1403 if (x2 <= x1 || y2 <= y1)
1406 const int width = x2 - x1;
1407 const int height = y2 - y1;
1409 bool isUnclipped = rectClipped
1410 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1412 if (pe && isUnclipped) {
1413 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1415 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1416 || (mode == QPainter::CompositionMode_SourceOver
1417 && qAlpha(data->solid.color) == 255)))
1419 data->fillRect(data->rasterBuffer, x1, y1, width, height,
1425 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1427 const int nspans = 256;
1428 QT_FT_Span spans[nspans];
1430 Q_ASSERT(data->blend);
1433 int n = qMin(nspans, y2 - y);
1437 spans[i].len = width;
1439 spans[i].coverage = 255;
1443 blend(n, spans, data);
1451 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1453 #ifdef QT_DEBUG_DRAW
1454 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1456 Q_D(QRasterPaintEngine);
1458 QRasterPaintEngineState *s = state();
1462 if (s->brushData.blend) {
1463 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1464 const QRect *r = rects;
1465 const QRect *lastRect = rects + rectCount;
1467 int offset_x = int(s->matrix.dx());
1468 int offset_y = int(s->matrix.dy());
1469 while (r < lastRect) {
1470 QRect rect = r->normalized();
1471 QRect rr = rect.translated(offset_x, offset_y);
1472 fillRect_normalized(rr, &s->brushData, d);
1476 QRectVectorPath path;
1477 for (int i=0; i<rectCount; ++i) {
1479 fill(path, s->brush);
1485 if (s->penData.blend) {
1486 QRectVectorPath path;
1487 if (s->flags.fast_pen) {
1488 QCosmeticStroker stroker(s, d->deviceRect);
1489 for (int i = 0; i < rectCount; ++i) {
1491 stroker.drawPath(path);
1494 for (int i = 0; i < rectCount; ++i) {
1496 stroke(path, s->pen);
1505 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1507 #ifdef QT_DEBUG_DRAW
1508 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1510 #ifdef QT_FAST_SPANS
1511 Q_D(QRasterPaintEngine);
1513 QRasterPaintEngineState *s = state();
1516 if (s->flags.tx_noshear) {
1518 if (s->brushData.blend) {
1519 d->initializeRasterizer(&s->brushData);
1520 for (int i = 0; i < rectCount; ++i) {
1521 const QRectF &rect = rects[i].normalized();
1524 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1525 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1526 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1531 if (s->penData.blend) {
1532 QRectVectorPath path;
1533 if (s->flags.fast_pen) {
1534 QCosmeticStroker stroker(s, d->deviceRect);
1535 for (int i = 0; i < rectCount; ++i) {
1537 stroker.drawPath(path);
1540 for (int i = 0; i < rectCount; ++i) {
1542 QPaintEngineEx::stroke(path, s->lastPen);
1549 #endif // QT_FAST_SPANS
1550 QPaintEngineEx::drawRects(rects, rectCount);
1557 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1559 Q_D(QRasterPaintEngine);
1560 QRasterPaintEngineState *s = state();
1563 if (!s->penData.blend)
1566 if (s->flags.fast_pen) {
1567 QCosmeticStroker stroker(s, d->deviceRect);
1568 stroker.drawPath(path);
1569 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1570 qreal width = s->lastPen.isCosmetic()
1571 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1572 : qpen_widthf(s->lastPen) * s->txscale;
1574 qreal dashOffset = s->lastPen.dashOffset();
1576 qreal patternLength = 0;
1577 const QVector<qreal> pattern = s->lastPen.dashPattern();
1578 for (int i = 0; i < pattern.size(); ++i)
1579 patternLength += pattern.at(i);
1581 if (patternLength > 0) {
1582 int n = qFloor(dashOffset / patternLength);
1583 dashOffset -= n * patternLength;
1584 while (dashOffset >= pattern.at(dashIndex)) {
1585 dashOffset -= pattern.at(dashIndex);
1586 if (++dashIndex >= pattern.size())
1592 Q_D(QRasterPaintEngine);
1593 d->initializeRasterizer(&s->penData);
1594 int lineCount = path.elementCount() / 2;
1595 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1597 for (int i = 0; i < lineCount; ++i) {
1598 if (lines[i].p1() == lines[i].p2()) {
1599 if (s->lastPen.capStyle() != Qt::FlatCap) {
1600 QPointF p = lines[i].p1();
1601 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1602 QPointF(p.x() + width*0.5, p.y())));
1603 d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
1608 const QLineF line = s->matrix.map(lines[i]);
1609 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1610 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1611 width / line.length(),
1612 s->lastPen.capStyle() == Qt::SquareCap);
1614 d->rasterizeLine_dashed(line, width,
1615 &dashIndex, &dashOffset, &inDash);
1620 QPaintEngineEx::stroke(path, pen);
1623 static inline QRect toNormalizedFillRect(const QRectF &rect)
1625 int x1 = qRound(rect.x() + aliasedCoordinateDelta);
1626 int y1 = qRound(rect.y() + aliasedCoordinateDelta);
1627 int x2 = qRound(rect.right() + aliasedCoordinateDelta);
1628 int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
1635 return QRect(x1, y1, x2 - x1, y2 - y1);
1641 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1645 #ifdef QT_DEBUG_DRAW
1646 QRectF rf = path.controlPointRect();
1647 qDebug() << "QRasterPaintEngine::fill(): "
1648 << "size=" << path.elementCount()
1649 << ", hints=" << hex << path.hints()
1653 Q_D(QRasterPaintEngine);
1654 QRasterPaintEngineState *s = state();
1657 if (!s->brushData.blend)
1660 if (path.shape() == QVectorPath::RectangleHint) {
1661 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1662 const qreal *p = path.points();
1663 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1664 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1665 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1669 if (s->flags.tx_noshear) {
1670 d->initializeRasterizer(&s->brushData);
1671 // ### Is normalizing really necessary here?
1672 const qreal *p = path.points();
1673 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1675 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1676 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1677 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1683 // ### Optimize for non transformed ellipses and rectangles...
1684 QRectF cpRect = path.controlPointRect();
1685 const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
1686 ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
1689 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1690 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1691 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1692 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1694 // ### Falonc: implement....
1695 // if (!s->flags.antialiased && !do_clip) {
1696 // d->initializeRasterizer(&s->brushData);
1697 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1701 ensureOutlineMapper();
1702 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1705 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1707 Q_D(QRasterPaintEngine);
1708 QRasterPaintEngineState *s = state();
1710 if (!s->flags.antialiased) {
1711 uint txop = s->matrix.type();
1712 if (txop == QTransform::TxNone) {
1713 fillRect_normalized(toNormalizedFillRect(r), data, d);
1715 } else if (txop == QTransform::TxTranslate) {
1716 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1717 fillRect_normalized(rr, data, d);
1719 } else if (txop == QTransform::TxScale) {
1720 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1721 fillRect_normalized(rr, data, d);
1726 if (s->flags.tx_noshear) {
1727 d->initializeRasterizer(data);
1728 QRectF nr = r.normalized();
1729 if (!nr.isEmpty()) {
1730 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1731 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1732 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1739 ensureOutlineMapper();
1740 fillPath(path, data);
1746 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1748 #ifdef QT_DEBUG_DRAW
1749 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1751 QRasterPaintEngineState *s = state();
1754 if (!s->brushData.blend)
1757 fillRect(r, &s->brushData);
1763 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1765 #ifdef QT_DEBUG_DRAW
1766 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1768 Q_D(QRasterPaintEngine);
1769 QRasterPaintEngineState *s = state();
1771 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
1772 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
1773 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1776 d->solid_color_filler.clip = d->clip();
1777 d->solid_color_filler.adjustSpanMethods();
1778 fillRect(r, &d->solid_color_filler);
1781 static inline bool isAbove(const QPointF *a, const QPointF *b)
1783 return a->y() < b->y();
1786 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1791 Q_ASSERT(pointCount >= 2);
1793 QVector<const QPointF *> sorted;
1794 sorted.reserve(pointCount);
1796 upper->reserve(pointCount * 3 / 4);
1797 lower->reserve(pointCount * 3 / 4);
1799 for (int i = 0; i < pointCount; ++i)
1800 sorted << points + i;
1802 qSort(sorted.begin(), sorted.end(), isAbove);
1804 qreal splitY = sorted.at(sorted.size() / 2)->y();
1806 const QPointF *end = points + pointCount;
1807 const QPointF *last = end - 1;
1809 QVector<QPointF> *bin[2] = { upper, lower };
1811 for (const QPointF *p = points; p < end; ++p) {
1812 int side = p->y() < splitY;
1813 int lastSide = last->y() < splitY;
1815 if (side != lastSide) {
1816 if (qFuzzyCompare(p->y(), splitY)) {
1817 bin[!side]->append(*p);
1818 } else if (qFuzzyCompare(last->y(), splitY)) {
1819 bin[side]->append(*last);
1821 QPointF delta = *p - *last;
1822 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1824 bin[0]->append(intersection);
1825 bin[1]->append(intersection);
1829 bin[side]->append(*p);
1834 // give up if we couldn't reduce the point count
1835 return upper->size() < pointCount && lower->size() < pointCount;
1841 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1843 Q_D(QRasterPaintEngine);
1844 QRasterPaintEngineState *s = state();
1846 const int maxPoints = 0xffff;
1848 // max amount of points that raster engine can reliably handle
1849 if (pointCount > maxPoints) {
1850 QVector<QPointF> upper, lower;
1852 if (splitPolygon(points, pointCount, &upper, &lower)) {
1853 fillPolygon(upper.constData(), upper.size(), mode);
1854 fillPolygon(lower.constData(), lower.size(), mode);
1856 qWarning("Polygon too complex for filling.");
1861 // Compose polygon fill..,
1862 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1863 ensureOutlineMapper();
1864 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1867 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1869 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1875 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1877 Q_D(QRasterPaintEngine);
1878 QRasterPaintEngineState *s = state();
1880 #ifdef QT_DEBUG_DRAW
1881 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1882 for (int i=0; i<pointCount; ++i)
1883 qDebug() << " - " << points[i];
1885 Q_ASSERT(pointCount >= 2);
1887 if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
1888 QRectF r(points[0], points[2]);
1894 if (mode != PolylineMode) {
1897 if (s->brushData.blend) {
1898 d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
1899 fillPolygon(points, pointCount, mode);
1900 d->outlineMapper->setCoordinateRounding(false);
1904 // Do the outline...
1905 if (s->penData.blend) {
1906 QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1907 if (s->flags.fast_pen) {
1908 QCosmeticStroker stroker(s, d->deviceRect);
1909 stroker.drawPath(vp);
1911 QPaintEngineEx::stroke(vp, s->lastPen);
1919 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1921 Q_D(QRasterPaintEngine);
1922 QRasterPaintEngineState *s = state();
1924 #ifdef QT_DEBUG_DRAW
1925 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1926 for (int i=0; i<pointCount; ++i)
1927 qDebug() << " - " << points[i];
1929 Q_ASSERT(pointCount >= 2);
1930 if (mode != PolylineMode && isRect((int *) points, pointCount)) {
1931 QRect r(points[0].x(),
1933 points[2].x() - points[0].x(),
1934 points[2].y() - points[0].y());
1942 if (mode != PolylineMode) {
1944 if (s->brushData.blend) {
1945 // Compose polygon fill..,
1946 ensureOutlineMapper();
1947 d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
1948 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1949 d->outlineMapper->moveTo(*points);
1950 const QPoint *p = points;
1951 const QPoint *ep = points + pointCount - 1;
1953 d->outlineMapper->lineTo(*(++p));
1955 d->outlineMapper->endOutline();
1958 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1960 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1961 d->outlineMapper->setCoordinateRounding(false);
1965 // Do the outline...
1966 if (s->penData.blend) {
1967 int count = pointCount * 2;
1968 QVarLengthArray<qreal> fpoints(count);
1969 for (int i=0; i<count; ++i)
1970 fpoints[i] = ((int *) points)[i];
1971 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
1973 if (s->flags.fast_pen) {
1974 QCosmeticStroker stroker(s, d->deviceRect);
1975 stroker.drawPath(vp);
1977 QPaintEngineEx::stroke(vp, s->lastPen);
1985 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1987 #ifdef QT_DEBUG_DRAW
1988 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1991 QPlatformPixmap *pd = pixmap.handle();
1992 if (pd->classId() == QPlatformPixmap::RasterClass) {
1993 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
1994 if (image.depth() == 1) {
1995 Q_D(QRasterPaintEngine);
1996 QRasterPaintEngineState *s = state();
1997 if (s->matrix.type() <= QTransform::TxTranslate) {
1999 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2001 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2004 QRasterPaintEngine::drawImage(pos, image);
2007 const QImage image = pixmap.toImage();
2008 if (pixmap.depth() == 1) {
2009 Q_D(QRasterPaintEngine);
2010 QRasterPaintEngineState *s = state();
2011 if (s->matrix.type() <= QTransform::TxTranslate) {
2013 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2015 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2018 QRasterPaintEngine::drawImage(pos, image);
2026 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2028 #ifdef QT_DEBUG_DRAW
2029 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2032 QPlatformPixmap* pd = pixmap.handle();
2033 if (pd->classId() == QPlatformPixmap::RasterClass) {
2034 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2035 if (image.depth() == 1) {
2036 Q_D(QRasterPaintEngine);
2037 QRasterPaintEngineState *s = state();
2038 if (s->matrix.type() <= QTransform::TxTranslate
2039 && r.size() == sr.size()
2040 && r.size() == pixmap.size()) {
2042 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2045 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2048 drawImage(r, image, sr);
2051 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2052 const QImage image = pd->toImage(clippedSource);
2053 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2054 if (image.depth() == 1) {
2055 Q_D(QRasterPaintEngine);
2056 QRasterPaintEngineState *s = state();
2057 if (s->matrix.type() <= QTransform::TxTranslate
2058 && r.size() == sr.size()
2059 && r.size() == pixmap.size()) {
2061 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2064 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2067 drawImage(r, image, translatedSource);
2072 // assumes that rect has positive width and height
2073 static inline const QRect toRect_normalized(const QRectF &rect)
2075 const int x = qRound(rect.x());
2076 const int y = qRound(rect.y());
2077 const int w = int(rect.width() + qreal(0.5));
2078 const int h = int(rect.height() + qreal(0.5));
2080 return QRect(x, y, w, h);
2083 static inline int fast_ceil_positive(const qreal &v)
2085 const int iv = int(v);
2092 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2094 const int xmin = int(rect.x());
2095 const int xmax = int(fast_ceil_positive(rect.right()));
2096 const int ymin = int(rect.y());
2097 const int ymax = int(fast_ceil_positive(rect.bottom()));
2098 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2104 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2106 #ifdef QT_DEBUG_DRAW
2107 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2110 Q_D(QRasterPaintEngine);
2111 QRasterPaintEngineState *s = state();
2113 if (s->matrix.type() > QTransform::TxTranslate) {
2114 drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
2116 QRectF(0, 0, img.width(), img.height()));
2119 const QClipData *clip = d->clip();
2120 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2122 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2123 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2126 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2128 } else if (clip->hasRectClip) {
2129 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2137 d->image_filler.clip = clip;
2138 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2139 if (!d->image_filler.blend)
2141 d->image_filler.dx = -pt.x();
2142 d->image_filler.dy = -pt.y();
2143 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2145 fillRect_normalized(rr, &d->image_filler, d);
2150 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2152 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2163 inline RotationType qRotationType(const QTransform &transform)
2165 QTransform::TransformationType type = transform.type();
2167 if (type > QTransform::TxRotate)
2170 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2171 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2174 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2175 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2178 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2179 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2185 inline bool isPixelAligned(const QRectF &rect) {
2186 return QRectF(rect.toRect()) == rect;
2193 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2194 Qt::ImageConversionFlags)
2196 #ifdef QT_DEBUG_DRAW
2197 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2203 Q_D(QRasterPaintEngine);
2204 QRasterPaintEngineState *s = state();
2205 int sr_l = qFloor(sr.left());
2206 int sr_r = qCeil(sr.right()) - 1;
2207 int sr_t = qFloor(sr.top());
2208 int sr_b = qCeil(sr.bottom()) - 1;
2210 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2211 // as fillRect will apply the aliased coordinate delta we need to
2212 // subtract it here as we don't use it for image drawing
2213 QTransform old = s->matrix;
2214 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2216 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2217 QRgb color = img.pixel(sr_l, sr_t);
2218 switch (img.format()) {
2219 case QImage::Format_ARGB32_Premultiplied:
2220 case QImage::Format_ARGB8565_Premultiplied:
2221 case QImage::Format_ARGB6666_Premultiplied:
2222 case QImage::Format_ARGB8555_Premultiplied:
2223 case QImage::Format_ARGB4444_Premultiplied:
2224 // Combine premultiplied color with the opacity set on the painter.
2225 d->solid_color_filler.solid.color =
2226 ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
2227 | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
2230 d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
2234 if ((d->solid_color_filler.solid.color & 0xff000000) == 0
2235 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
2239 d->solid_color_filler.clip = d->clip();
2240 d->solid_color_filler.adjustSpanMethods();
2241 fillRect(r, &d->solid_color_filler);
2247 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2249 const QClipData *clip = d->clip();
2251 if (s->matrix.type() > QTransform::TxTranslate
2253 && (!clip || clip->hasRectClip)
2254 && s->intOpacity == 256
2255 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2256 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2257 && d->rasterBuffer->format == img.format()
2258 && (d->rasterBuffer->format == QImage::Format_RGB16
2259 || d->rasterBuffer->format == QImage::Format_RGB32
2260 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2261 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2263 RotationType rotationType = qRotationType(s->matrix);
2265 if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2266 QRectF transformedTargetRect = s->matrix.mapRect(r);
2268 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2269 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2271 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2272 if (clippedTransformedTargetRect.isNull())
2275 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2277 QRect clippedSourceRect
2278 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2279 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2281 uint dbpl = d->rasterBuffer->bytesPerLine();
2282 uint sbpl = img.bytesPerLine();
2284 uchar *dst = d->rasterBuffer->buffer();
2285 uint bpp = img.depth() >> 3;
2287 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2288 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2290 uint cw = clippedSourceRect.width();
2291 uint ch = clippedSourceRect.height();
2293 qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2300 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2302 QRectF targetBounds = s->matrix.mapRect(r);
2303 bool exceedsPrecision = targetBounds.width() > 0xffff
2304 || targetBounds.height() > 0xffff;
2306 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2307 if (s->matrix.type() > QTransform::TxScale) {
2308 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2309 if (func && (!clip || clip->hasRectClip)) {
2310 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2311 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2312 s->matrix, s->intOpacity);
2316 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2317 if (func && (!clip || clip->hasRectClip)) {
2318 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2319 img.bits(), img.bytesPerLine(),
2320 qt_mapRect_non_normalizing(r, s->matrix), sr,
2321 !clip ? d->deviceRect : clip->clipRect,
2328 QTransform copy = s->matrix;
2329 copy.translate(r.x(), r.y());
2331 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2332 copy.translate(-sr.x(), -sr.y());
2334 d->image_filler_xform.clip = clip;
2335 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2336 if (!d->image_filler_xform.blend)
2338 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2340 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2341 QRectF rr = s->matrix.mapRect(r);
2343 const int x1 = qRound(rr.x());
2344 const int y1 = qRound(rr.y());
2345 const int x2 = qRound(rr.right());
2346 const int y2 = qRound(rr.bottom());
2348 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2352 #ifdef QT_FAST_SPANS
2354 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2355 d->initializeRasterizer(&d->image_filler_xform);
2356 d->rasterizer->setAntialiased(s->flags.antialiased);
2358 const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
2360 const QRectF &rect = r.normalized();
2361 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2362 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2364 if (s->flags.tx_noshear)
2365 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2367 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2371 const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
2374 QTransform m = s->matrix;
2375 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2376 m.m21(), m.m22(), m.m23(),
2377 m.m31() - offs, m.m32() - offs, m.m33());
2378 fillPath(path, &d->image_filler_xform);
2381 if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2382 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2384 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2386 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2388 } else if (clip->hasRectClip) {
2389 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2395 d->image_filler.clip = clip;
2396 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2397 if (!d->image_filler.blend)
2399 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2400 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2403 rr.translate(s->matrix.dx(), s->matrix.dy());
2405 const int x1 = qRound(rr.x());
2406 const int y1 = qRound(rr.y());
2407 const int x2 = qRound(rr.right());
2408 const int y2 = qRound(rr.bottom());
2410 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2417 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2419 #ifdef QT_DEBUG_DRAW
2420 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2422 Q_D(QRasterPaintEngine);
2423 QRasterPaintEngineState *s = state();
2427 QPlatformPixmap *pd = pixmap.handle();
2428 if (pd->classId() == QPlatformPixmap::RasterClass) {
2429 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2431 image = pixmap.toImage();
2434 if (image.depth() == 1)
2435 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2437 if (s->matrix.type() > QTransform::TxTranslate) {
2438 QTransform copy = s->matrix;
2439 copy.translate(r.x(), r.y());
2440 copy.translate(-sr.x(), -sr.y());
2441 d->image_filler_xform.clip = d->clip();
2442 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2443 if (!d->image_filler_xform.blend)
2445 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2447 #ifdef QT_FAST_SPANS
2449 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2450 d->initializeRasterizer(&d->image_filler_xform);
2451 d->rasterizer->setAntialiased(s->flags.antialiased);
2453 const QRectF &rect = r.normalized();
2454 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2455 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2456 if (s->flags.tx_noshear)
2457 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2459 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2465 fillPath(path, &d->image_filler_xform);
2467 d->image_filler.clip = d->clip();
2469 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2470 if (!d->image_filler.blend)
2472 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2473 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2476 rr.translate(s->matrix.dx(), s->matrix.dy());
2477 fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
2483 static inline bool monoVal(const uchar* s, int x)
2485 return (s[x>>3] << (x&7)) & 0x80;
2491 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
2493 Q_D(QRasterPaintEngine);
2494 QRasterPaintEngineState *s = state();
2496 if (!s->penData.blend)
2499 QRasterBuffer *rb = d->rasterBuffer.data();
2501 const QRect rect(rx, ry, w, h);
2502 const QClipData *clip = d->clip();
2503 bool unclipped = false;
2505 // inlined QRect::intersects
2506 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2507 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2509 if (clip->hasRectClip) {
2510 unclipped = rx > clip->xmin
2511 && rx + w < clip->xmax
2513 && ry + h < clip->ymax;
2519 // inlined QRect::intersects
2520 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2521 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2525 // inlined QRect::contains
2526 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2527 && rect.top() >= 0 && rect.bottom() < rb->height();
2529 unclipped = contains && d->isUnclipped_normalized(rect);
2532 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2533 const uchar * scanline = static_cast<const uchar *>(src);
2535 if (s->flags.fast_text) {
2538 if (s->penData.bitmapBlit) {
2539 s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
2540 scanline, w, h, bpl);
2543 } else if (depth == 8) {
2544 if (s->penData.alphamapBlit) {
2545 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2546 scanline, w, h, bpl, 0);
2549 } else if (depth == 32) {
2550 // (A)RGB Alpha mask where the alpha component is not used.
2551 if (s->penData.alphaRGBBlit) {
2552 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2553 (const uint *) scanline, w, h, bpl / 4, 0);
2557 } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
2558 // (A)RGB Alpha mask where the alpha component is not used.
2560 int nx = qMax(0, rx);
2561 int ny = qMax(0, ry);
2563 // Move scanline pointer to compensate for moved x and y
2564 int xdiff = nx - rx;
2565 int ydiff = ny - ry;
2566 scanline += ydiff * bpl;
2567 scanline += xdiff * (depth == 32 ? 4 : 1);
2572 if (nx + w > d->rasterBuffer->width())
2573 w = d->rasterBuffer->width() - nx;
2574 if (ny + h > d->rasterBuffer->height())
2575 h = d->rasterBuffer->height() - ny;
2580 if (depth == 8 && s->penData.alphamapBlit) {
2581 s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
2582 scanline, w, h, bpl, clip);
2583 } else if (depth == 32 && s->penData.alphaRGBBlit) {
2584 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
2585 (const uint *) scanline, w, h, bpl / 4, clip);
2600 scanline += bpl * y0;
2604 w = qMin(w, rb->width() - qMax(0, rx));
2605 h = qMin(h, rb->height() - qMax(0, ry));
2607 if (w <= 0 || h <= 0)
2610 const int NSPANS = 256;
2611 QSpan spans[NSPANS];
2614 const int x1 = x0 + w;
2615 const int y1 = y0 + h;
2618 for (int y = y0; y < y1; ++y) {
2619 for (int x = x0; x < x1; ) {
2620 if (!monoVal(scanline, x)) {
2625 if (current == NSPANS) {
2626 blend(current, spans, &s->penData);
2629 spans[current].x = x + rx;
2630 spans[current].y = y + ry;
2631 spans[current].coverage = 255;
2634 // extend span until we find a different one.
2635 while (x < x1 && monoVal(scanline, x)) {
2639 spans[current].len = len;
2644 } else if (depth == 8) {
2645 for (int y = y0; y < y1; ++y) {
2646 for (int x = x0; x < x1; ) {
2647 // Skip those with 0 coverage
2648 if (scanline[x] == 0) {
2653 if (current == NSPANS) {
2654 blend(current, spans, &s->penData);
2657 int coverage = scanline[x];
2658 spans[current].x = x + rx;
2659 spans[current].y = y + ry;
2660 spans[current].coverage = coverage;
2664 // extend span until we find a different one.
2665 while (x < x1 && scanline[x] == coverage) {
2669 spans[current].len = len;
2674 } else { // 32-bit alpha...
2675 uint *sl = (uint *) src;
2676 for (int y = y0; y < y1; ++y) {
2677 for (int x = x0; x < x1; ) {
2678 // Skip those with 0 coverage
2679 if ((sl[x] & 0x00ffffff) == 0) {
2684 if (current == NSPANS) {
2685 blend(current, spans, &s->penData);
2688 uint rgbCoverage = sl[x];
2689 int coverage = qGreen(rgbCoverage);
2690 spans[current].x = x + rx;
2691 spans[current].y = y + ry;
2692 spans[current].coverage = coverage;
2696 // extend span until we find a different one.
2697 while (x < x1 && sl[x] == rgbCoverage) {
2701 spans[current].len = len;
2704 sl += bpl / sizeof(uint);
2707 // qDebug() << "alphaPenBlt: num spans=" << current
2708 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2709 // Call span func for current set of spans.
2711 blend(current, spans, &s->penData);
2714 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2715 const QFixedPoint *positions, QFontEngine *fontEngine)
2717 Q_D(QRasterPaintEngine);
2718 QRasterPaintEngineState *s = state();
2720 #if !defined(QT_NO_FREETYPE)
2721 if (fontEngine->type() == QFontEngine::Freetype) {
2722 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
2723 QFontEngineFT::GlyphFormat neededFormat =
2724 painter()->device()->devType() == QInternal::Widget
2725 ? fe->defaultGlyphFormat()
2726 : QFontEngineFT::Format_A8;
2728 if (d_func()->mono_surface
2729 || fe->isBitmapFont() // alphaPenBlt can handle mono, too
2731 neededFormat = QFontEngineFT::Format_Mono;
2733 if (neededFormat == QFontEngineFT::Format_None)
2734 neededFormat = QFontEngineFT::Format_A8;
2736 QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
2737 if (s->matrix.type() >= QTransform::TxScale) {
2738 if (s->matrix.isAffine())
2739 gset = fe->loadTransformedGlyphSet(s->matrix);
2744 if (!gset || gset->outline_drawing
2745 || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
2748 FT_Face lockedFace = 0;
2751 switch (neededFormat) {
2752 case QFontEngineFT::Format_Mono:
2755 case QFontEngineFT::Format_A8:
2758 case QFontEngineFT::Format_A32:
2766 for (int i = 0; i < numGlyphs; i++) {
2767 QFixed spp = fe->subPixelPositionForX(positions[i].x);
2768 QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
2770 if (!glyph || glyph->format != neededFormat) {
2772 lockedFace = fe->lockFace();
2773 glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
2776 if (!glyph || !glyph->data)
2780 switch (neededFormat) {
2781 case QFontEngineFT::Format_Mono:
2782 pitch = ((glyph->width + 31) & ~31) >> 3;
2784 case QFontEngineFT::Format_A8:
2785 pitch = (glyph->width + 3) & ~3;
2787 case QFontEngineFT::Format_A32:
2788 pitch = glyph->width * 4;
2795 alphaPenBlt(glyph->data, pitch, depth,
2796 qFloor(positions[i].x) + glyph->x,
2797 qFloor(positions[i].y) - glyph->y,
2798 glyph->width, glyph->height);
2805 QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
2807 QImageTextureGlyphCache *cache =
2808 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
2810 cache = new QImageTextureGlyphCache(glyphType, s->matrix);
2811 fontEngine->setGlyphCache(0, cache);
2814 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2815 cache->fillInPendingGlyphs();
2817 const QImage &image = cache->image();
2818 int bpl = image.bytesPerLine();
2820 int depth = image.depth();
2824 leftShift = 2; // multiply by 4
2825 else if (depth == 1)
2826 rightShift = 3; // divide by 8
2828 int margin = cache->glyphMargin();
2829 const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
2830 const uchar *bits = image.bits();
2831 for (int i=0; i<numGlyphs; ++i) {
2833 QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
2834 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2835 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2839 int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
2840 int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
2842 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2845 // c.baseLineX, c.baseLineY,
2848 // positions[i].x.toInt(), positions[i].y.toInt());
2850 alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
2856 #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
2857 void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
2859 Q_D(QRasterPaintEngine);
2860 QRasterPaintEngineState *s = state();
2862 QFontEngine *fontEngine = ti.fontEngine;
2863 if (fontEngine->type() != QFontEngine::S60FontEngine) {
2864 QPaintEngineEx::drawTextItem(p, ti);
2868 QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
2870 QVarLengthArray<QFixedPoint> positions;
2871 QVarLengthArray<glyph_t> glyphs;
2872 QTransform matrix = s->matrix;
2873 matrix.translate(p.x(), p.y());
2874 if (matrix.type() == QTransform::TxScale)
2875 fe->setFontScale(matrix.m11());
2876 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
2878 const QFixed aliasDelta = QFixed::fromReal(aliasedCoordinateDelta);
2880 for (int i=0; i<glyphs.size(); ++i) {
2881 TOpenFontCharMetrics tmetrics;
2882 const TUint8 *glyphBitmapBytes;
2883 TSize glyphBitmapSize;
2884 fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
2885 const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta);
2886 const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta);
2887 alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
2890 if (matrix.type() == QTransform::TxScale)
2891 fe->setFontScale(1.0);
2895 #endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
2898 * Returns true if the rectangle is completely within the current clip
2899 * state of the paint engine.
2901 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2903 const QClipData *cl = clip();
2905 // inline contains() for performance (we know the rects are normalized)
2906 const QRect &r1 = deviceRect;
2907 return (r.left() >= r1.left() && r.right() <= r1.right()
2908 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2912 if (cl->hasRectClip) {
2913 // currently all painting functions clips to deviceRect internally
2914 if (cl->clipRect == deviceRect)
2917 // inline contains() for performance (we know the rects are normalized)
2918 const QRect &r1 = cl->clipRect;
2919 return (r.left() >= r1.left() && r.right() <= r1.right()
2920 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2922 return qt_region_strictContains(cl->clipRegion, r);
2926 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2929 Q_Q(const QRasterPaintEngine);
2930 const QRasterPaintEngineState *s = q->state();
2931 const QClipData *cl = clip();
2933 QRect r = rect.normalized();
2934 // inline contains() for performance (we know the rects are normalized)
2935 const QRect &r1 = deviceRect;
2936 return (r.left() >= r1.left() && r.right() <= r1.right()
2937 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2941 // currently all painting functions that call this function clip to deviceRect internally
2942 if (cl->hasRectClip && cl->clipRect == deviceRect)
2945 if (s->flags.antialiased)
2948 QRect r = rect.normalized();
2950 r.setX(r.x() - penWidth);
2951 r.setY(r.y() - penWidth);
2952 r.setWidth(r.width() + 2 * penWidth);
2953 r.setHeight(r.height() + 2 * penWidth);
2956 if (cl->hasRectClip) {
2957 // inline contains() for performance (we know the rects are normalized)
2958 const QRect &r1 = cl->clipRect;
2959 return (r.left() >= r1.left() && r.right() <= r1.right()
2960 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2962 return qt_region_strictContains(cl->clipRegion, r);
2966 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2969 return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
2973 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2974 const QSpanData *data) const
2976 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2980 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2981 const QSpanData *data) const
2983 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2987 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2988 const QSpanData *data) const
2990 Q_Q(const QRasterPaintEngine);
2991 const QRasterPaintEngineState *s = q->state();
2993 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2995 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2996 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3002 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3007 QFontEngine *fontEngine = textItem->fontEngine();
3008 if (shouldDrawCachedGlyphs(fontEngine->fontDef.pixelSize, state()->matrix)) {
3009 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3012 QPaintEngineEx::drawStaticTextItem(textItem);
3019 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3021 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3022 QRasterPaintEngineState *s = state();
3024 #ifdef QT_DEBUG_DRAW
3025 Q_D(QRasterPaintEngine);
3026 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3027 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3034 #if defined (Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_WS_QPA)
3036 if (!supportsTransformations(ti.fontEngine)) {
3037 QVarLengthArray<QFixedPoint> positions;
3038 QVarLengthArray<glyph_t> glyphs;
3040 QTransform matrix = s->matrix;
3041 matrix.translate(p.x(), p.y());
3043 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3045 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3049 #elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_OS_WIN || Q_WS_MAC || Q_WS_QPA
3050 if (s->matrix.type() <= QTransform::TxTranslate
3051 || (s->matrix.type() == QTransform::TxScale
3052 && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
3053 drawGlyphsS60(p, ti);
3056 #else // Q_OS_WIN || Q_WS_MAC || Q_WS_QPA
3058 QFontEngine *fontEngine = ti.fontEngine;
3060 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3062 if (fontEngine->type() != QFontEngine::Freetype) {
3063 QPaintEngineEx::drawTextItem(p, ti);
3067 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3069 QTransform matrix = s->matrix;
3070 matrix.translate(p.x(), p.y());
3072 QVarLengthArray<QFixedPoint> positions;
3073 QVarLengthArray<glyph_t> glyphs;
3074 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3075 if (glyphs.size() == 0)
3078 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3079 QPaintEngine::drawTextItem(p, ti);
3085 QPaintEngineEx::drawTextItem(p, ti);
3091 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3093 Q_D(QRasterPaintEngine);
3094 QRasterPaintEngineState *s = state();
3097 if (!s->penData.blend)
3100 if (!s->flags.fast_pen) {
3101 QPaintEngineEx::drawPoints(points, pointCount);
3105 QCosmeticStroker stroker(s, d->deviceRect);
3106 stroker.drawPoints(points, pointCount);
3110 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3112 Q_D(QRasterPaintEngine);
3113 QRasterPaintEngineState *s = state();
3116 if (!s->penData.blend)
3119 if (!s->flags.fast_pen) {
3120 QPaintEngineEx::drawPoints(points, pointCount);
3124 QCosmeticStroker stroker(s, d->deviceRect);
3125 stroker.drawPoints(points, pointCount);
3131 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3133 #ifdef QT_DEBUG_DRAW
3134 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3136 Q_D(QRasterPaintEngine);
3137 QRasterPaintEngineState *s = state();
3140 if (!s->penData.blend)
3143 if (s->flags.fast_pen) {
3144 QCosmeticStroker stroker(s, d->deviceRect);
3145 for (int i=0; i<lineCount; ++i) {
3146 const QLine &l = lines[i];
3147 stroker.drawLine(l.p1(), l.p2());
3150 QPaintEngineEx::drawLines(lines, lineCount);
3154 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3160 Q_Q(QRasterPaintEngine);
3161 QRasterPaintEngineState *s = q->state();
3163 const QPen &pen = s->lastPen;
3164 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3165 const QVector<qreal> pattern = pen.dashPattern();
3167 qreal patternLength = 0;
3168 for (int i = 0; i < pattern.size(); ++i)
3169 patternLength += pattern.at(i);
3171 if (patternLength <= 0)
3174 qreal length = line.length();
3175 Q_ASSERT(length > 0);
3176 while (length > 0) {
3177 const bool rasterize = *inDash;
3178 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3181 if (dash >= length) {
3183 *dashOffset += dash / width;
3187 *inDash = !(*inDash);
3188 if (++*dashIndex >= pattern.size())
3195 if (rasterize && dash > 0)
3196 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3203 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3205 #ifdef QT_DEBUG_DRAW
3206 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3208 Q_D(QRasterPaintEngine);
3209 QRasterPaintEngineState *s = state();
3212 if (!s->penData.blend)
3214 if (s->flags.fast_pen) {
3215 QCosmeticStroker stroker(s, d->deviceRect);
3216 for (int i=0; i<lineCount; ++i) {
3217 QLineF line = lines[i];
3218 stroker.drawLine(line.p1(), line.p2());
3221 QPaintEngineEx::drawLines(lines, lineCount);
3229 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3231 Q_D(QRasterPaintEngine);
3232 QRasterPaintEngineState *s = state();
3235 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3236 || (qpen_style(s->lastPen) == Qt::NoPen))
3237 && !s->flags.antialiased
3238 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3240 && s->matrix.type() <= QTransform::TxScale) // no shear
3243 const QRectF r = s->matrix.mapRect(rect);
3244 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3245 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3246 const QRect brect = QRect(int(r.x()), int(r.y()),
3247 int_dim(r.x(), r.width()),
3248 int_dim(r.y(), r.height()));
3250 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3251 &s->penData, &s->brushData);
3255 QPaintEngineEx::drawEllipse(rect);
3262 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3264 Q_D(QRasterPaintEngine);
3271 CGContextRef QRasterPaintEngine::getCGContext() const
3273 Q_D(const QRasterPaintEngine);
3274 return d->cgContext;
3282 void QRasterPaintEngine::setDC(HDC hdc) {
3283 Q_D(QRasterPaintEngine);
3290 HDC QRasterPaintEngine::getDC() const
3292 Q_D(const QRasterPaintEngine);
3299 void QRasterPaintEngine::releaseDC(HDC) const
3305 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3307 const QTransform &m = state()->matrix;
3308 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3309 QFontEngine::Type fontEngineType = fontEngine->type();
3310 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3311 || (m.type() <= QTransform::TxTranslate
3312 && (fontEngineType == QFontEngine::TestFontEngine
3313 || fontEngineType == QFontEngine::Box))) {
3317 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3320 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3322 #if defined(Q_WS_MAC)
3323 // Mac font engines don't support scaling and rotation
3324 if (m.type() > QTransform::TxTranslate)
3326 if (m.type() >= QTransform::TxProject)
3330 return !shouldDrawCachedGlyphs(pixelSize, m);
3336 QPoint QRasterPaintEngine::coordinateOffset() const
3338 return QPoint(0, 0);
3341 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3346 Q_D(QRasterPaintEngine);
3348 Q_ASSERT(image.depth() == 1);
3350 const int spanCount = 256;
3351 QT_FT_Span spans[spanCount];
3355 int w = image.width();
3356 int h = image.height();
3357 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3358 int ymin = qMax(qRound(pos.y()), 0);
3359 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3360 int xmin = qMax(qRound(pos.x()), 0);
3362 int x_offset = xmin - qRound(pos.x());
3364 QImage::Format format = image.format();
3365 for (int y = ymin; y < ymax; ++y) {
3366 const uchar *src = image.scanLine(y - qRound(pos.y()));
3367 if (format == QImage::Format_MonoLSB) {
3368 for (int x = 0; x < xmax - xmin; ++x) {
3369 int src_x = x + x_offset;
3370 uchar pixel = src[src_x >> 3];
3375 if (pixel & (0x1 << (src_x & 7))) {
3376 spans[n].x = xmin + x;
3378 spans[n].coverage = 255;
3380 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3384 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3387 if (n == spanCount) {
3388 fg->blend(n, spans, fg);
3394 for (int x = 0; x < xmax - xmin; ++x) {
3395 int src_x = x + x_offset;
3396 uchar pixel = src[src_x >> 3];
3401 if (pixel & (0x80 >> (x & 7))) {
3402 spans[n].x = xmin + x;
3404 spans[n].coverage = 255;
3406 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3410 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3413 if (n == spanCount) {
3414 fg->blend(n, spans, fg);
3422 fg->blend(n, spans, fg);
3428 \enum QRasterPaintEngine::ClipType
3431 \value RectClip Indicates that the currently set clip is a single rectangle.
3432 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3437 Returns the type of the clip currently set.
3439 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3441 Q_D(const QRasterPaintEngine);
3443 const QClipData *clip = d->clip();
3444 if (!clip || clip->hasRectClip)
3452 Returns the bounding rect of the currently set clip.
3454 QRect QRasterPaintEngine::clipBoundingRect() const
3456 Q_D(const QRasterPaintEngine);
3458 const QClipData *clip = d->clip();
3461 return d->deviceRect;
3463 if (clip->hasRectClip)
3464 return clip->clipRect;
3466 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3469 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3471 Q_Q(QRasterPaintEngine);
3472 QRasterPaintEngineState *s = q->state();
3474 rasterizer->setAntialiased(s->flags.antialiased);
3476 QRect clipRect(deviceRect);
3478 // ### get from optimized rectbased QClipData
3480 const QClipData *c = clip();
3482 const QRect r(QPoint(c->xmin, c->ymin),
3483 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3484 clipRect = clipRect.intersected(r);
3485 blend = data->blend;
3487 blend = data->unclipped_blend;
3490 rasterizer->setClipRect(clipRect);
3491 rasterizer->initialize(blend, data);
3494 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3495 ProcessSpans callback,
3496 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3498 if (!callback || !outline)
3501 Q_Q(QRasterPaintEngine);
3502 QRasterPaintEngineState *s = q->state();
3504 if (!s->flags.antialiased) {
3505 initializeRasterizer(spanData);
3507 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3511 rasterizer->rasterize(outline, fillRule);
3515 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3519 int q_gray_rendered_spans(QT_FT_Raster raster);
3522 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3523 ProcessSpans callback,
3524 void *userData, QRasterBuffer *)
3526 if (!callback || !outline)
3529 Q_Q(QRasterPaintEngine);
3530 QRasterPaintEngineState *s = q->state();
3532 if (!s->flags.antialiased) {
3533 rasterizer->setAntialiased(s->flags.antialiased);
3534 rasterizer->setClipRect(deviceRect);
3535 rasterizer->initialize(callback, userData);
3537 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3541 rasterizer->rasterize(outline, fillRule);
3545 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3546 // minimize memory reallocations. However if initial size for
3547 // raster pool is changed for lower value, reallocations will
3549 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3550 int rasterPoolSize = rasterPoolInitialSize;
3551 unsigned char *rasterPoolBase;
3552 #if defined(Q_OS_WIN64)
3554 // We make use of setjmp and longjmp in qgrayraster.c which requires
3555 // 16-byte alignment, hence we hardcode this requirement here..
3556 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3558 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3559 rasterPoolBase = rasterPoolOnStack;
3561 Q_CHECK_PTR(rasterPoolBase);
3563 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3565 void *data = userData;
3567 QT_FT_BBox clip_box = { deviceRect.x(),
3569 deviceRect.x() + deviceRect.width(),
3570 deviceRect.y() + deviceRect.height() };
3572 QT_FT_Raster_Params rasterParams;
3573 rasterParams.target = 0;
3574 rasterParams.source = outline;
3575 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3576 rasterParams.gray_spans = 0;
3577 rasterParams.black_spans = 0;
3578 rasterParams.bit_test = 0;
3579 rasterParams.bit_set = 0;
3580 rasterParams.user = data;
3581 rasterParams.clip_box = clip_box;
3586 int rendered_spans = 0;
3590 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3591 rasterParams.gray_spans = callback;
3592 rasterParams.skip_spans = rendered_spans;
3593 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3595 // Out of memory, reallocate some more and try again...
3596 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3597 int new_size = rasterPoolSize * 2;
3598 if (new_size > 1024 * 1024) {
3599 qWarning("QPainter: Rasterization of primitive failed");
3603 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3605 #if defined(Q_OS_WIN64)
3606 _aligned_free(rasterPoolBase);
3608 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3609 free(rasterPoolBase);
3612 rasterPoolSize = new_size;
3614 #if defined(Q_OS_WIN64)
3615 // We make use of setjmp and longjmp in qgrayraster.c which requires
3616 // 16-byte alignment, hence we hardcode this requirement here..
3617 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3619 (unsigned char *) malloc(rasterPoolSize);
3621 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3623 qt_ft_grays_raster.raster_done(*grayRaster.data());
3624 qt_ft_grays_raster.raster_new(grayRaster.data());
3625 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3631 #if defined(Q_OS_WIN64)
3632 _aligned_free(rasterPoolBase);
3634 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3635 free(rasterPoolBase);
3639 void QRasterPaintEnginePrivate::recalculateFastImages()
3641 Q_Q(QRasterPaintEngine);
3642 QRasterPaintEngineState *s = q->state();
3644 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3645 && s->matrix.type() <= QTransform::TxShear;
3648 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3650 Q_Q(const QRasterPaintEngine);
3651 const QRasterPaintEngineState *s = q->state();
3653 return s->flags.fast_images
3654 && (mode == QPainter::CompositionMode_SourceOver
3655 || (mode == QPainter::CompositionMode_Source
3656 && !image.hasAlphaChannel()));
3659 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3661 Q_ASSERT(image.depth() == 1);
3663 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3664 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3666 QRgb fg = PREMUL(color.rgba());
3669 int height = sourceImage.height();
3670 int width = sourceImage.width();
3671 for (int y=0; y<height; ++y) {
3672 uchar *source = sourceImage.scanLine(y);
3673 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3674 if (!source || !target)
3675 QT_THROW(std::bad_alloc()); // we must have run out of memory
3676 for (int x=0; x < width; ++x)
3677 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3682 QRasterBuffer::~QRasterBuffer()
3686 void QRasterBuffer::init()
3688 compositionMode = QPainter::CompositionMode_SourceOver;
3689 monoDestinationWithClut = false;
3694 QImage::Format QRasterBuffer::prepare(QImage *image)
3696 m_buffer = (uchar *)image->bits();
3697 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3698 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3699 bytes_per_pixel = image->depth()/8;
3700 bytes_per_line = image->bytesPerLine();
3702 format = image->format();
3703 drawHelper = qDrawHelper + format;
3704 if (image->depth() == 1 && image->colorTable().size() == 2) {
3705 monoDestinationWithClut = true;
3706 destColor0 = PREMUL(image->colorTable()[0]);
3707 destColor1 = PREMUL(image->colorTable()[1]);
3713 void QRasterBuffer::resetBuffer(int val)
3715 memset(m_buffer, val, m_height*bytes_per_line);
3718 QClipData::QClipData(int height)
3720 clipSpanHeight = height;
3725 xmin = xmax = ymin = ymax = 0;
3729 hasRectClip = hasRegionClip = false;
3732 QClipData::~QClipData()
3740 void QClipData::initialize()
3746 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3748 Q_CHECK_PTR(m_clipLines);
3750 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3751 allocated = clipSpanHeight;
3752 Q_CHECK_PTR(m_spans);
3758 m_clipLines[y].spans = 0;
3759 m_clipLines[y].count = 0;
3763 const int len = clipRect.width();
3766 QSpan *span = m_spans + count;
3770 span->coverage = 255;
3773 m_clipLines[y].spans = span;
3774 m_clipLines[y].count = 1;
3778 while (y < clipSpanHeight) {
3779 m_clipLines[y].spans = 0;
3780 m_clipLines[y].count = 0;
3783 } else if (hasRegionClip) {
3785 const QVector<QRect> rects = clipRegion.rects();
3786 const int numRects = rects.size();
3789 const int maxSpans = (ymax - ymin) * numRects;
3790 if (maxSpans > allocated) {
3791 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3792 allocated = maxSpans;
3797 int firstInBand = 0;
3799 while (firstInBand < numRects) {
3800 const int currMinY = rects.at(firstInBand).y();
3801 const int currMaxY = currMinY + rects.at(firstInBand).height();
3803 while (y < currMinY) {
3804 m_clipLines[y].spans = 0;
3805 m_clipLines[y].count = 0;
3809 int lastInBand = firstInBand;
3810 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3813 while (y < currMaxY) {
3815 m_clipLines[y].spans = m_spans + count;
3816 m_clipLines[y].count = lastInBand - firstInBand + 1;
3818 for (int r = firstInBand; r <= lastInBand; ++r) {
3819 const QRect &currRect = rects.at(r);
3820 QSpan *span = m_spans + count;
3821 span->x = currRect.x();
3822 span->len = currRect.width();
3824 span->coverage = 255;
3830 firstInBand = lastInBand + 1;
3833 Q_ASSERT(count <= allocated);
3835 while (y < clipSpanHeight) {
3836 m_clipLines[y].spans = 0;
3837 m_clipLines[y].count = 0;
3843 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3848 free(m_clipLines); // same for clipLines
3854 void QClipData::fixup()
3859 ymin = ymax = xmin = xmax = 0;
3864 ymin = m_spans[0].y;
3865 ymax = m_spans[count-1].y + 1;
3869 const int firstLeft = m_spans[0].x;
3870 const int firstRight = m_spans[0].x + m_spans[0].len;
3873 for (int i = 0; i < count; ++i) {
3874 QT_FT_Span_& span = m_spans[i];
3877 if (span.y != y + 1 && y != -1)
3880 m_clipLines[y].spans = &span;
3881 m_clipLines[y].count = 1;
3883 ++m_clipLines[y].count;
3885 const int spanLeft = span.x;
3886 const int spanRight = spanLeft + span.len;
3888 if (spanLeft < xmin)
3891 if (spanRight > xmax)
3894 if (spanLeft != firstLeft || spanRight != firstRight)
3900 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3905 Convert \a rect to clip spans.
3907 void QClipData::setClipRect(const QRect &rect)
3909 if (hasRectClip && rect == clipRect)
3912 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3914 hasRegionClip = false;
3918 xmax = rect.x() + rect.width();
3919 ymin = qMin(rect.y(), clipSpanHeight);
3920 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3927 // qDebug() << xmin << xmax << ymin << ymax;
3931 Convert \a region to clip spans.
3933 void QClipData::setClipRegion(const QRegion ®ion)
3935 if (region.rectCount() == 1) {
3936 setClipRect(region.rects().at(0));
3940 hasRegionClip = true;
3941 hasRectClip = false;
3942 clipRegion = region;
3944 { // set bounding rect
3945 const QRect rect = region.boundingRect();
3947 xmax = rect.x() + rect.width();
3949 ymax = rect.y() + rect.height();
3961 spans must be sorted on y
3963 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3964 const QSpan *spans, const QSpan *end,
3965 QSpan **outSpans, int available)
3967 const_cast<QClipData *>(clip)->initialize();
3969 QSpan *out = *outSpans;
3971 const QSpan *clipSpans = clip->m_spans + *currentClip;
3972 const QSpan *clipEnd = clip->m_spans + clip->count;
3974 while (available && spans < end ) {
3975 if (clipSpans >= clipEnd) {
3979 if (clipSpans->y > spans->y) {
3983 if (spans->y != clipSpans->y) {
3984 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3985 clipSpans = clip->m_clipLines[spans->y].spans;
3990 Q_ASSERT(spans->y == clipSpans->y);
3993 int sx2 = sx1 + spans->len;
3994 int cx1 = clipSpans->x;
3995 int cx2 = cx1 + clipSpans->len;
3997 if (cx1 < sx1 && cx2 < sx1) {
4000 } else if (sx1 < cx1 && sx2 < cx1) {
4004 int x = qMax(sx1, cx1);
4005 int len = qMin(sx2, cx2) - x;
4007 out->x = qMax(sx1, cx1);
4008 out->len = qMin(sx2, cx2) - out->x;
4010 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4022 *currentClip = clipSpans - clip->m_spans;
4026 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4028 // qDebug() << "qt_span_fill_clipped" << spanCount;
4029 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4031 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4033 const int NSPANS = 256;
4034 QSpan cspans[NSPANS];
4035 int currentClip = 0;
4036 const QSpan *end = spans + spanCount;
4037 while (spans < end) {
4038 QSpan *clipped = cspans;
4039 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4040 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4041 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4043 if (clipped - cspans)
4044 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4050 Clip spans to \a{clip}-rectangle.
4051 Returns number of unclipped spans
4053 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4056 const short minx = clip.left();
4057 const short miny = clip.top();
4058 const short maxx = clip.right();
4059 const short maxy = clip.bottom();
4062 for (int i = 0; i < numSpans; ++i) {
4063 if (spans[i].y > maxy)
4065 if (spans[i].y < miny
4066 || spans[i].x > maxx
4067 || spans[i].x + spans[i].len <= minx) {
4070 if (spans[i].x < minx) {
4071 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4074 spans[n].x = spans[i].x;
4075 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4077 if (spans[n].len == 0)
4079 spans[n].y = spans[i].y;
4080 spans[n].coverage = spans[i].coverage;
4087 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4090 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4091 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4093 Q_ASSERT(fillData->clip);
4094 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4096 // hw: check if this const_cast<> is safe!!!
4097 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4098 fillData->clip->clipRect);
4100 fillData->unclipped_blend(count, spans, fillData);
4103 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4105 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4107 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4108 // for (int i = 0; i < qMin(count, 10); ++i) {
4109 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4112 switch (clipData->operation) {
4114 case Qt::IntersectClip:
4116 QClipData *newClip = clipData->newClip;
4117 newClip->initialize();
4119 int currentClip = 0;
4120 const QSpan *end = spans + count;
4121 while (spans < end) {
4122 QSpan *newspans = newClip->m_spans + newClip->count;
4123 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4124 &newspans, newClip->allocated - newClip->count);
4125 newClip->count = newspans - newClip->m_spans;
4127 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4128 newClip->allocated *= 2;
4134 case Qt::ReplaceClip:
4135 clipData->newClip->appendSpans(spans, count);
4143 QImage QRasterBuffer::bufferImage() const
4145 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4147 for (int y = 0; y < m_height; ++y) {
4148 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4150 for (int x=0; x<m_width; ++x) {
4151 uint argb = span[x];
4152 image.setPixel(x, y, argb);
4160 void QRasterBuffer::flushToARGBImage(QImage *target) const
4162 int w = qMin(m_width, target->width());
4163 int h = qMin(m_height, target->height());
4165 for (int y=0; y<h; ++y) {
4166 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4167 QRgb *dest = (QRgb *) target->scanLine(y);
4168 for (int x=0; x<w; ++x) {
4169 QRgb pixel = sourceLine[x];
4170 int alpha = qAlpha(pixel);
4174 dest[x] = (alpha << 24)
4175 | ((255*qRed(pixel)/alpha) << 16)
4176 | ((255*qGreen(pixel)/alpha) << 8)
4177 | ((255*qBlue(pixel)/alpha) << 0);
4184 class QGradientCache
4188 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4189 stops(s), opacity(op), interpolationMode(mode) {}
4190 uint buffer[GRADIENT_STOPTABLE_SIZE];
4191 QGradientStops stops;
4193 QGradient::InterpolationMode interpolationMode;
4196 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4199 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4200 quint64 hash_val = 0;
4202 QGradientStops stops = gradient.stops();
4203 for (int i = 0; i < stops.size() && i <= 2; i++)
4204 hash_val += stops[i].second.rgba();
4206 QMutexLocker lock(&mutex);
4207 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4209 if (it == cache.constEnd())
4210 return addCacheElement(hash_val, gradient, opacity);
4213 const CacheInfo &cache_info = it.value();
4214 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4215 return cache_info.buffer;
4217 } while (it != cache.constEnd() && it.key() == hash_val);
4218 // an exact match for these stops and opacity was not found, create new cache
4219 return addCacheElement(hash_val, gradient, opacity);
4223 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4225 inline int maxCacheSize() const { return 60; }
4226 inline void generateGradientColorTable(const QGradient& g,
4228 int size, int opacity) const;
4229 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4230 if (cache.size() == maxCacheSize()) {
4231 // may remove more than 1, but OK
4232 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4234 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4235 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4236 return cache.insert(hash_val, cache_entry).value().buffer;
4239 QGradientColorTableHash cache;
4243 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4245 QGradientStops stops = gradient.stops();
4246 int stopCount = stops.count();
4247 Q_ASSERT(stopCount > 0);
4249 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4251 if (stopCount == 2) {
4252 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4253 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4255 qreal first_stop = stops[0].first;
4256 qreal second_stop = stops[1].first;
4258 if (second_stop < first_stop) {
4259 qSwap(first_color, second_color);
4260 qSwap(first_stop, second_stop);
4263 if (colorInterpolation) {
4264 first_color = PREMUL(first_color);
4265 second_color = PREMUL(second_color);
4268 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4269 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4271 uint red_first = qRed(first_color) << 16;
4272 uint green_first = qGreen(first_color) << 16;
4273 uint blue_first = qBlue(first_color) << 16;
4274 uint alpha_first = qAlpha(first_color) << 16;
4276 uint red_second = qRed(second_color) << 16;
4277 uint green_second = qGreen(second_color) << 16;
4278 uint blue_second = qBlue(second_color) << 16;
4279 uint alpha_second = qAlpha(second_color) << 16;
4282 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4283 if (colorInterpolation)
4284 colorTable[i] = first_color;
4286 colorTable[i] = PREMUL(first_color);
4289 if (i < second_index) {
4290 qreal reciprocal = qreal(1) / (second_index - first_index);
4292 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4293 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4294 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4295 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4298 red_first += 1 << 15;
4299 green_first += 1 << 15;
4300 blue_first += 1 << 15;
4301 alpha_first += 1 << 15;
4303 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4304 red_first += red_delta;
4305 green_first += green_delta;
4306 blue_first += blue_delta;
4307 alpha_first += alpha_delta;
4309 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4310 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4312 if (colorInterpolation)
4313 colorTable[i] = color;
4315 colorTable[i] = PREMUL(color);
4319 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4320 if (colorInterpolation)
4321 colorTable[i] = second_color;
4323 colorTable[i] = PREMUL(second_color);
4329 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4330 if (stopCount == 1) {
4331 current_color = PREMUL(current_color);
4332 for (int i = 0; i < size; ++i)
4333 colorTable[i] = current_color;
4337 // The position where the gradient begins and ends
4338 qreal begin_pos = stops[0].first;
4339 qreal end_pos = stops[stopCount-1].first;
4341 int pos = 0; // The position in the color table.
4344 qreal incr = 1 / qreal(size); // the double increment.
4345 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4347 // Up to first point
4348 colorTable[pos++] = PREMUL(current_color);
4349 while (dpos <= begin_pos) {
4350 colorTable[pos] = colorTable[pos - 1];
4355 int current_stop = 0; // We always interpolate between current and current + 1.
4357 qreal t; // position between current left and right stops
4358 qreal t_delta; // the t increment per entry in the color table
4360 if (dpos < end_pos) {
4362 while (dpos > stops[current_stop+1].first)
4365 if (current_stop != 0)
4366 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4367 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4369 if (colorInterpolation) {
4370 current_color = PREMUL(current_color);
4371 next_color = PREMUL(next_color);
4374 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4375 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4376 t = (dpos - stops[current_stop].first) * c;
4380 Q_ASSERT(current_stop < stopCount);
4382 int dist = qRound(t);
4383 int idist = 256 - dist;
4385 if (colorInterpolation)
4386 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4388 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4393 if (dpos >= end_pos)
4399 while (dpos > stops[current_stop+skip+1].first)
4403 current_stop += skip;
4405 current_color = next_color;
4407 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4408 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4410 if (colorInterpolation) {
4412 current_color = PREMUL(current_color);
4413 next_color = PREMUL(next_color);
4416 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4417 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4418 t = (dpos - stops[current_stop].first) * c;
4425 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4426 while (pos < size - 1) {
4427 colorTable[pos] = current_color;
4431 // Make sure the last color stop is represented at the end of the table
4432 colorTable[size - 1] = current_color;
4435 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4438 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4444 m11 = m22 = m33 = 1.;
4445 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4446 clip = pe ? pe->d_func()->clip() : 0;
4449 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4451 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4453 Qt::BrushStyle brushStyle = qbrush_style(brush);
4454 switch (brushStyle) {
4455 case Qt::SolidPattern: {
4457 QColor c = qbrush_color(brush);
4458 QRgb rgba = c.rgba();
4459 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4460 if ((solid.color & 0xff000000) == 0
4461 && compositionMode == QPainter::CompositionMode_SourceOver) {
4467 case Qt::LinearGradientPattern:
4469 type = LinearGradient;
4470 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4471 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4472 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4473 gradient.spread = g->spread();
4475 QLinearGradientData &linearData = gradient.linear;
4477 linearData.origin.x = g->start().x();
4478 linearData.origin.y = g->start().y();
4479 linearData.end.x = g->finalStop().x();
4480 linearData.end.y = g->finalStop().y();
4484 case Qt::RadialGradientPattern:
4486 type = RadialGradient;
4487 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4488 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4489 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4490 gradient.spread = g->spread();
4492 QRadialGradientData &radialData = gradient.radial;
4494 QPointF center = g->center();
4495 radialData.center.x = center.x();
4496 radialData.center.y = center.y();
4497 radialData.center.radius = g->centerRadius();
4498 QPointF focal = g->focalPoint();
4499 radialData.focal.x = focal.x();
4500 radialData.focal.y = focal.y();
4501 radialData.focal.radius = g->focalRadius();
4505 case Qt::ConicalGradientPattern:
4507 type = ConicalGradient;
4508 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4509 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4510 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4511 gradient.spread = QGradient::RepeatSpread;
4513 QConicalGradientData &conicalData = gradient.conical;
4515 QPointF center = g->center();
4516 conicalData.center.x = center.x();
4517 conicalData.center.y = center.y();
4518 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4522 case Qt::Dense1Pattern:
4523 case Qt::Dense2Pattern:
4524 case Qt::Dense3Pattern:
4525 case Qt::Dense4Pattern:
4526 case Qt::Dense5Pattern:
4527 case Qt::Dense6Pattern:
4528 case Qt::Dense7Pattern:
4529 case Qt::HorPattern:
4530 case Qt::VerPattern:
4531 case Qt::CrossPattern:
4532 case Qt::BDiagPattern:
4533 case Qt::FDiagPattern:
4534 case Qt::DiagCrossPattern:
4537 tempImage = new QImage();
4538 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4539 initTexture(tempImage, alpha, QTextureData::Tiled);
4541 case Qt::TexturePattern:
4544 tempImage = new QImage();
4546 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4547 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4549 *tempImage = brush.textureImage();
4550 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4558 adjustSpanMethods();
4561 void QSpanData::adjustSpanMethods()
4571 unclipped_blend = 0;
4574 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4575 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4576 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4577 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4578 fillRect = rasterBuffer->drawHelper->fillRect;
4580 case LinearGradient:
4581 case RadialGradient:
4582 case ConicalGradient:
4583 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4586 unclipped_blend = qBlendTexture;
4587 if (!texture.imageData)
4588 unclipped_blend = 0;
4593 if (!unclipped_blend) {
4596 blend = unclipped_blend;
4597 } else if (clip->hasRectClip) {
4598 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4600 blend = qt_span_fill_clipped;
4604 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4607 // make sure we round off correctly in qdrawhelper.cpp
4608 delta.translate(1.0 / 65536, 1.0 / 65536);
4610 QTransform inv = (delta * matrix).inverted();
4623 const bool affine = !m13 && !m23;
4624 fast_matrix = affine
4625 && m11 * m11 + m21 * m21 < 1e4
4626 && m12 * m12 + m22 * m22 < 1e4
4630 adjustSpanMethods();
4633 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4635 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4637 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4638 if (!d || d->height == 0) {
4639 texture.imageData = 0;
4646 texture.bytesPerLine = 0;
4647 texture.format = QImage::Format_Invalid;
4648 texture.colorTable = 0;
4649 texture.hasAlpha = alpha != 256;
4651 texture.imageData = d->data;
4652 texture.width = d->width;
4653 texture.height = d->height;
4655 if (sourceRect.isNull()) {
4658 texture.x2 = texture.width;
4659 texture.y2 = texture.height;
4661 texture.x1 = sourceRect.x();
4662 texture.y1 = sourceRect.y();
4663 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4664 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4667 texture.bytesPerLine = d->bytes_per_line;
4669 texture.format = d->format;
4670 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4671 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4673 texture.const_alpha = alpha;
4674 texture.type = _type;
4676 adjustSpanMethods();
4681 \a x and \a y is relative to the midpoint of \a rect.
4683 static inline void drawEllipsePoints(int x, int y, int length,
4686 ProcessSpans pen_func, ProcessSpans brush_func,
4687 QSpanData *pen_data, QSpanData *brush_data)
4692 QT_FT_Span outline[4];
4693 const int midx = rect.x() + (rect.width() + 1) / 2;
4694 const int midy = rect.y() + (rect.height() + 1) / 2;
4700 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4701 outline[0].len = qMin(length, x - outline[0].x);
4703 outline[0].coverage = 255;
4707 outline[1].len = length;
4709 outline[1].coverage = 255;
4712 outline[2].x = outline[0].x;
4713 outline[2].len = outline[0].len;
4714 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4715 outline[2].coverage = 255;
4719 outline[3].len = length;
4720 outline[3].y = outline[2].y;
4721 outline[3].coverage = 255;
4723 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4727 fill[0].x = outline[0].x + outline[0].len - 1;
4728 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4729 fill[0].y = outline[1].y;
4730 fill[0].coverage = 255;
4733 fill[1].x = outline[2].x + outline[2].len - 1;
4734 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4735 fill[1].y = outline[3].y;
4736 fill[1].coverage = 255;
4738 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4739 n = qt_intersect_spans(fill, n, clip);
4741 brush_func(n, fill, brush_data);
4744 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4745 n = qt_intersect_spans(outline, n, clip);
4747 pen_func(n, outline, pen_data);
4753 Draws an ellipse using the integer point midpoint algorithm.
4755 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4756 ProcessSpans pen_func, ProcessSpans brush_func,
4757 QSpanData *pen_data, QSpanData *brush_data)
4759 const qreal a = qreal(rect.width()) / 2;
4760 const qreal b = qreal(rect.height()) / 2;
4761 qreal d = b*b - (a*a*b) + 0.25*a*a;
4764 int y = (rect.height() + 1) / 2;
4768 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4769 if (d < 0) { // select E
4772 } else { // select SE
4773 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4774 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4775 pen_func, brush_func, pen_data, brush_data);
4780 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4781 pen_func, brush_func, pen_data, brush_data);
4784 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4785 const int miny = rect.height() & 0x1;
4787 if (d < 0) { // select SE
4788 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4790 } else { // select S
4791 d += a*a*(-2*y + 3);
4794 drawEllipsePoints(x, y, 1, rect, clip,
4795 pen_func, brush_func, pen_data, brush_data);
4800 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4803 Draws the first \a pointCount points in the buffer \a points
4805 The default implementation converts the first \a pointCount QPoints in \a points
4806 to QPointFs and calls the floating point version of drawPoints.
4810 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4813 Reimplement this function to draw the largest ellipse that can be
4814 contained within rectangle \a rect.
4817 #ifdef QT_DEBUG_DRAW
4818 void dumpClip(int width, int height, const QClipData *clip)
4820 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4821 clipImg.fill(0xffff0000);
4828 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4830 for (int i = 0; i < clip->count; ++i) {
4831 const QSpan *span = ((QClipData *) clip)->spans() + i;
4832 for (int j = 0; j < span->len; ++j)
4833 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4834 x0 = qMin(x0, int(span->x));
4835 x1 = qMax(x1, int(span->x + span->len - 1));
4837 y0 = qMin(y0, int(span->y));
4838 y1 = qMax(y1, int(span->y));
4841 static int counter = 0;
4848 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4849 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));