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 (!supportsTransformations(fontEngine)) {
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 if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64)
3367 QPoint QRasterPaintEngine::coordinateOffset() const
3369 return QPoint(0, 0);
3372 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3377 Q_D(QRasterPaintEngine);
3379 Q_ASSERT(image.depth() == 1);
3381 const int spanCount = 256;
3382 QT_FT_Span spans[spanCount];
3386 int w = image.width();
3387 int h = image.height();
3388 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3389 int ymin = qMax(qRound(pos.y()), 0);
3390 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3391 int xmin = qMax(qRound(pos.x()), 0);
3393 int x_offset = xmin - qRound(pos.x());
3395 QImage::Format format = image.format();
3396 for (int y = ymin; y < ymax; ++y) {
3397 const uchar *src = image.scanLine(y - qRound(pos.y()));
3398 if (format == QImage::Format_MonoLSB) {
3399 for (int x = 0; x < xmax - xmin; ++x) {
3400 int src_x = x + x_offset;
3401 uchar pixel = src[src_x >> 3];
3406 if (pixel & (0x1 << (src_x & 7))) {
3407 spans[n].x = xmin + x;
3409 spans[n].coverage = 255;
3411 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3415 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3418 if (n == spanCount) {
3419 fg->blend(n, spans, fg);
3425 for (int x = 0; x < xmax - xmin; ++x) {
3426 int src_x = x + x_offset;
3427 uchar pixel = src[src_x >> 3];
3432 if (pixel & (0x80 >> (x & 7))) {
3433 spans[n].x = xmin + x;
3435 spans[n].coverage = 255;
3437 while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3441 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3444 if (n == spanCount) {
3445 fg->blend(n, spans, fg);
3453 fg->blend(n, spans, fg);
3459 \enum QRasterPaintEngine::ClipType
3462 \value RectClip Indicates that the currently set clip is a single rectangle.
3463 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3468 Returns the type of the clip currently set.
3470 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3472 Q_D(const QRasterPaintEngine);
3474 const QClipData *clip = d->clip();
3475 if (!clip || clip->hasRectClip)
3483 Returns the bounding rect of the currently set clip.
3485 QRect QRasterPaintEngine::clipBoundingRect() const
3487 Q_D(const QRasterPaintEngine);
3489 const QClipData *clip = d->clip();
3492 return d->deviceRect;
3494 if (clip->hasRectClip)
3495 return clip->clipRect;
3497 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3500 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3502 Q_Q(QRasterPaintEngine);
3503 QRasterPaintEngineState *s = q->state();
3505 rasterizer->setAntialiased(s->flags.antialiased);
3507 QRect clipRect(deviceRect);
3509 // ### get from optimized rectbased QClipData
3511 const QClipData *c = clip();
3513 const QRect r(QPoint(c->xmin, c->ymin),
3514 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3515 clipRect = clipRect.intersected(r);
3516 blend = data->blend;
3518 blend = data->unclipped_blend;
3521 rasterizer->setClipRect(clipRect);
3522 rasterizer->initialize(blend, data);
3525 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3526 ProcessSpans callback,
3527 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3529 if (!callback || !outline)
3532 Q_Q(QRasterPaintEngine);
3533 QRasterPaintEngineState *s = q->state();
3535 if (!s->flags.antialiased) {
3536 initializeRasterizer(spanData);
3538 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3542 rasterizer->rasterize(outline, fillRule);
3546 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3550 int q_gray_rendered_spans(QT_FT_Raster raster);
3553 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3554 ProcessSpans callback,
3555 void *userData, QRasterBuffer *)
3557 if (!callback || !outline)
3560 Q_Q(QRasterPaintEngine);
3561 QRasterPaintEngineState *s = q->state();
3563 if (!s->flags.antialiased) {
3564 rasterizer->setAntialiased(s->flags.antialiased);
3565 rasterizer->setClipRect(deviceRect);
3566 rasterizer->initialize(callback, userData);
3568 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3572 rasterizer->rasterize(outline, fillRule);
3576 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3577 // minimize memory reallocations. However if initial size for
3578 // raster pool is changed for lower value, reallocations will
3580 const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
3581 int rasterPoolSize = rasterPoolInitialSize;
3582 unsigned char *rasterPoolBase;
3583 #if defined(Q_OS_WIN64)
3585 // We make use of setjmp and longjmp in qgrayraster.c which requires
3586 // 16-byte alignment, hence we hardcode this requirement here..
3587 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3589 unsigned char rasterPoolOnStack[rasterPoolInitialSize];
3590 rasterPoolBase = rasterPoolOnStack;
3592 Q_CHECK_PTR(rasterPoolBase);
3594 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3596 void *data = userData;
3598 QT_FT_BBox clip_box = { deviceRect.x(),
3600 deviceRect.x() + deviceRect.width(),
3601 deviceRect.y() + deviceRect.height() };
3603 QT_FT_Raster_Params rasterParams;
3604 rasterParams.target = 0;
3605 rasterParams.source = outline;
3606 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3607 rasterParams.gray_spans = 0;
3608 rasterParams.black_spans = 0;
3609 rasterParams.bit_test = 0;
3610 rasterParams.bit_set = 0;
3611 rasterParams.user = data;
3612 rasterParams.clip_box = clip_box;
3617 int rendered_spans = 0;
3621 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3622 rasterParams.gray_spans = callback;
3623 rasterParams.skip_spans = rendered_spans;
3624 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3626 // Out of memory, reallocate some more and try again...
3627 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3628 int new_size = rasterPoolSize * 2;
3629 if (new_size > 1024 * 1024) {
3630 qWarning("QPainter: Rasterization of primitive failed");
3634 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3636 #if defined(Q_OS_WIN64)
3637 _aligned_free(rasterPoolBase);
3639 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3640 free(rasterPoolBase);
3643 rasterPoolSize = new_size;
3645 #if defined(Q_OS_WIN64)
3646 // We make use of setjmp and longjmp in qgrayraster.c which requires
3647 // 16-byte alignment, hence we hardcode this requirement here..
3648 (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
3650 (unsigned char *) malloc(rasterPoolSize);
3652 Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3654 qt_ft_grays_raster.raster_done(*grayRaster.data());
3655 qt_ft_grays_raster.raster_new(grayRaster.data());
3656 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3662 #if defined(Q_OS_WIN64)
3663 _aligned_free(rasterPoolBase);
3665 if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
3666 free(rasterPoolBase);
3670 void QRasterPaintEnginePrivate::recalculateFastImages()
3672 Q_Q(QRasterPaintEngine);
3673 QRasterPaintEngineState *s = q->state();
3675 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3676 && s->matrix.type() <= QTransform::TxShear;
3679 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3681 Q_Q(const QRasterPaintEngine);
3682 const QRasterPaintEngineState *s = q->state();
3684 return s->flags.fast_images
3685 && (mode == QPainter::CompositionMode_SourceOver
3686 || (mode == QPainter::CompositionMode_Source
3687 && !image.hasAlphaChannel()));
3690 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3692 Q_ASSERT(image.depth() == 1);
3694 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3695 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3697 QRgb fg = PREMUL(color.rgba());
3700 int height = sourceImage.height();
3701 int width = sourceImage.width();
3702 for (int y=0; y<height; ++y) {
3703 uchar *source = sourceImage.scanLine(y);
3704 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3705 if (!source || !target)
3706 QT_THROW(std::bad_alloc()); // we must have run out of memory
3707 for (int x=0; x < width; ++x)
3708 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3713 QRasterBuffer::~QRasterBuffer()
3717 void QRasterBuffer::init()
3719 compositionMode = QPainter::CompositionMode_SourceOver;
3720 monoDestinationWithClut = false;
3725 QImage::Format QRasterBuffer::prepare(QImage *image)
3727 m_buffer = (uchar *)image->bits();
3728 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3729 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3730 bytes_per_pixel = image->depth()/8;
3731 bytes_per_line = image->bytesPerLine();
3733 format = image->format();
3734 drawHelper = qDrawHelper + format;
3735 if (image->depth() == 1 && image->colorTable().size() == 2) {
3736 monoDestinationWithClut = true;
3737 destColor0 = PREMUL(image->colorTable()[0]);
3738 destColor1 = PREMUL(image->colorTable()[1]);
3744 void QRasterBuffer::resetBuffer(int val)
3746 memset(m_buffer, val, m_height*bytes_per_line);
3749 QClipData::QClipData(int height)
3751 clipSpanHeight = height;
3756 xmin = xmax = ymin = ymax = 0;
3760 hasRectClip = hasRegionClip = false;
3763 QClipData::~QClipData()
3771 void QClipData::initialize()
3777 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3779 Q_CHECK_PTR(m_clipLines);
3781 m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
3782 allocated = clipSpanHeight;
3783 Q_CHECK_PTR(m_spans);
3789 m_clipLines[y].spans = 0;
3790 m_clipLines[y].count = 0;
3794 const int len = clipRect.width();
3797 QSpan *span = m_spans + count;
3801 span->coverage = 255;
3804 m_clipLines[y].spans = span;
3805 m_clipLines[y].count = 1;
3809 while (y < clipSpanHeight) {
3810 m_clipLines[y].spans = 0;
3811 m_clipLines[y].count = 0;
3814 } else if (hasRegionClip) {
3816 const QVector<QRect> rects = clipRegion.rects();
3817 const int numRects = rects.size();
3820 const int maxSpans = (ymax - ymin) * numRects;
3821 if (maxSpans > allocated) {
3822 m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
3823 allocated = maxSpans;
3828 int firstInBand = 0;
3830 while (firstInBand < numRects) {
3831 const int currMinY = rects.at(firstInBand).y();
3832 const int currMaxY = currMinY + rects.at(firstInBand).height();
3834 while (y < currMinY) {
3835 m_clipLines[y].spans = 0;
3836 m_clipLines[y].count = 0;
3840 int lastInBand = firstInBand;
3841 while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
3844 while (y < currMaxY) {
3846 m_clipLines[y].spans = m_spans + count;
3847 m_clipLines[y].count = lastInBand - firstInBand + 1;
3849 for (int r = firstInBand; r <= lastInBand; ++r) {
3850 const QRect &currRect = rects.at(r);
3851 QSpan *span = m_spans + count;
3852 span->x = currRect.x();
3853 span->len = currRect.width();
3855 span->coverage = 255;
3861 firstInBand = lastInBand + 1;
3864 Q_ASSERT(count <= allocated);
3866 while (y < clipSpanHeight) {
3867 m_clipLines[y].spans = 0;
3868 m_clipLines[y].count = 0;
3874 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3879 free(m_clipLines); // same for clipLines
3885 void QClipData::fixup()
3890 ymin = ymax = xmin = xmax = 0;
3895 ymin = m_spans[0].y;
3896 ymax = m_spans[count-1].y + 1;
3900 const int firstLeft = m_spans[0].x;
3901 const int firstRight = m_spans[0].x + m_spans[0].len;
3904 for (int i = 0; i < count; ++i) {
3905 QT_FT_Span_& span = m_spans[i];
3908 if (span.y != y + 1 && y != -1)
3911 m_clipLines[y].spans = &span;
3912 m_clipLines[y].count = 1;
3914 ++m_clipLines[y].count;
3916 const int spanLeft = span.x;
3917 const int spanRight = spanLeft + span.len;
3919 if (spanLeft < xmin)
3922 if (spanRight > xmax)
3925 if (spanLeft != firstLeft || spanRight != firstRight)
3931 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3936 Convert \a rect to clip spans.
3938 void QClipData::setClipRect(const QRect &rect)
3940 if (hasRectClip && rect == clipRect)
3943 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3945 hasRegionClip = false;
3949 xmax = rect.x() + rect.width();
3950 ymin = qMin(rect.y(), clipSpanHeight);
3951 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3958 // qDebug() << xmin << xmax << ymin << ymax;
3962 Convert \a region to clip spans.
3964 void QClipData::setClipRegion(const QRegion ®ion)
3966 if (region.rectCount() == 1) {
3967 setClipRect(region.rects().at(0));
3971 hasRegionClip = true;
3972 hasRectClip = false;
3973 clipRegion = region;
3975 { // set bounding rect
3976 const QRect rect = region.boundingRect();
3978 xmax = rect.x() + rect.width();
3980 ymax = rect.y() + rect.height();
3992 spans must be sorted on y
3994 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
3995 const QSpan *spans, const QSpan *end,
3996 QSpan **outSpans, int available)
3998 const_cast<QClipData *>(clip)->initialize();
4000 QSpan *out = *outSpans;
4002 const QSpan *clipSpans = clip->m_spans + *currentClip;
4003 const QSpan *clipEnd = clip->m_spans + clip->count;
4005 while (available && spans < end ) {
4006 if (clipSpans >= clipEnd) {
4010 if (clipSpans->y > spans->y) {
4014 if (spans->y != clipSpans->y) {
4015 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4016 clipSpans = clip->m_clipLines[spans->y].spans;
4021 Q_ASSERT(spans->y == clipSpans->y);
4024 int sx2 = sx1 + spans->len;
4025 int cx1 = clipSpans->x;
4026 int cx2 = cx1 + clipSpans->len;
4028 if (cx1 < sx1 && cx2 < sx1) {
4031 } else if (sx1 < cx1 && sx2 < cx1) {
4035 int x = qMax(sx1, cx1);
4036 int len = qMin(sx2, cx2) - x;
4038 out->x = qMax(sx1, cx1);
4039 out->len = qMin(sx2, cx2) - out->x;
4041 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4053 *currentClip = clipSpans - clip->m_spans;
4057 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4059 // qDebug() << "qt_span_fill_clipped" << spanCount;
4060 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4062 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4064 const int NSPANS = 256;
4065 QSpan cspans[NSPANS];
4066 int currentClip = 0;
4067 const QSpan *end = spans + spanCount;
4068 while (spans < end) {
4069 QSpan *clipped = cspans;
4070 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4071 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4072 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4074 if (clipped - cspans)
4075 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4081 Clip spans to \a{clip}-rectangle.
4082 Returns number of unclipped spans
4084 static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
4087 const short minx = clip.left();
4088 const short miny = clip.top();
4089 const short maxx = clip.right();
4090 const short maxy = clip.bottom();
4093 for (int i = 0; i < numSpans; ++i) {
4094 if (spans[i].y > maxy)
4096 if (spans[i].y < miny
4097 || spans[i].x > maxx
4098 || spans[i].x + spans[i].len <= minx) {
4101 if (spans[i].x < minx) {
4102 spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
4105 spans[n].x = spans[i].x;
4106 spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
4108 if (spans[n].len == 0)
4110 spans[n].y = spans[i].y;
4111 spans[n].coverage = spans[i].coverage;
4118 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4121 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4122 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4124 Q_ASSERT(fillData->clip);
4125 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4127 // hw: check if this const_cast<> is safe!!!
4128 count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
4129 fillData->clip->clipRect);
4131 fillData->unclipped_blend(count, spans, fillData);
4134 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4136 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4138 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4139 // for (int i = 0; i < qMin(count, 10); ++i) {
4140 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4143 switch (clipData->operation) {
4145 case Qt::IntersectClip:
4147 QClipData *newClip = clipData->newClip;
4148 newClip->initialize();
4150 int currentClip = 0;
4151 const QSpan *end = spans + count;
4152 while (spans < end) {
4153 QSpan *newspans = newClip->m_spans + newClip->count;
4154 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4155 &newspans, newClip->allocated - newClip->count);
4156 newClip->count = newspans - newClip->m_spans;
4158 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4159 newClip->allocated *= 2;
4165 case Qt::ReplaceClip:
4166 clipData->newClip->appendSpans(spans, count);
4174 QImage QRasterBuffer::bufferImage() const
4176 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
4178 for (int y = 0; y < m_height; ++y) {
4179 uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4181 for (int x=0; x<m_width; ++x) {
4182 uint argb = span[x];
4183 image.setPixel(x, y, argb);
4191 void QRasterBuffer::flushToARGBImage(QImage *target) const
4193 int w = qMin(m_width, target->width());
4194 int h = qMin(m_height, target->height());
4196 for (int y=0; y<h; ++y) {
4197 uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
4198 QRgb *dest = (QRgb *) target->scanLine(y);
4199 for (int x=0; x<w; ++x) {
4200 QRgb pixel = sourceLine[x];
4201 int alpha = qAlpha(pixel);
4205 dest[x] = (alpha << 24)
4206 | ((255*qRed(pixel)/alpha) << 16)
4207 | ((255*qGreen(pixel)/alpha) << 8)
4208 | ((255*qBlue(pixel)/alpha) << 0);
4215 class QGradientCache
4219 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4220 stops(s), opacity(op), interpolationMode(mode) {}
4221 uint buffer[GRADIENT_STOPTABLE_SIZE];
4222 QGradientStops stops;
4224 QGradient::InterpolationMode interpolationMode;
4227 typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
4230 inline const uint *getBuffer(const QGradient &gradient, int opacity) {
4231 quint64 hash_val = 0;
4233 QGradientStops stops = gradient.stops();
4234 for (int i = 0; i < stops.size() && i <= 2; i++)
4235 hash_val += stops[i].second.rgba();
4237 QMutexLocker lock(&mutex);
4238 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4240 if (it == cache.constEnd())
4241 return addCacheElement(hash_val, gradient, opacity);
4244 const CacheInfo &cache_info = it.value();
4245 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
4246 return cache_info.buffer;
4248 } while (it != cache.constEnd() && it.key() == hash_val);
4249 // an exact match for these stops and opacity was not found, create new cache
4250 return addCacheElement(hash_val, gradient, opacity);
4254 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4256 inline int maxCacheSize() const { return 60; }
4257 inline void generateGradientColorTable(const QGradient& g,
4259 int size, int opacity) const;
4260 uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4261 if (cache.size() == maxCacheSize()) {
4262 // may remove more than 1, but OK
4263 cache.erase(cache.begin() + (qrand() % maxCacheSize()));
4265 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
4266 generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
4267 return cache.insert(hash_val, cache_entry).value().buffer;
4270 QGradientColorTableHash cache;
4274 void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
4276 QGradientStops stops = gradient.stops();
4277 int stopCount = stops.count();
4278 Q_ASSERT(stopCount > 0);
4280 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4282 if (stopCount == 2) {
4283 uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4284 uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
4286 qreal first_stop = stops[0].first;
4287 qreal second_stop = stops[1].first;
4289 if (second_stop < first_stop) {
4290 qSwap(first_color, second_color);
4291 qSwap(first_stop, second_stop);
4294 if (colorInterpolation) {
4295 first_color = PREMUL(first_color);
4296 second_color = PREMUL(second_color);
4299 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4300 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4302 uint red_first = qRed(first_color) << 16;
4303 uint green_first = qGreen(first_color) << 16;
4304 uint blue_first = qBlue(first_color) << 16;
4305 uint alpha_first = qAlpha(first_color) << 16;
4307 uint red_second = qRed(second_color) << 16;
4308 uint green_second = qGreen(second_color) << 16;
4309 uint blue_second = qBlue(second_color) << 16;
4310 uint alpha_second = qAlpha(second_color) << 16;
4313 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4314 if (colorInterpolation)
4315 colorTable[i] = first_color;
4317 colorTable[i] = PREMUL(first_color);
4320 if (i < second_index) {
4321 qreal reciprocal = qreal(1) / (second_index - first_index);
4323 int red_delta = qRound(int(red_second - red_first) * reciprocal);
4324 int green_delta = qRound(int(green_second - green_first) * reciprocal);
4325 int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
4326 int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
4329 red_first += 1 << 15;
4330 green_first += 1 << 15;
4331 blue_first += 1 << 15;
4332 alpha_first += 1 << 15;
4334 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4335 red_first += red_delta;
4336 green_first += green_delta;
4337 blue_first += blue_delta;
4338 alpha_first += alpha_delta;
4340 const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
4341 | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
4343 if (colorInterpolation)
4344 colorTable[i] = color;
4346 colorTable[i] = PREMUL(color);
4350 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4351 if (colorInterpolation)
4352 colorTable[i] = second_color;
4354 colorTable[i] = PREMUL(second_color);
4360 uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
4361 if (stopCount == 1) {
4362 current_color = PREMUL(current_color);
4363 for (int i = 0; i < size; ++i)
4364 colorTable[i] = current_color;
4368 // The position where the gradient begins and ends
4369 qreal begin_pos = stops[0].first;
4370 qreal end_pos = stops[stopCount-1].first;
4372 int pos = 0; // The position in the color table.
4375 qreal incr = 1 / qreal(size); // the double increment.
4376 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4378 // Up to first point
4379 colorTable[pos++] = PREMUL(current_color);
4380 while (dpos <= begin_pos) {
4381 colorTable[pos] = colorTable[pos - 1];
4386 int current_stop = 0; // We always interpolate between current and current + 1.
4388 qreal t; // position between current left and right stops
4389 qreal t_delta; // the t increment per entry in the color table
4391 if (dpos < end_pos) {
4393 while (dpos > stops[current_stop+1].first)
4396 if (current_stop != 0)
4397 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4398 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4400 if (colorInterpolation) {
4401 current_color = PREMUL(current_color);
4402 next_color = PREMUL(next_color);
4405 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4406 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4407 t = (dpos - stops[current_stop].first) * c;
4411 Q_ASSERT(current_stop < stopCount);
4413 int dist = qRound(t);
4414 int idist = 256 - dist;
4416 if (colorInterpolation)
4417 colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
4419 colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
4424 if (dpos >= end_pos)
4430 while (dpos > stops[current_stop+skip+1].first)
4434 current_stop += skip;
4436 current_color = next_color;
4438 current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
4439 next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
4441 if (colorInterpolation) {
4443 current_color = PREMUL(current_color);
4444 next_color = PREMUL(next_color);
4447 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4448 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4449 t = (dpos - stops[current_stop].first) * c;
4456 current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
4457 while (pos < size - 1) {
4458 colorTable[pos] = current_color;
4462 // Make sure the last color stop is represented at the end of the table
4463 colorTable[size - 1] = current_color;
4466 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4469 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4475 m11 = m22 = m33 = 1.;
4476 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4477 clip = pe ? pe->d_func()->clip() : 0;
4480 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4482 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4484 Qt::BrushStyle brushStyle = qbrush_style(brush);
4485 switch (brushStyle) {
4486 case Qt::SolidPattern: {
4488 QColor c = qbrush_color(brush);
4489 QRgb rgba = c.rgba();
4490 solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
4491 if ((solid.color & 0xff000000) == 0
4492 && compositionMode == QPainter::CompositionMode_SourceOver) {
4498 case Qt::LinearGradientPattern:
4500 type = LinearGradient;
4501 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4502 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4503 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4504 gradient.spread = g->spread();
4506 QLinearGradientData &linearData = gradient.linear;
4508 linearData.origin.x = g->start().x();
4509 linearData.origin.y = g->start().y();
4510 linearData.end.x = g->finalStop().x();
4511 linearData.end.y = g->finalStop().y();
4515 case Qt::RadialGradientPattern:
4517 type = RadialGradient;
4518 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4519 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4520 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4521 gradient.spread = g->spread();
4523 QRadialGradientData &radialData = gradient.radial;
4525 QPointF center = g->center();
4526 radialData.center.x = center.x();
4527 radialData.center.y = center.y();
4528 radialData.center.radius = g->centerRadius();
4529 QPointF focal = g->focalPoint();
4530 radialData.focal.x = focal.x();
4531 radialData.focal.y = focal.y();
4532 radialData.focal.radius = g->focalRadius();
4536 case Qt::ConicalGradientPattern:
4538 type = ConicalGradient;
4539 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4540 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4541 gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
4542 gradient.spread = QGradient::RepeatSpread;
4544 QConicalGradientData &conicalData = gradient.conical;
4546 QPointF center = g->center();
4547 conicalData.center.x = center.x();
4548 conicalData.center.y = center.y();
4549 conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
4553 case Qt::Dense1Pattern:
4554 case Qt::Dense2Pattern:
4555 case Qt::Dense3Pattern:
4556 case Qt::Dense4Pattern:
4557 case Qt::Dense5Pattern:
4558 case Qt::Dense6Pattern:
4559 case Qt::Dense7Pattern:
4560 case Qt::HorPattern:
4561 case Qt::VerPattern:
4562 case Qt::CrossPattern:
4563 case Qt::BDiagPattern:
4564 case Qt::FDiagPattern:
4565 case Qt::DiagCrossPattern:
4568 tempImage = new QImage();
4569 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4570 initTexture(tempImage, alpha, QTextureData::Tiled);
4572 case Qt::TexturePattern:
4575 tempImage = new QImage();
4577 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4578 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4580 *tempImage = brush.textureImage();
4581 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4589 adjustSpanMethods();
4592 void QSpanData::adjustSpanMethods()
4602 unclipped_blend = 0;
4605 unclipped_blend = rasterBuffer->drawHelper->blendColor;
4606 bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
4607 alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
4608 alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
4609 fillRect = rasterBuffer->drawHelper->fillRect;
4611 case LinearGradient:
4612 case RadialGradient:
4613 case ConicalGradient:
4614 unclipped_blend = rasterBuffer->drawHelper->blendGradient;
4617 unclipped_blend = qBlendTexture;
4618 if (!texture.imageData)
4619 unclipped_blend = 0;
4624 if (!unclipped_blend) {
4627 blend = unclipped_blend;
4628 } else if (clip->hasRectClip) {
4629 blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
4631 blend = qt_span_fill_clipped;
4635 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4638 // make sure we round off correctly in qdrawhelper.cpp
4639 delta.translate(1.0 / 65536, 1.0 / 65536);
4641 QTransform inv = (delta * matrix).inverted();
4654 const bool affine = !m13 && !m23;
4655 fast_matrix = affine
4656 && m11 * m11 + m21 * m21 < 1e4
4657 && m12 * m12 + m22 * m22 < 1e4
4661 adjustSpanMethods();
4664 extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
4666 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4668 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4669 if (!d || d->height == 0) {
4670 texture.imageData = 0;
4677 texture.bytesPerLine = 0;
4678 texture.format = QImage::Format_Invalid;
4679 texture.colorTable = 0;
4680 texture.hasAlpha = alpha != 256;
4682 texture.imageData = d->data;
4683 texture.width = d->width;
4684 texture.height = d->height;
4686 if (sourceRect.isNull()) {
4689 texture.x2 = texture.width;
4690 texture.y2 = texture.height;
4692 texture.x1 = sourceRect.x();
4693 texture.y1 = sourceRect.y();
4694 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4695 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4698 texture.bytesPerLine = d->bytes_per_line;
4700 texture.format = d->format;
4701 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
4702 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4704 texture.const_alpha = alpha;
4705 texture.type = _type;
4707 adjustSpanMethods();
4712 \a x and \a y is relative to the midpoint of \a rect.
4714 static inline void drawEllipsePoints(int x, int y, int length,
4717 ProcessSpans pen_func, ProcessSpans brush_func,
4718 QSpanData *pen_data, QSpanData *brush_data)
4723 QT_FT_Span outline[4];
4724 const int midx = rect.x() + (rect.width() + 1) / 2;
4725 const int midy = rect.y() + (rect.height() + 1) / 2;
4731 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4732 outline[0].len = qMin(length, x - outline[0].x);
4734 outline[0].coverage = 255;
4738 outline[1].len = length;
4740 outline[1].coverage = 255;
4743 outline[2].x = outline[0].x;
4744 outline[2].len = outline[0].len;
4745 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4746 outline[2].coverage = 255;
4750 outline[3].len = length;
4751 outline[3].y = outline[2].y;
4752 outline[3].coverage = 255;
4754 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4758 fill[0].x = outline[0].x + outline[0].len - 1;
4759 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4760 fill[0].y = outline[1].y;
4761 fill[0].coverage = 255;
4764 fill[1].x = outline[2].x + outline[2].len - 1;
4765 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4766 fill[1].y = outline[3].y;
4767 fill[1].coverage = 255;
4769 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4770 n = qt_intersect_spans(fill, n, clip);
4772 brush_func(n, fill, brush_data);
4775 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4776 n = qt_intersect_spans(outline, n, clip);
4778 pen_func(n, outline, pen_data);
4784 Draws an ellipse using the integer point midpoint algorithm.
4786 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4787 ProcessSpans pen_func, ProcessSpans brush_func,
4788 QSpanData *pen_data, QSpanData *brush_data)
4790 const qreal a = qreal(rect.width()) / 2;
4791 const qreal b = qreal(rect.height()) / 2;
4792 qreal d = b*b - (a*a*b) + 0.25*a*a;
4795 int y = (rect.height() + 1) / 2;
4799 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4800 if (d < 0) { // select E
4803 } else { // select SE
4804 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4805 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4806 pen_func, brush_func, pen_data, brush_data);
4811 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4812 pen_func, brush_func, pen_data, brush_data);
4815 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4816 const int miny = rect.height() & 0x1;
4818 if (d < 0) { // select SE
4819 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4821 } else { // select S
4822 d += a*a*(-2*y + 3);
4825 drawEllipsePoints(x, y, 1, rect, clip,
4826 pen_func, brush_func, pen_data, brush_data);
4831 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4834 Draws the first \a pointCount points in the buffer \a points
4836 The default implementation converts the first \a pointCount QPoints in \a points
4837 to QPointFs and calls the floating point version of drawPoints.
4841 \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
4844 Reimplement this function to draw the largest ellipse that can be
4845 contained within rectangle \a rect.
4848 #ifdef QT_DEBUG_DRAW
4849 void dumpClip(int width, int height, const QClipData *clip)
4851 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4852 clipImg.fill(0xffff0000);
4859 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4861 for (int i = 0; i < clip->count; ++i) {
4862 const QSpan *span = ((QClipData *) clip)->spans() + i;
4863 for (int j = 0; j < span->len; ++j)
4864 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4865 x0 = qMin(x0, int(span->x));
4866 x1 = qMax(x1, int(span->x + span->len - 1));
4868 y0 = qMin(y0, int(span->y));
4869 y1 = qMax(y1, int(span->y));
4872 static int counter = 0;
4879 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4880 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));