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)
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
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
3058 QFontEngine *fontEngine = ti.fontEngine;
3061 if (s->matrix.type() < QTransform::TxScale) {
3063 QVarLengthArray<QFixedPoint> positions;
3064 QVarLengthArray<glyph_t> glyphs;
3065 QTransform matrix = state()->transform();
3067 qreal _x = qFloor(p.x());
3068 qreal _y = qFloor(p.y());
3069 matrix.translate(_x, _y);
3071 fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3072 if (glyphs.size() == 0)
3075 for(int i = 0; i < glyphs.size(); i++) {
3076 QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
3077 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
3078 // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here?
3079 alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
3080 qRound(positions[i].x + metrics.x),
3081 qRound(positions[i].y + metrics.y),
3082 img.width(), img.height());
3088 #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
3090 if (fontEngine->type() != QFontEngine::Freetype) {
3091 QPaintEngineEx::drawTextItem(p, ti);
3095 QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
3097 QTransform matrix = s->matrix;
3098 matrix.translate(p.x(), p.y());
3100 QVarLengthArray<QFixedPoint> positions;
3101 QVarLengthArray<glyph_t> glyphs;
3102 fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3103 if (glyphs.size() == 0)
3106 if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
3107 QPaintEngine::drawTextItem(p, ti);
3113 QPaintEngineEx::drawTextItem(p, ti);
3119 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3121 Q_D(QRasterPaintEngine);
3122 QRasterPaintEngineState *s = state();
3125 if (!s->penData.blend)
3128 if (!s->flags.fast_pen) {
3129 QPaintEngineEx::drawPoints(points, pointCount);
3133 QCosmeticStroker stroker(s, d->deviceRect);
3134 stroker.drawPoints(points, pointCount);
3138 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3140 Q_D(QRasterPaintEngine);
3141 QRasterPaintEngineState *s = state();
3144 if (!s->penData.blend)
3147 if (!s->flags.fast_pen) {
3148 QPaintEngineEx::drawPoints(points, pointCount);
3152 QCosmeticStroker stroker(s, d->deviceRect);
3153 stroker.drawPoints(points, pointCount);
3159 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3161 #ifdef QT_DEBUG_DRAW
3162 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3164 Q_D(QRasterPaintEngine);
3165 QRasterPaintEngineState *s = state();
3168 if (!s->penData.blend)
3171 if (s->flags.fast_pen) {
3172 QCosmeticStroker stroker(s, d->deviceRect);
3173 for (int i=0; i<lineCount; ++i) {
3174 const QLine &l = lines[i];
3175 stroker.drawLine(l.p1(), l.p2());
3178 QPaintEngineEx::drawLines(lines, lineCount);
3182 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3188 Q_Q(QRasterPaintEngine);
3189 QRasterPaintEngineState *s = q->state();
3191 const QPen &pen = s->lastPen;
3192 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3193 const QVector<qreal> pattern = pen.dashPattern();
3195 qreal patternLength = 0;
3196 for (int i = 0; i < pattern.size(); ++i)
3197 patternLength += pattern.at(i);
3199 if (patternLength <= 0)
3202 qreal length = line.length();
3203 Q_ASSERT(length > 0);
3204 while (length > 0) {
3205 const bool rasterize = *inDash;
3206 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3209 if (dash >= length) {
3211 *dashOffset += dash / width;
3215 *inDash = !(*inDash);
3216 if (++*dashIndex >= pattern.size())
3223 if (rasterize && dash > 0)
3224 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3231 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3233 #ifdef QT_DEBUG_DRAW
3234 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3236 Q_D(QRasterPaintEngine);
3237 QRasterPaintEngineState *s = state();
3240 if (!s->penData.blend)
3242 if (s->flags.fast_pen) {
3243 QCosmeticStroker stroker(s, d->deviceRect);
3244 for (int i=0; i<lineCount; ++i) {
3245 QLineF line = lines[i];
3246 stroker.drawLine(line.p1(), line.p2());
3249 QPaintEngineEx::drawLines(lines, lineCount);
3257 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3259 Q_D(QRasterPaintEngine);
3260 QRasterPaintEngineState *s = state();
3263 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3264 || (qpen_style(s->lastPen) == Qt::NoPen))
3265 && !s->flags.antialiased
3266 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3268 && s->matrix.type() <= QTransform::TxScale) // no shear
3271 const QRectF r = s->matrix.mapRect(rect);
3272 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3273 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3274 const QRect brect = QRect(int(r.x()), int(r.y()),
3275 int_dim(r.x(), r.width()),
3276 int_dim(r.y(), r.height()));
3278 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3279 &s->penData, &s->brushData);
3283 QPaintEngineEx::drawEllipse(rect);
3290 void QRasterPaintEngine::setCGContext(CGContextRef ctx)
3292 Q_D(QRasterPaintEngine);
3299 CGContextRef QRasterPaintEngine::getCGContext() const
3301 Q_D(const QRasterPaintEngine);
3302 return d->cgContext;
3310 void QRasterPaintEngine::setDC(HDC hdc) {
3311 Q_D(QRasterPaintEngine);
3318 HDC QRasterPaintEngine::getDC() const
3320 Q_D(const QRasterPaintEngine);
3327 void QRasterPaintEngine::releaseDC(HDC) const
3333 bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const
3335 const QTransform &m = state()->matrix;
3336 #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
3337 QFontEngine::Type fontEngineType = fontEngine->type();
3338 if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate)
3339 || (m.type() <= QTransform::TxTranslate
3340 && (fontEngineType == QFontEngine::TestFontEngine
3341 || fontEngineType == QFontEngine::Box))) {
3345 return supportsTransformations(fontEngine->fontDef.pixelSize, m);
3348 bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const
3350 #if defined(Q_WS_MAC)
3351 // Mac font engines don't support scaling and rotation
3352 if (m.type() > QTransform::TxTranslate)
3354 if (m.type() >= QTransform::TxProject)
3358 return !shouldDrawCachedGlyphs(pixelSize, m);
3364 QPoint QRasterPaintEngine::coordinateOffset() const
3366 return QPoint(0, 0);
3369 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3374 Q_D(QRasterPaintEngine);
3376 Q_ASSERT(image.depth() == 1);
3378 const int spanCount = 256;
3379 QT_FT_Span spans[spanCount];
3383 int w = image.width();
3384 int h = image.height();
3385 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3386 int ymin = qMax(qRound(pos.y()), 0);
3387 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3388 int xmin = qMax(qRound(pos.x()), 0);
3390 int x_offset = xmin - qRound(pos.x());
3392 QImage::Format format = image.format();
3393 for (int y = ymin; y < ymax; ++y) {
3394 const uchar *src = image.scanLine(y - qRound(pos.y()));
3395 if (format == QImage::Format_MonoLSB) {
3396 for (int x = 0; x < xmax - xmin; ++x) {
3397 int src_x = x + x_offset;
3398 uchar pixel = src[src_x >> 3];
3403 if (pixel & (0x1 << (src_x & 7))) {
3404 spans[n].x = xmin + x;
3406 spans[n].coverage = 255;
3408 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3412 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3415 if (n == spanCount) {
3416 fg->blend(n, spans, fg);
3422 for (int x = 0; x < xmax - xmin; ++x) {
3423 int src_x = x + x_offset;
3424 uchar pixel = src[src_x >> 3];
3429 if (pixel & (0x80 >> (x & 7))) {
3430 spans[n].x = xmin + x;
3432 spans[n].coverage = 255;
3434 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3438 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3441 if (n == spanCount) {
3442 fg->blend(n, spans, fg);
3450 fg->blend(n, spans, fg);
3456 \enum QRasterPaintEngine::ClipType
3459 \value RectClip Indicates that the currently set clip is a single rectangle.
3460 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3465 Returns the type of the clip currently set.
3467 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3469 Q_D(const QRasterPaintEngine);
3471 const QClipData *clip = d->clip();
3472 if (!clip || clip->hasRectClip)
3480 Returns the bounding rect of the currently set clip.
3482 QRect QRasterPaintEngine::clipBoundingRect() const
3484 Q_D(const QRasterPaintEngine);
3486 const QClipData *clip = d->clip();
3489 return d->deviceRect;
3491 if (clip->hasRectClip)
3492 return clip->clipRect;
3494 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3497 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3499 Q_Q(QRasterPaintEngine);
3500 QRasterPaintEngineState *s = q->state();
3502 rasterizer->setAntialiased(s->flags.antialiased);
3504 QRect clipRect(deviceRect);
3506 // ### get from optimized rectbased QClipData
3508 const QClipData *c = clip();
3510 const QRect r(QPoint(c->xmin, c->ymin),
3511 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3512 clipRect = clipRect.intersected(r);
3513 blend = data->blend;
3515 blend = data->unclipped_blend;
3518 rasterizer->setClipRect(clipRect);
3519 rasterizer->initialize(blend, data);
3522 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3523 ProcessSpans callback,
3524 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3526 if (!callback || !outline)
3529 Q_Q(QRasterPaintEngine);
3530 QRasterPaintEngineState *s = q->state();
3532 if (!s->flags.antialiased) {
3533 initializeRasterizer(spanData);
3535 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3539 rasterizer->rasterize(outline, fillRule);
3543 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3547 int q_gray_rendered_spans(QT_FT_Raster raster);
3550 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3551 ProcessSpans callback,
3552 void *userData, QRasterBuffer *)
3554 if (!callback || !outline)
3557 Q_Q(QRasterPaintEngine);
3558 QRasterPaintEngineState *s = q->state();
3560 if (!s->flags.antialiased) {
3561 rasterizer->setAntialiased(s->flags.antialiased);
3562 rasterizer->setClipRect(deviceRect);
3563 rasterizer->initialize(callback, userData);
3565 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3569 rasterizer->rasterize(outline, fillRule);
3573 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3574 // minimize memory reallocations. However if initial size for
3575 // raster pool is changed for lower value, reallocations will
3577 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3578 int rasterPoolSize = rasterPoolInitialSize;
3579 unsigned char *rasterPoolBase;
3580 #if defined(Q_OS_WIN64)
3582 // We make use of setjmp and longjmp in qgrayraster.c which requires
3583 // 16-byte alignment, hence we hardcode this requirement here..
3584 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3586 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3587 rasterPoolBase = rasterPoolOnStack;
3589 Q_CHECK_PTR(rasterPoolBase);
3591 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3593 void *data = userData;
3595 QT_FT_BBox clip_box = { deviceRect.x(),
3597 deviceRect.x() + deviceRect.width(),
3598 deviceRect.y() + deviceRect.height() };
3600 QT_FT_Raster_Params rasterParams;
3601 rasterParams.target = 0;
3602 rasterParams.source = outline;
3603 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3604 rasterParams.gray_spans = 0;
3605 rasterParams.black_spans = 0;
3606 rasterParams.bit_test = 0;
3607 rasterParams.bit_set = 0;
3608 rasterParams.user = data;
3609 rasterParams.clip_box = clip_box;
3614 int rendered_spans = 0;
3618 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3619 rasterParams.gray_spans = callback;
3620 rasterParams.skip_spans = rendered_spans;
3621 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3623 // Out of memory, reallocate some more and try again...
3624 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3625 int new_size = rasterPoolSize * 2;
3626 if (new_size > 1024 * 1024) {
3627 qWarning("QPainter: Rasterization of primitive failed");
3631 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3633 #if defined(Q_OS_WIN64)
3634 _aligned_free(rasterPoolBase);
3636 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3637 free(rasterPoolBase);
3640 rasterPoolSize = new_size;
3642 #if defined(Q_OS_WIN64)
3643 // We make use of setjmp and longjmp in qgrayraster.c which requires
3644 // 16-byte alignment, hence we hardcode this requirement here..
3645 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3647 (unsigned char *) malloc(rasterPoolSize);
3649 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3651 qt_ft_grays_raster.raster_done(*grayRaster.data());
3652 qt_ft_grays_raster.raster_new(grayRaster.data());
3653 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3659 #if defined(Q_OS_WIN64)
3660 _aligned_free(rasterPoolBase);
3662 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3663 free(rasterPoolBase);
3667 void QRasterPaintEnginePrivate::recalculateFastImages()
3669 Q_Q(QRasterPaintEngine);
3670 QRasterPaintEngineState *s = q->state();
3672 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3673 && s->matrix.type() <= QTransform::TxShear;
3676 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3678 Q_Q(const QRasterPaintEngine);
3679 const QRasterPaintEngineState *s = q->state();
3681 return s->flags.fast_images
3682 && (mode == QPainter::CompositionMode_SourceOver
3683 || (mode == QPainter::CompositionMode_Source
3684 && !image.hasAlphaChannel()));
3687 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3689 Q_ASSERT(image.depth() == 1);
3691 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3692 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3694 QRgb fg = PREMUL(color.rgba());
3697 int height = sourceImage.height();
3698 int width = sourceImage.width();
3699 for (int y=0; y<height; ++y) {
3700 uchar *source = sourceImage.scanLine(y);
3701 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3702 if (!source || !target)
3703 QT_THROW(std::bad_alloc()); // we must have run out of memory
3704 for (int x=0; x < width; ++x)
3705 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3710 QRasterBuffer::~QRasterBuffer()
3714 void QRasterBuffer::init()
3716 compositionMode = QPainter::CompositionMode_SourceOver;
3717 monoDestinationWithClut = false;
3722 QImage::Format QRasterBuffer::prepare(QImage *image)
3724 m_buffer = (uchar *)image->bits();
3725 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3726 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3727 bytes_per_pixel = image->depth()/8;
3728 bytes_per_line = image->bytesPerLine();
3730 format = image->format();
3731 drawHelper = qDrawHelper + format;
3732 if (image->depth() == 1 && image->colorTable().size() == 2) {
3733 monoDestinationWithClut = true;
3734 destColor0 = PREMUL(image->colorTable()[0]);
3735 destColor1 = PREMUL(image->colorTable()[1]);
3741 void QRasterBuffer::resetBuffer(int val)
3743 memset(m_buffer, val, m_height*bytes_per_line);
3746 QClipData::QClipData(int height)
3748 clipSpanHeight = height;
3753 xmin = xmax = ymin = ymax = 0;
3757 hasRectClip = hasRegionClip = false;
3760 QClipData::~QClipData()
3768 void QClipData::initialize()
3774 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3776 Q_CHECK_PTR(m_clipLines);
3778 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3779 allocated = clipSpanHeight;
3780 Q_CHECK_PTR(m_spans);
3786 m_clipLines[y].spans = 0;
3787 m_clipLines[y].count = 0;
3791 const int len = clipRect.width();
3794 QSpan *span = m_spans + count;
3798 span->coverage = 255;
3801 m_clipLines[y].spans = span;
3802 m_clipLines[y].count = 1;
3806 while (y < clipSpanHeight) {
3807 m_clipLines[y].spans = 0;
3808 m_clipLines[y].count = 0;
3811 } else if (hasRegionClip) {
3813 const QVector<QRect> rects = clipRegion.rects();
3814 const int numRects = rects.size();
3817 const int maxSpans = (ymax - ymin) * numRects;
3818 if (maxSpans > allocated) {
3819 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3820 allocated = maxSpans;
3825 int firstInBand = 0;
3827 while (firstInBand < numRects) {
3828 const int currMinY = rects.at(firstInBand).y();
3829 const int currMaxY = currMinY + rects.at(firstInBand).height();
3831 while (y < currMinY) {
3832 m_clipLines[y].spans = 0;
3833 m_clipLines[y].count = 0;
3837 int lastInBand = firstInBand;
3838 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3841 while (y < currMaxY) {
3843 m_clipLines[y].spans = m_spans + count;
3844 m_clipLines[y].count = lastInBand - firstInBand + 1;
3846 for (int r = firstInBand; r <= lastInBand; ++r) {
3847 const QRect &currRect = rects.at(r);
3848 QSpan *span = m_spans + count;
3849 span->x = currRect.x();
3850 span->len = currRect.width();
3852 span->coverage = 255;
3858 firstInBand = lastInBand + 1;
3861 Q_ASSERT(count <= allocated);
3863 while (y < clipSpanHeight) {
3864 m_clipLines[y].spans = 0;
3865 m_clipLines[y].count = 0;
3871 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3876 free(m_clipLines); // same for clipLines
3882 void QClipData::fixup()
3887 ymin = ymax = xmin = xmax = 0;
3892 ymin = m_spans[0].y;
3893 ymax = m_spans[count-1].y + 1;
3897 const int firstLeft = m_spans[0].x;
3898 const int firstRight = m_spans[0].x + m_spans[0].len;
3901 for (int i = 0; i < count; ++i) {
3902 QT_FT_Span_& span = m_spans[i];
3905 if (span.y != y + 1 && y != -1)
3908 m_clipLines[y].spans = &span;
3909 m_clipLines[y].count = 1;
3911 ++m_clipLines[y].count;
3913 const int spanLeft = span.x;
3914 const int spanRight = spanLeft + span.len;
3916 if (spanLeft < xmin)
3919 if (spanRight > xmax)
3922 if (spanLeft != firstLeft || spanRight != firstRight)
3928 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3933 Convert \a rect to clip spans.
3935 void QClipData::setClipRect(const QRect &rect)
3937 if (hasRectClip && rect == clipRect)
3940 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3942 hasRegionClip = false;
3946 xmax = rect.x() + rect.width();
3947 ymin = qMin(rect.y(), clipSpanHeight);
3948 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3955 // qDebug() << xmin << xmax << ymin << ymax;
3959 Convert \a region to clip spans.
3961 void QClipData::setClipRegion(const QRegion ®ion)
3963 if (region.rectCount() == 1) {
3964 setClipRect(region.rects().at(0));
3968 hasRegionClip = true;
3969 hasRectClip = false;
3970 clipRegion = region;
3972 { // set bounding rect
3973 const QRect rect = region.boundingRect();
3975 xmax = rect.x() + rect.width();
3977 ymax = rect.y() + rect.height();
3989 spans must be sorted on y
3991 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3992 const QSpan *spans, const QSpan *end,
3993 QSpan **outSpans, int available)
3995 const_cast<QClipData *>(clip)->initialize();
3997 QSpan *out = *outSpans;
3999 const QSpan *clipSpans = clip->m_spans + *currentClip;
4000 const QSpan *clipEnd = clip->m_spans + clip->count;
4002 while (available && spans < end ) {
4003 if (clipSpans >= clipEnd) {
4007 if (clipSpans->y > spans->y) {
4011 if (spans->y != clipSpans->y) {
4012 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4013 clipSpans = clip->m_clipLines[spans->y].spans;
4018 Q_ASSERT(spans->y == clipSpans->y);
4021 int sx2 = sx1 + spans->len;
4022 int cx1 = clipSpans->x;
4023 int cx2 = cx1 + clipSpans->len;
4025 if (cx1 < sx1 && cx2 < sx1) {
4028 } else if (sx1 < cx1 && sx2 < cx1) {
4032 int x = qMax(sx1, cx1);
4033 int len = qMin(sx2, cx2) - x;
4035 out->x = qMax(sx1, cx1);
4036 out->len = qMin(sx2, cx2) - out->x;
4038 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4050 *currentClip = clipSpans - clip->m_spans;
4054 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4056 // qDebug() << "qt_span_fill_clipped" << spanCount;
4057 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4059 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4061 const int NSPANS = 256;
4062 QSpan cspans[NSPANS];
4063 int currentClip = 0;
4064 const QSpan *end = spans + spanCount;
4065 while (spans < end) {
4066 QSpan *clipped = cspans;
4067 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4068 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4069 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4071 if (clipped - cspans)
4072 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4078 Clip spans to \a{clip}-rectangle.
4079 Returns number of unclipped spans
4081 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4084 const short minx = clip.left();
4085 const short miny = clip.top();
4086 const short maxx = clip.right();
4087 const short maxy = clip.bottom();
4090 for (int i = 0; i < numSpans; ++i) {
4091 if (spans[i].y > maxy)
4093 if (spans[i].y < miny
4094 || spans[i].x > maxx
4095 || spans[i].x + spans[i].len <= minx) {
4098 if (spans[i].x < minx) {
4099 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4102 spans[n].x = spans[i].x;
4103 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4105 if (spans[n].len == 0)
4107 spans[n].y = spans[i].y;
4108 spans[n].coverage = spans[i].coverage;
4115 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4118 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4119 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4121 Q_ASSERT(fillData->clip);
4122 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4124 // hw: check if this const_cast<> is safe!!!
4125 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4126 fillData->clip->clipRect);
4128 fillData->unclipped_blend(count, spans, fillData);
4131 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4133 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4135 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4136 // for (int i = 0; i < qMin(count, 10); ++i) {
4137 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4140 switch (clipData->operation) {
4142 case Qt::IntersectClip:
4144 QClipData *newClip = clipData->newClip;
4145 newClip->initialize();
4147 int currentClip = 0;
4148 const QSpan *end = spans + count;
4149 while (spans < end) {
4150 QSpan *newspans = newClip->m_spans + newClip->count;
4151 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4152 &newspans, newClip->allocated - newClip->count);
4153 newClip->count = newspans - newClip->m_spans;
4155 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4156 newClip->allocated *= 2;
4162 case Qt::ReplaceClip:
4163 clipData->newClip->appendSpans(spans, count);
4171 QImage QRasterBuffer::bufferImage() const
4173 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4175 for (int y = 0; y < m_height; ++y) {
4176 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4178 for (int x=0; x<m_width; ++x) {
4179 uint argb = span[x];
4180 image.setPixel(x, y, argb);
4188 void QRasterBuffer::flushToARGBImage(QImage *target) const
4190 int w = qMin(m_width, target->width());
4191 int h = qMin(m_height, target->height());
4193 for (int y=0; y<h; ++y) {
4194 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4195 QRgb *dest = (QRgb *) target->scanLine(y);
4196 for (int x=0; x<w; ++x) {
4197 QRgb pixel = sourceLine[x];
4198 int alpha = qAlpha(pixel);
4202 dest[x] = (alpha << 24)
4203 | ((255*qRed(pixel)/alpha) << 16)
4204 | ((255*qGreen(pixel)/alpha) << 8)
4205 | ((255*qBlue(pixel)/alpha) << 0);
4212 class QGradientCache
4216 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4217 stops(s), opacity(op), interpolationMode(mode) {}
4218 uint buffer[GRADIENT_STOPTABLE_SIZE];
4219 QGradientStops stops;
4221 QGradient::InterpolationMode interpolationMode;
4224 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4227 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4228 quint64 hash_val = 0;
4230 QGradientStops stops = gradient.stops();
4231 for (int i = 0; i < stops.size() && i <= 2; i++)
4232 hash_val += stops[i].second.rgba();
4234 QMutexLocker lock(&mutex);
4235 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4237 if (it == cache.constEnd())
4238 return addCacheElement(hash_val, gradient, opacity);
4241 const CacheInfo &cache_info = it.value();
4242 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4243 return cache_info.buffer;
4245 } while (it != cache.constEnd() && it.key() == hash_val);
4246 // an exact match for these stops and opacity was not found, create new cache
4247 return addCacheElement(hash_val, gradient, opacity);
4251 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4253 inline int maxCacheSize() const { return 60; }
4254 inline void generateGradientColorTable(const QGradient& g,
4256 int size, int opacity) const;
4257 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4258 if (cache.size() == maxCacheSize()) {
4259 // may remove more than 1, but OK
4260 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4262 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4263 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4264 return cache.insert(hash_val, cache_entry).value().buffer;
4267 QGradientColorTableHash cache;
4271 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4273 QGradientStops stops = gradient.stops();
4274 int stopCount = stops.count();
4275 Q_ASSERT(stopCount > 0);
4277 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4279 if (stopCount == 2) {
4280 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4281 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4283 qreal first_stop = stops[0].first;
4284 qreal second_stop = stops[1].first;
4286 if (second_stop < first_stop) {
4287 qSwap(first_color, second_color);
4288 qSwap(first_stop, second_stop);
4291 if (colorInterpolation) {
4292 first_color = PREMUL(first_color);
4293 second_color = PREMUL(second_color);
4296 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4297 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4299 uint red_first = qRed(first_color) << 16;
4300 uint green_first = qGreen(first_color) << 16;
4301 uint blue_first = qBlue(first_color) << 16;
4302 uint alpha_first = qAlpha(first_color) << 16;
4304 uint red_second = qRed(second_color) << 16;
4305 uint green_second = qGreen(second_color) << 16;
4306 uint blue_second = qBlue(second_color) << 16;
4307 uint alpha_second = qAlpha(second_color) << 16;
4310 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4311 if (colorInterpolation)
4312 colorTable[i] = first_color;
4314 colorTable[i] = PREMUL(first_color);
4317 if (i < second_index) {
4318 qreal reciprocal = qreal(1) / (second_index - first_index);
4320 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4321 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4322 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4323 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4326 red_first += 1 << 15;
4327 green_first += 1 << 15;
4328 blue_first += 1 << 15;
4329 alpha_first += 1 << 15;
4331 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4332 red_first += red_delta;
4333 green_first += green_delta;
4334 blue_first += blue_delta;
4335 alpha_first += alpha_delta;
4337 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4338 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4340 if (colorInterpolation)
4341 colorTable[i] = color;
4343 colorTable[i] = PREMUL(color);
4347 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4348 if (colorInterpolation)
4349 colorTable[i] = second_color;
4351 colorTable[i] = PREMUL(second_color);
4357 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4358 if (stopCount == 1) {
4359 current_color = PREMUL(current_color);
4360 for (int i = 0; i < size; ++i)
4361 colorTable[i] = current_color;
4365 // The position where the gradient begins and ends
4366 qreal begin_pos = stops[0].first;
4367 qreal end_pos = stops[stopCount-1].first;
4369 int pos = 0; // The position in the color table.
4372 qreal incr = 1 / qreal(size); // the double increment.
4373 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4375 // Up to first point
4376 colorTable[pos++] = PREMUL(current_color);
4377 while (dpos <= begin_pos) {
4378 colorTable[pos] = colorTable[pos - 1];
4383 int current_stop = 0; // We always interpolate between current and current + 1.
4385 qreal t; // position between current left and right stops
4386 qreal t_delta; // the t increment per entry in the color table
4388 if (dpos < end_pos) {
4390 while (dpos > stops[current_stop+1].first)
4393 if (current_stop != 0)
4394 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4395 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4397 if (colorInterpolation) {
4398 current_color = PREMUL(current_color);
4399 next_color = PREMUL(next_color);
4402 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4403 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4404 t = (dpos - stops[current_stop].first) * c;
4408 Q_ASSERT(current_stop < stopCount);
4410 int dist = qRound(t);
4411 int idist = 256 - dist;
4413 if (colorInterpolation)
4414 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4416 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4421 if (dpos >= end_pos)
4427 while (dpos > stops[current_stop+skip+1].first)
4431 current_stop += skip;
4433 current_color = next_color;
4435 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4436 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4438 if (colorInterpolation) {
4440 current_color = PREMUL(current_color);
4441 next_color = PREMUL(next_color);
4444 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4445 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4446 t = (dpos - stops[current_stop].first) * c;
4453 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4454 while (pos < size - 1) {
4455 colorTable[pos] = current_color;
4459 // Make sure the last color stop is represented at the end of the table
4460 colorTable[size - 1] = current_color;
4463 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4466 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4472 m11 = m22 = m33 = 1.;
4473 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4474 clip = pe ? pe->d_func()->clip() : 0;
4477 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4479 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4481 Qt::BrushStyle brushStyle = qbrush_style(brush);
4482 switch (brushStyle) {
4483 case Qt::SolidPattern: {
4485 QColor c = qbrush_color(brush);
4486 QRgb rgba = c.rgba();
4487 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4488 if ((solid.color & 0xff000000) == 0
4489 && compositionMode == QPainter::CompositionMode_SourceOver) {
4495 case Qt::LinearGradientPattern:
4497 type = LinearGradient;
4498 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4499 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4500 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4501 gradient.spread = g->spread();
4503 QLinearGradientData &linearData = gradient.linear;
4505 linearData.origin.x = g->start().x();
4506 linearData.origin.y = g->start().y();
4507 linearData.end.x = g->finalStop().x();
4508 linearData.end.y = g->finalStop().y();
4512 case Qt::RadialGradientPattern:
4514 type = RadialGradient;
4515 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4516 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4517 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4518 gradient.spread = g->spread();
4520 QRadialGradientData &radialData = gradient.radial;
4522 QPointF center = g->center();
4523 radialData.center.x = center.x();
4524 radialData.center.y = center.y();
4525 radialData.center.radius = g->centerRadius();
4526 QPointF focal = g->focalPoint();
4527 radialData.focal.x = focal.x();
4528 radialData.focal.y = focal.y();
4529 radialData.focal.radius = g->focalRadius();
4533 case Qt::ConicalGradientPattern:
4535 type = ConicalGradient;
4536 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4537 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4538 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4539 gradient.spread = QGradient::RepeatSpread;
4541 QConicalGradientData &conicalData = gradient.conical;
4543 QPointF center = g->center();
4544 conicalData.center.x = center.x();
4545 conicalData.center.y = center.y();
4546 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4550 case Qt::Dense1Pattern:
4551 case Qt::Dense2Pattern:
4552 case Qt::Dense3Pattern:
4553 case Qt::Dense4Pattern:
4554 case Qt::Dense5Pattern:
4555 case Qt::Dense6Pattern:
4556 case Qt::Dense7Pattern:
4557 case Qt::HorPattern:
4558 case Qt::VerPattern:
4559 case Qt::CrossPattern:
4560 case Qt::BDiagPattern:
4561 case Qt::FDiagPattern:
4562 case Qt::DiagCrossPattern:
4565 tempImage = new QImage();
4566 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4567 initTexture(tempImage, alpha, QTextureData::Tiled);
4569 case Qt::TexturePattern:
4572 tempImage = new QImage();
4574 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4575 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4577 *tempImage = brush.textureImage();
4578 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4586 adjustSpanMethods();
4589 void QSpanData::adjustSpanMethods()
4599 unclipped_blend = 0;
4602 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4603 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4604 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4605 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4606 fillRect = rasterBuffer->drawHelper->fillRect;
4608 case LinearGradient:
4609 case RadialGradient:
4610 case ConicalGradient:
4611 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4614 unclipped_blend = qBlendTexture;
4615 if (!texture.imageData)
4616 unclipped_blend = 0;
4621 if (!unclipped_blend) {
4624 blend = unclipped_blend;
4625 } else if (clip->hasRectClip) {
4626 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4628 blend = qt_span_fill_clipped;
4632 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4635 // make sure we round off correctly in qdrawhelper.cpp
4636 delta.translate(1.0 / 65536, 1.0 / 65536);
4638 QTransform inv = (delta * matrix).inverted();
4651 const bool affine = !m13 && !m23;
4652 fast_matrix = affine
4653 && m11 * m11 + m21 * m21 < 1e4
4654 && m12 * m12 + m22 * m22 < 1e4
4658 adjustSpanMethods();
4661 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4663 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4665 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4666 if (!d || d->height == 0) {
4667 texture.imageData = 0;
4674 texture.bytesPerLine = 0;
4675 texture.format = QImage::Format_Invalid;
4676 texture.colorTable = 0;
4677 texture.hasAlpha = alpha != 256;
4679 texture.imageData = d->data;
4680 texture.width = d->width;
4681 texture.height = d->height;
4683 if (sourceRect.isNull()) {
4686 texture.x2 = texture.width;
4687 texture.y2 = texture.height;
4689 texture.x1 = sourceRect.x();
4690 texture.y1 = sourceRect.y();
4691 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4692 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4695 texture.bytesPerLine = d->bytes_per_line;
4697 texture.format = d->format;
4698 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4699 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4701 texture.const_alpha = alpha;
4702 texture.type = _type;
4704 adjustSpanMethods();
4709 \a x and \a y is relative to the midpoint of \a rect.
4711 static inline void drawEllipsePoints(int x, int y, int length,
4714 ProcessSpans pen_func, ProcessSpans brush_func,
4715 QSpanData *pen_data, QSpanData *brush_data)
4720 QT_FT_Span outline[4];
4721 const int midx = rect.x() + (rect.width() + 1) / 2;
4722 const int midy = rect.y() + (rect.height() + 1) / 2;
4728 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4729 outline[0].len = qMin(length, x - outline[0].x);
4731 outline[0].coverage = 255;
4735 outline[1].len = length;
4737 outline[1].coverage = 255;
4740 outline[2].x = outline[0].x;
4741 outline[2].len = outline[0].len;
4742 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4743 outline[2].coverage = 255;
4747 outline[3].len = length;
4748 outline[3].y = outline[2].y;
4749 outline[3].coverage = 255;
4751 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4755 fill[0].x = outline[0].x + outline[0].len - 1;
4756 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4757 fill[0].y = outline[1].y;
4758 fill[0].coverage = 255;
4761 fill[1].x = outline[2].x + outline[2].len - 1;
4762 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4763 fill[1].y = outline[3].y;
4764 fill[1].coverage = 255;
4766 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4767 n = qt_intersect_spans(fill, n, clip);
4769 brush_func(n, fill, brush_data);
4772 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4773 n = qt_intersect_spans(outline, n, clip);
4775 pen_func(n, outline, pen_data);
4781 Draws an ellipse using the integer point midpoint algorithm.
4783 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4784 ProcessSpans pen_func, ProcessSpans brush_func,
4785 QSpanData *pen_data, QSpanData *brush_data)
4787 const qreal a = qreal(rect.width()) / 2;
4788 const qreal b = qreal(rect.height()) / 2;
4789 qreal d = b*b - (a*a*b) + 0.25*a*a;
4792 int y = (rect.height() + 1) / 2;
4796 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4797 if (d < 0) { // select E
4800 } else { // select SE
4801 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4802 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4803 pen_func, brush_func, pen_data, brush_data);
4808 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4809 pen_func, brush_func, pen_data, brush_data);
4812 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4813 const int miny = rect.height() & 0x1;
4815 if (d < 0) { // select SE
4816 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4818 } else { // select S
4819 d += a*a*(-2*y + 3);
4822 drawEllipsePoints(x, y, 1, rect, clip,
4823 pen_func, brush_func, pen_data, brush_data);
4828 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4831 Draws the first \a pointCount points in the buffer \a points
4833 The default implementation converts the first \a pointCount QPoints in \a points
4834 to QPointFs and calls the floating point version of drawPoints.
4838 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4841 Reimplement this function to draw the largest ellipse that can be
4842 contained within rectangle \a rect.
4845 #ifdef QT_DEBUG_DRAW
4846 void dumpClip(int width, int height, const QClipData *clip)
4848 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4849 clipImg.fill(0xffff0000);
4856 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4858 for (int i = 0; i < clip->count; ++i) {
4859 const QSpan *span = ((QClipData *) clip)->spans() + i;
4860 for (int j = 0; j < span->len; ++j)
4861 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4862 x0 = qMin(x0, int(span->x));
4863 x1 = qMax(x1, int(span->x + span->len - 1));
4865 y0 = qMin(y0, int(span->y));
4866 y1 = qMax(y1, int(span->y));
4869 static int counter = 0;
4876 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4877 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));